refactor: chuẩn hóa xử lý lỗi bằng cách giới thiệu tiện ích getErrorMessage và cập nhật kiểu catch thành unknown.

This commit is contained in:
Ho Ngoc Hai
2026-01-04 13:31:11 +07:00
parent 19e5e9b242
commit 76b1c9b3a0
29 changed files with 472 additions and 232 deletions

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
import os
import re
from pathlib import Path
# Base directory
base_dir = Path("/Users/velikho/Desktop/WORKING/Base/services/iam-service/src")
# Find all files that use getErrorMessage
for root, dirs, files in os.walk(base_dir):
# Skip test directories
if '__tests__' in root or 'node_modules' in root:
continue
for file in files:
if not (file.endswith('.controller.ts') or file.endswith('.middleware.ts') or file.endswith('.service.ts')):
continue
if file.endswith('.test.ts') or file.endswith('.e2e.ts'):
continue
filepath = Path(root) / file
# Read file content
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Check if file uses getErrorMessage
if 'getErrorMessage(' not in content:
continue
# Check if import already exists
if re.search(r'import.*getErrorMessage.*from.*error-utils', content):
continue
# Calculate relative path
rel_path = filepath.relative_to(base_dir)
depth = len(rel_path.parts) - 1 # -1 for the file itself
# Build relative import path
if depth == 1: # src/middlewares/
import_path = '../utils/error-utils'
elif depth == 2: # src/modules/xxx/
import_path = '../../utils/error-utils'
elif depth == 3: # src/modules/xxx/yyy/
import_path = '../../../utils/error-utils'
else:
import_path = '../../utils/error-utils' # default
# Find the last import line
lines = content.split('\n')
last_import_idx = -1
for i, line in enumerate(lines):
if line.strip().startswith('import '):
last_import_idx = i
if last_import_idx >= 0:
# Insert new import after last import
import_statement = f"import {{ getErrorMessage }} from '{import_path}';"
lines.insert(last_import_idx + 1, import_statement)
# Write back
with open(filepath, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines))
print(f"Added import to {filepath}")
print("Done!")

42
scripts/add-error-imports.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
# Script to add getErrorMessage import to files that use it
cd /Users/velikho/Desktop/WORKING/Base/services/iam-service/src
# Find all files that use getErrorMessage but don't have the import
for file in $(find . \( -name "*.controller.ts" -o -name "*.middleware.ts" -o -name "*.service.ts" \) -not -path "*/__tests__/*" -not -name "*.test.ts" -exec grep -l "getErrorMessage" {} \;); do
# Check if import already exists
if ! grep -q "getErrorMessage.*from.*error-utils" "$file"; then
# Calculate relative path to error-utils
depth=$(echo "$file" | grep -o "/" | wc -l)
if [ $depth -eq 2 ]; then
# File is in src/modules/xxx/
relative_path="../utils/error-utils"
elif [ $depth -eq 3 ]; then
# File is in src/modules/xxx/yyy/
relative_path="../../utils/error-utils"
elif [ $depth -eq 4 ]; then
# File is in src/modules/xxx/yyy/zzz/
relative_path="../../../utils/error-utils"
else
# Default to ../../utils/error-utils
relative_path="../../utils/error-utils"
fi
# Add import after the last import statement
awk -v path="$relative_path" '
/^import/ { last_import=NR }
{ lines[NR]=$0 }
END {
for(i=1; i<=NR; i++) {
print lines[i]
if(i==last_import) {
print "import { getErrorMessage } from \"" path "\";"
}
}
}
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
echo "Added import to $file"
fi
done

View File

