Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@simplewebauthn/server": "^9.0.3",
"@supabase/supabase-js": "^2.49.4",
"argon2": "^0.41.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"connect-redis": "^8.0.1",
"express-session": "^1.18.1",
Expand Down
14 changes: 14 additions & 0 deletions src/auth/dto/challenge.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { Challenge } from '../entities/challenge.entity';
import { ApiProperty } from '@nestjs/swagger';

export class ChallengeDTO {
@ApiProperty({
description: 'The challenge string for WebAuthn registration',
example: 'dGhpcyBpcyBhIHNhbXBsZSBjaGFsbGVuZ2U=',
type: String,
required: true,
})
challenge: string;

@ApiProperty({
description: 'The expiration date of the challenge',
example: '2024-10-01T12:00:00Z',
type: Date,
required: true,
})
expiresAt: Date;

constructor(challenge: Challenge) {
Expand Down
28 changes: 28 additions & 0 deletions src/auth/dto/register-user.input.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsString, MinLength, IsNotEmpty } from 'class-validator';

export class RegisterUserDto {
@ApiProperty({
description: 'The username of the user',
example: 'john_doe',
})
@IsNotEmpty()
@IsString()
username: string;

@ApiProperty({
description: 'The email address of the user',
example: 'john.doe@email.com',
uniqueItems: true,
})
@IsEmail()
@IsNotEmpty()
@IsString()
email: string;

@ApiProperty({
description: 'The password for the user account',
example: 'S3cureP@ssw0rd!',
minLength: 8,
maxLength: 128,
})
@IsNotEmpty()
@IsString()
@MinLength(8)
password: string;
}
12 changes: 12 additions & 0 deletions src/group/dto/create-group.dto.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { IsNotEmpty, IsString, IsOptional, IsArray } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CreateGroupDto {
@ApiProperty({
description: 'The name of the group',
example: 'Fitness Enthusiasts',
maxLength: 100,
type: String,
})
@IsNotEmpty()
@IsString()
groupName: string;

@ApiProperty({
description: 'The ids of the members to be added to the group',
example: ['user123', 'user456'],
type: [String],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
Expand Down
36 changes: 36 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@ import * as process from 'node:process';
import Redis from 'ioredis';
import { RedisStore } from 'connect-redis';
import * as passport from 'passport';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

// Global validation pipe
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
transformOptions: {
enableImplicitConversion: true,
},
}),
);

app.use(
session({
secret: process.env.SESSION_SECRET as string,
Expand All @@ -26,6 +41,27 @@ async function bootstrap() {
app.use(passport.initialize());
app.use(passport.session());

// Swagger API documentation setup
if (process.env.NODE_ENV !== 'production') {
const config = new DocumentBuilder()
.setTitle('MotiMate API')
.setDescription('API documentation for the MotiMate backend')
.setVersion('1.0')
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
in: 'header',
description: 'Enter your JWT token here',
},
'access-token',
)
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
}

await app.listen(process.env.PORT ?? 3000);
}

Expand Down
34 changes: 32 additions & 2 deletions src/user/dto/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
import { IsEmail, IsNotEmpty, MinLength, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
@ApiProperty({
description: 'The name of the user',
example: 'John Doe',
required: true,
type: String,
minLength: 1,
maxLength: 255,
pattern: '^[a-zA-Z0-9 ]+$', // Allows alphanumeric characters and spaces
})
@IsNotEmpty()
name: string;

@ApiProperty({
description: 'The email of the user',
example: 'john.doe@email.com',
required: true,
type: String,
format: 'email',
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', // Basic email validation
})
@IsEmail()
@IsString()
@IsNotEmpty()
email: string;

@ApiProperty({
description: 'The password of the user',
example: 'S3cureP@ssw0rd!',
required: true,
type: String,
minLength: 8,
maxLength: 128,
})
@IsString()
@IsNotEmpty()
@MinLength(6) //Confirm the minlength we are using
@MinLength(8)
password: string;
}
32 changes: 30 additions & 2 deletions src/user/dto/update-user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
import { IsEmail, IsOptional, MinLength } from 'class-validator';
import { IsEmail, IsOptional, MinLength, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class UpdateUserDto {
@ApiProperty({
description: 'The name of the user',
example: 'John Doe',
required: false,
type: String,
minLength: 1,
maxLength: 255,
pattern: '^[a-zA-Z0-9 ]+$', // Allows alphanumeric characters and spaces
})
@IsString()
@IsOptional()
name?: string;

@ApiProperty({
description: 'The email of the user',
example: 'john.doe@email.com',
required: false,
type: String,
format: 'email',
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', // Basic email validation
})
@IsOptional()
@IsEmail()
email?: string;

@ApiProperty({
description: 'The password of the user',
example: 'S3cureP@ssw0rd!',
required: false,
type: String,
minLength: 8,
maxLength: 128,
})
@IsOptional()
@MinLength(6)
@MinLength(8)
password?: string;
}
4 changes: 3 additions & 1 deletion src/user/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { UserWeeklyTarget } from '../../user-weekly-target/entities/user-weekly-
import { Group } from '../../group/entities/group.entity';
import { PasskeyEntity } from '../../auth/entities/passkey.entity';
import { Challenge } from '../../auth/entities/challenge.entity';
import { Exclude } from 'class-transformer';

@Entity()
@Unique(['email'])
Expand All @@ -32,7 +33,8 @@ export class User {
@Column()
account_status: boolean;

@Column({ length: 255, nullable: false })
@Column({ length: 128, nullable: false })
@Exclude({ toPlainOnly: true }) // Exclude password from serialization
password: string;

@Column({ length: 255 })
Expand Down
Loading