Skip to content

Commit 6a0a571

Browse files
feat: Add core backend modules for user, veil, gallery, service, and payment management with initial configuration and payment strategies.
1 parent 383d314 commit 6a0a571

30 files changed

Lines changed: 601 additions & 83 deletions

backend/package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@nestjs/common": "^11.0.1",
2424
"@nestjs/config": "^4.0.3",
2525
"@nestjs/core": "^11.0.1",
26+
"@nestjs/mapped-types": "^2.1.0",
2627
"@nestjs/mongoose": "^11.0.4",
2728
"@nestjs/platform-express": "^11.0.1",
2829
"class-transformer": "^0.5.1",

backend/src/auth/telegram-auth.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ export class TelegramAuthService {
6060
username?: string;
6161
language_code?: string;
6262
}
63-
63+
6464
const telegramUser = JSON.parse(userJson) as TelegramUser;
65-
65+
6666
return this.userService.findOrCreate(telegramUser.id, {
6767
firstName: telegramUser.first_name,
6868
lastName: telegramUser.last_name,

backend/src/common/config/app-config.service.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,54 +10,79 @@ export class AppConfigService {
1010
}
1111

1212
get nodeEnv(): string {
13-
return this.configService.get<string>('NODE_ENV', { infer: true }) ?? 'development';
13+
return (
14+
this.configService.get<string>('NODE_ENV', { infer: true }) ??
15+
'development'
16+
);
1417
}
1518

1619
get apiPrefix(): string {
17-
return this.configService.get<string>('API_PREFIX', { infer: true }) ?? 'api';
20+
return (
21+
this.configService.get<string>('API_PREFIX', { infer: true }) ?? 'api'
22+
);
1823
}
1924

2025
get frontendUrl(): string {
21-
return this.configService.get<string>('FRONTEND_URL', { infer: true }) ?? '';
26+
return (
27+
this.configService.get<string>('FRONTEND_URL', { infer: true }) ?? ''
28+
);
2229
}
2330

2431
get mongoUri(): string {
2532
return this.configService.get<string>('MONGO_URI', { infer: true }) ?? '';
2633
}
2734

2835
get mongoDbName(): string {
29-
return this.configService.get<string>('MONGO_DB_NAME', { infer: true }) ?? 'mavluda-beauty';
36+
return (
37+
this.configService.get<string>('MONGO_DB_NAME', { infer: true }) ??
38+
'mavluda-beauty'
39+
);
3040
}
3141

3242
get jwtSecret(): string {
3343
return this.configService.get<string>('JWT_SECRET', { infer: true }) ?? '';
3444
}
3545

3646
get jwtExpiration(): string {
37-
return this.configService.get<string>('JWT_EXPIRATION', { infer: true }) ?? '1d';
47+
return (
48+
this.configService.get<string>('JWT_EXPIRATION', { infer: true }) ?? '1d'
49+
);
3850
}
3951

4052
get telegramBotToken(): string {
41-
return this.configService.get<string>('TELEGRAM_BOT_TOKEN', { infer: true }) ?? '';
53+
return (
54+
this.configService.get<string>('TELEGRAM_BOT_TOKEN', { infer: true }) ??
55+
''
56+
);
4257
}
4358

4459
get telegramWebAppUrl(): string {
45-
return this.configService.get<string>('TELEGRAM_WEBAPP_URL', { infer: true }) ?? '';
60+
return (
61+
this.configService.get<string>('TELEGRAM_WEBAPP_URL', { infer: true }) ??
62+
''
63+
);
4664
}
4765

4866
get alifMerchantId(): string {
49-
return this.configService.get<string>('ALIF_MERCHANT_ID', { infer: true }) ?? '';
67+
return (
68+
this.configService.get<string>('ALIF_MERCHANT_ID', { infer: true }) ?? ''
69+
);
5070
}
5171

5272
get alifToken(): string {
5373
return this.configService.get<string>('ALIF_TOKEN', { infer: true }) ?? '';
5474
}
5575

5676
get alifCallbackKey(): string {
57-
return this.configService.get<string>('ALIF_CALLBACK_KEY', { infer: true }) ?? '';
77+
return (
78+
this.configService.get<string>('ALIF_CALLBACK_KEY', { infer: true }) ?? ''
79+
);
5880
}
5981

6082
get cardPaymentApiKey(): string {
61-
return this.configService.get<string>('CARD_PAYMENT_API_KEY', { infer: true }) ?? '';
83+
return (
84+
this.configService.get<string>('CARD_PAYMENT_API_KEY', { infer: true }) ??
85+
''
86+
);
6287
}
6388
}

backend/src/common/config/env.validation.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { plainToInstance } from 'class-transformer';
2-
import { IsNumber, IsString, IsUrl, IsEnum, IsOptional, validateSync } from 'class-validator';
2+
import {
3+
IsNumber,
4+
IsString,
5+
IsUrl,
6+
IsEnum,
7+
IsOptional,
8+
validateSync,
9+
} from 'class-validator';
310

411
class EnvironmentVariables {
512
@IsNumber()
@@ -56,13 +63,9 @@ class EnvironmentVariables {
5663
}
5764

5865
export function validate(config: Record<string, unknown>) {
59-
const validatedConfig = plainToInstance(
60-
EnvironmentVariables,
61-
config,
62-
{
63-
enableImplicitConversion: true,
64-
},
65-
);
66+
const validatedConfig = plainToInstance(EnvironmentVariables, config, {
67+
enableImplicitConversion: true,
68+
});
6669
const errors = validateSync(validatedConfig, {
6770
skipMissingProperties: false,
6871
});

backend/src/gallery/application/gallery.service.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,29 @@ export class GalleryService {
1313
async create(gallery: Omit<Gallery, 'id' | 'createdAt'>): Promise<Gallery> {
1414
return this.galleryRepository.create(gallery);
1515
}
16+
async findOne(id: string): Promise<Gallery> {
17+
const gallery = await this.galleryRepository.findById(id);
18+
if (!gallery) {
19+
throw new Error(`Gallery item with ID ${id} not found`);
20+
}
21+
return gallery;
22+
}
23+
24+
async update(
25+
id: string,
26+
updateGalleryDto: Partial<Gallery>,
27+
): Promise<Gallery> {
28+
const updated = await this.galleryRepository.update(id, updateGalleryDto);
29+
if (!updated) {
30+
throw new Error(`Gallery item with ID ${id} not found`);
31+
}
32+
return updated;
33+
}
34+
35+
async remove(id: string): Promise<void> {
36+
const deleted = await this.galleryRepository.delete(id);
37+
if (!deleted) {
38+
throw new Error(`Gallery item with ID ${id} not found`);
39+
}
40+
}
1641
}

backend/src/gallery/infrastructure/repositories/gallery.repository.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { Injectable } from '@nestjs/common';
22
import { InjectModel } from '@nestjs/mongoose';
33
import { Model } from 'mongoose';
44
import { Gallery } from '@gallery/domain/gallery.entity';
5-
import { GalleryDocument, GallerySchemaEntity } from '@gallery/infrastructure/schemas/gallery.schema';
5+
import {
6+
GalleryDocument,
7+
GallerySchemaEntity,
8+
} from '@gallery/infrastructure/schemas/gallery.schema';
69

710
@Injectable()
811
export class GalleryRepository {
@@ -22,14 +25,38 @@ export class GalleryRepository {
2225
return this.toDomain(doc);
2326
}
2427

28+
async findById(id: string): Promise<Gallery | null> {
29+
if (!id || !id.match(/^[0-9a-fA-F]{24}$/)) {
30+
return null;
31+
}
32+
const doc = await this.galleryModel.findById(id).exec();
33+
return doc ? this.toDomain(doc) : null;
34+
}
35+
36+
async update(
37+
id: string,
38+
updateData: Partial<Gallery>,
39+
): Promise<Gallery | null> {
40+
const doc = await this.galleryModel
41+
.findByIdAndUpdate(id, { $set: updateData }, { new: true })
42+
.exec();
43+
return doc ? this.toDomain(doc) : null;
44+
}
45+
46+
async delete(id: string): Promise<boolean> {
47+
const result = await this.galleryModel.findByIdAndDelete(id).exec();
48+
return !!result;
49+
}
50+
2551
private toDomain(doc: GalleryDocument): Gallery {
52+
const d = doc as any;
2653
return new Gallery(
27-
doc._id.toString(),
28-
doc.title,
29-
doc.imageUrl,
30-
doc.category,
31-
doc.tags,
32-
(doc as any).createdAt,
54+
d._id.toString(),
55+
d.title,
56+
d.imageUrl,
57+
d.category,
58+
d.tags,
59+
d.createdAt,
3360
);
3461
}
3562
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { IsString, IsNotEmpty, IsUrl, IsEnum, IsOptional, IsArray } from 'class-validator';
2+
3+
export class CreateGalleryDto {
4+
@IsString()
5+
@IsNotEmpty()
6+
title: string;
7+
8+
@IsUrl()
9+
@IsNotEmpty()
10+
imageUrl: string;
11+
12+
@IsString()
13+
@IsNotEmpty()
14+
category: string;
15+
16+
@IsArray()
17+
@IsString({ each: true })
18+
@IsOptional()
19+
tags?: string[];
20+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { PartialType } from '@nestjs/mapped-types';
2+
import { CreateGalleryDto } from './create-gallery.dto';
3+
4+
export class UpdateGalleryDto extends PartialType(CreateGalleryDto) {}

backend/src/gallery/presentation/gallery.controller.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
import { Controller, Get, Post, Body } from '@nestjs/common';
1+
import {
2+
Controller,
3+
Get,
4+
Post,
5+
Body,
6+
Put,
7+
Param,
8+
Delete,
9+
} from '@nestjs/common';
210
import { GalleryService } from '../application/gallery.service';
311
import { Gallery } from '../domain/gallery.entity';
12+
import { CreateGalleryDto } from './dto/create-gallery.dto';
13+
import { UpdateGalleryDto } from './dto/update-gallery.dto';
414

515
@Controller('gallery')
616
export class GalleryController {
@@ -11,8 +21,34 @@ export class GalleryController {
1121
return this.galleryService.findAll();
1222
}
1323

24+
@Get(':id')
25+
async findOne(@Param('id') id: string): Promise<Gallery> {
26+
return this.galleryService.findOne(id);
27+
}
28+
1429
@Post()
15-
async create(@Body() gallery: Omit<Gallery, 'id' | 'createdAt'>): Promise<Gallery> {
30+
async create(@Body() createGalleryDto: CreateGalleryDto): Promise<Gallery> {
31+
// Mapping DTO to Domain Entity
32+
const gallery = createGalleryDto as unknown as Omit<
33+
Gallery,
34+
'id' | 'createdAt'
35+
>;
1636
return this.galleryService.create(gallery);
1737
}
38+
39+
@Put(':id')
40+
async update(
41+
@Param('id') id: string,
42+
@Body() updateGalleryDto: UpdateGalleryDto,
43+
): Promise<Gallery> {
44+
return this.galleryService.update(
45+
id,
46+
updateGalleryDto as unknown as Partial<Gallery>,
47+
);
48+
}
49+
50+
@Delete(':id')
51+
async remove(@Param('id') id: string): Promise<void> {
52+
return this.galleryService.remove(id);
53+
}
1854
}

0 commit comments

Comments
 (0)