@@ -3,6 +3,7 @@ import { ApiResponse } from '@goodgo/types';
import { Request, Response, NextFunction } from 'express';
import { jwtService } from '../modules/token/jwt.service';
import { getErrorMessage } from '../utils/error-utils';
/**
* EN: Extended Request interface with user information
@@ -97,9 +98,9 @@ export const authenticate = (_options: {
});
next();
} catch (error: any) {
} catch (error: unknown) {
logger.warn('Authentication failed / Xác thực thất bại', {
error: error.message,
error: getErrorMessage(error),
path: req.path,
method: req.method,
});
@@ -239,11 +240,11 @@ export const optionalAuth = (_options: {
}
next();
} catch (error: any) {
} catch (error: unknown) {
// EN: For optional auth, just continue without user info
// VI: Với optional auth, chỉ tiếp tục mà không có thông tin user
logger.debug('Optional authentication skipped / Xác thực tùy chọn bị bỏ qua', {
reason: error.message,
reason: getErrorMessage(error),
});
next();
}

View File

@@ -4,6 +4,7 @@ import { z } from 'zod';
import { DateRangeDto, RiskFiltersDto } from '../access.dto';
import { accessAnalyticsService } from './analytics.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Access Analytics Controller
@@ -27,7 +28,7 @@ export class AccessAnalyticsController {
success: true,
data: stats,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -44,7 +45,7 @@ export class AccessAnalyticsController {
success: false,
error: {
code: 'GET_USAGE_STATS_FAILED',
message: error.message || 'Failed to get usage statistics',
message: getErrorMessage(error) || 'Failed to get usage statistics',
},
});
}
@@ -62,12 +63,12 @@ export class AccessAnalyticsController {
success: true,
data: distribution,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_PERMISSION_DISTRIBUTION_FAILED',
message: error.message || 'Failed to get permission distribution',
message: getErrorMessage(error) || 'Failed to get permission distribution',
},
});
}
@@ -86,12 +87,12 @@ export class AccessAnalyticsController {
success: true,
data: summary,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_USER_SUMMARY_FAILED',
message: error.message || 'Failed to get user access summary',
message: getErrorMessage(error) || 'Failed to get user access summary',
},
});
}
@@ -114,7 +115,7 @@ export class AccessAnalyticsController {
success: true,
data: trends,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -131,7 +132,7 @@ export class AccessAnalyticsController {
success: false,
error: {
code: 'GET_TRENDS_FAILED',
message: error.message || 'Failed to get access trends',
message: getErrorMessage(error) || 'Failed to get access trends',
},
});
}
@@ -156,7 +157,7 @@ export class AccessAnalyticsController {
success: true,
data: analysis,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -173,7 +174,7 @@ export class AccessAnalyticsController {
success: false,
error: {
code: 'GET_RISK_ANALYSIS_FAILED',
message: error.message || 'Failed to get risk analysis',
message: getErrorMessage(error) || 'Failed to get risk analysis',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError, BadRequestError } from '../../../errors/http-error';
import { CreateAccessRequestDto, ApproveAccessRequestDto, RejectAccessRequestDto } from '../access.dto';
import { accessRequestService } from './request.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Access Request Controller
@@ -36,12 +37,12 @@ export class AccessRequestController {
success: true,
data: requests,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'LIST_REQUESTS_FAILED',
message: error.message || 'Failed to list access requests',
message: getErrorMessage(error) || 'Failed to list access requests',
},
});
}
@@ -73,7 +74,7 @@ export class AccessRequestController {
data: request,
message: 'Access request created successfully / Yêu cầu truy cập đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -90,7 +91,7 @@ export class AccessRequestController {
success: false,
error: {
code: 'CREATE_REQUEST_FAILED',
message: error.message || 'Failed to create access request',
message: getErrorMessage(error) || 'Failed to create access request',
},
});
}
@@ -109,7 +110,7 @@ export class AccessRequestController {
success: true,
data: request,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -119,7 +120,7 @@ export class AccessRequestController {
success: false,
error: {
code: 'GET_REQUEST_FAILED',
message: error.message || 'Failed to get access request',
message: getErrorMessage(error) || 'Failed to get access request',
},
});
}
@@ -152,7 +153,7 @@ export class AccessRequestController {
data: request,
message: 'Access request approved successfully / Yêu cầu truy cập đã được phê duyệt thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -174,7 +175,7 @@ export class AccessRequestController {
success: false,
error: {
code: 'APPROVE_REQUEST_FAILED',
message: error.message || 'Failed to approve access request',
message: getErrorMessage(error) || 'Failed to approve access request',
},
});
}
@@ -207,7 +208,7 @@ export class AccessRequestController {
data: request,
message: 'Access request rejected / Yêu cầu truy cập đã bị từ chối',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -229,7 +230,7 @@ export class AccessRequestController {
success: false,
error: {
code: 'REJECT_REQUEST_FAILED',
message: error.message || 'Failed to reject access request',
message: getErrorMessage(error) || 'Failed to reject access request',
},
});
}
@@ -260,7 +261,7 @@ export class AccessRequestController {
success: true,
message: 'Access request cancelled successfully / Yêu cầu truy cập đã được hủy thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof BadRequestError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -270,7 +271,7 @@ export class AccessRequestController {
success: false,
error: {
code: 'CANCEL_REQUEST_FAILED',
message: error.message || 'Failed to cancel access request',
message: getErrorMessage(error) || 'Failed to cancel access request',
},
});
}
@@ -300,12 +301,12 @@ export class AccessRequestController {
success: true,
data: requests,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_PENDING_APPROVALS_FAILED',
message: error.message || 'Failed to get pending approvals',
message: getErrorMessage(error) || 'Failed to get pending approvals',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError, BadRequestError } from '../../../errors/http-error';
import { CreateAccessReviewDto, ReviewAccessItemDto } from '../access.dto';
import { accessReviewService } from './review.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Access Review Controller
@@ -24,12 +25,12 @@ export class AccessReviewController {
success: true,
data: reviews,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'LIST_REVIEWS_FAILED',
message: error.message || 'Failed to list reviews',
message: getErrorMessage(error) || 'Failed to list reviews',
},
});
}
@@ -61,7 +62,7 @@ export class AccessReviewController {
data: review,
message: 'Access review created successfully / Đánh giá truy cập đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -83,7 +84,7 @@ export class AccessReviewController {
success: false,
error: {
code: 'CREATE_REVIEW_FAILED',
message: error.message || 'Failed to create review',
message: getErrorMessage(error) || 'Failed to create review',
},
});
}
@@ -102,7 +103,7 @@ export class AccessReviewController {
success: true,
data: review,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -112,7 +113,7 @@ export class AccessReviewController {
success: false,
error: {
code: 'GET_REVIEW_FAILED',
message: error.message || 'Failed to get review',
message: getErrorMessage(error) || 'Failed to get review',
},
});
}
@@ -132,7 +133,7 @@ export class AccessReviewController {
data: review,
message: 'Access review started successfully / Đánh giá truy cập đã được bắt đầu thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof BadRequestError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -142,7 +143,7 @@ export class AccessReviewController {
success: false,
error: {
code: 'START_REVIEW_FAILED',
message: error.message || 'Failed to start review',
message: getErrorMessage(error) || 'Failed to start review',
},
});
}
@@ -162,7 +163,7 @@ export class AccessReviewController {
data: review,
message: 'Access review completed successfully / Đánh giá truy cập đã được hoàn thành thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof BadRequestError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -172,7 +173,7 @@ export class AccessReviewController {
success: false,
error: {
code: 'COMPLETE_REVIEW_FAILED',
message: error.message || 'Failed to complete review',
message: getErrorMessage(error) || 'Failed to complete review',
},
});
}
@@ -191,12 +192,12 @@ export class AccessReviewController {
success: true,
data: items,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_REVIEW_ITEMS_FAILED',
message: error.message || 'Failed to get review items',
message: getErrorMessage(error) || 'Failed to get review items',
},
});
}
@@ -229,7 +230,7 @@ export class AccessReviewController {
data: item,
message: 'Review item updated successfully / Item đánh giá đã được cập nhật thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -251,7 +252,7 @@ export class AccessReviewController {
success: false,
error: {
code: 'REVIEW_ITEM_FAILED',
message: error.message || 'Failed to review item',
message: getErrorMessage(error) || 'Failed to review item',
},
});
}

View File

@@ -5,6 +5,7 @@ import { z } from 'zod';
import { RegisterDto, LoginDto } from './auth.dto';
import { authService } from './auth.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
@@ -35,7 +36,7 @@ export class AuthController {
tokens: result.tokens,
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -52,7 +53,7 @@ export class AuthController {
success: false,
error: {
code: 'REGISTRATION_FAILED',
message: error.message || 'Registration failed',
message: getErrorMessage(error) || 'Registration failed',
},
});
}
@@ -92,7 +93,7 @@ export class AuthController {
tokens: result.tokens,
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -109,7 +110,7 @@ export class AuthController {
success: false,
error: {
code: 'LOGIN_FAILED',
message: error.message || 'Login failed',
message: getErrorMessage(error) || 'Login failed',
},
});
}
@@ -137,12 +138,12 @@ export class AuthController {
success: true,
data: { message: 'Logged out successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'LOGOUT_FAILED',
message: error.message || 'Logout failed',
message: getErrorMessage(error) || 'Logout failed',
},
});
}
@@ -178,12 +179,12 @@ export class AuthController {
success: true,
data: result,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(401).json({
success: false,
error: {
code: 'REFRESH_FAILED',
message: error.message || 'Token refresh failed',
message: getErrorMessage(error) || 'Token refresh failed',
},
});
}

View File

@@ -3,6 +3,7 @@ import { z } from 'zod';
import { ChangePasswordDto } from './auth.dto';
import { changePasswordService } from './change-password.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
* EN: Change Password Controller
@@ -38,7 +39,7 @@ export class ChangePasswordController {
success: true,
data: { message: 'Password changed successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -55,7 +56,7 @@ export class ChangePasswordController {
success: false,
error: {
code: 'CHANGE_PASSWORD_FAILED',
message: error.message || 'Failed to change password',
message: getErrorMessage(error) || 'Failed to change password',
},
});
}

View File

@@ -2,6 +2,7 @@ import { logger } from '@goodgo/logger';
import { PrismaClient } from '@prisma/client';
import { DatabaseError } from '../../errors/http-error';
import { formatErrorForLogging, hasCode } from '../../utils/error-utils';
/**
* EN: Base repository class providing common database operations
@@ -30,8 +31,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { id });
return entity;
} catch (error: any) {
logger.error(`Failed to find ${this.modelName} by ID / Không thể tìm ${this.modelName} theo ID`, { error, id });
} catch (error: unknown) {
logger.error(`Failed to find ${this.modelName} by ID / Không thể tìm ${this.modelName} theo ID`, { ...formatErrorForLogging(error), id });
throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error });
}
}
@@ -40,7 +41,7 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
* EN: Find entity by unique field
* VI: Tìm entity theo field duy nhất
*/
async findByUnique(field: string, value: any): Promise<T | null> {
async findByUnique(field: string, value: unknown): Promise<T | null> {
try {
logger.debug(`Finding ${this.modelName} by ${field} / Tìm ${this.modelName} theo ${field}`, { field, value });
@@ -50,8 +51,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { field, value });
return entity;
} catch (error: any) {
logger.error(`Failed to find ${this.modelName} by ${field} / Không thể tìm ${this.modelName} theo ${field}`, { error, field, value });
} catch (error: unknown) {
logger.error(`Failed to find ${this.modelName} by ${field} / Không thể tìm ${this.modelName} theo ${field}`, { ...formatErrorForLogging(error), field, value });
throw new DatabaseError(`Failed to find ${this.modelName}`, { field, value, originalError: error });
}
}
@@ -74,8 +75,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`Found ${entities.length} ${this.modelName} entities / Đã tìm thấy ${entities.length} ${this.modelName} entities`);
return entities;
} catch (error: any) {
logger.error(`Failed to find all ${this.modelName} / Không thể tìm tất cả ${this.modelName}`, { error, options });
} catch (error: unknown) {
logger.error(`Failed to find all ${this.modelName} / Không thể tìm tất cả ${this.modelName}`, { ...formatErrorForLogging(error), options });
throw new DatabaseError(`Failed to find ${this.modelName} entities`, { options, originalError: error });
}
}
@@ -94,8 +95,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} created successfully / ${this.modelName} đã được tạo thành công`, { id: (entity as any).id });
return entity;
} catch (error: any) {
logger.error(`Failed to create ${this.modelName} / Không thể tạo ${this.modelName}`, { error, data });
} catch (error: unknown) {
logger.error(`Failed to create ${this.modelName} / Không thể tạo ${this.modelName}`, { ...formatErrorForLogging(error), data });
throw new DatabaseError(`Failed to create ${this.modelName}`, { data, originalError: error });
}
}
@@ -115,12 +116,12 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} updated successfully / ${this.modelName} đã được cập nhật thành công`, { id });
return entity;
} catch (error: any) {
if (error.code === 'P2025') {
} catch (error: unknown) {
if (hasCode(error) && error.code === 'P2025') {
logger.warn(`${this.modelName} not found for update / ${this.modelName} không tìm thấy để cập nhật`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to update ${this.modelName} / Không thể cập nhật ${this.modelName}`, { error, id, data });
logger.error(`Failed to update ${this.modelName} / Không thể cập nhật ${this.modelName}`, { ...formatErrorForLogging(error), id, data });
throw new DatabaseError(`Failed to update ${this.modelName}`, { id, data, originalError: error });
}
}
@@ -139,12 +140,12 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} deleted successfully / ${this.modelName} đã được xóa thành công`, { id });
return true;
} catch (error: any) {
if (error.code === 'P2025') {
} catch (error: unknown) {
if (hasCode(error) && error.code === 'P2025') {
logger.warn(`${this.modelName} not found for deletion / ${this.modelName} không tìm thấy để xóa`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to delete ${this.modelName} / Không thể xóa ${this.modelName}`, { error, id });
logger.error(`Failed to delete ${this.modelName} / Không thể xóa ${this.modelName}`, { ...formatErrorForLogging(error), id });
throw new DatabaseError(`Failed to delete ${this.modelName}`, { id, originalError: error });
}
}
@@ -163,8 +164,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`Counted ${count} ${this.modelName} entities / Đã đếm ${count} ${this.modelName} entities`);
return count;
} catch (error: any) {
logger.error(`Failed to count ${this.modelName} / Không thể đếm ${this.modelName}`, { error, where });
} catch (error: unknown) {
logger.error(`Failed to count ${this.modelName} / Không thể đếm ${this.modelName}`, { ...formatErrorForLogging(error), where });
throw new DatabaseError(`Failed to count ${this.modelName}`, { where, originalError: error });
}
}
@@ -177,8 +178,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
try {
const count = await this.count({ id });
return count > 0;
} catch (error: any) {
logger.error(`Failed to check if ${this.modelName} exists / Không thể kiểm tra ${this.modelName} có tồn tại`, { error, id });
} catch (error: unknown) {
logger.error(`Failed to check if ${this.modelName} exists / Không thể kiểm tra ${this.modelName} có tồn tại`, { ...formatErrorForLogging(error), id });
throw error;
}
}
@@ -197,8 +198,8 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
logger.debug(`${this.modelName} transaction completed successfully / Transaction ${this.modelName} đã hoàn thành thành công`);
return result;
} catch (error: any) {
logger.error(`${this.modelName} transaction failed / Transaction ${this.modelName} thất bại`, { error });
} catch (error: unknown) {
logger.error(`${this.modelName} transaction failed / Transaction ${this.modelName} thất bại`, formatErrorForLogging(error));
throw new DatabaseError(`${this.modelName} transaction failed`, { originalError: error });
}
}
@@ -210,7 +211,7 @@ export abstract class BaseRepository<T, CreateInput, UpdateInput> {
*/
export interface IRepository<T, CreateInput, UpdateInput> {
findById(id: string): Promise<T | null>;
findByUnique(field: string, value: any): Promise<T | null>;
findByUnique(field: string, value: unknown): Promise<T | null>;
findAll(options?: any): Promise<T[]>;
create(data: CreateInput): Promise<T>;
update(id: string, data: UpdateInput): Promise<T>;

View File

@@ -4,6 +4,7 @@ import { Request, Response } from 'express';
import { asyncHandler } from '../../middlewares/error.middleware';
import { FeatureService } from './feature.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
@@ -83,12 +84,12 @@ export class FeatureController {
timestamp: new Date().toISOString(),
};
res.json(response);
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'FEATURE_004',
message: error.message || 'Failed to retrieve feature / Không thể lấy feature',
message: getErrorMessage(error) || 'Failed to retrieve feature / Không thể lấy feature',
},
timestamp: new Date().toISOString(),
});
@@ -112,12 +113,12 @@ export class FeatureController {
timestamp: new Date().toISOString(),
};
res.json(response);
} catch (error: any) {
} catch (error: unknown) {
res.status(400).json({
success: false,
error: {
code: 'FEATURE_005',
message: error.message || 'Failed to update feature / Không thể cập nhật feature',
message: getErrorMessage(error) || 'Failed to update feature / Không thể cập nhật feature',
},
timestamp: new Date().toISOString(),
});
@@ -139,12 +140,12 @@ export class FeatureController {
timestamp: new Date().toISOString(),
};
res.json(response);
} catch (error: any) {
} catch (error: unknown) {
res.status(400).json({
success: false,
error: {
code: 'FEATURE_006',
message: error.message || 'Failed to delete feature / Không thể xóa feature',
message: getErrorMessage(error) || 'Failed to delete feature / Không thể xóa feature',
},
timestamp: new Date().toISOString(),
});
@@ -167,12 +168,12 @@ export class FeatureController {
timestamp: new Date().toISOString(),
};
res.json(response);
} catch (error: any) {
} catch (error: unknown) {
res.status(400).json({
success: false,
error: {
code: 'FEATURE_007',
message: error.message || 'Failed to toggle feature / Không thể chuyển đổi feature',
message: getErrorMessage(error) || 'Failed to toggle feature / Không thể chuyển đổi feature',
},
timestamp: new Date().toISOString(),
});

View File

@@ -3,6 +3,7 @@ import { logger } from '@goodgo/logger';
import { prisma } from '../../config/database.config';
import { ConflictError } from '../../errors/http-error';
import { BaseRepository, IRepository } from '../common/repository';
import { hasCode } from '../../utils/error-utils';
// EN: Feature entity type from Prisma
// VI: Feature entity type từ Prisma
@@ -11,7 +12,7 @@ type Feature = {
name: string;
title: string | null;
description: string | null;
config: any;
config: Record<string, unknown>;
enabled: boolean;
version: string | null;
tags: string[];
@@ -25,14 +26,14 @@ type CreateFeatureInput = {
name: string;
title?: string;
description?: string;
config?: any;
config?: Record<string, unknown>;
tags?: string[];
};
type UpdateFeatureInput = {
title?: string;
description?: string;
config?: any;
config?: Record<string, unknown>;
enabled?: boolean;
tags?: string[];
};
@@ -229,8 +230,8 @@ export class FeatureRepository extends BaseRepository<Feature, CreateFeatureInpu
* EN: Handle database-specific errors
* VI: Xử lý lỗi database-specific
*/
private handleDatabaseError(error: any, context?: any) {
if (error.code === 'P2002') {
private handleDatabaseError(error: unknown, context?: unknown) {
if (hasCode(error) && error.code === 'P2002') {
return new ConflictError('Feature with this name already exists / Feature với tên này đã tồn tại', context);
}
return error;

View File

@@ -13,7 +13,7 @@ export class FeatureService {
* EN: Create a new feature
* VI: Tạo một feature mới
*/
async create(data: { name: string; title?: string; description?: string; config?: any; tags?: string[] }) {
async create(data: { name: string; title?: string; description?: string; config?: Record<string, unknown>; tags?: string[] }) {
logger.info('Creating feature / Tạo feature', { data });
const feature = await featureRepository.create(data);
@@ -77,7 +77,7 @@ export class FeatureService {
* EN: Update feature
* VI: Cập nhật feature
*/
async update(id: string, data: Partial<{ title?: string; description?: string; config?: any; enabled?: boolean; tags?: string[] }>) {
async update(id: string, data: Partial<{ title?: string; description?: string; config?: Record<string, unknown>; enabled?: boolean; tags?: string[] }>) {
logger.info('Updating feature / Cập nhật feature', { id, data });
const feature = await featureRepository.update(id, data);

View File

@@ -5,6 +5,7 @@ import { NotFoundError, BadRequestError } from '../../../errors/http-error';
import { GenerateComplianceReportDto, ReportFiltersDto } from '../governance.dto';
import { complianceService } from './compliance.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Compliance Controller
@@ -39,7 +40,7 @@ export class ComplianceController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -56,7 +57,7 @@ export class ComplianceController {
success: false,
error: {
code: 'LIST_REPORTS_FAILED',
message: error.message || 'Failed to list reports',
message: getErrorMessage(error) || 'Failed to list reports',
},
});
}
@@ -97,7 +98,7 @@ export class ComplianceController {
data: report,
message: 'Compliance report generated successfully / Báo cáo tuân thủ đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -119,7 +120,7 @@ export class ComplianceController {
success: false,
error: {
code: 'GENERATE_REPORT_FAILED',
message: error.message || 'Failed to generate report',
message: getErrorMessage(error) || 'Failed to generate report',
},
});
}
@@ -138,7 +139,7 @@ export class ComplianceController {
success: true,
data: report,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -148,7 +149,7 @@ export class ComplianceController {
success: false,
error: {
code: 'GET_REPORT_FAILED',
message: error.message || 'Failed to get report',
message: getErrorMessage(error) || 'Failed to get report',
},
});
}
@@ -182,7 +183,7 @@ export class ComplianceController {
},
});
}
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -192,7 +193,7 @@ export class ComplianceController {
success: false,
error: {
code: 'EXPORT_REPORT_FAILED',
message: error.message || 'Failed to export report',
message: getErrorMessage(error) || 'Failed to export report',
},
});
}
@@ -212,7 +213,7 @@ export class ComplianceController {
data: report,
message: 'Report published successfully / Báo cáo đã được xuất bản thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -222,7 +223,7 @@ export class ComplianceController {
success: false,
error: {
code: 'PUBLISH_REPORT_FAILED',
message: error.message || 'Failed to publish report',
message: getErrorMessage(error) || 'Failed to publish report',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError } from '../../../errors/http-error';
import { CreatePolicyTemplateDto, TestPolicyDto } from '../governance.dto';
import { policyGovernanceService } from './policy-governance.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Policy Governance Controller
@@ -24,12 +25,12 @@ export class PolicyGovernanceController {
success: true,
data: templates,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_TEMPLATES_FAILED',
message: error.message || 'Failed to get templates',
message: getErrorMessage(error) || 'Failed to get templates',
},
});
}
@@ -49,7 +50,7 @@ export class PolicyGovernanceController {
data: template,
message: 'Policy template created successfully / Template policy đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -66,7 +67,7 @@ export class PolicyGovernanceController {
success: false,
error: {
code: 'CREATE_TEMPLATE_FAILED',
message: error.message || 'Failed to create template',
message: getErrorMessage(error) || 'Failed to create template',
},
});
}
@@ -85,7 +86,7 @@ export class PolicyGovernanceController {
success: true,
data: versions,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -95,7 +96,7 @@ export class PolicyGovernanceController {
success: false,
error: {
code: 'GET_VERSIONS_FAILED',
message: error.message || 'Failed to get policy versions',
message: getErrorMessage(error) || 'Failed to get policy versions',
},
});
}
@@ -115,7 +116,7 @@ export class PolicyGovernanceController {
success: true,
data: result,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -137,7 +138,7 @@ export class PolicyGovernanceController {
success: false,
error: {
code: 'TEST_POLICY_FAILED',
message: error.message || 'Failed to test policy',
message: getErrorMessage(error) || 'Failed to test policy',
},
});
}

View File

@@ -4,6 +4,7 @@ import { z } from 'zod';
import { AccessSummaryFiltersDto, UserActivityFiltersDto, SecurityEventsFiltersDto } from '../governance.dto';
import { reportingService } from './reporting.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Reporting Controller
@@ -28,7 +29,7 @@ export class ReportingController {
success: true,
data: summary,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -45,7 +46,7 @@ export class ReportingController {
success: false,
error: {
code: 'GET_ACCESS_SUMMARY_FAILED',
message: error.message || 'Failed to get access summary',
message: getErrorMessage(error) || 'Failed to get access summary',
},
});
}
@@ -70,7 +71,7 @@ export class ReportingController {
success: true,
data: activity,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -87,7 +88,7 @@ export class ReportingController {
success: false,
error: {
code: 'GET_USER_ACTIVITY_FAILED',
message: error.message || 'Failed to get user activity',
message: getErrorMessage(error) || 'Failed to get user activity',
},
});
}
@@ -121,7 +122,7 @@ export class ReportingController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -138,7 +139,7 @@ export class ReportingController {
success: false,
error: {
code: 'GET_SECURITY_EVENTS_FAILED',
message: error.message || 'Failed to get security events',
message: getErrorMessage(error) || 'Failed to get security events',
},
});
}
@@ -156,12 +157,12 @@ export class ReportingController {
success: true,
data: status,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_COMPLIANCE_STATUS_FAILED',
message: error.message || 'Failed to get compliance status',
message: getErrorMessage(error) || 'Failed to get compliance status',
},
});
}
@@ -179,12 +180,12 @@ export class ReportingController {
success: true,
data: overview,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_RISK_OVERVIEW_FAILED',
message: error.message || 'Failed to get risk overview',
message: getErrorMessage(error) || 'Failed to get risk overview',
},
});
}

View File

@@ -6,6 +6,7 @@ import { NotFoundError } from '../../../errors/http-error';
import { CalculateRiskScoreDto, RiskFiltersDto } from '../governance.dto';
import { riskService } from './risk.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Risk Management Controller
@@ -41,7 +42,7 @@ export class RiskController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -58,7 +59,7 @@ export class RiskController {
success: false,
error: {
code: 'LIST_RISK_SCORES_FAILED',
message: error.message || 'Failed to list risk scores',
message: getErrorMessage(error) || 'Failed to list risk scores',
},
});
}
@@ -77,7 +78,7 @@ export class RiskController {
success: true,
data: score,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -87,7 +88,7 @@ export class RiskController {
success: false,
error: {
code: 'GET_RISK_SCORE_FAILED',
message: error.message || 'Failed to get risk score',
message: getErrorMessage(error) || 'Failed to get risk score',
},
});
}
@@ -107,7 +108,7 @@ export class RiskController {
data: score,
message: 'Risk score calculated successfully / Điểm rủi ro đã được tính thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -129,7 +130,7 @@ export class RiskController {
success: false,
error: {
code: 'CALCULATE_RISK_SCORE_FAILED',
message: error.message || 'Failed to calculate risk score',
message: getErrorMessage(error) || 'Failed to calculate risk score',
},
});
}
@@ -170,12 +171,12 @@ export class RiskController {
totalUsers: allScores.length,
},
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_DASHBOARD_FAILED',
message: error.message || 'Failed to get risk dashboard',
message: getErrorMessage(error) || 'Failed to get risk dashboard',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError, ConflictError } from '../../../errors/http-error';
import { CreateGroupDto, UpdateGroupDto, AddGroupMemberDto } from '../identity.dto';
import { groupService } from './group.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Group Controller
@@ -24,12 +25,12 @@ export class GroupController {
success: true,
data: groups,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'LIST_GROUPS_FAILED',
message: error.message || 'Failed to list groups',
message: getErrorMessage(error) || 'Failed to list groups',
},
});
}
@@ -50,7 +51,7 @@ export class GroupController {
data: group,
message: 'Group created successfully / Nhóm đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -72,7 +73,7 @@ export class GroupController {
success: false,
error: {
code: 'CREATE_GROUP_FAILED',
message: error.message || 'Failed to create group',
message: getErrorMessage(error) || 'Failed to create group',
},
});
}
@@ -91,7 +92,7 @@ export class GroupController {
success: true,
data: group,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -101,7 +102,7 @@ export class GroupController {
success: false,
error: {
code: 'GET_GROUP_FAILED',
message: error.message || 'Failed to get group',
message: getErrorMessage(error) || 'Failed to get group',
},
});
}
@@ -122,7 +123,7 @@ export class GroupController {
data: group,
message: 'Group updated successfully / Nhóm đã được cập nhật thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -144,7 +145,7 @@ export class GroupController {
success: false,
error: {
code: 'UPDATE_GROUP_FAILED',
message: error.message || 'Failed to update group',
message: getErrorMessage(error) || 'Failed to update group',
},
});
}
@@ -163,7 +164,7 @@ export class GroupController {
success: true,
message: 'Group deleted successfully / Nhóm đã được xóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof ConflictError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -173,7 +174,7 @@ export class GroupController {
success: false,
error: {
code: 'DELETE_GROUP_FAILED',
message: error.message || 'Failed to delete group',
message: getErrorMessage(error) || 'Failed to delete group',
},
});
}
@@ -192,12 +193,12 @@ export class GroupController {
success: true,
data: members,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_GROUP_MEMBERS_FAILED',
message: error.message || 'Failed to get group members',
message: getErrorMessage(error) || 'Failed to get group members',
},
});
}
@@ -219,7 +220,7 @@ export class GroupController {
success: true,
message: 'Member added successfully / Thành viên đã được thêm thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -241,7 +242,7 @@ export class GroupController {
success: false,
error: {
code: 'ADD_MEMBER_FAILED',
message: error.message || 'Failed to add member',
message: getErrorMessage(error) || 'Failed to add member',
},
});
}
@@ -260,7 +261,7 @@ export class GroupController {
success: true,
message: 'Member removed successfully / Thành viên đã được xóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -270,7 +271,7 @@ export class GroupController {
success: false,
error: {
code: 'REMOVE_MEMBER_FAILED',
message: error.message || 'Failed to remove member',
message: getErrorMessage(error) || 'Failed to remove member',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError, ConflictError } from '../../../errors/http-error';
import { CreateOrganizationDto, UpdateOrganizationDto } from '../identity.dto';
import { organizationService } from './organization.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Organization Controller
@@ -33,12 +34,12 @@ export class OrganizationController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'LIST_ORGANIZATIONS_FAILED',
message: error.message || 'Failed to list organizations',
message: getErrorMessage(error) || 'Failed to list organizations',
},
});
}
@@ -58,7 +59,7 @@ export class OrganizationController {
data: organization,
message: 'Organization created successfully / Tổ chức đã được tạo thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -80,7 +81,7 @@ export class OrganizationController {
success: false,
error: {
code: 'CREATE_ORGANIZATION_FAILED',
message: error.message || 'Failed to create organization',
message: getErrorMessage(error) || 'Failed to create organization',
},
});
}
@@ -99,7 +100,7 @@ export class OrganizationController {
success: true,
data: organization,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -109,7 +110,7 @@ export class OrganizationController {
success: false,
error: {
code: 'GET_ORGANIZATION_FAILED',
message: error.message || 'Failed to get organization',
message: getErrorMessage(error) || 'Failed to get organization',
},
});
}
@@ -130,7 +131,7 @@ export class OrganizationController {
data: organization,
message: 'Organization updated successfully / Tổ chức đã được cập nhật thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -152,7 +153,7 @@ export class OrganizationController {
success: false,
error: {
code: 'UPDATE_ORGANIZATION_FAILED',
message: error.message || 'Failed to update organization',
message: getErrorMessage(error) || 'Failed to update organization',
},
});
}
@@ -171,7 +172,7 @@ export class OrganizationController {
success: true,
message: 'Organization deleted successfully / Tổ chức đã được xóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof ConflictError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -181,7 +182,7 @@ export class OrganizationController {
success: false,
error: {
code: 'DELETE_ORGANIZATION_FAILED',
message: error.message || 'Failed to delete organization',
message: getErrorMessage(error) || 'Failed to delete organization',
},
});
}
@@ -210,7 +211,7 @@ export class OrganizationController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -220,7 +221,7 @@ export class OrganizationController {
success: false,
error: {
code: 'GET_ORGANIZATION_USERS_FAILED',
message: error.message || 'Failed to get organization users',
message: getErrorMessage(error) || 'Failed to get organization users',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError } from '../../../errors/http-error';
import { UpdateUserProfileDto } from '../identity.dto';
import { profileService } from './profile.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Profile Management Controller
@@ -35,12 +36,12 @@ export class ProfileController {
success: true,
data: profile,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_PROFILE_FAILED',
message: error.message || 'Failed to get profile',
message: getErrorMessage(error) || 'Failed to get profile',
},
});
}
@@ -61,7 +62,7 @@ export class ProfileController {
data: profile,
message: 'Profile updated successfully / Profile đã được cập nhật thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -83,7 +84,7 @@ export class ProfileController {
success: false,
error: {
code: 'UPDATE_PROFILE_FAILED',
message: error.message || 'Failed to update profile',
message: getErrorMessage(error) || 'Failed to update profile',
},
});
}
@@ -116,7 +117,7 @@ export class ProfileController {
data: profile,
message: 'Avatar uploaded successfully / Avatar đã được upload thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -126,7 +127,7 @@ export class ProfileController {
success: false,
error: {
code: 'UPLOAD_AVATAR_FAILED',
message: error.message || 'Failed to upload avatar',
message: getErrorMessage(error) || 'Failed to upload avatar',
},
});
}
@@ -146,7 +147,7 @@ export class ProfileController {
data: profile,
message: 'Avatar deleted successfully / Avatar đã được xóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -156,7 +157,7 @@ export class ProfileController {
success: false,
error: {
code: 'DELETE_AVATAR_FAILED',
message: error.message || 'Failed to delete avatar',
message: getErrorMessage(error) || 'Failed to delete avatar',
},
});
}

View File

@@ -5,6 +5,7 @@ import { NotFoundError } from '../../../errors/http-error';
import { UpdateUserDto, UserFiltersDto, BulkImportUsersDto } from '../identity.dto';
import { userManagementService } from './user.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: User Management Controller
@@ -39,7 +40,7 @@ export class UserManagementController {
},
},
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -56,7 +57,7 @@ export class UserManagementController {
success: false,
error: {
code: 'LIST_USERS_FAILED',
message: error.message || 'Failed to list users',
message: getErrorMessage(error) || 'Failed to list users',
},
});
}
@@ -75,7 +76,7 @@ export class UserManagementController {
success: true,
data: user,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -85,7 +86,7 @@ export class UserManagementController {
success: false,
error: {
code: 'GET_USER_FAILED',
message: error.message || 'Failed to get user',
message: getErrorMessage(error) || 'Failed to get user',
},
});
}
@@ -106,7 +107,7 @@ export class UserManagementController {
data: user,
message: 'User updated successfully / User đã được cập nhật thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -128,7 +129,7 @@ export class UserManagementController {
success: false,
error: {
code: 'UPDATE_USER_FAILED',
message: error.message || 'Failed to update user',
message: getErrorMessage(error) || 'Failed to update user',
},
});
}
@@ -147,7 +148,7 @@ export class UserManagementController {
success: true,
message: 'User deleted successfully / User đã được xóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -157,7 +158,7 @@ export class UserManagementController {
success: false,
error: {
code: 'DELETE_USER_FAILED',
message: error.message || 'Failed to delete user',
message: getErrorMessage(error) || 'Failed to delete user',
},
});
}
@@ -177,7 +178,7 @@ export class UserManagementController {
data: user,
message: 'User deactivated successfully / User đã được vô hiệu hóa thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -187,7 +188,7 @@ export class UserManagementController {
success: false,
error: {
code: 'DEACTIVATE_USER_FAILED',
message: error.message || 'Failed to deactivate user',
message: getErrorMessage(error) || 'Failed to deactivate user',
},
});
}
@@ -207,7 +208,7 @@ export class UserManagementController {
data: user,
message: 'User reactivated successfully / User đã được kích hoạt lại thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
@@ -217,7 +218,7 @@ export class UserManagementController {
success: false,
error: {
code: 'REACTIVATE_USER_FAILED',
message: error.message || 'Failed to reactivate user',
message: getErrorMessage(error) || 'Failed to reactivate user',
},
});
}
@@ -237,7 +238,7 @@ export class UserManagementController {
data: result,
message: `Imported ${result.created} users successfully / Đã import ${result.created} users thành công`,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -254,7 +255,7 @@ export class UserManagementController {
success: false,
error: {
code: 'BULK_IMPORT_FAILED',
message: error.message || 'Failed to import users',
message: getErrorMessage(error) || 'Failed to import users',
},
});
}
@@ -284,7 +285,7 @@ export class UserManagementController {
exportedAt: new Date().toISOString(),
total: users.length,
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -301,7 +302,7 @@ export class UserManagementController {
success: false,
error: {
code: 'BULK_EXPORT_FAILED',
message: error.message || 'Failed to export users',
message: getErrorMessage(error) || 'Failed to export users',
},
});
}

View File

@@ -8,6 +8,7 @@ import { NotFoundError, ConflictError } from '../../../errors/http-error';
import { UserProfileRepository } from '../../../repositories/user-profile.repository';
import { UserRepository } from '../../../repositories/user.repository';
import { UpdateUserDto, UserFiltersDto, BulkImportUsersDto } from '../identity.dto';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: User Management Service for user lifecycle operations
@@ -246,8 +247,8 @@ export class UserManagementService {
success: true,
});
}
} catch (error: any) {
errors.push({ email: userData.email, error: error.message });
} catch (error: unknown) {
errors.push({ email: userData.email, error: getErrorMessage(error) });
}
}

View File

@@ -5,6 +5,7 @@ import { BadRequestError, NotFoundError } from '../../../errors/http-error';
import { VerifyEmailDto, VerifyPhoneDto } from '../identity.dto';
import { verificationService } from './verification.service';
import { getErrorMessage } from '../../../utils/error-utils';
/**
* EN: Identity Verification Controller
@@ -36,7 +37,7 @@ export class VerificationController {
data: result,
message: 'Verification email sent / Email xác thực đã được gửi',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof BadRequestError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -46,7 +47,7 @@ export class VerificationController {
success: false,
error: {
code: 'REQUEST_VERIFICATION_FAILED',
message: error.message || 'Failed to request verification',
message: getErrorMessage(error) || 'Failed to request verification',
},
});
}
@@ -66,7 +67,7 @@ export class VerificationController {
data: result,
message: 'Email verified successfully / Email đã được xác thực thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -88,7 +89,7 @@ export class VerificationController {
success: false,
error: {
code: 'VERIFY_EMAIL_FAILED',
message: error.message || 'Failed to verify email',
message: getErrorMessage(error) || 'Failed to verify email',
},
});
}
@@ -132,7 +133,7 @@ export class VerificationController {
data: result,
message: 'Verification code sent / Mã xác thực đã được gửi',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof NotFoundError || error instanceof BadRequestError) {
res.status(error.statusCode).json(error.toApiResponse());
return;
@@ -142,7 +143,7 @@ export class VerificationController {
success: false,
error: {
code: 'REQUEST_PHONE_VERIFICATION_FAILED',
message: error.message || 'Failed to request phone verification',
message: getErrorMessage(error) || 'Failed to request phone verification',
},
});
}
@@ -162,7 +163,7 @@ export class VerificationController {
data: result,
message: 'Phone verified successfully / Điện thoại đã được xác thực thành công',
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -184,7 +185,7 @@ export class VerificationController {
success: false,
error: {
code: 'VERIFY_PHONE_FAILED',
message: error.message || 'Failed to verify phone',
message: getErrorMessage(error) || 'Failed to verify phone',
},
});
}
@@ -216,12 +217,12 @@ export class VerificationController {
success: true,
data: status,
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_VERIFICATION_STATUS_FAILED',
message: error.message || 'Failed to get verification status',
message: getErrorMessage(error) || 'Failed to get verification status',
},
});
}

View File

@@ -2,6 +2,7 @@ import { Request, Response } from 'express';
import { z } from 'zod';
import { mfaService } from './mfa.service';
import { getErrorMessage } from '../../utils/error-utils';
const VerifyTOTPDto = z.object({
token: z.string().length(6),
@@ -38,12 +39,12 @@ export class MFAController {
message: 'Scan QR code with authenticator app and verify with token',
},
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'ENABLE_TOTP_FAILED',
message: error.message || 'Failed to enable TOTP',
message: getErrorMessage(error) || 'Failed to enable TOTP',
},
});
}
@@ -80,7 +81,7 @@ export class MFAController {
error: { code: 'INVALID_TOKEN', message: 'Invalid TOTP token' },
});
}
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -97,7 +98,7 @@ export class MFAController {
success: false,
error: {
code: 'VERIFY_TOTP_FAILED',
message: error.message || 'Failed to verify TOTP',
message: getErrorMessage(error) || 'Failed to verify TOTP',
},
});
}
@@ -133,7 +134,7 @@ export class MFAController {
error: { code: 'INVALID_TOKEN', message: 'Invalid TOTP token' },
});
}
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -150,7 +151,7 @@ export class MFAController {
success: false,
error: {
code: 'VERIFY_TOTP_FAILED',
message: error.message || 'Failed to verify TOTP',
message: getErrorMessage(error) || 'Failed to verify TOTP',
},
});
}
@@ -178,12 +179,12 @@ export class MFAController {
success: true,
data: { message: 'MFA disabled successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'DISABLE_MFA_FAILED',
message: error.message || 'Failed to disable MFA',
message: getErrorMessage(error) || 'Failed to disable MFA',
},
});
}
@@ -217,12 +218,12 @@ export class MFAController {
createdAt: d.createdAt,
})),
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_MFA_DEVICES_FAILED',
message: error.message || 'Failed to get MFA devices',
message: getErrorMessage(error) || 'Failed to get MFA devices',
},
});
}

View File

@@ -2,6 +2,7 @@ import { logger } from '@goodgo/logger';
import { Request, Response } from 'express';
import { oidcProviderService } from './oidc-provider.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
* EN: OIDC Controller
@@ -16,7 +17,7 @@ export class OIDCController {
try {
const discovery = oidcProviderService.getDiscoveryDocument();
res.json(discovery);
} catch (error: any) {
} catch (error: unknown) {
logger.error('OIDC discovery failed', { error });
res.status(500).json({
success: false,
@@ -70,7 +71,7 @@ export class OIDCController {
}
res.redirect(redirectUrl.toString());
} catch (error: any) {
} catch (error: unknown) {
logger.error('OIDC authorization failed', { error });
res.status(500).json({
success: false,
@@ -115,13 +116,13 @@ export class OIDCController {
token_type: 'Bearer',
expires_in: 900, // 15 minutes
});
} catch (error: any) {
} catch (error: unknown) {
logger.error('OIDC token exchange failed', { error });
res.status(400).json({
success: false,
error: {
code: 'TOKEN_EXCHANGE_FAILED',
message: error.message || 'Token exchange failed',
message: getErrorMessage(error) || 'Token exchange failed',
},
});
}
@@ -170,7 +171,7 @@ export class OIDCController {
name: user.username,
updated_at: Math.floor(user.updatedAt.getTime() / 1000),
});
} catch (error: any) {
} catch (error: unknown) {
logger.error('OIDC userinfo failed', { error });
res.status(500).json({
success: false,
@@ -190,7 +191,7 @@ export class OIDCController {
try {
const jwks = oidcProviderService.getJWKS();
res.json(jwks);
} catch (error: any) {
} catch (error: unknown) {
logger.error('OIDC JWKS failed', { error });
res.status(500).json({
success: false,

View File

@@ -2,6 +2,7 @@ import { Request, Response } from 'express';
import { z } from 'zod';
import { rbacService } from './rbac.service';
import { getErrorMessage } from '../../utils/error-utils';
const AssignRoleDto = z.object({
userId: z.string(),
@@ -46,12 +47,12 @@ export class RBACController {
roles,
},
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_PERMISSIONS_FAILED',
message: error.message || 'Failed to get permissions',
message: getErrorMessage(error) || 'Failed to get permissions',
},
});
}
@@ -75,7 +76,7 @@ export class RBACController {
success: true,
data: { message: 'Role assigned successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -92,7 +93,7 @@ export class RBACController {
success: false,
error: {
code: 'ASSIGN_ROLE_FAILED',
message: error.message || 'Failed to assign role',
message: getErrorMessage(error) || 'Failed to assign role',
},
});
}
@@ -120,12 +121,12 @@ export class RBACController {
success: true,
data: { message: 'Role revoked successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'REVOKE_ROLE_FAILED',
message: error.message || 'Failed to revoke role',
message: getErrorMessage(error) || 'Failed to revoke role',
},
});
}
@@ -149,7 +150,7 @@ export class RBACController {
success: true,
data: { message: 'Permission granted successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
@@ -166,7 +167,7 @@ export class RBACController {
success: false,
error: {
code: 'GRANT_PERMISSION_FAILED',
message: error.message || 'Failed to grant permission',
message: getErrorMessage(error) || 'Failed to grant permission',
},
});
}
@@ -203,12 +204,12 @@ export class RBACController {
success: true,
data: { hasPermission },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'CHECK_PERMISSION_FAILED',
message: error.message || 'Failed to check permission',
message: getErrorMessage(error) || 'Failed to check permission',
},
});
}

View File

@@ -1,6 +1,7 @@
import { Request, Response } from 'express';
import { sessionService } from './session.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
* EN: Sessions Controller
@@ -36,12 +37,12 @@ export class SessionsController {
expiresAt: s.expiresAt,
})),
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'GET_SESSIONS_FAILED',
message: error.message || 'Failed to get sessions',
message: getErrorMessage(error) || 'Failed to get sessions',
},
});
}
@@ -70,12 +71,12 @@ export class SessionsController {
success: true,
data: { message: 'Session revoked successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'REVOKE_SESSION_FAILED',
message: error.message || 'Failed to revoke session',
message: getErrorMessage(error) || 'Failed to revoke session',
},
});
}
@@ -103,12 +104,12 @@ export class SessionsController {
success: true,
data: { message: 'All sessions revoked successfully' },
});
} catch (error: any) {
} catch (error: unknown) {
res.status(500).json({
success: false,
error: {
code: 'REVOKE_ALL_SESSIONS_FAILED',
message: error.message || 'Failed to revoke all sessions',
message: getErrorMessage(error) || 'Failed to revoke all sessions',
},
});
}

View File

@@ -8,6 +8,7 @@ import { cookieService } from '../token/cookie.service';
import { jwtService } from '../token/jwt.service';
import { socialAuthService } from './social.service';
import { getErrorMessage } from '../../utils/error-utils';
/**
* EN: Social Auth Controller
@@ -24,7 +25,7 @@ export class SocialAuthController {
const provider = new GoogleProvider();
const url = provider.getAuthorizationUrl();
res.redirect(url);
} catch (error: any) {
} catch (error: unknown) {
logger.error('Google auth initiation failed', { error });
res.status(500).json({
success: false,
@@ -84,9 +85,9 @@ export class SocialAuthController {
// Redirect to frontend
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/callback?token=${tokens.accessToken}`);
} catch (error: any) {
} catch (error: unknown) {
logger.error('Google callback failed', { error });
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(error.message)}`);
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(getErrorMessage(error))}`);
}
}
@@ -100,7 +101,7 @@ export class SocialAuthController {
const provider = new FacebookProvider();
const url = provider.getAuthorizationUrl();
res.redirect(url);
} catch (error: any) {
} catch (error: unknown) {
logger.error('Facebook auth initiation failed', { error });
res.status(500).json({
success: false,
@@ -154,9 +155,9 @@ export class SocialAuthController {
});
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/callback?token=${tokens.accessToken}`);
} catch (error: any) {
} catch (error: unknown) {
logger.error('Facebook callback failed', { error });
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(error.message)}`);
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(getErrorMessage(error))}`);
}
}
@@ -170,7 +171,7 @@ export class SocialAuthController {
const provider = new GitHubProvider();
const url = provider.getAuthorizationUrl();
res.redirect(url);
} catch (error: any) {
} catch (error: unknown) {
logger.error('GitHub auth initiation failed', { error });
res.status(500).json({
success: false,
@@ -224,9 +225,9 @@ export class SocialAuthController {
});
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/callback?token=${tokens.accessToken}`);
} catch (error: any) {
} catch (error: unknown) {
logger.error('GitHub callback failed', { error });
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(error.message)}`);
res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:3000'}/auth/error?message=${encodeURIComponent(getErrorMessage(error))}`);
}
}
}

