Skip to content

Commit ecbb0ff

Browse files
feat: implement user authentication system with registration, login, and session management
1 parent 9f8622c commit ecbb0ff

31 files changed

Lines changed: 164 additions & 150 deletions

backend/src/main.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ async function bootstrap() {
1010
const app = await NestFactory.create(AppModule);
1111
const configService = app.get(ConfigService);
1212
const origins = configService.get<string>('FRONTEND_URL');
13-
14-
1513
app.use(cookieParser());
16-
1714
app.useGlobalPipes(
1815
new ValidationPipe({
1916
whitelist: true,

backend/src/modules/auth/auth.controller.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ export class AuthController {
103103
return user;
104104
}
105105

106+
@Post('logout')
107+
logout(@Res({ passthrough: true }) res: Response) {
108+
res.clearCookie('refreshToken');
109+
return { message: 'Logged out successfully' };
110+
}
111+
106112
@Get('check-admin')
107113
async isAdmin(
108114
@Req() req: AuthenticatedRequest,
@@ -132,7 +138,7 @@ export class AuthController {
132138
httpOnly: true,
133139
secure: isProduction,
134140
sameSite: 'lax',
135-
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
141+
maxAge: 7 * 24 * 60 * 60 * 1000,
136142
});
137143
}
138144
}

backend/src/modules/auth/auth.service.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class AuthService {
3737
email,
3838
lastName,
3939
photoUrl,
40-
telegramId,
40+
phone,
4141
username,
4242
} = user;
4343
return {
@@ -48,7 +48,7 @@ export class AuthService {
4848
photoUrl,
4949
role,
5050
username,
51-
telegramId,
51+
phone,
5252
createdAt,
5353
};
5454
}
@@ -113,7 +113,6 @@ export class AuthService {
113113
email: registerDto.email,
114114
passwordHash: passwordHash,
115115
role: 'user',
116-
username: registerDto.username,
117116
} as unknown as Omit<User, 'id' | 'createdAt'>);
118117

119118
const tokens = this.generateTokens(newUser);
@@ -145,10 +144,10 @@ export class AuthService {
145144
firstName,
146145
lastName,
147146
email,
147+
phone,
148148
photoUrl,
149149
role,
150150
username,
151-
telegramId,
152151
} = user;
153152

154153
return {
@@ -158,10 +157,10 @@ export class AuthService {
158157
email,
159158
firstName,
160159
lastName,
160+
phone,
161161
photoUrl,
162162
role,
163163
username,
164-
telegramId,
165164
},
166165
};
167166
} catch {

backend/src/modules/auth/dto/register.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class RegisterDto {
1313

1414
@IsOptional()
1515
@IsString()
16-
lastName?: string;
16+
lastName: string;
1717

1818
@IsNotEmpty()
1919
@IsEmail()
@@ -26,5 +26,5 @@ export class RegisterDto {
2626

2727
@IsOptional()
2828
@IsString()
29-
username?: string;
29+
phone?: string;
3030
}

backend/src/modules/user/application/user.service.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,6 @@ export class UserService {
1919
return await this.userRepository.findByEmail(email);
2020
}
2121

22-
async findOrCreate(
23-
telegramId: number,
24-
profile: {
25-
firstName: string;
26-
lastName?: string;
27-
username?: string;
28-
photoUrl?: string;
29-
},
30-
): Promise<User> {
31-
let user = await this.userRepository.findByTelegramId(telegramId);
32-
33-
if (!user) {
34-
user = await this.userRepository.create({
35-
telegramId,
36-
...profile,
37-
role: 'user',
38-
});
39-
}
40-
41-
return user;
42-
}
43-
4422
async create(
4523
user: Omit<User, 'id' | 'createdAt'> & { password?: string },
4624
): Promise<User> {
@@ -67,7 +45,7 @@ export class UserService {
6745
lastName,
6846
role,
6947
email,
70-
telegramId,
48+
phone,
7149
username,
7250
photoUrl,
7351
} = user;
@@ -79,7 +57,7 @@ export class UserService {
7957
photoUrl,
8058
role,
8159
username,
82-
telegramId,
60+
phone,
8361
createdAt,
8462
};
8563
}

backend/src/modules/user/domain/user.entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ export class User {
22
constructor(
33
public readonly id: string,
44
public readonly firstName: string,
5-
public readonly telegramId?: number,
65
public readonly email?: string,
76
public readonly passwordHash?: string,
87
public readonly lastName?: string,
98
public readonly username?: string,
109
public readonly photoUrl?: string,
10+
public readonly phone?: string,
1111
public readonly role: 'user' | 'admin' = 'user',
1212
public readonly createdAt: Date = new Date(),
1313
) {}

backend/src/modules/user/infrastructure/repositories/user.repository.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@ export class UserRepository {
1818
const docs = await this.userModel.find().exec();
1919
return docs.map((doc) => this.toDomain(doc));
2020
}
21-
22-
async findByTelegramId(telegramId: number): Promise<User | null> {
23-
const doc = await this.userModel.findOne({ telegramId }).exec();
24-
return doc ? this.toDomain(doc) : null;
25-
}
26-
2721
async create(user: Omit<User, 'id' | 'createdAt'>): Promise<User> {
2822
const createdUser = new this.userModel(user);
2923
const doc = await createdUser.save();
@@ -61,12 +55,12 @@ export class UserRepository {
6155
return new User(
6256
doc._id.toString(),
6357
doc.firstName,
64-
doc.telegramId,
6558
doc.email,
6659
doc.passwordHash,
6760
doc.lastName,
6861
doc.username,
6962
doc.photoUrl,
63+
doc.phone,
7064
doc.role as 'user' | 'admin',
7165
// doc.createdAt is available because of timestamps: true
7266
(doc as unknown as { createdAt: Date }).createdAt,

backend/src/modules/user/infrastructure/schemas/user.schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ export type UserDocument = HydratedDocument<UserSchemaEntity>;
55

66
@Schema({ collection: 'users', timestamps: true })
77
export class UserSchemaEntity {
8-
@Prop({ required: false, unique: true, index: true, sparse: true })
9-
telegramId: number;
10-
118
@Prop({ required: false, unique: true, index: true, sparse: true })
129
email: string;
1310

@@ -26,6 +23,9 @@ export class UserSchemaEntity {
2623
@Prop({ required: false })
2724
photoUrl: string;
2825

26+
@Prop({ required: false, type: String })
27+
phone: string;
28+
2929
@Prop({ required: true, default: 'user', enum: ['user', 'admin'] })
3030
role: string;
3131
}

backend/src/modules/user/presentation/dto/create-user.dto.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,25 @@ import {
44
IsOptional,
55
IsNumber,
66
IsEnum,
7+
IsEmail,
78
} from 'class-validator';
89

910
export class CreateUserDto {
10-
@IsOptional()
11-
@IsNumber()
12-
telegramId?: number;
13-
14-
@IsOptional()
15-
@IsString()
16-
email?: string;
11+
@IsEmail()
12+
@IsNotEmpty()
13+
email: string;
1714

1815
@IsString()
1916
@IsNotEmpty()
2017
firstName: string;
2118

22-
@IsOptional()
2319
@IsString()
24-
lastName?: string;
20+
@IsNotEmpty()
21+
lastName: string;
2522

26-
@IsOptional()
2723
@IsString()
28-
username?: string;
24+
@IsNotEmpty()
25+
username: string;
2926

3027
@IsOptional()
3128
@IsString()
@@ -37,5 +34,9 @@ export class CreateUserDto {
3734

3835
@IsOptional()
3936
@IsString()
40-
password?: string;
37+
phone?: string;
38+
39+
@IsString()
40+
@IsNotEmpty()
41+
password: string;
4142
}

frontend/angular.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
},
3636
"browser": "src/main.ts",
3737
"tsConfig": "tsconfig.json",
38-
"styles": [ "node_modules/leaflet/dist/leaflet.css" ],
38+
"styles": [
39+
"node_modules/leaflet/dist/leaflet.css",
40+
"src/styles.scss"
41+
],
3942
"polyfills": [
4043
"@angular/localize/init"
4144
],

0 commit comments

Comments
 (0)