@superdangerous/app-framework 4.9.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.
Files changed (239) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/dist/api/logsRouter.d.ts +20 -0
  4. package/dist/api/logsRouter.d.ts.map +1 -0
  5. package/dist/api/logsRouter.js +515 -0
  6. package/dist/api/logsRouter.js.map +1 -0
  7. package/dist/cli/dev-server.d.ts +7 -0
  8. package/dist/cli/dev-server.d.ts.map +1 -0
  9. package/dist/cli/dev-server.js +640 -0
  10. package/dist/cli/dev-server.js.map +1 -0
  11. package/dist/cli/index.d.ts +7 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +26 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/core/StandardServer.d.ts +129 -0
  16. package/dist/core/StandardServer.d.ts.map +1 -0
  17. package/dist/core/StandardServer.js +453 -0
  18. package/dist/core/StandardServer.js.map +1 -0
  19. package/dist/core/apiResponse.d.ts +69 -0
  20. package/dist/core/apiResponse.d.ts.map +1 -0
  21. package/dist/core/apiResponse.js +127 -0
  22. package/dist/core/apiResponse.js.map +1 -0
  23. package/dist/core/healthCheck.d.ts +160 -0
  24. package/dist/core/healthCheck.d.ts.map +1 -0
  25. package/dist/core/healthCheck.js +398 -0
  26. package/dist/core/healthCheck.js.map +1 -0
  27. package/dist/core/index.d.ts +40 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +40 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/logger.d.ts +117 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +826 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/portUtils.d.ts +71 -0
  36. package/dist/core/portUtils.d.ts.map +1 -0
  37. package/dist/core/portUtils.js +240 -0
  38. package/dist/core/portUtils.js.map +1 -0
  39. package/dist/core/storageService.d.ts +119 -0
  40. package/dist/core/storageService.d.ts.map +1 -0
  41. package/dist/core/storageService.js +405 -0
  42. package/dist/core/storageService.js.map +1 -0
  43. package/dist/desktop/bundler.d.ts +40 -0
  44. package/dist/desktop/bundler.d.ts.map +1 -0
  45. package/dist/desktop/bundler.js +176 -0
  46. package/dist/desktop/bundler.js.map +1 -0
  47. package/dist/desktop/index.d.ts +25 -0
  48. package/dist/desktop/index.d.ts.map +1 -0
  49. package/dist/desktop/index.js +15 -0
  50. package/dist/desktop/index.js.map +1 -0
  51. package/dist/desktop/native-modules.d.ts +66 -0
  52. package/dist/desktop/native-modules.d.ts.map +1 -0
  53. package/dist/desktop/native-modules.js +200 -0
  54. package/dist/desktop/native-modules.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/logging/LogCategories.d.ts +87 -0
  60. package/dist/logging/LogCategories.d.ts.map +1 -0
  61. package/dist/logging/LogCategories.js +205 -0
  62. package/dist/logging/LogCategories.js.map +1 -0
  63. package/dist/middleware/aiErrorHandler.d.ts +31 -0
  64. package/dist/middleware/aiErrorHandler.d.ts.map +1 -0
  65. package/dist/middleware/aiErrorHandler.js +181 -0
  66. package/dist/middleware/aiErrorHandler.js.map +1 -0
  67. package/dist/middleware/auth.d.ts +101 -0
  68. package/dist/middleware/auth.d.ts.map +1 -0
  69. package/dist/middleware/auth.js +230 -0
  70. package/dist/middleware/auth.js.map +1 -0
  71. package/dist/middleware/cors.d.ts +56 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +123 -0
  74. package/dist/middleware/cors.js.map +1 -0
  75. package/dist/middleware/errorHandler.d.ts +13 -0
  76. package/dist/middleware/errorHandler.d.ts.map +1 -0
  77. package/dist/middleware/errorHandler.js +85 -0
  78. package/dist/middleware/errorHandler.js.map +1 -0
  79. package/dist/middleware/fileUpload.d.ts +62 -0
  80. package/dist/middleware/fileUpload.d.ts.map +1 -0
  81. package/dist/middleware/fileUpload.js +175 -0
  82. package/dist/middleware/fileUpload.js.map +1 -0
  83. package/dist/middleware/health.d.ts +48 -0
  84. package/dist/middleware/health.d.ts.map +1 -0
  85. package/dist/middleware/health.js +143 -0
  86. package/dist/middleware/health.js.map +1 -0
  87. package/dist/middleware/index.d.ts +20 -0
  88. package/dist/middleware/index.d.ts.map +1 -0
  89. package/dist/middleware/index.js +18 -0
  90. package/dist/middleware/index.js.map +1 -0
  91. package/dist/middleware/openapi.d.ts +64 -0
  92. package/dist/middleware/openapi.d.ts.map +1 -0
  93. package/dist/middleware/openapi.js +258 -0
  94. package/dist/middleware/openapi.js.map +1 -0
  95. package/dist/middleware/requestLogging.d.ts +22 -0
  96. package/dist/middleware/requestLogging.d.ts.map +1 -0
  97. package/dist/middleware/requestLogging.js +61 -0
  98. package/dist/middleware/requestLogging.js.map +1 -0
  99. package/dist/middleware/session.d.ts +84 -0
  100. package/dist/middleware/session.d.ts.map +1 -0
  101. package/dist/middleware/session.js +189 -0
  102. package/dist/middleware/session.js.map +1 -0
  103. package/dist/middleware/validation.d.ts +1337 -0
  104. package/dist/middleware/validation.d.ts.map +1 -0
  105. package/dist/middleware/validation.js +483 -0
  106. package/dist/middleware/validation.js.map +1 -0
  107. package/dist/services/aiService.d.ts +180 -0
  108. package/dist/services/aiService.d.ts.map +1 -0
  109. package/dist/services/aiService.js +547 -0
  110. package/dist/services/aiService.js.map +1 -0
  111. package/dist/services/conversationStorage.d.ts +38 -0
  112. package/dist/services/conversationStorage.d.ts.map +1 -0
  113. package/dist/services/conversationStorage.js +158 -0
  114. package/dist/services/conversationStorage.js.map +1 -0
  115. package/dist/services/crossPlatformBuffer.d.ts +84 -0
  116. package/dist/services/crossPlatformBuffer.d.ts.map +1 -0
  117. package/dist/services/crossPlatformBuffer.js +246 -0
  118. package/dist/services/crossPlatformBuffer.js.map +1 -0
  119. package/dist/services/index.d.ts +17 -0
  120. package/dist/services/index.d.ts.map +1 -0
  121. package/dist/services/index.js +18 -0
  122. package/dist/services/index.js.map +1 -0
  123. package/dist/services/networkService.d.ts +81 -0
  124. package/dist/services/networkService.d.ts.map +1 -0
  125. package/dist/services/networkService.js +268 -0
  126. package/dist/services/networkService.js.map +1 -0
  127. package/dist/services/queueService.d.ts +112 -0
  128. package/dist/services/queueService.d.ts.map +1 -0
  129. package/dist/services/queueService.js +338 -0
  130. package/dist/services/queueService.js.map +1 -0
  131. package/dist/services/settingsService.d.ts +135 -0
  132. package/dist/services/settingsService.d.ts.map +1 -0
  133. package/dist/services/settingsService.js +425 -0
  134. package/dist/services/settingsService.js.map +1 -0
  135. package/dist/services/systemMonitor.d.ts +208 -0
  136. package/dist/services/systemMonitor.d.ts.map +1 -0
  137. package/dist/services/systemMonitor.js +693 -0
  138. package/dist/services/systemMonitor.js.map +1 -0
  139. package/dist/services/updateService.d.ts +78 -0
  140. package/dist/services/updateService.d.ts.map +1 -0
  141. package/dist/services/updateService.js +252 -0
  142. package/dist/services/updateService.js.map +1 -0
  143. package/dist/services/websocketEvents.d.ts +372 -0
  144. package/dist/services/websocketEvents.d.ts.map +1 -0
  145. package/dist/services/websocketEvents.js +338 -0
  146. package/dist/services/websocketEvents.js.map +1 -0
  147. package/dist/services/websocketServer.d.ts +80 -0
  148. package/dist/services/websocketServer.d.ts.map +1 -0
  149. package/dist/services/websocketServer.js +299 -0
  150. package/dist/services/websocketServer.js.map +1 -0
  151. package/dist/settings/SettingsSchema.d.ts +151 -0
  152. package/dist/settings/SettingsSchema.d.ts.map +1 -0
  153. package/dist/settings/SettingsSchema.js +424 -0
  154. package/dist/settings/SettingsSchema.js.map +1 -0
  155. package/dist/testing/TestServer.d.ts +69 -0
  156. package/dist/testing/TestServer.d.ts.map +1 -0
  157. package/dist/testing/TestServer.js +250 -0
  158. package/dist/testing/TestServer.js.map +1 -0
  159. package/dist/types/index.d.ts +137 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/index.js +5 -0
  162. package/dist/types/index.js.map +1 -0
  163. package/dist/utils/appPaths.d.ts +74 -0
  164. package/dist/utils/appPaths.d.ts.map +1 -0
  165. package/dist/utils/appPaths.js +162 -0
  166. package/dist/utils/appPaths.js.map +1 -0
  167. package/dist/utils/fs-utils.d.ts +50 -0
  168. package/dist/utils/fs-utils.d.ts.map +1 -0
  169. package/dist/utils/fs-utils.js +114 -0
  170. package/dist/utils/fs-utils.js.map +1 -0
  171. package/dist/utils/index.d.ts +12 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +10 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/standardConfig.d.ts +61 -0
  176. package/dist/utils/standardConfig.d.ts.map +1 -0
  177. package/dist/utils/standardConfig.js +109 -0
  178. package/dist/utils/standardConfig.js.map +1 -0
  179. package/dist/utils/startupBanner.d.ts +34 -0
  180. package/dist/utils/startupBanner.d.ts.map +1 -0
  181. package/dist/utils/startupBanner.js +169 -0
  182. package/dist/utils/startupBanner.js.map +1 -0
  183. package/dist/utils/startupLogger.d.ts +45 -0
  184. package/dist/utils/startupLogger.d.ts.map +1 -0
  185. package/dist/utils/startupLogger.js +200 -0
  186. package/dist/utils/startupLogger.js.map +1 -0
  187. package/package.json +151 -0
  188. package/src/api/logsRouter.ts +600 -0
  189. package/src/cli/dev-server.ts +803 -0
  190. package/src/cli/index.ts +31 -0
  191. package/src/core/StandardServer.ts +587 -0
  192. package/src/core/apiResponse.ts +202 -0
  193. package/src/core/healthCheck.ts +565 -0
  194. package/src/core/index.ts +80 -0
  195. package/src/core/logger.ts +1092 -0
  196. package/src/core/portUtils.ts +319 -0
  197. package/src/core/storageService.ts +595 -0
  198. package/src/desktop/bundler.ts +271 -0
  199. package/src/desktop/index.ts +18 -0
  200. package/src/desktop/native-modules.ts +289 -0
  201. package/src/index.ts +142 -0
  202. package/src/logging/LogCategories.ts +302 -0
  203. package/src/middleware/aiErrorHandler.ts +278 -0
  204. package/src/middleware/auth.ts +329 -0
  205. package/src/middleware/cors.ts +187 -0
  206. package/src/middleware/errorHandler.ts +103 -0
  207. package/src/middleware/fileUpload.ts +252 -0
  208. package/src/middleware/health.ts +206 -0
  209. package/src/middleware/index.ts +71 -0
  210. package/src/middleware/openapi.ts +305 -0
  211. package/src/middleware/requestLogging.ts +92 -0
  212. package/src/middleware/session.ts +238 -0
  213. package/src/middleware/validation.ts +603 -0
  214. package/src/services/aiService.ts +789 -0
  215. package/src/services/conversationStorage.ts +232 -0
  216. package/src/services/crossPlatformBuffer.ts +341 -0
  217. package/src/services/index.ts +47 -0
  218. package/src/services/networkService.ts +351 -0
  219. package/src/services/queueService.ts +446 -0
  220. package/src/services/settingsService.ts +549 -0
  221. package/src/services/systemMonitor.ts +936 -0
  222. package/src/services/updateService.ts +334 -0
  223. package/src/services/websocketEvents.ts +409 -0
  224. package/src/services/websocketServer.ts +394 -0
  225. package/src/settings/SettingsSchema.ts +664 -0
  226. package/src/testing/TestServer.ts +312 -0
  227. package/src/types/index.ts +154 -0
  228. package/src/utils/appPaths.ts +196 -0
  229. package/src/utils/fs-utils.ts +130 -0
  230. package/src/utils/index.ts +15 -0
  231. package/src/utils/standardConfig.ts +178 -0
  232. package/src/utils/startupBanner.ts +287 -0
  233. package/src/utils/startupLogger.ts +268 -0
  234. package/ui/dist/index.d.mts +1221 -0
  235. package/ui/dist/index.d.ts +1221 -0
  236. package/ui/dist/index.js +73 -0
  237. package/ui/dist/index.js.map +1 -0
  238. package/ui/dist/index.mjs +73 -0
  239. package/ui/dist/index.mjs.map +1 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Authentication Middleware