View File

@@ -208,7 +208,7 @@ export class JWTService {
* EN: Verify ID token (OIDC)
* VI: Xác thực ID token (OIDC)
*/
verifyIdToken(token: string, expectedAudience: string): any {
verifyIdToken(token: string, expectedAudience: string): unknown {
try {
const decoded = verify(token, jwtConfig.idSecret, {
issuer: jwtConfig.issuer,
@@ -226,7 +226,7 @@ export class JWTService {
* EN: Decode token without verification (for debugging)
* VI: Giải mã token không xác thực (để debug)
*/
decodeToken(token: string): any {
decodeToken(token: string): unknown {
return decode(token);
}

View File

@@ -0,0 +1,107 @@
/**
* EN: Error handling utilities for type-safe error handling
* VI: Tiện ích xử lý lỗi cho xử lý lỗi type-safe
*/
/**
* EN: Type guard to check if error is an Error instance
* VI: Type guard để kiểm tra xem error có phải là Error instance không
*/
export function isError(error: unknown): error is Error {
return error instanceof Error;
}
/**
* EN: Type guard to check if error has a message property
* VI: Type guard để kiểm tra xem error có property message không
*/
export function hasMessage(error: unknown): error is { message: string } {
return (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof (error as { message: unknown }).message === 'string'
);
}
/**
* EN: Type guard to check if error has a code property
* VI: Type guard để kiểm tra xem error có property code không
*/
export function hasCode(error: unknown): error is { code: string } {
return (
typeof error === 'object' &&
error !== null &&
'code' in error &&
typeof (error as { code: unknown }).code === 'string'
);
}
/**
* EN: Get error message from unknown error
* VI: Lấy error message từ unknown error
*/
export function getErrorMessage(error: unknown): string {
if (isError(error)) {
return error.message;
}
if (hasMessage(error)) {
return error.message;
}
if (typeof error === 'string') {
return error;
}
return 'Unknown error occurred';
}
/**
* EN: Get error code from unknown error
* VI: Lấy error code từ unknown error
*/
export function getErrorCode(error: unknown): string | undefined {
if (hasCode(error)) {
return error.code;
}
return undefined;
}
/**
* EN: Format error for logging
* VI: Format error để logging
*/
export function formatErrorForLogging(error: unknown): {
message: string;
code?: string;
stack?: string;
details?: unknown;
} {
const message = getErrorMessage(error);
const code = getErrorCode(error);
const formatted: {
message: string;
code?: string;
stack?: string;
details?: unknown;
} = { message };
if (code) {
formatted.code = code;
}
if (isError(error) && error.stack) {
formatted.stack = error.stack;
}
// EN: Include original error for debugging
// VI: Bao gồm error gốc để debug
if (typeof error === 'object' && error !== null) {
formatted.details = error;
}
return formatted;
}