Skip to content

Commit 43b14d7

Browse files
committed
refactor: response status codes to use centralized constants
- Updated rateLimiter middleware to use HTTP_STATUS.TOO_MANY_REQUESTS instead of hardcoded status. - Modified validation middleware to return HTTP_STATUS.BAD_REQUEST for validation errors. - Changed various routes (analytics, auth, database, management, search) to utilize HTTP_STATUS constants for error responses. - Refactored UserService to throw AppError with HTTP_STATUS constants for user-related errors. - Centralized API key and user types into dedicated files for better organization. - Introduced new types for search analytics, requests, responses, and suggestions to enhance type safety and maintainability. - Created configuration types for app and external services to standardize configuration management.
1 parent 7a462cd commit 43b14d7

37 files changed

Lines changed: 665 additions & 445 deletions

src/app.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function createApp(): express.Application {
5353

5454
// CORS debugging middleware (only in development)
5555
if (config.environment === 'development') {
56-
app.use((req, res, next) => {
56+
app.use((req, _res, next) => {
5757
const origin = req.get('Origin');
5858
if (origin && req.method === 'OPTIONS') {
5959
logger.debug(`CORS preflight from origin: ${origin}`);
@@ -76,7 +76,7 @@ export function createApp(): express.Application {
7676
}
7777

7878
// Health check endpoint for monitoring
79-
app.get('/health', (req, res) => {
79+
app.get('/health', (_req, res) => {
8080
res.json({
8181
status: 'healthy',
8282
timestamp: new Date().toISOString(),
@@ -97,8 +97,8 @@ export function createApp(): express.Application {
9797
app.use('/api/v1', apiV1);
9898

9999
// 404 handler
100-
app.use('*', (req, res) => {
101-
res.status(404).json({
100+
app.use('*', (_req, res) => {
101+
res.status(HTTP_STATUS.NOT_FOUND).json({
102102
success: false,
103103
error: {
104104
code: 'NOT_FOUND',

src/config/constants.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,23 @@ export const PORTS = {
2222

2323
// HTTP Status Codes (commonly used ones not already covered by standard libraries)
2424
export const HTTP_STATUS = {
25+
// Success codes
26+
OK: 200,
27+
CREATED: 201,
2528
NO_CONTENT: 204,
29+
30+
// Client error codes
31+
BAD_REQUEST: 400,
32+
UNAUTHORIZED: 401,
33+
FORBIDDEN: 403,
34+
NOT_FOUND: 404,
35+
CONFLICT: 409,
36+
UNPROCESSABLE_ENTITY: 422,
37+
TOO_MANY_REQUESTS: 429,
38+
39+
// Server error codes
40+
INTERNAL_SERVER_ERROR: 500,
41+
SERVICE_UNAVAILABLE: 503,
2642
} as const;
2743

2844
// File and Memory Limits

src/config/heroku.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,9 @@
44
*/
55

66
import { PORTS } from './constants';
7+
import type { ParsedDatabaseUrl, ParsedRedisUrl } from '@/types/config';
78

8-
export interface ParsedDatabaseUrl {
9-
host: string;
10-
port: number;
11-
username: string;
12-
password: string;
13-
database: string;
14-
}
15-
16-
export interface ParsedRedisUrl {
17-
host: string;
18-
port: number;
19-
password?: string;
20-
}
9+
// Types imported from centralized location
2110

2211
/**
2312
* Parse a database URL in the format:

src/controllers/ApiKeyController.ts

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
* - Use revokeApiKey() to deactivate API keys
1111
* - Use updateApiKey() to modify API key properties
1212
*/
13-
import type { ApiKeyAuthenticatedRequest } from '@/middleware/apiKeyAuth';
14-
import { ApiKeyService, type CreateApiKeyRequest } from '@/services/ApiKeyService';
15-
import type { ApiResponse } from '@/types';
13+
import { HTTP_STATUS } from '@/config/constants';
14+
import { ApiKeyService } from '@/services/ApiKeyService';
15+
import type { ApiKeyAuthenticatedRequest, ApiResponse, CreateApiKeyRequest } from '@/types';
1616
import { logger } from '@/utils/logger';
1717
import type { Response } from 'express';
1818

@@ -112,7 +112,7 @@ export class ApiKeyController {
112112
public async createApiKey(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
113113
try {
114114
if (!req.user) {
115-
res.status(401).json({
115+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
116116
success: false,
117117
error: {
118118
code: 'UNAUTHORIZED',
@@ -140,7 +140,7 @@ export class ApiKeyController {
140140
});
141141

142142
if (!requestValidation.isValid) {
143-
res.status(400).json({
143+
res.status(HTTP_STATUS.BAD_REQUEST).json({
144144
success: false,
145145
error: requestValidation.error,
146146
} as ApiResponse);
@@ -150,7 +150,7 @@ export class ApiKeyController {
150150
// Check user permissions
151151
const permissionValidation = this.validateUserPermissions(req.user.role, rateLimitTier);
152152
if (!permissionValidation.isValid) {
153-
res.status(403).json({
153+
res.status(HTTP_STATUS.FORBIDDEN).json({
154154
success: false,
155155
error: permissionValidation.error,
156156
} as ApiResponse);
@@ -171,7 +171,7 @@ export class ApiKeyController {
171171

172172
logger.info(`API key created for user ${req.user.id}: ${result.apiKey.name}`);
173173

174-
res.status(201).json({
174+
res.status(HTTP_STATUS.CREATED).json({
175175
success: true,
176176
data: {
177177
apiKey: {
@@ -197,7 +197,7 @@ export class ApiKeyController {
197197
} as ApiResponse);
198198
} catch (error) {
199199
logger.error('Failed to create API key:', error);
200-
res.status(500).json({
200+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
201201
success: false,
202202
error: {
203203
code: 'INTERNAL_ERROR',
@@ -213,7 +213,7 @@ export class ApiKeyController {
213213
public async listApiKeys(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
214214
try {
215215
if (!req.user) {
216-
res.status(401).json({
216+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
217217
success: false,
218218
error: {
219219
code: 'UNAUTHORIZED',
@@ -255,7 +255,7 @@ export class ApiKeyController {
255255
} as ApiResponse);
256256
} catch (error) {
257257
logger.error('Failed to list API keys:', error);
258-
res.status(500).json({
258+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
259259
success: false,
260260
error: {
261261
code: 'INTERNAL_ERROR',
@@ -271,7 +271,7 @@ export class ApiKeyController {
271271
public async updateApiKey(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
272272
try {
273273
if (!req.user) {
274-
res.status(401).json({
274+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
275275
success: false,
276276
error: {
277277
code: 'UNAUTHORIZED',
@@ -285,7 +285,7 @@ export class ApiKeyController {
285285
const { name, permissions, rateLimitTier, expiresAt } = req.body;
286286

287287
if (!keyId) {
288-
res.status(400).json({
288+
res.status(HTTP_STATUS.BAD_REQUEST).json({
289289
success: false,
290290
error: {
291291
code: 'MISSING_KEY_ID',
@@ -299,7 +299,7 @@ export class ApiKeyController {
299299

300300
if (name !== undefined) {
301301
if (typeof name !== 'string' || name.trim().length < 3) {
302-
res.status(400).json({
302+
res.status(HTTP_STATUS.BAD_REQUEST).json({
303303
success: false,
304304
error: {
305305
code: 'INVALID_NAME',
@@ -314,7 +314,7 @@ export class ApiKeyController {
314314
if (permissions !== undefined) {
315315
const validPermissions = ['search', 'analytics', 'admin'];
316316
if (!Array.isArray(permissions) || !permissions.every(p => validPermissions.includes(p))) {
317-
res.status(400).json({
317+
res.status(HTTP_STATUS.BAD_REQUEST).json({
318318
success: false,
319319
error: {
320320
code: 'INVALID_PERMISSIONS',
@@ -328,7 +328,7 @@ export class ApiKeyController {
328328

329329
if (rateLimitTier !== undefined) {
330330
if (!['free', 'pro', 'enterprise'].includes(rateLimitTier)) {
331-
res.status(400).json({
331+
res.status(HTTP_STATUS.BAD_REQUEST).json({
332332
success: false,
333333
error: {
334334
code: 'INVALID_RATE_LIMIT_TIER',
@@ -340,7 +340,7 @@ export class ApiKeyController {
340340

341341
// Check if user can set this tier
342342
if (req.user.role !== 'admin' && rateLimitTier !== 'free') {
343-
res.status(403).json({
343+
res.status(HTTP_STATUS.FORBIDDEN).json({
344344
success: false,
345345
error: {
346346
code: 'INSUFFICIENT_PRIVILEGES',
@@ -360,7 +360,7 @@ export class ApiKeyController {
360360
const updatedKey = await this.apiKeyService.updateApiKey(keyId, req.user.id, updates);
361361

362362
if (!updatedKey) {
363-
res.status(404).json({
363+
res.status(HTTP_STATUS.NOT_FOUND).json({
364364
success: false,
365365
error: {
366366
code: 'API_KEY_NOT_FOUND',
@@ -398,7 +398,7 @@ export class ApiKeyController {
398398
} as ApiResponse);
399399
} catch (error) {
400400
logger.error('Failed to update API key:', error);
401-
res.status(500).json({
401+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
402402
success: false,
403403
error: {
404404
code: 'INTERNAL_ERROR',
@@ -414,7 +414,7 @@ export class ApiKeyController {
414414
public async revokeApiKey(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
415415
try {
416416
if (!req.user) {
417-
res.status(401).json({
417+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
418418
success: false,
419419
error: {
420420
code: 'UNAUTHORIZED',
@@ -427,7 +427,7 @@ export class ApiKeyController {
427427
const { keyId } = req.params;
428428

429429
if (!keyId) {
430-
res.status(400).json({
430+
res.status(HTTP_STATUS.BAD_REQUEST).json({
431431
success: false,
432432
error: {
433433
code: 'MISSING_KEY_ID',
@@ -440,7 +440,7 @@ export class ApiKeyController {
440440
const success = await this.apiKeyService.revokeApiKey(keyId, req.user.id);
441441

442442
if (!success) {
443-
res.status(404).json({
443+
res.status(HTTP_STATUS.NOT_FOUND).json({
444444
success: false,
445445
error: {
446446
code: 'API_KEY_NOT_FOUND',
@@ -466,7 +466,7 @@ export class ApiKeyController {
466466
} as ApiResponse);
467467
} catch (error) {
468468
logger.error('Failed to revoke API key:', error);
469-
res.status(500).json({
469+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
470470
success: false,
471471
error: {
472472
code: 'INTERNAL_ERROR',
@@ -482,7 +482,7 @@ export class ApiKeyController {
482482
public async getApiKeyUsage(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
483483
try {
484484
if (!req.user) {
485-
res.status(401).json({
485+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
486486
success: false,
487487
error: {
488488
code: 'UNAUTHORIZED',
@@ -495,7 +495,7 @@ export class ApiKeyController {
495495
const { keyId } = req.params;
496496

497497
if (!keyId) {
498-
res.status(400).json({
498+
res.status(HTTP_STATUS.BAD_REQUEST).json({
499499
success: false,
500500
error: {
501501
code: 'MISSING_KEY_ID',
@@ -510,7 +510,7 @@ export class ApiKeyController {
510510
if (daysParam !== undefined) {
511511
const days = parseInt(daysParam as string, 10);
512512
if (!Number.isFinite(days) || days < 1) {
513-
res.status(400).json({
513+
res.status(HTTP_STATUS.BAD_REQUEST).json({
514514
success: false,
515515
error: {
516516
code: 'VALIDATION_ERROR',
@@ -520,7 +520,7 @@ export class ApiKeyController {
520520
return;
521521
}
522522
if (days > 365) {
523-
res.status(400).json({
523+
res.status(HTTP_STATUS.BAD_REQUEST).json({
524524
success: false,
525525
error: {
526526
code: 'VALIDATION_ERROR',
@@ -534,7 +534,7 @@ export class ApiKeyController {
534534
const usage = await this.apiKeyService.getApiKeyUsage(keyId, req.user.id);
535535

536536
if (!usage) {
537-
res.status(404).json({
537+
res.status(HTTP_STATUS.NOT_FOUND).json({
538538
success: false,
539539
error: {
540540
code: 'API_KEY_NOT_FOUND',
@@ -557,7 +557,7 @@ export class ApiKeyController {
557557
} as ApiResponse);
558558
} catch (error) {
559559
logger.error('Failed to get API key usage:', error);
560-
res.status(500).json({
560+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
561561
success: false,
562562
error: {
563563
code: 'INTERNAL_ERROR',
@@ -573,7 +573,7 @@ export class ApiKeyController {
573573
public async regenerateApiKey(req: ApiKeyAuthenticatedRequest, res: Response): Promise<void> {
574574
try {
575575
if (!req.user) {
576-
res.status(401).json({
576+
res.status(HTTP_STATUS.UNAUTHORIZED).json({
577577
success: false,
578578
error: {
579579
code: 'UNAUTHORIZED',
@@ -586,7 +586,7 @@ export class ApiKeyController {
586586
const { keyId } = req.params;
587587

588588
if (!keyId) {
589-
res.status(400).json({
589+
res.status(HTTP_STATUS.BAD_REQUEST).json({
590590
success: false,
591591
error: {
592592
code: 'MISSING_KEY_ID',
@@ -601,7 +601,7 @@ export class ApiKeyController {
601601
const existingKey = existingKeys.find(key => key.id === keyId);
602602

603603
if (!existingKey) {
604-
res.status(404).json({
604+
res.status(HTTP_STATUS.NOT_FOUND).json({
605605
success: false,
606606
error: {
607607
code: 'API_KEY_NOT_FOUND',
@@ -654,7 +654,7 @@ export class ApiKeyController {
654654
} as ApiResponse);
655655
} catch (error) {
656656
logger.error('Failed to regenerate API key:', error);
657-
res.status(500).json({
657+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
658658
success: false,
659659
error: {
660660
code: 'INTERNAL_ERROR',

0 commit comments

Comments
 (0)