fragment-ts 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/.env.example +0 -0
- package/base.ts +1810 -0
- package/base2.ts +968 -0
- package/bin/frg.ts +5 -0
- package/config/fragment.lock.yaml +0 -0
- package/config/fragment.yaml +0 -0
- package/dist/app.d.ts +15 -0
- package/dist/app.js +90 -0
- package/dist/auth/auth.controller.d.ts +10 -0
- package/dist/auth/auth.controller.js +87 -0
- package/dist/auth/auth.middleware.d.ts +2 -0
- package/dist/auth/auth.middleware.js +24 -0
- package/dist/auth/auth.service.d.ts +20 -0
- package/dist/auth/auth.service.js +143 -0
- package/dist/auth/dto/login.dto.d.ts +9 -0
- package/dist/auth/dto/login.dto.js +2 -0
- package/dist/cli/cli.d.ts +12 -0
- package/dist/cli/cli.js +186 -0
- package/dist/cli/commands/build.command.d.ts +3 -0
- package/dist/cli/commands/build.command.js +23 -0
- package/dist/cli/commands/config.command.d.ts +6 -0
- package/dist/cli/commands/config.command.js +284 -0
- package/dist/cli/commands/generate.command.d.ts +8 -0
- package/dist/cli/commands/generate.command.js +180 -0
- package/dist/cli/commands/init.command.d.ts +7 -0
- package/dist/cli/commands/init.command.js +380 -0
- package/dist/cli/commands/migrate.command.d.ts +7 -0
- package/dist/cli/commands/migrate.command.js +116 -0
- package/dist/cli/commands/serve.command.d.ts +6 -0
- package/dist/cli/commands/serve.command.js +31 -0
- package/dist/cli/templates/controller.template.d.ts +1 -0
- package/dist/cli/templates/controller.template.js +52 -0
- package/dist/cli/templates/entity.template.d.ts +1 -0
- package/dist/cli/templates/entity.template.js +23 -0
- package/dist/cli/templates/repository.template.d.ts +1 -0
- package/dist/cli/templates/repository.template.js +43 -0
- package/dist/cli/templates/service.template.d.ts +1 -0
- package/dist/cli/templates/service.template.js +43 -0
- package/dist/cli/utils/file-generator.d.ts +9 -0
- package/dist/cli/utils/file-generator.js +67 -0
- package/dist/cli/utils/logger.d.ts +14 -0
- package/dist/cli/utils/logger.js +49 -0
- package/dist/controllers/health.controller.d.ts +13 -0
- package/dist/controllers/health.controller.js +50 -0
- package/dist/core/config/config-loader.d.ts +31 -0
- package/dist/core/config/config-loader.js +98 -0
- package/dist/core/container/di-container.d.ts +9 -0
- package/dist/core/container/di-container.js +37 -0
- package/dist/core/decorators/auth-guard.decorator.d.ts +3 -0
- package/dist/core/decorators/auth-guard.decorator.js +18 -0
- package/dist/core/decorators/autowire.decorator.d.ts +3 -0
- package/dist/core/decorators/autowire.decorator.js +17 -0
- package/dist/core/decorators/controller.decorator.d.ts +4 -0
- package/dist/core/decorators/controller.decorator.js +16 -0
- package/dist/core/decorators/injectable.decorator.d.ts +3 -0
- package/dist/core/decorators/injectable.decorator.js +14 -0
- package/dist/core/decorators/middleware.decorator.d.ts +3 -0
- package/dist/core/decorators/middleware.decorator.js +20 -0
- package/dist/core/decorators/repository.decorator.d.ts +1 -0
- package/dist/core/decorators/repository.decorator.js +7 -0
- package/dist/core/decorators/route.decorator.d.ts +14 -0
- package/dist/core/decorators/route.decorator.js +32 -0
- package/dist/core/decorators/service.decorator.d.ts +1 -0
- package/dist/core/decorators/service.decorator.js +7 -0
- package/dist/core/openai/openai-client.d.ts +12 -0
- package/dist/core/openai/openai-client.js +93 -0
- package/dist/database/data-source.d.ts +4 -0
- package/dist/database/data-source.js +26 -0
- package/dist/entities/session.entity.d.ts +9 -0
- package/dist/entities/session.entity.js +45 -0
- package/dist/entities/user.entity.d.ts +10 -0
- package/dist/entities/user.entity.js +48 -0
- package/dist/middlewares/logging.middleware.d.ts +2 -0
- package/dist/middlewares/logging.middleware.js +28 -0
- package/dist/repositories/session.repository.d.ts +9 -0
- package/dist/repositories/session.repository.js +50 -0
- package/dist/repositories/user.repository.d.ts +10 -0
- package/dist/repositories/user.repository.js +43 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +30 -0
- package/dist/services/health.service.d.ts +13 -0
- package/dist/services/health.service.js +44 -0
- package/package.json +46 -0
- package/readme.md +120 -0
- package/src/app.ts +121 -0
- package/src/auth/auth.controller.ts +52 -0
- package/src/auth/auth.middleware.ts +27 -0
- package/src/auth/auth.service.ts +110 -0
- package/src/auth/dto/login.dto.ts +11 -0
- package/src/cli/cli.ts +212 -0
- package/src/cli/commands/build.command.ts +24 -0
- package/src/cli/commands/config.command.ts +280 -0
- package/src/cli/commands/generate.command.ts +170 -0
- package/src/cli/commands/init.command.ts +395 -0
- package/src/cli/commands/migrate.command.ts +118 -0
- package/src/cli/commands/serve.command.ts +37 -0
- package/src/cli/templates/controller.template.ts +51 -0
- package/src/cli/templates/entity.template.ts +22 -0
- package/src/cli/templates/repository.template.ts +42 -0
- package/src/cli/templates/service.template.ts +42 -0
- package/src/cli/utils/file-generator.ts +37 -0
- package/src/cli/utils/logger.ts +52 -0
- package/src/controllers/health.controller.ts +24 -0
- package/src/core/config/config-loader.ts +98 -0
- package/src/core/container/di-container.ts +43 -0
- package/src/core/decorators/auth-guard.decorator.ts +15 -0
- package/src/core/decorators/autowire.decorator.ts +18 -0
- package/src/core/decorators/controller.decorator.ts +15 -0
- package/src/core/decorators/injectable.decorator.ts +13 -0
- package/src/core/decorators/middleware.decorator.ts +18 -0
- package/src/core/decorators/repository.decorator.ts +6 -0
- package/src/core/decorators/route.decorator.ts +33 -0
- package/src/core/decorators/service.decorator.ts +6 -0
- package/src/core/openai/openai-client.ts +99 -0
- package/src/database/data-source.ts +29 -0
- package/src/entities/session.entity.ts +25 -0
- package/src/entities/user.entity.ts +27 -0
- package/src/middlewares/logging.middleware.ts +28 -0
- package/src/repositories/session.repository.ts +42 -0
- package/src/repositories/user.repository.ts +37 -0
- package/src/server.ts +32 -0
- package/src/services/health.service.ts +29 -0
- package/tsconfig.json +20 -0
package/base2.ts
ADDED
|
@@ -0,0 +1,968 @@
|
|
|
1
|
+
// // ============================================================================
|
|
2
|
+
// // PROJECT STRUCTURE
|
|
3
|
+
// // ============================================================================
|
|
4
|
+
// /*
|
|
5
|
+
// springts-framework/
|
|
6
|
+
// ├── src/
|
|
7
|
+
// │ ├── core/
|
|
8
|
+
// │ │ ├── decorators/
|
|
9
|
+
// │ │ │ ├── controller.decorator.ts
|
|
10
|
+
// │ │ │ ├── service.decorator.ts
|
|
11
|
+
// │ │ │ ├── repository.decorator.ts
|
|
12
|
+
// │ │ │ ├── injectable.decorator.ts
|
|
13
|
+
// │ │ │ ├── autowire.decorator.ts
|
|
14
|
+
// │ │ │ ├── route.decorator.ts
|
|
15
|
+
// │ │ │ ├── middleware.decorator.ts
|
|
16
|
+
// │ │ │ └── auth-guard.decorator.ts
|
|
17
|
+
// │ │ ├── container/
|
|
18
|
+
// │ │ │ └── di-container.ts
|
|
19
|
+
// │ │ ├── config/
|
|
20
|
+
// │ │ │ └── config-loader.ts
|
|
21
|
+
// │ │ └── openai/
|
|
22
|
+
// │ │ └── openai-client.ts
|
|
23
|
+
// │ ├── auth/
|
|
24
|
+
// │ │ ├── auth.service.ts
|
|
25
|
+
// │ │ ├── auth.controller.ts
|
|
26
|
+
// │ │ ├── auth.middleware.ts
|
|
27
|
+
// │ │ └── dto/
|
|
28
|
+
// │ │ └── login.dto.ts
|
|
29
|
+
// │ ├── entities/
|
|
30
|
+
// │ │ ├── user.entity.ts
|
|
31
|
+
// │ │ └── session.entity.ts
|
|
32
|
+
// │ ├── repositories/
|
|
33
|
+
// │ │ ├── user.repository.ts
|
|
34
|
+
// │ │ └── session.repository.ts
|
|
35
|
+
// │ ├── services/
|
|
36
|
+
// │ │ └── health.service.ts
|
|
37
|
+
// │ ├── controllers/
|
|
38
|
+
// │ │ └── health.controller.ts
|
|
39
|
+
// │ ├── middlewares/
|
|
40
|
+
// │ │ └── logging.middleware.ts
|
|
41
|
+
// │ ├── database/
|
|
42
|
+
// │ │ └── data-source.ts
|
|
43
|
+
// │ ├── app.ts
|
|
44
|
+
// │ └── server.ts
|
|
45
|
+
// ├── config/
|
|
46
|
+
// │ ├── app.yaml
|
|
47
|
+
// │ └── app.lock.yaml
|
|
48
|
+
// ├── package.yaml
|
|
49
|
+
// ├── package.lock.yaml
|
|
50
|
+
// ├── tsconfig.json
|
|
51
|
+
// └── .env.example
|
|
52
|
+
// */
|
|
53
|
+
|
|
54
|
+
// // ============================================================================
|
|
55
|
+
// // FILE: src/core/decorators/injectable.decorator.ts
|
|
56
|
+
// // ============================================================================
|
|
57
|
+
// import 'reflect-metadata';
|
|
58
|
+
// import { DIContainer } from '../container/di-container';
|
|
59
|
+
|
|
60
|
+
// export const INJECTABLE_METADATA = 'injectable:metadata';
|
|
61
|
+
|
|
62
|
+
// export function Injectable(): ClassDecorator {
|
|
63
|
+
// return (target: any) => {
|
|
64
|
+
// Reflect.defineMetadata(INJECTABLE_METADATA, true, target);
|
|
65
|
+
// DIContainer.register(target);
|
|
66
|
+
// return target;
|
|
67
|
+
// };
|
|
68
|
+
// }
|
|
69
|
+
|
|
70
|
+
// // ============================================================================
|
|
71
|
+
// // FILE: src/core/decorators/service.decorator.ts
|
|
72
|
+
// // ============================================================================
|
|
73
|
+
// import { Injectable } from './injectable.decorator';
|
|
74
|
+
|
|
75
|
+
// export function Service(): ClassDecorator {
|
|
76
|
+
// return Injectable();
|
|
77
|
+
// }
|
|
78
|
+
|
|
79
|
+
// // ============================================================================
|
|
80
|
+
// // FILE: src/core/decorators/repository.decorator.ts
|
|
81
|
+
// // ============================================================================
|
|
82
|
+
// import { Injectable } from './injectable.decorator';
|
|
83
|
+
|
|
84
|
+
// export function Repository(): ClassDecorator {
|
|
85
|
+
// return Injectable();
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// // ============================================================================
|
|
89
|
+
// // FILE: src/core/decorators/controller.decorator.ts
|
|
90
|
+
// // ============================================================================
|
|
91
|
+
// import 'reflect-metadata';
|
|
92
|
+
// import { DIContainer } from '../container/di-container';
|
|
93
|
+
|
|
94
|
+
// export const CONTROLLER_METADATA = 'controller:metadata';
|
|
95
|
+
// export const CONTROLLER_PATH = 'controller:path';
|
|
96
|
+
|
|
97
|
+
// export function Controller(path: string = ''): ClassDecorator {
|
|
98
|
+
// return (target: any) => {
|
|
99
|
+
// Reflect.defineMetadata(CONTROLLER_METADATA, true, target);
|
|
100
|
+
// Reflect.defineMetadata(CONTROLLER_PATH, path, target);
|
|
101
|
+
// DIContainer.register(target);
|
|
102
|
+
// return target;
|
|
103
|
+
// };
|
|
104
|
+
// }
|
|
105
|
+
|
|
106
|
+
// // ============================================================================
|
|
107
|
+
// // FILE: src/core/decorators/autowire.decorator.ts
|
|
108
|
+
// // ============================================================================
|
|
109
|
+
// import 'reflect-metadata';
|
|
110
|
+
|
|
111
|
+
// export const AUTOWIRE_METADATA = 'autowire:metadata';
|
|
112
|
+
|
|
113
|
+
// export function Autowire(): ParameterDecorator {
|
|
114
|
+
// return (target: any, propertyKey: string | symbol | undefined, parameterIndex: number) => {
|
|
115
|
+
// const existingParams = Reflect.getOwnMetadata(AUTOWIRE_METADATA, target) || [];
|
|
116
|
+
// const types = Reflect.getMetadata('design:paramtypes', target) || [];
|
|
117
|
+
|
|
118
|
+
// existingParams.push({
|
|
119
|
+
// index: parameterIndex,
|
|
120
|
+
// type: types[parameterIndex]
|
|
121
|
+
// });
|
|
122
|
+
|
|
123
|
+
// Reflect.defineMetadata(AUTOWIRE_METADATA, existingParams, target);
|
|
124
|
+
// };
|
|
125
|
+
// }
|
|
126
|
+
|
|
127
|
+
// // ============================================================================
|
|
128
|
+
// // FILE: src/core/decorators/route.decorator.ts
|
|
129
|
+
// // ============================================================================
|
|
130
|
+
// import 'reflect-metadata';
|
|
131
|
+
|
|
132
|
+
// export const ROUTE_METADATA = 'route:metadata';
|
|
133
|
+
|
|
134
|
+
// export enum HttpMethod {
|
|
135
|
+
// GET = 'get',
|
|
136
|
+
// POST = 'post',
|
|
137
|
+
// PUT = 'put',
|
|
138
|
+
// DELETE = 'delete',
|
|
139
|
+
// PATCH = 'patch'
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
// function createRouteDecorator(method: HttpMethod) {
|
|
143
|
+
// return (path: string = ''): MethodDecorator => {
|
|
144
|
+
// return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
|
145
|
+
// const routes = Reflect.getOwnMetadata(ROUTE_METADATA, target.constructor) || [];
|
|
146
|
+
// routes.push({
|
|
147
|
+
// method,
|
|
148
|
+
// path,
|
|
149
|
+
// handlerName: propertyKey
|
|
150
|
+
// });
|
|
151
|
+
// Reflect.defineMetadata(ROUTE_METADATA, routes, target.constructor);
|
|
152
|
+
// return descriptor;
|
|
153
|
+
// };
|
|
154
|
+
// };
|
|
155
|
+
// }
|
|
156
|
+
|
|
157
|
+
// export const Get = createRouteDecorator(HttpMethod.GET);
|
|
158
|
+
// export const Post = createRouteDecorator(HttpMethod.POST);
|
|
159
|
+
// export const Put = createRouteDecorator(HttpMethod.PUT);
|
|
160
|
+
// export const Delete = createRouteDecorator(HttpMethod.DELETE);
|
|
161
|
+
// export const Patch = createRouteDecorator(HttpMethod.PATCH);
|
|
162
|
+
|
|
163
|
+
// // ============================================================================
|
|
164
|
+
// // FILE: src/core/decorators/middleware.decorator.ts
|
|
165
|
+
// // ============================================================================
|
|
166
|
+
// import 'reflect-metadata';
|
|
167
|
+
|
|
168
|
+
// export const MIDDLEWARE_METADATA = 'middleware:metadata';
|
|
169
|
+
|
|
170
|
+
// export function Middleware(...middlewares: Function[]): ClassDecorator | MethodDecorator {
|
|
171
|
+
// return (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
172
|
+
// if (propertyKey) {
|
|
173
|
+
// // Method-level middleware
|
|
174
|
+
// const existing = Reflect.getOwnMetadata(MIDDLEWARE_METADATA, target.constructor, propertyKey) || [];
|
|
175
|
+
// Reflect.defineMetadata(MIDDLEWARE_METADATA, [...existing, ...middlewares], target.constructor, propertyKey);
|
|
176
|
+
// } else {
|
|
177
|
+
// // Class-level middleware
|
|
178
|
+
// const existing = Reflect.getOwnMetadata(MIDDLEWARE_METADATA, target) || [];
|
|
179
|
+
// Reflect.defineMetadata(MIDDLEWARE_METADATA, [...existing, ...middlewares], target);
|
|
180
|
+
// }
|
|
181
|
+
// };
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
// // ============================================================================
|
|
185
|
+
// // FILE: src/core/decorators/auth-guard.decorator.ts
|
|
186
|
+
// // ============================================================================
|
|
187
|
+
// import 'reflect-metadata';
|
|
188
|
+
|
|
189
|
+
// export const AUTH_GUARD_METADATA = 'auth:guard';
|
|
190
|
+
|
|
191
|
+
// export function AuthGuard(): ClassDecorator | MethodDecorator {
|
|
192
|
+
// return (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
193
|
+
// if (propertyKey) {
|
|
194
|
+
// Reflect.defineMetadata(AUTH_GUARD_METADATA, true, target.constructor, propertyKey);
|
|
195
|
+
// } else {
|
|
196
|
+
// Reflect.defineMetadata(AUTH_GUARD_METADATA, true, target);
|
|
197
|
+
// }
|
|
198
|
+
// };
|
|
199
|
+
// }
|
|
200
|
+
|
|
201
|
+
// // ============================================================================
|
|
202
|
+
// // FILE: src/core/container/di-container.ts
|
|
203
|
+
// // ============================================================================
|
|
204
|
+
// import 'reflect-metadata';
|
|
205
|
+
// import { AUTOWIRE_METADATA } from '../decorators/autowire.decorator';
|
|
206
|
+
|
|
207
|
+
// export class DIContainer {
|
|
208
|
+
// private static instances = new Map<any, any>();
|
|
209
|
+
// private static classes = new Set<any>();
|
|
210
|
+
|
|
211
|
+
// static register(target: any): void {
|
|
212
|
+
// this.classes.add(target);
|
|
213
|
+
// }
|
|
214
|
+
|
|
215
|
+
// static resolve<T>(target: any): T {
|
|
216
|
+
// // Return existing singleton if available
|
|
217
|
+
// if (this.instances.has(target)) {
|
|
218
|
+
// return this.instances.get(target);
|
|
219
|
+
// }
|
|
220
|
+
|
|
221
|
+
// // Get constructor parameter metadata
|
|
222
|
+
// const params = Reflect.getOwnMetadata(AUTOWIRE_METADATA, target) || [];
|
|
223
|
+
// const sortedParams = params.sort((a: any, b: any) => a.index - b.index);
|
|
224
|
+
|
|
225
|
+
// // Resolve dependencies recursively
|
|
226
|
+
// const dependencies = sortedParams.map((param: any) => {
|
|
227
|
+
// return this.resolve(param.type);
|
|
228
|
+
// });
|
|
229
|
+
|
|
230
|
+
// // Create instance
|
|
231
|
+
// const instance = new target(...dependencies);
|
|
232
|
+
// this.instances.set(target, instance);
|
|
233
|
+
|
|
234
|
+
// return instance;
|
|
235
|
+
// }
|
|
236
|
+
|
|
237
|
+
// static getRegisteredClasses(): any[] {
|
|
238
|
+
// return Array.from(this.classes);
|
|
239
|
+
// }
|
|
240
|
+
|
|
241
|
+
// static clear(): void {
|
|
242
|
+
// this.instances.clear();
|
|
243
|
+
// this.classes.clear();
|
|
244
|
+
// }
|
|
245
|
+
// }
|
|
246
|
+
|
|
247
|
+
// // ============================================================================
|
|
248
|
+
// // FILE: src/core/config/config-loader.ts
|
|
249
|
+
// // ============================================================================
|
|
250
|
+
// import * as fs from 'fs';
|
|
251
|
+
// import * as path from 'path';
|
|
252
|
+
// import * as yaml from 'js-yaml';
|
|
253
|
+
|
|
254
|
+
// export interface AppConfig {
|
|
255
|
+
// app: {
|
|
256
|
+
// name: string;
|
|
257
|
+
// port: number;
|
|
258
|
+
// env: string;
|
|
259
|
+
// };
|
|
260
|
+
// database: {
|
|
261
|
+
// type: string;
|
|
262
|
+
// host: string;
|
|
263
|
+
// port: number;
|
|
264
|
+
// username: string;
|
|
265
|
+
// password: string;
|
|
266
|
+
// database: string;
|
|
267
|
+
// synchronize: boolean;
|
|
268
|
+
// logging: boolean;
|
|
269
|
+
// };
|
|
270
|
+
// openai: {
|
|
271
|
+
// apiKey: string;
|
|
272
|
+
// model: string;
|
|
273
|
+
// };
|
|
274
|
+
// auth: {
|
|
275
|
+
// tokenExpiry: string;
|
|
276
|
+
// sessionExpiry: string;
|
|
277
|
+
// };
|
|
278
|
+
// }
|
|
279
|
+
|
|
280
|
+
// export class ConfigLoader {
|
|
281
|
+
// private static config: AppConfig | null = null;
|
|
282
|
+
|
|
283
|
+
// static load(useYaml: boolean = true): AppConfig {
|
|
284
|
+
// if (this.config) {
|
|
285
|
+
// return this.config;
|
|
286
|
+
// }
|
|
287
|
+
|
|
288
|
+
// const configDir = path.join(process.cwd(), 'config');
|
|
289
|
+
// let configPath: string;
|
|
290
|
+
|
|
291
|
+
// if (useYaml) {
|
|
292
|
+
// // Try .lock.yaml first, then .yaml
|
|
293
|
+
// const lockPath = path.join(configDir, 'app.lock.yaml');
|
|
294
|
+
// const yamlPath = path.join(configDir, 'app.yaml');
|
|
295
|
+
|
|
296
|
+
// configPath = fs.existsSync(lockPath) ? lockPath : yamlPath;
|
|
297
|
+
|
|
298
|
+
// if (!fs.existsSync(configPath)) {
|
|
299
|
+
// throw new Error('Configuration file not found');
|
|
300
|
+
// }
|
|
301
|
+
|
|
302
|
+
// const fileContent = fs.readFileSync(configPath, 'utf8');
|
|
303
|
+
// this.config = yaml.load(fileContent) as AppConfig;
|
|
304
|
+
// } else {
|
|
305
|
+
// // JSON fallback
|
|
306
|
+
// configPath = path.join(configDir, 'app.json');
|
|
307
|
+
// const fileContent = fs.readFileSync(configPath, 'utf8');
|
|
308
|
+
// this.config = JSON.parse(fileContent);
|
|
309
|
+
// }
|
|
310
|
+
|
|
311
|
+
// // Override with environment variables
|
|
312
|
+
// this.config = this.mergeEnvVariables(this.config);
|
|
313
|
+
|
|
314
|
+
// return this.config;
|
|
315
|
+
// }
|
|
316
|
+
|
|
317
|
+
// private static mergeEnvVariables(config: AppConfig): AppConfig {
|
|
318
|
+
// return {
|
|
319
|
+
// ...config,
|
|
320
|
+
// app: {
|
|
321
|
+
// ...config.app,
|
|
322
|
+
// port: process.env.PORT ? parseInt(process.env.PORT) : config.app.port,
|
|
323
|
+
// env: process.env.NODE_ENV || config.app.env
|
|
324
|
+
// },
|
|
325
|
+
// database: {
|
|
326
|
+
// ...config.database,
|
|
327
|
+
// host: process.env.DB_HOST || config.database.host,
|
|
328
|
+
// port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : config.database.port,
|
|
329
|
+
// username: process.env.DB_USERNAME || config.database.username,
|
|
330
|
+
// password: process.env.DB_PASSWORD || config.database.password,
|
|
331
|
+
// database: process.env.DB_DATABASE || config.database.database
|
|
332
|
+
// },
|
|
333
|
+
// openai: {
|
|
334
|
+
// ...config.openai,
|
|
335
|
+
// apiKey: process.env.OPENAI_API_KEY || config.openai.apiKey
|
|
336
|
+
// }
|
|
337
|
+
// };
|
|
338
|
+
// }
|
|
339
|
+
|
|
340
|
+
// static get(): AppConfig {
|
|
341
|
+
// if (!this.config) {
|
|
342
|
+
// throw new Error('Config not loaded. Call ConfigLoader.load() first.');
|
|
343
|
+
// }
|
|
344
|
+
// return this.config;
|
|
345
|
+
// }
|
|
346
|
+
// }
|
|
347
|
+
|
|
348
|
+
// // ============================================================================
|
|
349
|
+
// // FILE: src/core/openai/openai-client.ts
|
|
350
|
+
// // ============================================================================
|
|
351
|
+
// import { Injectable } from '../decorators/injectable.decorator';
|
|
352
|
+
// import { ConfigLoader } from '../config/config-loader';
|
|
353
|
+
|
|
354
|
+
// @Injectable()
|
|
355
|
+
// export class OpenAIClient {
|
|
356
|
+
// private apiKey: string;
|
|
357
|
+
// private model: string;
|
|
358
|
+
// private baseURL: string = 'https://api.openai.com/v1';
|
|
359
|
+
|
|
360
|
+
// constructor() {
|
|
361
|
+
// const config = ConfigLoader.get();
|
|
362
|
+
// this.apiKey = config.openai.apiKey;
|
|
363
|
+
// this.model = config.openai.model;
|
|
364
|
+
// }
|
|
365
|
+
|
|
366
|
+
// async complete(prompt: string, options?: {
|
|
367
|
+
// model?: string;
|
|
368
|
+
// maxTokens?: number;
|
|
369
|
+
// temperature?: number;
|
|
370
|
+
// }): Promise<string> {
|
|
371
|
+
// const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
372
|
+
// method: 'POST',
|
|
373
|
+
// headers: {
|
|
374
|
+
// 'Content-Type': 'application/json',
|
|
375
|
+
// 'Authorization': `Bearer ${this.apiKey}`
|
|
376
|
+
// },
|
|
377
|
+
// body: JSON.stringify({
|
|
378
|
+
// model: options?.model || this.model,
|
|
379
|
+
// messages: [{ role: 'user', content: prompt }],
|
|
380
|
+
// max_tokens: options?.maxTokens || 1000,
|
|
381
|
+
// temperature: options?.temperature || 0.7
|
|
382
|
+
// })
|
|
383
|
+
// });
|
|
384
|
+
|
|
385
|
+
// if (!response.ok) {
|
|
386
|
+
// throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
387
|
+
// }
|
|
388
|
+
|
|
389
|
+
// const data = await response.json();
|
|
390
|
+
// return data.choices[0].message.content;
|
|
391
|
+
// }
|
|
392
|
+
|
|
393
|
+
// async streamComplete(prompt: string, onChunk: (chunk: string) => void): Promise<void> {
|
|
394
|
+
// const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
395
|
+
// method: 'POST',
|
|
396
|
+
// headers: {
|
|
397
|
+
// 'Content-Type': 'application/json',
|
|
398
|
+
// 'Authorization': `Bearer ${this.apiKey}`
|
|
399
|
+
// },
|
|
400
|
+
// body: JSON.stringify({
|
|
401
|
+
// model: this.model,
|
|
402
|
+
// messages: [{ role: 'user', content: prompt }],
|
|
403
|
+
// stream: true
|
|
404
|
+
// })
|
|
405
|
+
// });
|
|
406
|
+
|
|
407
|
+
// if (!response.ok) {
|
|
408
|
+
// throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
409
|
+
// }
|
|
410
|
+
|
|
411
|
+
// const reader = response.body?.getReader();
|
|
412
|
+
// const decoder = new TextDecoder();
|
|
413
|
+
|
|
414
|
+
// if (!reader) {
|
|
415
|
+
// throw new Error('No response body');
|
|
416
|
+
// }
|
|
417
|
+
|
|
418
|
+
// while (true) {
|
|
419
|
+
// const { done, value } = await reader.read();
|
|
420
|
+
// if (done) break;
|
|
421
|
+
|
|
422
|
+
// const chunk = decoder.decode(value);
|
|
423
|
+
// const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
424
|
+
|
|
425
|
+
// for (const line of lines) {
|
|
426
|
+
// if (line.startsWith('data: ')) {
|
|
427
|
+
// const data = line.slice(6);
|
|
428
|
+
// if (data === '[DONE]') continue;
|
|
429
|
+
|
|
430
|
+
// try {
|
|
431
|
+
// const parsed = JSON.parse(data);
|
|
432
|
+
// const content = parsed.choices[0]?.delta?.content;
|
|
433
|
+
// if (content) {
|
|
434
|
+
// onChunk(content);
|
|
435
|
+
// }
|
|
436
|
+
// } catch (e) {
|
|
437
|
+
// // Skip invalid JSON
|
|
438
|
+
// }
|
|
439
|
+
// }
|
|
440
|
+
// }
|
|
441
|
+
// }
|
|
442
|
+
// }
|
|
443
|
+
// }
|
|
444
|
+
|
|
445
|
+
// // ============================================================================
|
|
446
|
+
// // FILE: src/entities/user.entity.ts
|
|
447
|
+
// // ============================================================================
|
|
448
|
+
// import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
|
449
|
+
// import { Session } from './session.entity';
|
|
450
|
+
|
|
451
|
+
// @Entity('users')
|
|
452
|
+
// export class User {
|
|
453
|
+
// @PrimaryGeneratedColumn()
|
|
454
|
+
// id!: number;
|
|
455
|
+
|
|
456
|
+
// @Column({ length: 100 })
|
|
457
|
+
// name!: string;
|
|
458
|
+
|
|
459
|
+
// @Column({ unique: true, length: 255 })
|
|
460
|
+
// email!: string;
|
|
461
|
+
|
|
462
|
+
// @Column({ length: 255 })
|
|
463
|
+
// passwordHash!: string;
|
|
464
|
+
|
|
465
|
+
// @OneToMany(() => Session, (session) => session.user)
|
|
466
|
+
// sessions!: Session[];
|
|
467
|
+
|
|
468
|
+
// @CreateDateColumn()
|
|
469
|
+
// createdAt!: Date;
|
|
470
|
+
|
|
471
|
+
// @UpdateDateColumn()
|
|
472
|
+
// updatedAt!: Date;
|
|
473
|
+
// }
|
|
474
|
+
|
|
475
|
+
// // ============================================================================
|
|
476
|
+
// // FILE: src/entities/session.entity.ts
|
|
477
|
+
// // ============================================================================
|
|
478
|
+
// import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm';
|
|
479
|
+
// import { User } from './user.entity';
|
|
480
|
+
|
|
481
|
+
// @Entity('sessions')
|
|
482
|
+
// export class Session {
|
|
483
|
+
// @PrimaryGeneratedColumn()
|
|
484
|
+
// id!: number;
|
|
485
|
+
|
|
486
|
+
// @Column({ unique: true, length: 255 })
|
|
487
|
+
// token!: string;
|
|
488
|
+
|
|
489
|
+
// @Column()
|
|
490
|
+
// userId!: number;
|
|
491
|
+
|
|
492
|
+
// @ManyToOne(() => User, (user) => user.sessions, { onDelete: 'CASCADE' })
|
|
493
|
+
// @JoinColumn({ name: 'userId' })
|
|
494
|
+
// user!: User;
|
|
495
|
+
|
|
496
|
+
// @Column()
|
|
497
|
+
// expiresAt!: Date;
|
|
498
|
+
|
|
499
|
+
// @CreateDateColumn()
|
|
500
|
+
// createdAt!: Date;
|
|
501
|
+
// }
|
|
502
|
+
|
|
503
|
+
// // ============================================================================
|
|
504
|
+
// // FILE: src/repositories/user.repository.ts
|
|
505
|
+
// // ============================================================================
|
|
506
|
+
// import { Repository as TypeORMRepository } from 'typeorm';
|
|
507
|
+
// import { Repository } from '../core/decorators/repository.decorator';
|
|
508
|
+
// import { User } from '../entities/user.entity';
|
|
509
|
+
// import { AppDataSource } from '../database/data-source';
|
|
510
|
+
|
|
511
|
+
// @Repository()
|
|
512
|
+
// export class UserRepository {
|
|
513
|
+
// private repository: TypeORMRepository<User>;
|
|
514
|
+
|
|
515
|
+
// constructor() {
|
|
516
|
+
// this.repository = AppDataSource.getRepository(User);
|
|
517
|
+
// }
|
|
518
|
+
|
|
519
|
+
// async findById(id: number): Promise<User | null> {
|
|
520
|
+
// return await this.repository.findOne({ where: { id } });
|
|
521
|
+
// }
|
|
522
|
+
|
|
523
|
+
// async findByEmail(email: string): Promise<User | null> {
|
|
524
|
+
// return await this.repository.findOne({ where: { email } });
|
|
525
|
+
// }
|
|
526
|
+
|
|
527
|
+
// async create(userData: Partial<User>): Promise<User> {
|
|
528
|
+
// const user = this.repository.create(userData);
|
|
529
|
+
// return await this.repository.save(user);
|
|
530
|
+
// }
|
|
531
|
+
|
|
532
|
+
// async update(id: number, userData: Partial<User>): Promise<User | null> {
|
|
533
|
+
// await this.repository.update(id, userData);
|
|
534
|
+
// return await this.findById(id);
|
|
535
|
+
// }
|
|
536
|
+
|
|
537
|
+
// async delete(id: number): Promise<boolean> {
|
|
538
|
+
// const result = await this.repository.delete(id);
|
|
539
|
+
// return result.affected ? result.affected > 0 : false;
|
|
540
|
+
// }
|
|
541
|
+
// }
|
|
542
|
+
|
|
543
|
+
// // ============================================================================
|
|
544
|
+
// // FILE: src/repositories/session.repository.ts
|
|
545
|
+
// // ============================================================================
|
|
546
|
+
// import { Repository as TypeORMRepository, MoreThan } from 'typeorm';
|
|
547
|
+
// import { Repository } from '../core/decorators/repository.decorator';
|
|
548
|
+
// import { Session } from '../entities/session.entity';
|
|
549
|
+
// import { AppDataSource } from '../database/data-source';
|
|
550
|
+
|
|
551
|
+
// @Repository()
|
|
552
|
+
// export class SessionRepository {
|
|
553
|
+
// private repository: TypeORMRepository<Session>;
|
|
554
|
+
|
|
555
|
+
// constructor() {
|
|
556
|
+
// this.repository = AppDataSource.getRepository(Session);
|
|
557
|
+
// }
|
|
558
|
+
|
|
559
|
+
// async findByToken(token: string): Promise<Session | null> {
|
|
560
|
+
// return await this.repository.findOne({
|
|
561
|
+
// where: {
|
|
562
|
+
// token,
|
|
563
|
+
// expiresAt: MoreThan(new Date())
|
|
564
|
+
// },
|
|
565
|
+
// relations: ['user']
|
|
566
|
+
// });
|
|
567
|
+
// }
|
|
568
|
+
|
|
569
|
+
// async create(sessionData: Partial<Session>): Promise<Session> {
|
|
570
|
+
// const session = this.repository.create(sessionData);
|
|
571
|
+
// return await this.repository.save(session);
|
|
572
|
+
// }
|
|
573
|
+
|
|
574
|
+
// async deleteByToken(token: string): Promise<boolean> {
|
|
575
|
+
// const result = await this.repository.delete({ token });
|
|
576
|
+
// return result.affected ? result.affected > 0 : false;
|
|
577
|
+
// }
|
|
578
|
+
|
|
579
|
+
// async deleteExpired(): Promise<void> {
|
|
580
|
+
// await this.repository
|
|
581
|
+
// .createQueryBuilder()
|
|
582
|
+
// .delete()
|
|
583
|
+
// .where('expiresAt < :now', { now: new Date() })
|
|
584
|
+
// .execute();
|
|
585
|
+
// }
|
|
586
|
+
// }
|
|
587
|
+
|
|
588
|
+
// // ============================================================================
|
|
589
|
+
// // FILE: src/services/health.service.ts
|
|
590
|
+
// // ============================================================================
|
|
591
|
+
// import { Service } from '../core/decorators/service.decorator';
|
|
592
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
593
|
+
// import { OpenAIClient } from '../core/openai/openai-client';
|
|
594
|
+
|
|
595
|
+
// @Service()
|
|
596
|
+
// export class HealthService {
|
|
597
|
+
// constructor(
|
|
598
|
+
// @Autowire() private openAIClient: OpenAIClient
|
|
599
|
+
// ) {}
|
|
600
|
+
|
|
601
|
+
// async getStatus(): Promise<{ status: string; timestamp: string; database: string }> {
|
|
602
|
+
// return {
|
|
603
|
+
// status: 'ok',
|
|
604
|
+
// timestamp: new Date().toISOString(),
|
|
605
|
+
// database: 'connected'
|
|
606
|
+
// };
|
|
607
|
+
// }
|
|
608
|
+
|
|
609
|
+
// async generateHealthTip(): Promise<{ tip: string }> {
|
|
610
|
+
// const prompt = 'Give me a short health tip in one sentence.';
|
|
611
|
+
// const tip = await this.openAIClient.complete(prompt, {
|
|
612
|
+
// maxTokens: 50,
|
|
613
|
+
// temperature: 0.9
|
|
614
|
+
// });
|
|
615
|
+
|
|
616
|
+
// return { tip };
|
|
617
|
+
// }
|
|
618
|
+
// }
|
|
619
|
+
|
|
620
|
+
// // ============================================================================
|
|
621
|
+
// // FILE: src/controllers/health.controller.ts
|
|
622
|
+
// // ============================================================================
|
|
623
|
+
// import { Controller } from '../core/decorators/controller.decorator';
|
|
624
|
+
// import { Get } from '../core/decorators/route.decorator';
|
|
625
|
+
// import { Injectable } from '../core/decorators/injectable.decorator';
|
|
626
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
627
|
+
// import { HealthService } from '../services/health.service';
|
|
628
|
+
|
|
629
|
+
// @Controller('/api/health')
|
|
630
|
+
// @Injectable()
|
|
631
|
+
// export class HealthController {
|
|
632
|
+
// constructor(
|
|
633
|
+
// @Autowire() private healthService: HealthService
|
|
634
|
+
// ) {}
|
|
635
|
+
|
|
636
|
+
// @Get('/')
|
|
637
|
+
// async getStatus() {
|
|
638
|
+
// return await this.healthService.getStatus();
|
|
639
|
+
// }
|
|
640
|
+
|
|
641
|
+
// @Get('/tip')
|
|
642
|
+
// async getHealthTip() {
|
|
643
|
+
// return await this.healthService.generateHealthTip();
|
|
644
|
+
// }
|
|
645
|
+
// }
|
|
646
|
+
|
|
647
|
+
// // ============================================================================
|
|
648
|
+
// // FILE: src/auth/dto/login.dto.ts
|
|
649
|
+
// // ============================================================================
|
|
650
|
+
// export interface LoginDTO {
|
|
651
|
+
// email: string;
|
|
652
|
+
// password: string;
|
|
653
|
+
// }
|
|
654
|
+
|
|
655
|
+
// export interface RegisterDTO {
|
|
656
|
+
// name: string;
|
|
657
|
+
// email: string;
|
|
658
|
+
// password: string;
|
|
659
|
+
// }
|
|
660
|
+
|
|
661
|
+
// // ============================================================================
|
|
662
|
+
// // FILE: src/auth/auth.service.ts
|
|
663
|
+
// // ============================================================================
|
|
664
|
+
// import * as crypto from 'crypto';
|
|
665
|
+
// import * as bcrypt from 'bcrypt';
|
|
666
|
+
// import { Service } from '../core/decorators/service.decorator';
|
|
667
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
668
|
+
// import { UserRepository } from '../repositories/user.repository';
|
|
669
|
+
// import { SessionRepository } from '../repositories/session.repository';
|
|
670
|
+
// import { LoginDTO, RegisterDTO } from './dto/login.dto';
|
|
671
|
+
// import { ConfigLoader } from '../core/config/config-loader';
|
|
672
|
+
|
|
673
|
+
// @Service()
|
|
674
|
+
// export class AuthService {
|
|
675
|
+
// constructor(
|
|
676
|
+
// @Autowire() private userRepository: UserRepository,
|
|
677
|
+
// @Autowire() private sessionRepository: SessionRepository
|
|
678
|
+
// ) {}
|
|
679
|
+
|
|
680
|
+
// async register(dto: RegisterDTO): Promise<{ user: any; token: string }> {
|
|
681
|
+
// const existingUser = await this.userRepository.findByEmail(dto.email);
|
|
682
|
+
// if (existingUser) {
|
|
683
|
+
// throw new Error('User already exists');
|
|
684
|
+
// }
|
|
685
|
+
|
|
686
|
+
// const passwordHash = await bcrypt.hash(dto.password, 10);
|
|
687
|
+
// const user = await this.userRepository.create({
|
|
688
|
+
// name: dto.name,
|
|
689
|
+
// email: dto.email,
|
|
690
|
+
// passwordHash
|
|
691
|
+
// });
|
|
692
|
+
|
|
693
|
+
// const token = await this.createSession(user.id);
|
|
694
|
+
|
|
695
|
+
// return {
|
|
696
|
+
// user: { id: user.id, name: user.name, email: user.email },
|
|
697
|
+
// token
|
|
698
|
+
// };
|
|
699
|
+
// }
|
|
700
|
+
|
|
701
|
+
// async login(dto: LoginDTO): Promise<{ user: any; token: string }> {
|
|
702
|
+
// const user = await this.userRepository.findByEmail(dto.email);
|
|
703
|
+
// if (!user) {
|
|
704
|
+
// throw new Error('Invalid credentials');
|
|
705
|
+
// }
|
|
706
|
+
|
|
707
|
+
// const isValidPassword = await bcrypt.compare(dto.password, user.passwordHash);
|
|
708
|
+
// if (!isValidPassword) {
|
|
709
|
+
// throw new Error('Invalid credentials');
|
|
710
|
+
// }
|
|
711
|
+
|
|
712
|
+
// const token = await this.createSession(user.id);
|
|
713
|
+
|
|
714
|
+
// return {
|
|
715
|
+
// user: { id: user.id, name: user.name, email: user.email },
|
|
716
|
+
// token
|
|
717
|
+
// };
|
|
718
|
+
// }
|
|
719
|
+
|
|
720
|
+
// async logout(token: string): Promise<void> {
|
|
721
|
+
// await this.sessionRepository.deleteByToken(token);
|
|
722
|
+
// }
|
|
723
|
+
|
|
724
|
+
// async validateToken(token: string): Promise<any> {
|
|
725
|
+
// const session = await this.sessionRepository.findByToken(token);
|
|
726
|
+
// if (!session) {
|
|
727
|
+
// return null;
|
|
728
|
+
// }
|
|
729
|
+
|
|
730
|
+
// return {
|
|
731
|
+
// id: session.user.id,
|
|
732
|
+
// name: session.user.name,
|
|
733
|
+
// email: session.user.email
|
|
734
|
+
// };
|
|
735
|
+
// }
|
|
736
|
+
|
|
737
|
+
// private async createSession(userId: number): Promise<string> {
|
|
738
|
+
// const token = crypto.randomBytes(32).toString('hex');
|
|
739
|
+
// const config = ConfigLoader.get();
|
|
740
|
+
|
|
741
|
+
// // Parse token expiry (e.g., "7d" = 7 days)
|
|
742
|
+
// const expiryMs = this.parseExpiry(config.auth.tokenExpiry);
|
|
743
|
+
// const expiresAt = new Date(Date.now() + expiryMs);
|
|
744
|
+
|
|
745
|
+
// await this.sessionRepository.create({
|
|
746
|
+
// token,
|
|
747
|
+
// userId,
|
|
748
|
+
// expiresAt
|
|
749
|
+
// });
|
|
750
|
+
|
|
751
|
+
// return token;
|
|
752
|
+
// }
|
|
753
|
+
|
|
754
|
+
// private parseExpiry(expiry: string): number {
|
|
755
|
+
// const match = expiry.match(/^(\d+)([dhms])$/);
|
|
756
|
+
// if (!match) {
|
|
757
|
+
// return 7 * 24 * 60 * 60 * 1000; // default 7 days
|
|
758
|
+
// }
|
|
759
|
+
|
|
760
|
+
// const value = parseInt(match[1]);
|
|
761
|
+
// const unit = match[2];
|
|
762
|
+
|
|
763
|
+
// const multipliers: { [key: string]: number } = {
|
|
764
|
+
// 's': 1000,
|
|
765
|
+
// 'm': 60 * 1000,
|
|
766
|
+
// 'h': 60 * 60 * 1000,
|
|
767
|
+
// 'd': 24 * 60 * 60 * 1000
|
|
768
|
+
// };
|
|
769
|
+
|
|
770
|
+
// return value * multipliers[unit];
|
|
771
|
+
// }
|
|
772
|
+
// }
|
|
773
|
+
|
|
774
|
+
// // ============================================================================
|
|
775
|
+
// // FILE: src/auth/auth.controller.ts
|
|
776
|
+
// // ============================================================================
|
|
777
|
+
// import { Controller } from '../core/decorators/controller.decorator';
|
|
778
|
+
// import { Post, Get } from '../core/decorators/route.decorator';
|
|
779
|
+
// import { Injectable } from '../core/decorators/injectable.decorator';
|
|
780
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
781
|
+
// import { AuthGuard } from '../core/decorators/auth-guard.decorator';
|
|
782
|
+
// import { AuthService } from './auth.service';
|
|
783
|
+
// import { Request, Response } from 'express';
|
|
784
|
+
|
|
785
|
+
// @Controller('/api/auth')
|
|
786
|
+
// @Injectable()
|
|
787
|
+
// export class AuthController {
|
|
788
|
+
// constructor(
|
|
789
|
+
// @Autowire() private authService: AuthService
|
|
790
|
+
// ) {}
|
|
791
|
+
|
|
792
|
+
// @Post('/register')
|
|
793
|
+
// async register(req: Request, res: Response) {
|
|
794
|
+
// try {
|
|
795
|
+
// const result = await this.authService.register(req.body);
|
|
796
|
+
// return res.json(result);
|
|
797
|
+
// } catch (error: any) {
|
|
798
|
+
// return res.status(400).json({ error: error.message });
|
|
799
|
+
// }
|
|
800
|
+
// }
|
|
801
|
+
|
|
802
|
+
// @Post('/login')
|
|
803
|
+
// async login(req: Request, res: Response) {
|
|
804
|
+
// try {
|
|
805
|
+
// const result = await this.authService.login(req.body);
|
|
806
|
+
// return res.json(result);
|
|
807
|
+
// } catch (error: any) {
|
|
808
|
+
// return res.status(401).json({ error: error.message });
|
|
809
|
+
// }
|
|
810
|
+
// }
|
|
811
|
+
|
|
812
|
+
// @Post('/logout')
|
|
813
|
+
// @AuthGuard()
|
|
814
|
+
// async logout(req: Request, res: Response) {
|
|
815
|
+
// const token = req.headers.authorization?.replace('Bearer ', '');
|
|
816
|
+
// if (token) {
|
|
817
|
+
// await this.authService.logout(token);
|
|
818
|
+
// }
|
|
819
|
+
// return res.json({ message: 'Logged out successfully' });
|
|
820
|
+
// }
|
|
821
|
+
|
|
822
|
+
// @Get('/me')
|
|
823
|
+
// @AuthGuard()
|
|
824
|
+
// async getCurrentUser(req: Request, res: Response) {
|
|
825
|
+
// return res.json({ user: (req as any).user });
|
|
826
|
+
// }
|
|
827
|
+
// }
|
|
828
|
+
|
|
829
|
+
// // ============================================================================
|
|
830
|
+
// // FILE: src/auth/auth.middleware.ts
|
|
831
|
+
// // ============================================================================
|
|
832
|
+
// import { Request, Response, NextFunction } from 'express';
|
|
833
|
+
// import { DIContainer } from '../core/container/di-container';
|
|
834
|
+
// import { AuthService } from './auth.service';
|
|
835
|
+
|
|
836
|
+
// export async function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
837
|
+
// const authHeader = req.headers.authorization;
|
|
838
|
+
|
|
839
|
+
// if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
840
|
+
// return res.status(401).json({ error: 'Unauthorized' });
|
|
841
|
+
// }
|
|
842
|
+
|
|
843
|
+
// const token = authHeader.replace('Bearer ', '');
|
|
844
|
+
// const authService = DIContainer.resolve<AuthService>(AuthService);
|
|
845
|
+
|
|
846
|
+
// try {
|
|
847
|
+
// const user = await authService.validateToken(token);
|
|
848
|
+
// if (!user) {
|
|
849
|
+
// return res.status(401).json({ error: 'Invalid token' });
|
|
850
|
+
// }
|
|
851
|
+
|
|
852
|
+
// (req as any).user = user;
|
|
853
|
+
// next();
|
|
854
|
+
// } catch (error) {
|
|
855
|
+
// return res.status(401).json({ error: 'Unauthorized' });
|
|
856
|
+
// }
|
|
857
|
+
// }
|
|
858
|
+
|
|
859
|
+
// // ============================================================================
|
|
860
|
+
// // FILE: src/middlewares/logging.middleware.ts
|
|
861
|
+
// // ============================================================================
|
|
862
|
+
// import { Request, Response, NextFunction } from 'express';
|
|
863
|
+
// import pino from 'pino';
|
|
864
|
+
|
|
865
|
+
// const logger = pino({
|
|
866
|
+
// transport: {
|
|
867
|
+
// target: 'pino-pretty',
|
|
868
|
+
// options: {
|
|
869
|
+
// colorize: true
|
|
870
|
+
// }
|
|
871
|
+
// }
|
|
872
|
+
// });
|
|
873
|
+
|
|
874
|
+
// export function loggingMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
875
|
+
// const start = Date.now();
|
|
876
|
+
|
|
877
|
+
// res.on('finish', () => {
|
|
878
|
+
// const duration = Date.now() - start;
|
|
879
|
+
// logger.info({
|
|
880
|
+
// method: req.method,
|
|
881
|
+
// url: req.url,
|
|
882
|
+
// status: res.statusCode,
|
|
883
|
+
// duration: `${duration}ms`
|
|
884
|
+
// });
|
|
885
|
+
// });
|
|
886
|
+
|
|
887
|
+
// next();
|
|
888
|
+
// }
|
|
889
|
+
|
|
890
|
+
// // ============================================================================
|
|
891
|
+
// // FILE: src/database/data-source.ts
|
|
892
|
+
// // ============================================================================
|
|
893
|
+
// import { DataSource } from 'typeorm';
|
|
894
|
+
// import { ConfigLoader } from '../core/config/config-loader';
|
|
895
|
+
// import { User } from '../entities/user.entity';
|
|
896
|
+
// import { Session } from '../entities/session.entity';
|
|
897
|
+
|
|
898
|
+
// let AppDataSource: DataSource;
|
|
899
|
+
|
|
900
|
+
// export function initializeDataSource(): DataSource {
|
|
901
|
+
// const config = ConfigLoader.get();
|
|
902
|
+
|
|
903
|
+
// AppDataSource = new DataSource({
|
|
904
|
+
// type: config.database.type as any,
|
|
905
|
+
// host: config.database.host,
|
|
906
|
+
// port: config.database.port,
|
|
907
|
+
// username: config.database.username,
|
|
908
|
+
// password: config.database.password,
|
|
909
|
+
// database: config.database.database,
|
|
910
|
+
// synchronize: config.database.synchronize,
|
|
911
|
+
// logging: config.database.logging,
|
|
912
|
+
// entities: [User, Session],
|
|
913
|
+
// migrations: [],
|
|
914
|
+
// subscribers: []
|
|
915
|
+
// });
|
|
916
|
+
|
|
917
|
+
// return AppDataSource;
|
|
918
|
+
// }
|
|
919
|
+
|
|
920
|
+
// export { AppDataSource };
|
|
921
|
+
|
|
922
|
+
// // ============================================================================
|
|
923
|
+
// // FILE: src/app.ts
|
|
924
|
+
// // ============================================================================
|
|
925
|
+
// import express, { Express, Request, Response, NextFunction } from 'express';
|
|
926
|
+
// import { DIContainer } from './core/container/di-container';
|
|
927
|
+
// import { ConfigLoader } from './core/config/config-loader';
|
|
928
|
+
// import { initializeDataSource, AppDataSource } from './database/data-source';
|
|
929
|
+
// import { loggingMiddleware } from './middlewares/logging.middleware';
|
|
930
|
+
// import { authMiddleware } from './auth/auth.middleware';
|
|
931
|
+
// import {
|
|
932
|
+
// CONTROLLER_METADATA,
|
|
933
|
+
// CONTROLLER_PATH
|
|
934
|
+
// } from './core/decorators/controller.decorator';
|
|
935
|
+
// import {
|
|
936
|
+
// ROUTE_METADATA
|
|
937
|
+
// } from './core/decorators/route.decorator';
|
|
938
|
+
// import {
|
|
939
|
+
// AUTH_GUARD_METADATA
|
|
940
|
+
// } from './core/decorators/auth-guard.decorator';
|
|
941
|
+
// import {
|
|
942
|
+
// MIDDLEWARE_METADATA
|
|
943
|
+
// } from './core/decorators/middleware.decorator';
|
|
944
|
+
|
|
945
|
+
// // Import all controllers, services, repositories (ensures decorators run)
|
|
946
|
+
// import './controllers/health.controller';
|
|
947
|
+
// import './auth/auth.controller';
|
|
948
|
+
// import './services/health.service';
|
|
949
|
+
// import './repositories/user.repository';
|
|
950
|
+
// import './repositories/session.repository';
|
|
951
|
+
// import './auth/auth.service';
|
|
952
|
+
// import './core/openai/openai-client';
|
|
953
|
+
|
|
954
|
+
// export class Application {
|
|
955
|
+
// private app: Express;
|
|
956
|
+
|
|
957
|
+
// constructor() {
|
|
958
|
+
// this.app = express();
|
|
959
|
+
// }
|
|
960
|
+
|
|
961
|
+
// async initialize(): Promise<Express> {
|
|
962
|
+
// // Load configuration
|
|
963
|
+
// ConfigLoader.load(true); // Set to false for JSON
|
|
964
|
+
|
|
965
|
+
// // Initialize database
|
|
966
|
+
// const dataSource = initializeDataSource();
|
|
967
|
+
// await dataSource.initialize();
|
|
968
|
+
// console.log('✅ Database connected');
|