@exyconn/common 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +259 -0
- package/dist/client/http/index.d.mts +85 -0
- package/dist/client/http/index.d.ts +85 -0
- package/dist/client/http/index.js +127 -0
- package/dist/client/http/index.js.map +1 -0
- package/dist/client/http/index.mjs +109 -0
- package/dist/client/http/index.mjs.map +1 -0
- package/dist/client/index.d.mts +7 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.js +964 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +889 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/client/logger/index.d.mts +53 -0
- package/dist/client/logger/index.d.ts +53 -0
- package/dist/client/logger/index.js +120 -0
- package/dist/client/logger/index.js.map +1 -0
- package/dist/client/logger/index.mjs +116 -0
- package/dist/client/logger/index.mjs.map +1 -0
- package/dist/client/utils/index.d.mts +285 -0
- package/dist/client/utils/index.d.ts +285 -0
- package/dist/client/utils/index.js +403 -0
- package/dist/client/utils/index.js.map +1 -0
- package/dist/client/utils/index.mjs +362 -0
- package/dist/client/utils/index.mjs.map +1 -0
- package/dist/index-BNdT-2X4.d.ts +229 -0
- package/dist/index-CcrANHAQ.d.mts +59 -0
- package/dist/index-ClWtDfwk.d.ts +833 -0
- package/dist/index-DSW6JfD-.d.mts +833 -0
- package/dist/index-Du0LLt9f.d.mts +229 -0
- package/dist/index-iTKxFa78.d.ts +59 -0
- package/dist/index.d.mts +171 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.js +3806 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3792 -0
- package/dist/index.mjs.map +1 -0
- package/dist/response.types-D--UhLJq.d.mts +67 -0
- package/dist/response.types-D--UhLJq.d.ts +67 -0
- package/dist/server/db/index.d.mts +38 -0
- package/dist/server/db/index.d.ts +38 -0
- package/dist/server/db/index.js +68 -0
- package/dist/server/db/index.js.map +1 -0
- package/dist/server/db/index.mjs +60 -0
- package/dist/server/db/index.mjs.map +1 -0
- package/dist/server/enums/index.d.mts +46 -0
- package/dist/server/enums/index.d.ts +46 -0
- package/dist/server/enums/index.js +48 -0
- package/dist/server/enums/index.js.map +1 -0
- package/dist/server/enums/index.mjs +43 -0
- package/dist/server/enums/index.mjs.map +1 -0
- package/dist/server/index.d.mts +9 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +569 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +523 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/logger/index.d.mts +34 -0
- package/dist/server/logger/index.d.ts +34 -0
- package/dist/server/logger/index.js +125 -0
- package/dist/server/logger/index.js.map +1 -0
- package/dist/server/logger/index.mjs +113 -0
- package/dist/server/logger/index.mjs.map +1 -0
- package/dist/server/middleware/index.d.mts +56 -0
- package/dist/server/middleware/index.d.ts +56 -0
- package/dist/server/middleware/index.js +128 -0
- package/dist/server/middleware/index.js.map +1 -0
- package/dist/server/middleware/index.mjs +118 -0
- package/dist/server/middleware/index.mjs.map +1 -0
- package/dist/server/response/index.d.mts +86 -0
- package/dist/server/response/index.d.ts +86 -0
- package/dist/server/response/index.js +140 -0
- package/dist/server/response/index.js.map +1 -0
- package/dist/server/response/index.mjs +126 -0
- package/dist/server/response/index.mjs.map +1 -0
- package/dist/server/utils/index.d.mts +69 -0
- package/dist/server/utils/index.d.ts +69 -0
- package/dist/server/utils/index.js +114 -0
- package/dist/server/utils/index.js.map +1 -0
- package/dist/server/utils/index.mjs +106 -0
- package/dist/server/utils/index.mjs.map +1 -0
- package/dist/shared/index.d.mts +4 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.js +933 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/index.mjs +612 -0
- package/dist/shared/index.mjs.map +1 -0
- package/package.json +202 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logger Configuration Options
|
|
5
|
+
*/
|
|
6
|
+
interface LoggerConfig {
|
|
7
|
+
level?: string;
|
|
8
|
+
logsDir?: string;
|
|
9
|
+
maxSize?: string;
|
|
10
|
+
maxFiles?: string;
|
|
11
|
+
errorMaxFiles?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a configured Winston logger instance
|
|
15
|
+
*/
|
|
16
|
+
declare const createLogger: (config?: LoggerConfig) => winston.Logger;
|
|
17
|
+
declare const logger: winston.Logger;
|
|
18
|
+
/**
|
|
19
|
+
* Create a stream for Morgan HTTP logger
|
|
20
|
+
*/
|
|
21
|
+
declare const createMorganStream: (loggerInstance: winston.Logger) => {
|
|
22
|
+
write: (message: string) => void;
|
|
23
|
+
};
|
|
24
|
+
declare const stream: {
|
|
25
|
+
write: (message: string) => void;
|
|
26
|
+
};
|
|
27
|
+
declare const simpleLogger: {
|
|
28
|
+
info: (message: string, meta?: unknown) => void;
|
|
29
|
+
error: (message: string, meta?: unknown) => void;
|
|
30
|
+
warn: (message: string, meta?: unknown) => void;
|
|
31
|
+
debug: (message: string, meta?: unknown) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { type LoggerConfig, createLogger, createMorganStream, logger, simpleLogger, stream };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var winston = require('winston');
|
|
4
|
+
var DailyRotateFile = require('winston-daily-rotate-file');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var winston__default = /*#__PURE__*/_interopDefault(winston);
|
|
10
|
+
var DailyRotateFile__default = /*#__PURE__*/_interopDefault(DailyRotateFile);
|
|
11
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
|
+
|
|
13
|
+
// src/server/logger/winston-logger.ts
|
|
14
|
+
var defaultConfig = {
|
|
15
|
+
level: process.env.LOG_LEVEL || "info",
|
|
16
|
+
logsDir: "logs",
|
|
17
|
+
maxSize: "20m",
|
|
18
|
+
maxFiles: "14d",
|
|
19
|
+
errorMaxFiles: "30d"
|
|
20
|
+
};
|
|
21
|
+
var levels = {
|
|
22
|
+
error: 0,
|
|
23
|
+
warn: 1,
|
|
24
|
+
info: 2,
|
|
25
|
+
http: 3,
|
|
26
|
+
debug: 4
|
|
27
|
+
};
|
|
28
|
+
var colors = {
|
|
29
|
+
error: "red",
|
|
30
|
+
warn: "yellow",
|
|
31
|
+
info: "green",
|
|
32
|
+
http: "magenta",
|
|
33
|
+
debug: "blue"
|
|
34
|
+
};
|
|
35
|
+
winston__default.default.addColors(colors);
|
|
36
|
+
var fileFormat = winston__default.default.format.combine(
|
|
37
|
+
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
38
|
+
winston__default.default.format.errors({ stack: true }),
|
|
39
|
+
winston__default.default.format.splat(),
|
|
40
|
+
winston__default.default.format.json()
|
|
41
|
+
);
|
|
42
|
+
var consoleFormat = winston__default.default.format.combine(
|
|
43
|
+
winston__default.default.format.colorize({ all: true }),
|
|
44
|
+
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
45
|
+
winston__default.default.format.printf((info) => {
|
|
46
|
+
const timestamp = info.timestamp;
|
|
47
|
+
const level = info.level;
|
|
48
|
+
const message = info.message;
|
|
49
|
+
const stack = info.stack;
|
|
50
|
+
return `${timestamp} [${level}]: ${message}${stack ? "\n" + stack : ""}`;
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
var createLogger = (config = {}) => {
|
|
54
|
+
const finalConfig = { ...defaultConfig, ...config };
|
|
55
|
+
const transports = [
|
|
56
|
+
// Console transport
|
|
57
|
+
new winston__default.default.transports.Console({
|
|
58
|
+
format: consoleFormat
|
|
59
|
+
}),
|
|
60
|
+
// Combined logs (all levels)
|
|
61
|
+
new DailyRotateFile__default.default({
|
|
62
|
+
filename: path__default.default.join(finalConfig.logsDir, "combined-%DATE%.log"),
|
|
63
|
+
datePattern: "YYYY-MM-DD",
|
|
64
|
+
maxSize: finalConfig.maxSize,
|
|
65
|
+
maxFiles: finalConfig.maxFiles,
|
|
66
|
+
format: fileFormat
|
|
67
|
+
}),
|
|
68
|
+
// Error logs only
|
|
69
|
+
new DailyRotateFile__default.default({
|
|
70
|
+
filename: path__default.default.join(finalConfig.logsDir, "error-%DATE%.log"),
|
|
71
|
+
datePattern: "YYYY-MM-DD",
|
|
72
|
+
level: "error",
|
|
73
|
+
maxSize: finalConfig.maxSize,
|
|
74
|
+
maxFiles: finalConfig.errorMaxFiles,
|
|
75
|
+
format: fileFormat
|
|
76
|
+
})
|
|
77
|
+
];
|
|
78
|
+
return winston__default.default.createLogger({
|
|
79
|
+
level: finalConfig.level,
|
|
80
|
+
levels,
|
|
81
|
+
format: fileFormat,
|
|
82
|
+
transports,
|
|
83
|
+
exitOnError: false
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
var logger = createLogger();
|
|
87
|
+
var createMorganStream = (loggerInstance) => ({
|
|
88
|
+
write: (message) => {
|
|
89
|
+
loggerInstance.http(message.trim());
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
var stream = createMorganStream(logger);
|
|
93
|
+
var serializeMeta = (meta) => {
|
|
94
|
+
if (meta === void 0 || meta === null) return "";
|
|
95
|
+
if (meta instanceof Error) {
|
|
96
|
+
return JSON.stringify({ message: meta.message, stack: meta.stack }, null, 2);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
return JSON.stringify(meta, null, 2);
|
|
100
|
+
} catch {
|
|
101
|
+
return String(meta);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var simpleLogger = {
|
|
105
|
+
info: (message, meta) => {
|
|
106
|
+
console.log(`[INFO] ${message}`, serializeMeta(meta));
|
|
107
|
+
},
|
|
108
|
+
error: (message, meta) => {
|
|
109
|
+
console.error(`[ERROR] ${message}`, serializeMeta(meta));
|
|
110
|
+
},
|
|
111
|
+
warn: (message, meta) => {
|
|
112
|
+
console.warn(`[WARN] ${message}`, serializeMeta(meta));
|
|
113
|
+
},
|
|
114
|
+
debug: (message, meta) => {
|
|
115
|
+
console.debug(`[DEBUG] ${message}`, serializeMeta(meta));
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
exports.createLogger = createLogger;
|
|
120
|
+
exports.createMorganStream = createMorganStream;
|
|
121
|
+
exports.logger = logger;
|
|
122
|
+
exports.simpleLogger = simpleLogger;
|
|
123
|
+
exports.stream = stream;
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/server/logger/winston-logger.ts"],"names":["winston","DailyRotateFile","path"],"mappings":"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","file":"index.js","sourcesContent":["import winston from 'winston';\r\nimport DailyRotateFile from 'winston-daily-rotate-file';\r\nimport path from 'path';\r\n\r\n/**\r\n * Logger Configuration Options\r\n */\r\nexport interface LoggerConfig {\r\n level?: string;\r\n logsDir?: string;\r\n maxSize?: string;\r\n maxFiles?: string;\r\n errorMaxFiles?: string;\r\n}\r\n\r\n/**\r\n * Default logger configuration\r\n */\r\nconst defaultConfig: LoggerConfig = {\r\n level: process.env.LOG_LEVEL || 'info',\r\n logsDir: 'logs',\r\n maxSize: '20m',\r\n maxFiles: '14d',\r\n errorMaxFiles: '30d',\r\n};\r\n\r\n// Define log levels\r\nconst levels = {\r\n error: 0,\r\n warn: 1,\r\n info: 2,\r\n http: 3,\r\n debug: 4,\r\n};\r\n\r\n// Define colors for each level\r\nconst colors = {\r\n error: 'red',\r\n warn: 'yellow',\r\n info: 'green',\r\n http: 'magenta',\r\n debug: 'blue',\r\n};\r\n\r\nwinston.addColors(colors);\r\n\r\n// JSON format for file logs\r\nconst fileFormat = winston.format.combine(\r\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\r\n winston.format.errors({ stack: true }),\r\n winston.format.splat(),\r\n winston.format.json()\r\n);\r\n\r\n// Colorized format for console\r\nconst consoleFormat = winston.format.combine(\r\n winston.format.colorize({ all: true }),\r\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\r\n winston.format.printf((info) => {\r\n const timestamp = info.timestamp as string | undefined;\r\n const level = info.level as string;\r\n const message = info.message as string;\r\n const stack = info.stack as string | undefined;\r\n return `${timestamp} [${level}]: ${message}${stack ? '\\n' + stack : ''}`;\r\n })\r\n);\r\n\r\n/**\r\n * Create a configured Winston logger instance\r\n */\r\nexport const createLogger = (config: LoggerConfig = {}) => {\r\n const finalConfig = { ...defaultConfig, ...config };\r\n\r\n const transports: winston.transport[] = [\r\n // Console transport\r\n new winston.transports.Console({\r\n format: consoleFormat,\r\n }),\r\n\r\n // Combined logs (all levels)\r\n new DailyRotateFile({\r\n filename: path.join(finalConfig.logsDir!, 'combined-%DATE%.log'),\r\n datePattern: 'YYYY-MM-DD',\r\n maxSize: finalConfig.maxSize,\r\n maxFiles: finalConfig.maxFiles,\r\n format: fileFormat,\r\n }),\r\n\r\n // Error logs only\r\n new DailyRotateFile({\r\n filename: path.join(finalConfig.logsDir!, 'error-%DATE%.log'),\r\n datePattern: 'YYYY-MM-DD',\r\n level: 'error',\r\n maxSize: finalConfig.maxSize,\r\n maxFiles: finalConfig.errorMaxFiles,\r\n format: fileFormat,\r\n }),\r\n ];\r\n\r\n return winston.createLogger({\r\n level: finalConfig.level,\r\n levels,\r\n format: fileFormat,\r\n transports,\r\n exitOnError: false,\r\n });\r\n};\r\n\r\n// Default logger instance\r\nexport const logger = createLogger();\r\n\r\n/**\r\n * Create a stream for Morgan HTTP logger\r\n */\r\nexport const createMorganStream = (loggerInstance: winston.Logger) => ({\r\n write: (message: string) => {\r\n loggerInstance.http(message.trim());\r\n },\r\n});\r\n\r\nexport const stream = createMorganStream(logger);\r\n\r\n/**\r\n * Simple logger for development (no file output)\r\n */\r\nconst serializeMeta = (meta: unknown): string => {\r\n if (meta === undefined || meta === null) return '';\r\n if (meta instanceof Error) {\r\n return JSON.stringify({ message: meta.message, stack: meta.stack }, null, 2);\r\n }\r\n try {\r\n return JSON.stringify(meta, null, 2);\r\n } catch {\r\n return String(meta);\r\n }\r\n};\r\n\r\nexport const simpleLogger = {\r\n info: (message: string, meta?: unknown) => {\r\n console.log(`[INFO] ${message}`, serializeMeta(meta));\r\n },\r\n error: (message: string, meta?: unknown) => {\r\n console.error(`[ERROR] ${message}`, serializeMeta(meta));\r\n },\r\n warn: (message: string, meta?: unknown) => {\r\n console.warn(`[WARN] ${message}`, serializeMeta(meta));\r\n },\r\n debug: (message: string, meta?: unknown) => {\r\n console.debug(`[DEBUG] ${message}`, serializeMeta(meta));\r\n },\r\n};\r\n\r\nexport default logger;\r\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
// src/server/logger/winston-logger.ts
|
|
6
|
+
var defaultConfig = {
|
|
7
|
+
level: process.env.LOG_LEVEL || "info",
|
|
8
|
+
logsDir: "logs",
|
|
9
|
+
maxSize: "20m",
|
|
10
|
+
maxFiles: "14d",
|
|
11
|
+
errorMaxFiles: "30d"
|
|
12
|
+
};
|
|
13
|
+
var levels = {
|
|
14
|
+
error: 0,
|
|
15
|
+
warn: 1,
|
|
16
|
+
info: 2,
|
|
17
|
+
http: 3,
|
|
18
|
+
debug: 4
|
|
19
|
+
};
|
|
20
|
+
var colors = {
|
|
21
|
+
error: "red",
|
|
22
|
+
warn: "yellow",
|
|
23
|
+
info: "green",
|
|
24
|
+
http: "magenta",
|
|
25
|
+
debug: "blue"
|
|
26
|
+
};
|
|
27
|
+
winston.addColors(colors);
|
|
28
|
+
var fileFormat = winston.format.combine(
|
|
29
|
+
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
30
|
+
winston.format.errors({ stack: true }),
|
|
31
|
+
winston.format.splat(),
|
|
32
|
+
winston.format.json()
|
|
33
|
+
);
|
|
34
|
+
var consoleFormat = winston.format.combine(
|
|
35
|
+
winston.format.colorize({ all: true }),
|
|
36
|
+
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
37
|
+
winston.format.printf((info) => {
|
|
38
|
+
const timestamp = info.timestamp;
|
|
39
|
+
const level = info.level;
|
|
40
|
+
const message = info.message;
|
|
41
|
+
const stack = info.stack;
|
|
42
|
+
return `${timestamp} [${level}]: ${message}${stack ? "\n" + stack : ""}`;
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
var createLogger = (config = {}) => {
|
|
46
|
+
const finalConfig = { ...defaultConfig, ...config };
|
|
47
|
+
const transports = [
|
|
48
|
+
// Console transport
|
|
49
|
+
new winston.transports.Console({
|
|
50
|
+
format: consoleFormat
|
|
51
|
+
}),
|
|
52
|
+
// Combined logs (all levels)
|
|
53
|
+
new DailyRotateFile({
|
|
54
|
+
filename: path.join(finalConfig.logsDir, "combined-%DATE%.log"),
|
|
55
|
+
datePattern: "YYYY-MM-DD",
|
|
56
|
+
maxSize: finalConfig.maxSize,
|
|
57
|
+
maxFiles: finalConfig.maxFiles,
|
|
58
|
+
format: fileFormat
|
|
59
|
+
}),
|
|
60
|
+
// Error logs only
|
|
61
|
+
new DailyRotateFile({
|
|
62
|
+
filename: path.join(finalConfig.logsDir, "error-%DATE%.log"),
|
|
63
|
+
datePattern: "YYYY-MM-DD",
|
|
64
|
+
level: "error",
|
|
65
|
+
maxSize: finalConfig.maxSize,
|
|
66
|
+
maxFiles: finalConfig.errorMaxFiles,
|
|
67
|
+
format: fileFormat
|
|
68
|
+
})
|
|
69
|
+
];
|
|
70
|
+
return winston.createLogger({
|
|
71
|
+
level: finalConfig.level,
|
|
72
|
+
levels,
|
|
73
|
+
format: fileFormat,
|
|
74
|
+
transports,
|
|
75
|
+
exitOnError: false
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
var logger = createLogger();
|
|
79
|
+
var createMorganStream = (loggerInstance) => ({
|
|
80
|
+
write: (message) => {
|
|
81
|
+
loggerInstance.http(message.trim());
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
var stream = createMorganStream(logger);
|
|
85
|
+
var serializeMeta = (meta) => {
|
|
86
|
+
if (meta === void 0 || meta === null) return "";
|
|
87
|
+
if (meta instanceof Error) {
|
|
88
|
+
return JSON.stringify({ message: meta.message, stack: meta.stack }, null, 2);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return JSON.stringify(meta, null, 2);
|
|
92
|
+
} catch {
|
|
93
|
+
return String(meta);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var simpleLogger = {
|
|
97
|
+
info: (message, meta) => {
|
|
98
|
+
console.log(`[INFO] ${message}`, serializeMeta(meta));
|
|
99
|
+
},
|
|
100
|
+
error: (message, meta) => {
|
|
101
|
+
console.error(`[ERROR] ${message}`, serializeMeta(meta));
|
|
102
|
+
},
|
|
103
|
+
warn: (message, meta) => {
|
|
104
|
+
console.warn(`[WARN] ${message}`, serializeMeta(meta));
|
|
105
|
+
},
|
|
106
|
+
debug: (message, meta) => {
|
|
107
|
+
console.debug(`[DEBUG] ${message}`, serializeMeta(meta));
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export { createLogger, createMorganStream, logger, simpleLogger, stream };
|
|
112
|
+
//# sourceMappingURL=index.mjs.map
|
|
113
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/server/logger/winston-logger.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import winston from 'winston';\r\nimport DailyRotateFile from 'winston-daily-rotate-file';\r\nimport path from 'path';\r\n\r\n/**\r\n * Logger Configuration Options\r\n */\r\nexport interface LoggerConfig {\r\n level?: string;\r\n logsDir?: string;\r\n maxSize?: string;\r\n maxFiles?: string;\r\n errorMaxFiles?: string;\r\n}\r\n\r\n/**\r\n * Default logger configuration\r\n */\r\nconst defaultConfig: LoggerConfig = {\r\n level: process.env.LOG_LEVEL || 'info',\r\n logsDir: 'logs',\r\n maxSize: '20m',\r\n maxFiles: '14d',\r\n errorMaxFiles: '30d',\r\n};\r\n\r\n// Define log levels\r\nconst levels = {\r\n error: 0,\r\n warn: 1,\r\n info: 2,\r\n http: 3,\r\n debug: 4,\r\n};\r\n\r\n// Define colors for each level\r\nconst colors = {\r\n error: 'red',\r\n warn: 'yellow',\r\n info: 'green',\r\n http: 'magenta',\r\n debug: 'blue',\r\n};\r\n\r\nwinston.addColors(colors);\r\n\r\n// JSON format for file logs\r\nconst fileFormat = winston.format.combine(\r\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\r\n winston.format.errors({ stack: true }),\r\n winston.format.splat(),\r\n winston.format.json()\r\n);\r\n\r\n// Colorized format for console\r\nconst consoleFormat = winston.format.combine(\r\n winston.format.colorize({ all: true }),\r\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\r\n winston.format.printf((info) => {\r\n const timestamp = info.timestamp as string | undefined;\r\n const level = info.level as string;\r\n const message = info.message as string;\r\n const stack = info.stack as string | undefined;\r\n return `${timestamp} [${level}]: ${message}${stack ? '\\n' + stack : ''}`;\r\n })\r\n);\r\n\r\n/**\r\n * Create a configured Winston logger instance\r\n */\r\nexport const createLogger = (config: LoggerConfig = {}) => {\r\n const finalConfig = { ...defaultConfig, ...config };\r\n\r\n const transports: winston.transport[] = [\r\n // Console transport\r\n new winston.transports.Console({\r\n format: consoleFormat,\r\n }),\r\n\r\n // Combined logs (all levels)\r\n new DailyRotateFile({\r\n filename: path.join(finalConfig.logsDir!, 'combined-%DATE%.log'),\r\n datePattern: 'YYYY-MM-DD',\r\n maxSize: finalConfig.maxSize,\r\n maxFiles: finalConfig.maxFiles,\r\n format: fileFormat,\r\n }),\r\n\r\n // Error logs only\r\n new DailyRotateFile({\r\n filename: path.join(finalConfig.logsDir!, 'error-%DATE%.log'),\r\n datePattern: 'YYYY-MM-DD',\r\n level: 'error',\r\n maxSize: finalConfig.maxSize,\r\n maxFiles: finalConfig.errorMaxFiles,\r\n format: fileFormat,\r\n }),\r\n ];\r\n\r\n return winston.createLogger({\r\n level: finalConfig.level,\r\n levels,\r\n format: fileFormat,\r\n transports,\r\n exitOnError: false,\r\n });\r\n};\r\n\r\n// Default logger instance\r\nexport const logger = createLogger();\r\n\r\n/**\r\n * Create a stream for Morgan HTTP logger\r\n */\r\nexport const createMorganStream = (loggerInstance: winston.Logger) => ({\r\n write: (message: string) => {\r\n loggerInstance.http(message.trim());\r\n },\r\n});\r\n\r\nexport const stream = createMorganStream(logger);\r\n\r\n/**\r\n * Simple logger for development (no file output)\r\n */\r\nconst serializeMeta = (meta: unknown): string => {\r\n if (meta === undefined || meta === null) return '';\r\n if (meta instanceof Error) {\r\n return JSON.stringify({ message: meta.message, stack: meta.stack }, null, 2);\r\n }\r\n try {\r\n return JSON.stringify(meta, null, 2);\r\n } catch {\r\n return String(meta);\r\n }\r\n};\r\n\r\nexport const simpleLogger = {\r\n info: (message: string, meta?: unknown) => {\r\n console.log(`[INFO] ${message}`, serializeMeta(meta));\r\n },\r\n error: (message: string, meta?: unknown) => {\r\n console.error(`[ERROR] ${message}`, serializeMeta(meta));\r\n },\r\n warn: (message: string, meta?: unknown) => {\r\n console.warn(`[WARN] ${message}`, serializeMeta(meta));\r\n },\r\n debug: (message: string, meta?: unknown) => {\r\n console.debug(`[DEBUG] ${message}`, serializeMeta(meta));\r\n },\r\n};\r\n\r\nexport default logger;\r\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extended Request with auth information
|
|
5
|
+
*/
|
|
6
|
+
interface AuthRequest extends Request {
|
|
7
|
+
userId?: string;
|
|
8
|
+
organizationId?: string;
|
|
9
|
+
apiKey?: {
|
|
10
|
+
_id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
user?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* JWT Payload Interface
|
|
17
|
+
*/
|
|
18
|
+
interface JWTPayload {
|
|
19
|
+
userId: string;
|
|
20
|
+
organizationId?: string;
|
|
21
|
+
email?: string;
|
|
22
|
+
role?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* JWT Authentication Middleware
|
|
27
|
+
* Validates Bearer token from Authorization header
|
|
28
|
+
*/
|
|
29
|
+
declare const authenticateJWT: (secretKey: string) => (req: AuthRequest, res: Response, next: NextFunction) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Optional JWT Authentication Middleware
|
|
32
|
+
* Validates token if present, but doesn't require it
|
|
33
|
+
*/
|
|
34
|
+
declare const optionalAuthenticateJWT: (secretKey: string) => (req: AuthRequest, _res: Response, next: NextFunction) => void;
|
|
35
|
+
/**
|
|
36
|
+
* API Key Authentication Middleware
|
|
37
|
+
* Validates x-api-key header
|
|
38
|
+
*/
|
|
39
|
+
declare const authenticateApiKey: (validateKeyFn: (key: string) => Promise<{
|
|
40
|
+
valid: boolean;
|
|
41
|
+
organizationId?: string;
|
|
42
|
+
keyId?: string;
|
|
43
|
+
keyName?: string;
|
|
44
|
+
}>) => (req: AuthRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Organization Header Middleware
|
|
47
|
+
* Extracts organization ID from x-organization-id header
|
|
48
|
+
*/
|
|
49
|
+
declare const extractOrganization: (req: AuthRequest, _res: Response, next: NextFunction) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Require Organization Middleware
|
|
52
|
+
* Ensures organizationId is present in request
|
|
53
|
+
*/
|
|
54
|
+
declare const requireOrganization: (req: AuthRequest, res: Response, next: NextFunction) => void;
|
|
55
|
+
|
|
56
|
+
export { type AuthRequest, type JWTPayload, authenticateApiKey, authenticateJWT, extractOrganization, optionalAuthenticateJWT, requireOrganization };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extended Request with auth information
|
|
5
|
+
*/
|
|
6
|
+
interface AuthRequest extends Request {
|
|
7
|
+
userId?: string;
|
|
8
|
+
organizationId?: string;
|
|
9
|
+
apiKey?: {
|
|
10
|
+
_id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
user?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* JWT Payload Interface
|
|
17
|
+
*/
|
|
18
|
+
interface JWTPayload {
|
|
19
|
+
userId: string;
|
|
20
|
+
organizationId?: string;
|
|
21
|
+
email?: string;
|
|
22
|
+
role?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* JWT Authentication Middleware
|
|
27
|
+
* Validates Bearer token from Authorization header
|
|
28
|
+
*/
|
|
29
|
+
declare const authenticateJWT: (secretKey: string) => (req: AuthRequest, res: Response, next: NextFunction) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Optional JWT Authentication Middleware
|
|
32
|
+
* Validates token if present, but doesn't require it
|
|
33
|
+
*/
|
|
34
|
+
declare const optionalAuthenticateJWT: (secretKey: string) => (req: AuthRequest, _res: Response, next: NextFunction) => void;
|
|
35
|
+
/**
|
|
36
|
+
* API Key Authentication Middleware
|
|
37
|
+
* Validates x-api-key header
|
|
38
|
+
*/
|
|
39
|
+
declare const authenticateApiKey: (validateKeyFn: (key: string) => Promise<{
|
|
40
|
+
valid: boolean;
|
|
41
|
+
organizationId?: string;
|
|
42
|
+
keyId?: string;
|
|
43
|
+
keyName?: string;
|
|
44
|
+
}>) => (req: AuthRequest, res: Response, next: NextFunction) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Organization Header Middleware
|
|
47
|
+
* Extracts organization ID from x-organization-id header
|
|
48
|
+
*/
|
|
49
|
+
declare const extractOrganization: (req: AuthRequest, _res: Response, next: NextFunction) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Require Organization Middleware
|
|
52
|
+
* Ensures organizationId is present in request
|
|
53
|
+
*/
|
|
54
|
+
declare const requireOrganization: (req: AuthRequest, res: Response, next: NextFunction) => void;
|
|
55
|
+
|
|
56
|
+
export { type AuthRequest, type JWTPayload, authenticateApiKey, authenticateJWT, extractOrganization, optionalAuthenticateJWT, requireOrganization };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jwt = require('jsonwebtoken');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var jwt__default = /*#__PURE__*/_interopDefault(jwt);
|
|
8
|
+
|
|
9
|
+
// src/server/middleware/auth.middleware.ts
|
|
10
|
+
|
|
11
|
+
// src/server/response/response-object.ts
|
|
12
|
+
var unauthorizedResponse = (res, message = "Unauthorized access") => {
|
|
13
|
+
return res.status(401 /* UNAUTHORIZED */).json({
|
|
14
|
+
message,
|
|
15
|
+
data: null,
|
|
16
|
+
status: "unauthorized" /* UNAUTHORIZED */,
|
|
17
|
+
statusCode: 401 /* UNAUTHORIZED */
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var forbiddenResponse = (res, message = "Access forbidden") => {
|
|
21
|
+
return res.status(403 /* FORBIDDEN */).json({
|
|
22
|
+
message,
|
|
23
|
+
data: null,
|
|
24
|
+
status: "forbidden" /* FORBIDDEN */,
|
|
25
|
+
statusCode: 403 /* FORBIDDEN */
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/server/middleware/auth.middleware.ts
|
|
30
|
+
var authenticateJWT = (secretKey) => {
|
|
31
|
+
return (req, res, next) => {
|
|
32
|
+
const authHeader = req.headers.authorization;
|
|
33
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
34
|
+
unauthorizedResponse(res, "Authorization header missing or malformed");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const token = authHeader.split(" ")[1];
|
|
38
|
+
try {
|
|
39
|
+
const decoded = jwt__default.default.verify(token, secretKey);
|
|
40
|
+
req.userId = decoded.userId;
|
|
41
|
+
req.organizationId = decoded.organizationId;
|
|
42
|
+
req.user = decoded;
|
|
43
|
+
if (!decoded.userId) {
|
|
44
|
+
unauthorizedResponse(res, "User ID not found in token");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
next();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof jwt__default.default.TokenExpiredError) {
|
|
50
|
+
unauthorizedResponse(res, "Token has expired");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (err instanceof jwt__default.default.JsonWebTokenError) {
|
|
54
|
+
forbiddenResponse(res, "Invalid token");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
unauthorizedResponse(res, "Token validation failed");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
var optionalAuthenticateJWT = (secretKey) => {
|
|
62
|
+
return (req, _res, next) => {
|
|
63
|
+
const authHeader = req.headers.authorization;
|
|
64
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
65
|
+
next();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const token = authHeader.split(" ")[1];
|
|
69
|
+
try {
|
|
70
|
+
const decoded = jwt__default.default.verify(token, secretKey);
|
|
71
|
+
req.userId = decoded.userId;
|
|
72
|
+
req.organizationId = decoded.organizationId;
|
|
73
|
+
req.user = decoded;
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
next();
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
var authenticateApiKey = (validateKeyFn) => {
|
|
80
|
+
return async (req, res, next) => {
|
|
81
|
+
try {
|
|
82
|
+
const apiKey = req.headers["x-api-key"];
|
|
83
|
+
if (!apiKey) {
|
|
84
|
+
unauthorizedResponse(res, "API key is required. Please provide x-api-key header");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const validation = await validateKeyFn(apiKey);
|
|
88
|
+
if (!validation.valid) {
|
|
89
|
+
unauthorizedResponse(res, "Invalid or inactive API key");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (validation.organizationId) {
|
|
93
|
+
req.organizationId = validation.organizationId;
|
|
94
|
+
}
|
|
95
|
+
if (validation.keyId && validation.keyName) {
|
|
96
|
+
req.apiKey = {
|
|
97
|
+
_id: validation.keyId,
|
|
98
|
+
name: validation.keyName
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
next();
|
|
102
|
+
} catch {
|
|
103
|
+
unauthorizedResponse(res, "API key validation failed");
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
var extractOrganization = (req, _res, next) => {
|
|
108
|
+
const orgId = req.headers["x-organization-id"];
|
|
109
|
+
if (orgId) {
|
|
110
|
+
req.organizationId = orgId;
|
|
111
|
+
}
|
|
112
|
+
next();
|
|
113
|
+
};
|
|
114
|
+
var requireOrganization = (req, res, next) => {
|
|
115
|
+
if (!req.organizationId) {
|
|
116
|
+
unauthorizedResponse(res, "Organization ID is required");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
next();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
exports.authenticateApiKey = authenticateApiKey;
|
|
123
|
+
exports.authenticateJWT = authenticateJWT;
|
|
124
|
+
exports.extractOrganization = extractOrganization;
|
|
125
|
+
exports.optionalAuthenticateJWT = optionalAuthenticateJWT;
|
|
126
|
+
exports.requireOrganization = requireOrganization;
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/server/response/response-object.ts","../../../src/server/middleware/auth.middleware.ts"],"names":["jwt"],"mappings":"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","file":"index.js","sourcesContent":["import type { Response } from 'express';\r\nimport { StatusCode, StatusMessage } from '../enums/status';\r\n\r\n/**\r\n * Standard API Response Interface\r\n */\r\nexport interface ApiResponse<T = unknown> {\r\n message: string;\r\n data: T | null;\r\n status: string;\r\n statusCode: number;\r\n paginationData?: PaginationData;\r\n columns?: ColumnMetadata[];\r\n}\r\n\r\n/**\r\n * Pagination Data Interface\r\n */\r\nexport interface PaginationData {\r\n total?: number;\r\n page?: number;\r\n limit?: number;\r\n totalPages?: number;\r\n hasNextPage?: boolean;\r\n hasPrevPage?: boolean;\r\n}\r\n\r\n/**\r\n * Column Metadata Interface (for dynamic tables)\r\n */\r\nexport interface ColumnMetadata {\r\n name: string;\r\n datatype: string;\r\n required: boolean;\r\n}\r\n\r\n/**\r\n * Extract column metadata from data array\r\n */\r\nexport const extractColumns = <T extends Record<string, unknown>>(\r\n data: T[]\r\n): ColumnMetadata[] => {\r\n if (!Array.isArray(data) || data.length === 0) {\r\n return [];\r\n }\r\n\r\n const sample = data[0];\r\n return Object.entries(sample).map(([key, value]) => {\r\n let datatype: string = typeof value;\r\n\r\n if (value === null) {\r\n datatype = 'null';\r\n } else if (Array.isArray(value)) {\r\n datatype = 'array';\r\n } else if (value instanceof Date) {\r\n datatype = 'date';\r\n } else if (typeof value === 'object') {\r\n datatype = 'object';\r\n }\r\n\r\n return {\r\n name: key,\r\n datatype,\r\n required: value !== null && value !== undefined,\r\n };\r\n });\r\n};\r\n\r\n/**\r\n * Success Response (200)\r\n */\r\nexport const successResponse = <T>(\r\n res: Response,\r\n data: T,\r\n message = 'Operation successful'\r\n): Response => {\r\n return res.status(StatusCode.SUCCESS).json({\r\n message,\r\n data,\r\n status: StatusMessage.SUCCESS,\r\n statusCode: StatusCode.SUCCESS,\r\n });\r\n};\r\n\r\n/**\r\n * Success Response with Array & Pagination (200)\r\n */\r\nexport const successResponseArr = <T>(\r\n res: Response,\r\n data: T[],\r\n paginationData: PaginationData = {},\r\n message = 'Operation successful'\r\n): Response => {\r\n return res.status(StatusCode.SUCCESS).json({\r\n message,\r\n data,\r\n columns: extractColumns(data as Record<string, unknown>[]),\r\n paginationData,\r\n status: StatusMessage.SUCCESS,\r\n statusCode: StatusCode.SUCCESS,\r\n });\r\n};\r\n\r\n/**\r\n * Created Response (201)\r\n */\r\nexport const createdResponse = <T>(\r\n res: Response,\r\n data: T,\r\n message = 'Resource created successfully'\r\n): Response => {\r\n return res.status(StatusCode.CREATED).json({\r\n message,\r\n data,\r\n status: StatusMessage.CREATED,\r\n statusCode: StatusCode.CREATED,\r\n });\r\n};\r\n\r\n/**\r\n * No Content Response (204 as 200 with message)\r\n */\r\nexport const noContentResponse = <T = null>(\r\n res: Response,\r\n data: T = null as T,\r\n message = 'No data found'\r\n): Response => {\r\n return res.status(StatusCode.SUCCESS).json({\r\n message,\r\n data,\r\n status: StatusMessage.NO_CONTENT,\r\n statusCode: StatusCode.NO_CONTENT,\r\n });\r\n};\r\n\r\n/**\r\n * Bad Request Response (400)\r\n */\r\nexport const badRequestResponse = (\r\n res: Response,\r\n message = 'Bad request',\r\n errors: unknown = null\r\n): Response => {\r\n return res.status(StatusCode.BAD_REQUEST).json({\r\n message,\r\n data: errors,\r\n status: StatusMessage.BAD_REQUEST,\r\n statusCode: StatusCode.BAD_REQUEST,\r\n });\r\n};\r\n\r\n/**\r\n * Unauthorized Response (401)\r\n */\r\nexport const unauthorizedResponse = (\r\n res: Response,\r\n message = 'Unauthorized access'\r\n): Response => {\r\n return res.status(StatusCode.UNAUTHORIZED).json({\r\n message,\r\n data: null,\r\n status: StatusMessage.UNAUTHORIZED,\r\n statusCode: StatusCode.UNAUTHORIZED,\r\n });\r\n};\r\n\r\n/**\r\n * Forbidden Response (403)\r\n */\r\nexport const forbiddenResponse = (\r\n res: Response,\r\n message = 'Access forbidden'\r\n): Response => {\r\n return res.status(StatusCode.FORBIDDEN).json({\r\n message,\r\n data: null,\r\n status: StatusMessage.FORBIDDEN,\r\n statusCode: StatusCode.FORBIDDEN,\r\n });\r\n};\r\n\r\n/**\r\n * Not Found Response (404)\r\n */\r\nexport const notFoundResponse = (\r\n res: Response,\r\n message = 'Resource not found'\r\n): Response => {\r\n return res.status(StatusCode.NOT_FOUND).json({\r\n message,\r\n data: null,\r\n status: StatusMessage.NOT_FOUND,\r\n statusCode: StatusCode.NOT_FOUND,\r\n });\r\n};\r\n\r\n/**\r\n * Conflict Response (409)\r\n */\r\nexport const conflictResponse = (\r\n res: Response,\r\n message = 'Resource conflict'\r\n): Response => {\r\n return res.status(StatusCode.CONFLICT).json({\r\n message,\r\n data: null,\r\n status: StatusMessage.CONFLICT,\r\n statusCode: StatusCode.CONFLICT,\r\n });\r\n};\r\n\r\n/**\r\n * Validation Error Response (422)\r\n */\r\nexport const validationErrorResponse = (\r\n res: Response,\r\n errors: unknown,\r\n message = 'Validation failed'\r\n): Response => {\r\n return res.status(StatusCode.UNPROCESSABLE_ENTITY).json({\r\n message,\r\n data: errors,\r\n status: StatusMessage.UNPROCESSABLE_ENTITY,\r\n statusCode: StatusCode.UNPROCESSABLE_ENTITY,\r\n });\r\n};\r\n\r\n/**\r\n * Error Response (500)\r\n */\r\nexport const errorResponse = (\r\n res: Response,\r\n error: unknown = null,\r\n message = 'Something went wrong'\r\n): Response => {\r\n return res.status(StatusCode.INTERNAL_ERROR).json({\r\n message,\r\n data: error,\r\n status: StatusMessage.INTERNAL_ERROR,\r\n statusCode: StatusCode.INTERNAL_ERROR,\r\n });\r\n};\r\n\r\n/**\r\n * Rate Limit Response (429)\r\n */\r\nexport const rateLimitResponse = (\r\n res: Response,\r\n message = 'Too many requests, please try again later'\r\n): Response => {\r\n return res.status(StatusCode.TOO_MANY_REQUESTS).json({\r\n message,\r\n data: null,\r\n status: StatusMessage.TOO_MANY_REQUESTS,\r\n statusCode: StatusCode.TOO_MANY_REQUESTS,\r\n });\r\n};\r\n\r\nexport default {\r\n successResponse,\r\n successResponseArr,\r\n createdResponse,\r\n noContentResponse,\r\n badRequestResponse,\r\n unauthorizedResponse,\r\n forbiddenResponse,\r\n notFoundResponse,\r\n conflictResponse,\r\n validationErrorResponse,\r\n errorResponse,\r\n rateLimitResponse,\r\n extractColumns,\r\n};\r\n","import type { Request, Response, NextFunction } from 'express';\r\nimport jwt from 'jsonwebtoken';\r\nimport { unauthorizedResponse, forbiddenResponse } from '../response/response-object';\r\n\r\n/**\r\n * Extended Request with auth information\r\n */\r\nexport interface AuthRequest extends Request {\r\n userId?: string;\r\n organizationId?: string;\r\n apiKey?: { _id: string; name: string };\r\n user?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * JWT Payload Interface\r\n */\r\nexport interface JWTPayload {\r\n userId: string;\r\n organizationId?: string;\r\n email?: string;\r\n role?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * JWT Authentication Middleware\r\n * Validates Bearer token from Authorization header\r\n */\r\nexport const authenticateJWT = (secretKey: string) => {\r\n return (req: AuthRequest, res: Response, next: NextFunction): void => {\r\n const authHeader = req.headers.authorization;\r\n\r\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\r\n unauthorizedResponse(res, 'Authorization header missing or malformed');\r\n return;\r\n }\r\n\r\n const token = authHeader.split(' ')[1];\r\n\r\n try {\r\n const decoded = jwt.verify(token, secretKey) as JWTPayload;\r\n\r\n req.userId = decoded.userId;\r\n req.organizationId = decoded.organizationId;\r\n req.user = decoded as Record<string, unknown>;\r\n\r\n if (!decoded.userId) {\r\n unauthorizedResponse(res, 'User ID not found in token');\r\n return;\r\n }\r\n\r\n next();\r\n } catch (err) {\r\n if (err instanceof jwt.TokenExpiredError) {\r\n unauthorizedResponse(res, 'Token has expired');\r\n return;\r\n }\r\n if (err instanceof jwt.JsonWebTokenError) {\r\n forbiddenResponse(res, 'Invalid token');\r\n return;\r\n }\r\n unauthorizedResponse(res, 'Token validation failed');\r\n }\r\n };\r\n};\r\n\r\n/**\r\n * Optional JWT Authentication Middleware\r\n * Validates token if present, but doesn't require it\r\n */\r\nexport const optionalAuthenticateJWT = (secretKey: string) => {\r\n return (req: AuthRequest, _res: Response, next: NextFunction): void => {\r\n const authHeader = req.headers.authorization;\r\n\r\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\r\n next();\r\n return;\r\n }\r\n\r\n const token = authHeader.split(' ')[1];\r\n\r\n try {\r\n const decoded = jwt.verify(token, secretKey) as JWTPayload;\r\n req.userId = decoded.userId;\r\n req.organizationId = decoded.organizationId;\r\n req.user = decoded as Record<string, unknown>;\r\n } catch {\r\n // Token invalid, but continue without auth\r\n }\r\n\r\n next();\r\n };\r\n};\r\n\r\n/**\r\n * API Key Authentication Middleware\r\n * Validates x-api-key header\r\n */\r\nexport const authenticateApiKey = (\r\n validateKeyFn: (key: string) => Promise<{\r\n valid: boolean;\r\n organizationId?: string;\r\n keyId?: string;\r\n keyName?: string;\r\n }>\r\n) => {\r\n return async (req: AuthRequest, res: Response, next: NextFunction): Promise<void> => {\r\n try {\r\n const apiKey = req.headers['x-api-key'] as string;\r\n\r\n if (!apiKey) {\r\n unauthorizedResponse(res, 'API key is required. Please provide x-api-key header');\r\n return;\r\n }\r\n\r\n const validation = await validateKeyFn(apiKey);\r\n\r\n if (!validation.valid) {\r\n unauthorizedResponse(res, 'Invalid or inactive API key');\r\n return;\r\n }\r\n\r\n if (validation.organizationId) {\r\n req.organizationId = validation.organizationId;\r\n }\r\n if (validation.keyId && validation.keyName) {\r\n req.apiKey = {\r\n _id: validation.keyId,\r\n name: validation.keyName,\r\n };\r\n }\r\n\r\n next();\r\n } catch {\r\n unauthorizedResponse(res, 'API key validation failed');\r\n }\r\n };\r\n};\r\n\r\n/**\r\n * Organization Header Middleware\r\n * Extracts organization ID from x-organization-id header\r\n */\r\nexport const extractOrganization = (req: AuthRequest, _res: Response, next: NextFunction): void => {\r\n const orgId = req.headers['x-organization-id'] as string;\r\n\r\n if (orgId) {\r\n req.organizationId = orgId;\r\n }\r\n\r\n next();\r\n};\r\n\r\n/**\r\n * Require Organization Middleware\r\n * Ensures organizationId is present in request\r\n */\r\nexport const requireOrganization = (req: AuthRequest, res: Response, next: NextFunction): void => {\r\n if (!req.organizationId) {\r\n unauthorizedResponse(res, 'Organization ID is required');\r\n return;\r\n }\r\n next();\r\n};\r\n\r\nexport default {\r\n authenticateJWT,\r\n optionalAuthenticateJWT,\r\n authenticateApiKey,\r\n extractOrganization,\r\n requireOrganization,\r\n};\r\n"]}
|