3
+ * Generic authentication utilities for Express applications
4
+ */
5
+
6
+ import { Request, Response, NextFunction } from "express";
7
+ import { createLogger } from "../core/index.js";
8
+
9
+ let logger: any; // Will be initialized when needed
10
+
11
+ function ensureLogger() {
12
+ if (!logger) {
13
+ logger = createLogger("AuthMiddleware");
14
+ }
15
+ return logger;
16
+ }
17
+
18
+ // Extend Express Request type to include user
19
+ declare global {
20
+ // eslint-disable-next-line @typescript-eslint/no-namespace
21
+ namespace Express {
22
+ interface Request {
23
+ user?: {
24
+ id: string;
25
+ username: string;
26
+ roles?: string[];
27
+ };
28
+ }
29
+ }
30
+ }
31
+
32
+ // Extend express-session types
33
+ declare module "express-session" {
34
+ interface SessionData {
35
+ authenticated?: boolean;
36
+ username?: string;
37
+ userId?: string;
38
+ roles?: string[];
39
+ }
40
+ }
41
+
42
+ export interface AuthConfig {
43
+ loginPath?: string;
44
+ apiPrefix?: string;
45
+ excludePaths?: string[];
46
+ onUnauthorized?: (req: Request, res: Response) => void;
47
+ }
48
+
49
+ /**
50
+ * Creates authentication middleware with configuration
51
+ */
52
+ export function createAuthMiddleware(config: AuthConfig = {}) {
53
+ const {
54
+ loginPath = "/login",
55
+ apiPrefix = "/api/",
56
+ excludePaths = [],
57
+ onUnauthorized,
58
+ } = config;
59
+
60
+ return function requireAuth(
61
+ req: Request,
62
+ res: Response,
63
+ next: NextFunction,
64
+ ): void {
65
+ // Check if path is excluded
66
+ if (excludePaths.some((path) => req.path.startsWith(path))) {
67
+ return next();
68
+ }
69
+
70
+ // Check if user is authenticated
71
+ if (req.session && req.session.authenticated) {
72
+ // Attach user to request
73
+ req.user = {
74
+ id: req.session.userId || "",
75
+ username: req.session.username || "",
76
+ roles: req.session.roles || [],
77
+ };
78
+ return next();
79
+ }
80
+
81
+ // Handle unauthorized access
82
+ if (onUnauthorized) {
83
+ return onUnauthorized(req, res);
84
+ }
85
+
86
+ // Default behavior
87
+ if (req.path.startsWith(apiPrefix)) {
88
+ // For API routes, return JSON error
89
+ res.status(401).json({
90
+ success: false,
91
+ error: "Authentication required",
92
+ message: "Please log in to access this resource",
93
+ });
94
+ } else {
95
+ // For web routes, redirect to login
96
+ res.redirect(loginPath);
97
+ }
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Login handler factory
103
+ */
104
+ export interface LoginCredentials {
105
+ username: string;
106
+ password: string;
107
+ }
108
+
109
+ export interface AuthService {
110
+ validateCredentials: (credentials: LoginCredentials) => Promise<{
111
+ valid: boolean;
112
+ user?: {
113
+ id: string;
114
+ username: string;
115
+ roles?: string[];
116
+ };
117
+ error?: string;
118
+ }>;
119
+ }
120
+
121
+ export function createLoginHandler(authService: AuthService) {
122
+ return async (req: Request, res: Response): Promise<void> => {
123
+ try {
124
+ const { username, password } = req.body;
125
+
126
+ if (!username || !password) {
127
+ res.status(400).json({
128
+ success: false,
129
+ error: "Missing credentials",
130
+ message: "Username and password are required",
131
+ });
132
+ return;
133
+ }
134
+
135
+ const result = await authService.validateCredentials({
136
+ username,
137
+ password,
138
+ });
139
+
140
+ if (result.valid && result.user) {
141
+ // Set session
142
+ req.session.authenticated = true;
143
+ req.session.username = result.user.username;
144
+ req.session.userId = result.user.id;
145
+ req.session.roles = result.user.roles;
146
+
147
+ ensureLogger().info(`User logged in: ${username}`);
148
+
149
+ res.json({
150
+ success: true,
151
+ user: {
152
+ username: result.user.username,
153
+ roles: result.user.roles,
154
+ },
155
+ });
156
+ } else {
157
+ ensureLogger().warn(`Failed login attempt for user: ${username}`);
158
+ res.status(401).json({
159
+ success: false,
160
+ error: "Invalid credentials",
161
+ message: result.error || "Invalid username or password",
162
+ });
163
+ }
164
+ } catch (_error: any) {
165
+ ensureLogger().error("Login error:", _error);
166
+ res.status(500).json({
167
+ success: false,
168
+ error: "Login failed",
169
+ message: "An error occurred during login",
170
+ });
171
+ }
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Logout handler factory
177
+ */
178
+ export function createLogoutHandler() {
179
+ return (req: Request, res: Response): void => {
180
+ const username = req.session?.username;
181
+
182
+ req.session.destroy((err: any) => {
183
+ if (err) {
184
+ ensureLogger().error("Error destroying session:", err);
185
+ res.status(500).json({
186
+ success: false,
187
+ error: "Logout failed",
188
+ });
189
+ return;
190
+ }
191
+
192
+ if (username) {
193
+ ensureLogger().info(`User logged out: ${username}`);
194
+ }
195
+
196
+ res.json({ success: true });
197
+ });
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Auth check handler
203
+ */
204
+ export function createAuthCheckHandler() {
205
+ return (req: Request, res: Response): void => {
206
+ if (req.session?.authenticated) {
207
+ res.json({
208
+ authenticated: true,
209
+ user: {
210
+ username: req.session.username,
211
+ roles: req.session.roles,
212
+ },
213
+ });
214
+ } else {
215
+ res.status(401).json({
216
+ authenticated: false,
217
+ });
218
+ }
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Role-based access control middleware
224
+ */
225
+ export function requireRole(roles: string | string[]) {
226
+ const requiredRoles = Array.isArray(roles) ? roles : [roles];
227
+
228
+ return (req: Request, res: Response, next: NextFunction): void => {
229
+ if (!req.user) {
230
+ res.status(401).json({
231
+ success: false,
232
+ error: "Authentication required",
233
+ });
234
+ return;
235
+ }
236
+
237
+ const userRoles = req.user.roles || [];
238
+ const hasRole = requiredRoles.some((role) => userRoles.includes(role));
239
+
240
+ if (!hasRole) {
241
+ res.status(403).json({
242
+ success: false,
243
+ error: "Insufficient permissions",
244
+ message: `This action requires one of the following roles: ${requiredRoles.join(", ")}`,
245
+ });
246
+ return;
247
+ }
248
+
249
+ next();
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Simple in-memory auth service for development
255
+ */
256
+ export class SimpleAuthService implements AuthService {
257
+ private users: Map<
258
+ string,
259
+ { password: string; id: string; roles?: string[] }
260
+ >;
261
+
262
+ constructor(
263
+ users: Array<{
264
+ username: string;
265
+ password: string;
266
+ id?: string;
267
+ roles?: string[];
268
+ }> = [],
269
+ ) {
270
+ this.users = new Map();
271
+
272
+ // Add default admin user if no users provided
273
+ if (users.length === 0) {
274
+ users = [
275
+ {
276
+ username: "admin",
277
+ password: "admin",
278
+ id: "admin-1",
279
+ roles: ["admin"],
280
+ },
281
+ ];
282
+ }
283
+
284
+ users.forEach((user) => {
285
+ this.users.set(user.username, {
286
+ password: user.password,
287
+ id: user.id || `user-${user.username}`,
288
+ roles: user.roles,
289
+ });
290
+ });
291
+ }
292
+
293
+ async validateCredentials(credentials: LoginCredentials) {
294
+ const user = this.users.get(credentials.username);
295
+
296
+ if (!user || user.password !== credentials.password) {
297
+ return {
298
+ valid: false,
299
+ error: "Invalid username or password",
300
+ };
301
+ }
302
+
303
+ return {
304
+ valid: true,
305
+ user: {
306
+ id: user.id,
307
+ username: credentials.username,
308
+ roles: user.roles,
309
+ },
310
+ };
311
+ }
312
+
313
+ addUser(username: string, password: string, roles?: string[]) {
314
+ this.users.set(username, {
315
+ password,
316
+ id: `user-${username}`,
317
+ roles,
318
+ });
319
+ }
320
+ }
321
+
322
+ export default {
323
+ createAuthMiddleware,
324
+ createLoginHandler,
325
+ createLogoutHandler,
326
+ createAuthCheckHandler,
327
+ requireRole,
328
+ SimpleAuthService,
329
+ };
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Dynamic CORS Middleware
3
+ * Intelligently handles CORS based on environment and configuration
4
+ */
5
+
6
+ import cors from "cors";
7
+ import { createLogger } from "../core/logger.js";
8
+
9
+ const logger = createLogger("CORS");
10
+
11
+ export interface DynamicCorsOptions {
12
+ /**
13
+ * Explicitly allowed origins (optional)
14
+ */
15
+ allowedOrigins?: string[];
16
+
17
+ /**
18
+ * Port ranges to automatically allow in development
19
+ */
20
+ devPortRanges?: Array<{ start: number; end: number }>;
21
+
22
+ /**
23
+ * Common development ports to always allow
24
+ */
25
+ commonDevPorts?: number[];
26
+
27
+ /**
28
+ * Whether to allow credentials
29
+ */
30
+ credentials?: boolean;
31
+
32
+ /**
33
+ * Custom origin validator function
34
+ */
35
+ customValidator?: (origin: string) => boolean;
36
+ }
37
+
38
+ /**
39
+ * Creates dynamic CORS middleware that intelligently handles origins
40
+ */
41
+ export function createDynamicCors(options: DynamicCorsOptions = {}) {
42
+ const {
43
+ allowedOrigins = [],
44
+ devPortRanges = [
45
+ { start: 3000, end: 3010 },
46
+ { start: 5170, end: 5180 },
47
+ { start: 8080, end: 8090 },
48
+ ],
49
+ commonDevPorts = [3000, 3001, 5173, 5174, 5175, 8080, 8081],
50
+ credentials = true,
51
+ customValidator,
52
+ } = options;
53
+
54
+ // Build allowed origins list for development
55
+ const buildDevOrigins = (): string[] => {
56
+ const origins = new Set<string>();
57
+
58
+ // Add explicitly allowed origins
59
+ allowedOrigins.forEach((origin) => origins.add(origin));
60
+
61
+ // Add common development ports
62
+ commonDevPorts.forEach((port) => {
63
+ origins.add(`http://localhost:${port}`);
64
+ origins.add(`http://127.0.0.1:${port}`);
65
+ });
66
+
67
+ // Add port ranges
68
+ devPortRanges.forEach((range) => {
69
+ for (let port = range.start; port <= range.end; port++) {
70
+ origins.add(`http://localhost:${port}`);
71
+ origins.add(`http://127.0.0.1:${port}`);
72
+ }
73
+ });
74
+
75
+ return Array.from(origins);
76
+ };
77
+
78
+ // CORS origin function
79
+ const originFunction = (
80
+ origin: string | undefined,
81
+ callback: (err: Error | null, allow?: boolean | string) => void,
82
+ ) => {
83
+ // Allow requests with no origin (same-origin, Postman, etc.)
84
+ if (!origin) {
85
+ return callback(null, true);
86
+ }
87
+
88
+ // Production mode
89
+ if (process.env.NODE_ENV === "production") {
90
+ // Check against explicit allowed origins
91
+ if (allowedOrigins.length > 0 && allowedOrigins.includes(origin)) {
92
+ return callback(null, true);
93
+ }
94
+
95
+ // Check custom validator
96
+ if (customValidator && customValidator(origin)) {
97
+ return callback(null, true);
98
+ }
99
+
100
+ // Check if origin matches the app's domain
101
+ const appDomain = process.env.APP_DOMAIN || process.env.PRODUCTION_URL;
102
+ if (appDomain && origin.includes(appDomain)) {
103
+ return callback(null, true);
104
+ }
105
+
106
+ // Reject by default in production
107
+ logger.warn(`CORS rejected origin in production: ${origin}`);
108
+ return callback(new Error("Not allowed by CORS"));
109
+ }
110
+
111
+ // Development mode - be more permissive
112
+ const devOrigins = buildDevOrigins();
113
+
114
+ // Check if origin matches any development origin
115
+ if (devOrigins.some((allowed) => origin.startsWith(allowed))) {
116
+ return callback(null, true);
117
+ }
118
+
119
+ // Check if it's any localhost/127.0.0.1 origin
120
+ if (origin.includes("localhost") || origin.includes("127.0.0.1")) {
121
+ logger.debug(`Allowing localhost origin: ${origin}`);
122
+ return callback(null, true);
123
+ }
124
+
125
+ // Check custom validator
126
+ if (customValidator && customValidator(origin)) {
127
+ return callback(null, true);
128
+ }
129
+
130
+ // Log and allow in development (with warning)
131
+ logger.warn(`CORS: Allowing unrecognized origin in development: ${origin}`);
132
+ return callback(null, true);
133
+ };
134
+
135
+ // Create and return configured CORS middleware
136
+ const corsMiddleware = cors({
137
+ origin: originFunction,
138
+ credentials,
139
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
140
+ allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
141
+ exposedHeaders: ["X-Total-Count", "X-Page-Count"],
142
+ maxAge: 86400, // 24 hours
143
+ });
144
+
145
+ // Attach the origin function for testing purposes
146
+ (corsMiddleware as any)._originFunction = originFunction;
147
+
148
+ return corsMiddleware;
149
+ }
150
+
151
+ /**
152
+ * Simple CORS middleware for production with specific domains
153
+ */
154
+ export function createProductionCors(allowedDomains: string[]) {
155
+ const originFunction = (
156
+ origin: string | undefined,
157
+ callback: (err: Error | null, allow?: boolean) => void,
158
+ ) => {
159
+ if (!origin || allowedDomains.includes(origin)) {
160
+ callback(null, true);
161
+ } else {
162
+ callback(new Error("Not allowed by CORS"));
163
+ }
164
+ };
165
+
166
+ const corsMiddleware = cors({
167
+ origin: originFunction,
168
+ credentials: true,
169
+ });
170
+
171
+ // Attach for testing
172
+ (corsMiddleware as any)._originFunction = originFunction;
173
+
174
+ return corsMiddleware;
175
+ }
176
+
177
+ /**
178
+ * Permissive CORS for development
179
+ */
180
+ export function createDevCors() {
181
+ return createDynamicCors({
182
+ allowedOrigins: [],
183
+ credentials: true,
184
+ });
185
+ }
186
+
187
+ export default createDynamicCors;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Error Handling Middleware
3
+ */
4
+
5
+ import { Request, Response, NextFunction } from "express";
6
+ import { createLogger } from "../core/index.js";
7
+
8
+ const logger = createLogger("ErrorHandler");
9
+
10
+ // Custom error class
11
+ export class AppError extends Error {
12
+ public readonly statusCode: number;
13
+ public readonly isOperational: boolean;
14
+
15
+ constructor(
16
+ message: string,
17
+ statusCode: number = 500,
18
+ isOperational: boolean = true,
19
+ ) {
20
+ super(message);
21
+ this.statusCode = statusCode;
22
+ this.isOperational = isOperational;
23
+
24
+ Error.captureStackTrace(this, this.constructor);
25
+ }
26
+ }
27
+
28
+ // 404 Not Found handler
29
+ export function notFoundHandler(req: Request, res: Response) {
30
+ res.status(404).json({
31
+ success: false,
32
+ error: "Not Found",
33
+ message: `Cannot ${req.method} ${req.path}`,
34
+ path: req.path,
35
+ method: req.method,
36
+ });
37
+ }
38
+
39
+ // Global error handler
40
+ export function errorHandler(
41
+ err: any,
42
+ req: Request,
43
+ res: Response,
44
+ _next: NextFunction,
45
+ ) {
46
+ // Default to 500 server error
47
+ let statusCode = err.statusCode || 500;
48
+ let message = err.message || "Internal Server Error";
49
+
50
+ // Log the error
51
+ if (statusCode >= 500) {
52
+ logger.error("Server error", {
53
+ error: err,
54
+ request: {
55
+ method: req.method,
56
+ path: req.path,
57
+ query: req.query,
58
+ body: req.body,
59
+ headers: req.headers,
60
+ },
61
+ });
62
+ } else {
63
+ logger.warn("Client error", {
64
+ statusCode,
65
+ message,
66
+ path: req.path,
67
+ });
68
+ }
69
+
70
+ // Handle specific error types
71
+ if (err.name === "ValidationError") {
72
+ statusCode = 400;
73
+ message = "Validation failed";
74
+ } else if (err.name === "UnauthorizedError") {
75
+ statusCode = 401;
76
+ message = "Unauthorized";
77
+ } else if (err.name === "JsonWebTokenError") {
78
+ statusCode = 401;
79
+ message = "Invalid token";
80
+ } else if (err.name === "TokenExpiredError") {
81
+ statusCode = 401;
82
+ message = "Token expired";
83
+ }
84
+
85
+ // Send error response
86
+ res.status(statusCode).json({
87
+ success: false,
88
+ error: message,
89
+ ...(process.env.NODE_ENV === "development" && {
90
+ stack: err.stack,
91
+ details: err,
92
+ }),
93
+ });
94
+ }
95
+
96
+ // Async handler wrapper to catch errors in async routes
97
+ export function asyncHandler(
98
+ fn: (req: Request, res: Response, next: NextFunction) => any,
99
+ ) {
100
+ return (req: Request, res: Response, next: NextFunction) => {
101
+ Promise.resolve(fn(req, res, next)).catch(next);
102
+ };
103
+ }