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
13 changes: 12 additions & 1 deletion package-lock.json

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

19 changes: 10 additions & 9 deletions src/cache/controllers/cache.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Controller, Post, Get, Delete, UseGuards, HttpCode, HttpStatus } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { ApiSuccessResponse, ApiErrorResponse } from '../../common/decorators/api-response.decorator';
import { CacheService } from '../cache.service';
import { AuthGuard } from '../../modules/shared/guards/auth.guard';
import { RolesGuard } from '../../modules/shared/guards/roles.guard';
Expand All @@ -15,8 +16,8 @@ export class CacheController {
@Get('stats')
@Roles('admin')
@ApiOperation({ summary: 'Get cache statistics' })
@ApiResponse({ status: 200, description: 'Cache statistics retrieved successfully' })
@ApiResponse({ status: 403, description: 'Forbidden - Admin access required' })
@ApiSuccessResponse(200, 'Cache statistics retrieved successfully')
@ApiErrorResponse(403, 'Forbidden - Admin access required')
async getStats() {
return await this.cacheService.getStats();
}
Expand All @@ -25,8 +26,8 @@ export class CacheController {
@Roles('admin')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Clear entire cache' })
@ApiResponse({ status: 200, description: 'Cache cleared successfully' })
@ApiResponse({ status: 403, description: 'Forbidden - Admin access required' })
@ApiSuccessResponse(200, 'Cache cleared successfully')
@ApiErrorResponse(403, 'Forbidden - Admin access required')
async resetCache() {
await this.cacheService.reset();
return { message: 'Cache cleared successfully' };
Expand All @@ -36,8 +37,8 @@ export class CacheController {
@Roles('admin')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Invalidate cache for specific entity' })
@ApiResponse({ status: 200, description: 'Entity cache invalidated successfully' })
@ApiResponse({ status: 403, description: 'Forbidden - Admin access required' })
@ApiSuccessResponse(200, 'Entity cache invalidated successfully')
@ApiErrorResponse(403, 'Forbidden - Admin access required')
async invalidateEntity(entity: string) {
await this.cacheService.invalidateEntity(entity);
return { message: `Cache invalidated for entity: ${entity}` };
Expand All @@ -47,8 +48,8 @@ export class CacheController {
@Roles('admin')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Invalidate cache for specific entity action' })
@ApiResponse({ status: 200, description: 'Entity action cache invalidated successfully' })
@ApiResponse({ status: 403, description: 'Forbidden - Admin access required' })
@ApiSuccessResponse(200, 'Entity action cache invalidated successfully')
@ApiErrorResponse(403, 'Forbidden - Admin access required')
async invalidateAction(entity: string, action: string) {
await this.cacheService.invalidateAction(entity, action);
return { message: `Cache invalidated for entity: ${entity}, action: ${action}` };
Expand Down
97 changes: 20 additions & 77 deletions src/modules/attributes/controllers/attributes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
HttpStatus,
UseGuards,
} from "@nestjs/common"
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery, ApiBearerAuth } from "@nestjs/swagger"
import { ApiTags, ApiOperation, ApiParam, ApiQuery, ApiBearerAuth } from "@nestjs/swagger"
import { ApiSuccessResponse, ApiErrorResponse } from "../../../common/decorators/api-response.decorator"
import { CreateAttributeDto } from "../dto/create-attribute.dto"
import { UpdateAttributeDto } from "../dto/update-attribute.dto"
import { GetAttributesQueryDto } from "../dto/get-attributes-query.dto"
Expand All @@ -33,30 +34,16 @@ export class AttributeController {
@UseGuards(RolesGuard)
// @Roles(UserRole.ADMIN, UserRole.MANAGER)
@ApiOperation({ summary: 'Create a new attribute' })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Attribute created successfully',
type: AttributeResponseDto,
})
@ApiResponse({
status: HttpStatus.CONFLICT,
description: 'Attribute with this name already exists',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Invalid input data',
})
@ApiSuccessResponse(HttpStatus.CREATED, 'Attribute created successfully', AttributeResponseDto)
@ApiErrorResponse(HttpStatus.CONFLICT, 'Attribute with this name already exists')
@ApiErrorResponse(HttpStatus.BAD_REQUEST, 'Invalid input data')
async create(@Body() createAttributeDto: CreateAttributeDto) {
return this.attributeService.create(createAttributeDto);
}

@Get()
@ApiOperation({ summary: 'Get all attributes with pagination' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Attributes retrieved successfully',
type: PaginatedAttributesResponseDto,
})
@ApiSuccessResponse(HttpStatus.OK, 'Attributes retrieved successfully', PaginatedAttributesResponseDto)
@ApiQuery({ name: 'limit', required: false, type: Number })
@ApiQuery({ name: 'offset', required: false, type: Number })
@ApiQuery({ name: 'search', required: false, type: String })
Expand All @@ -67,15 +54,8 @@ export class AttributeController {
@Get(':id')
@ApiOperation({ summary: 'Get an attribute by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Attribute ID' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Attribute retrieved successfully',
type: AttributeResponseDto,
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Attribute not found',
})
@ApiSuccessResponse(HttpStatus.OK, 'Attribute retrieved successfully', AttributeResponseDto)
@ApiErrorResponse(HttpStatus.NOT_FOUND, 'Attribute not found')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.attributeService.findOne(id);
}
Expand All @@ -85,19 +65,9 @@ export class AttributeController {
// @Roles(UserRole.ADMIN, UserRole.MANAGER)
@ApiOperation({ summary: "Update an attribute" })
@ApiParam({ name: "id", type: Number, description: "Attribute ID" })
@ApiResponse({
status: HttpStatus.OK,
description: "Attribute updated successfully",
type: AttributeResponseDto,
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: "Attribute not found",
})
@ApiResponse({
status: HttpStatus.CONFLICT,
description: "Attribute with this name already exists",
})
@ApiSuccessResponse(HttpStatus.OK, "Attribute updated successfully", AttributeResponseDto)
@ApiErrorResponse(HttpStatus.NOT_FOUND, "Attribute not found")
@ApiErrorResponse(HttpStatus.CONFLICT, "Attribute with this name already exists")
async update(@Param('id', ParseIntPipe) id: number, @Body() updateAttributeDto: UpdateAttributeDto) {
return this.attributeService.update(id, updateAttributeDto)
}
Expand All @@ -107,14 +77,8 @@ export class AttributeController {
// @Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Delete an attribute' })
@ApiParam({ name: 'id', type: Number, description: 'Attribute ID' })
@ApiResponse({
status: HttpStatus.NO_CONTENT,
description: 'Attribute deleted successfully',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Attribute not found',
})
@ApiSuccessResponse(HttpStatus.NO_CONTENT, 'Attribute deleted successfully')
@ApiErrorResponse(HttpStatus.NOT_FOUND, 'Attribute not found')
async remove(@Param('id', ParseIntPipe) id: number) {
await this.attributeService.remove(id);
}
Expand All @@ -124,33 +88,18 @@ export class AttributeController {
// @Roles(UserRole.ADMIN, UserRole.MANAGER)
@ApiOperation({ summary: "Add a value to an attribute" })
@ApiParam({ name: "id", type: Number, description: "Attribute ID" })
@ApiResponse({
status: HttpStatus.CREATED,
description: "Attribute value added successfully",
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: "Attribute not found",
})
@ApiResponse({
status: HttpStatus.CONFLICT,
description: "Value already exists for this attribute",
})
@ApiSuccessResponse(HttpStatus.CREATED, "Attribute value added successfully")
@ApiErrorResponse(HttpStatus.NOT_FOUND, "Attribute not found")
@ApiErrorResponse(HttpStatus.CONFLICT, "Value already exists for this attribute")
async addValue(@Param('id', ParseIntPipe) id: number, @Body('value') value: string) {
return this.attributeService.addValue(id, value)
}

@Get(':id/values')
@ApiOperation({ summary: 'Get all values for an attribute' })
@ApiParam({ name: 'id', type: Number, description: 'Attribute ID' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Attribute values retrieved successfully',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Attribute not found',
})
@ApiSuccessResponse(HttpStatus.OK, 'Attribute values retrieved successfully')
@ApiErrorResponse(HttpStatus.NOT_FOUND, 'Attribute not found')
async getValues(@Param('id', ParseIntPipe) id: number) {
return this.attributeService.getAttributeValues(id);
}
Expand All @@ -161,14 +110,8 @@ export class AttributeController {
@ApiOperation({ summary: "Remove a value from an attribute" })
@ApiParam({ name: "id", type: Number, description: "Attribute ID" })
@ApiParam({ name: "valueId", type: Number, description: "Attribute Value ID" })
@ApiResponse({
status: HttpStatus.NO_CONTENT,
description: "Attribute value removed successfully",
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: "Attribute or value not found",
})
@ApiSuccessResponse(HttpStatus.NO_CONTENT, "Attribute value removed successfully")
@ApiErrorResponse(HttpStatus.NOT_FOUND, "Attribute or value not found")
async removeValue(@Param('id', ParseIntPipe) id: number, @Param('valueId', ParseIntPipe) valueId: number) {
await this.attributeService.removeValue(id, valueId)
}
Expand Down
69 changes: 15 additions & 54 deletions src/modules/auth/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import { Request, Response } from 'express';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBody,
ApiBearerAuth,
ApiCookieAuth,
} from '@nestjs/swagger';
import { ApiSuccessResponse, ApiErrorResponse, ApiAuthResponse } from '../../../common/decorators/api-response.decorator';
import { AuthService } from '../services/auth.service';
import { BadRequestError } from '../../../utils/errors';
import { StellarWalletLoginDto, RegisterUserDto, ChallengeDto } from '../dto/auth.dto';
Expand Down Expand Up @@ -48,15 +48,8 @@ export class AuthController {
'Generate a challenge message for wallet authentication. The user must sign this message with their Stellar wallet to authenticate.',
})
@ApiBody({ type: ChallengeDto })
@ApiResponse({
status: 200,
description: 'Challenge generated successfully',
type: ChallengeResponseDto,
})
@ApiResponse({
status: 400,
description: 'Invalid wallet address format',
})
@ApiSuccessResponse(200, 'Challenge generated successfully', ChallengeResponseDto)
@ApiErrorResponse(400, 'Invalid wallet address format')
async generateChallenge(@Body() challengeDto: ChallengeDto): Promise<ChallengeResponseDto> {
const challenge = this.authService.generateChallenge(challengeDto.walletAddress);

Expand All @@ -78,19 +71,9 @@ export class AuthController {
'Authenticate user using their Stellar wallet signature. The user must first get a challenge and sign it with their wallet.',
})
@ApiBody({ type: StellarWalletLoginDto })
@ApiResponse({
status: 200,
description: 'Login successful',
type: AuthResponseDto,
})
@ApiResponse({
status: 400,
description: 'Invalid signature or wallet address',
})
@ApiResponse({
status: 401,
description: 'Authentication failed',
})
@ApiAuthResponse(200, 'Login successful', AuthResponseDto)
@ApiErrorResponse(400, 'Invalid signature or wallet address')
@ApiErrorResponse(401, 'Authentication failed')
async loginWithWallet(
@Body() loginDto: StellarWalletLoginDto,
@Res({ passthrough: true }) res: Response
Expand All @@ -102,6 +85,7 @@ export class AuthController {
maxAge: result.expiresIn * 1000, // Convert to milliseconds
});

// Return standardized data; ResponseInterceptor will include token
return {
success: true,
data: {
Expand All @@ -128,19 +112,9 @@ export class AuthController {
'Register a new user using their Stellar wallet. User must specify their role (buyer or seller).',
})
@ApiBody({ type: RegisterUserDto })
@ApiResponse({
status: 201,
description: 'User registered successfully',
type: AuthResponseDto,
})
@ApiResponse({
status: 400,
description: 'Invalid signature, wallet address, or user already exists',
})
@ApiResponse({
status: 409,
description: 'User already exists',
})
@ApiAuthResponse(201, 'User registered successfully', AuthResponseDto)
@ApiErrorResponse(400, 'Invalid signature, wallet address, or user already exists')
@ApiErrorResponse(409, 'User already exists')
async registerWithWallet(
@Body() registerDto: RegisterUserDto,
@Res({ passthrough: true }) res: Response
Expand All @@ -158,6 +132,7 @@ export class AuthController {
maxAge: result.expiresIn * 1000, // Convert to milliseconds
});

// Return standardized data; ResponseInterceptor will include token
return {
success: true,
data: {
Expand Down Expand Up @@ -185,15 +160,8 @@ export class AuthController {
})
@ApiBearerAuth()
@ApiCookieAuth()
@ApiResponse({
status: 200,
description: 'User information retrieved successfully',
type: UserResponseDto,
})
@ApiResponse({
status: 401,
description: 'User not authenticated',
})
@ApiSuccessResponse(200, 'User information retrieved successfully', UserResponseDto)
@ApiErrorResponse(401, 'User not authenticated')
async getMe(@Req() req: Request): Promise<UserResponseDto> {
const userId = req.user?.id;
if (!userId) {
Expand Down Expand Up @@ -229,15 +197,8 @@ export class AuthController {
})
@ApiBearerAuth()
@ApiCookieAuth()
@ApiResponse({
status: 200,
description: 'Logout successful',
type: LogoutResponseDto,
})
@ApiResponse({
status: 401,
description: 'User not authenticated',
})
@ApiSuccessResponse(200, 'Logout successful', LogoutResponseDto)
@ApiErrorResponse(401, 'User not authenticated')
async logout(@Res({ passthrough: true }) res: Response): Promise<LogoutResponseDto> {
// Clear the JWT token using the helper function
clearToken(res);
Expand Down
7 changes: 7 additions & 0 deletions src/modules/auth/dto/auth-response.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ export class AuthResponseDto {
})
success: boolean;

@ApiProperty({
description: 'JWT token for authentication',
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
required: false,
})
token?: string;

@ApiProperty({
description: 'Authentication data',
example: {
Expand Down
4 changes: 2 additions & 2 deletions src/modules/auth/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export class AuthService {
return user;
}

async authenticateUser(walletAddress: string): Promise<{ access_token: string }> {
async authenticateUser(walletAddress: string): Promise<{ token: string }> {
// Try to find an existing user with this wallet address
let user = await this.userRepository.findOne({
where: { walletAddress },
Expand Down Expand Up @@ -350,7 +350,7 @@ export class AuthService {
// Create a JWT token containing user information
const payload = { sub: user.id, walletAddress: user.walletAddress, role: primaryRole };

return { access_token: this.jwtService.sign(payload) };
return { token: this.jwtService.sign(payload) };
}

async assignRole(walletAddress: string, roleName: RoleName): Promise<User> {
Expand Down
Loading