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.
Files changed (123) hide show
  1. package/.env.example +0 -0
  2. package/base.ts +1810 -0
  3. package/base2.ts +968 -0
  4. package/bin/frg.ts +5 -0
  5. package/config/fragment.lock.yaml +0 -0
  6. package/config/fragment.yaml +0 -0
  7. package/dist/app.d.ts +15 -0
  8. package/dist/app.js +90 -0
  9. package/dist/auth/auth.controller.d.ts +10 -0
  10. package/dist/auth/auth.controller.js +87 -0
  11. package/dist/auth/auth.middleware.d.ts +2 -0
  12. package/dist/auth/auth.middleware.js +24 -0
  13. package/dist/auth/auth.service.d.ts +20 -0
  14. package/dist/auth/auth.service.js +143 -0
  15. package/dist/auth/dto/login.dto.d.ts +9 -0
  16. package/dist/auth/dto/login.dto.js +2 -0
  17. package/dist/cli/cli.d.ts +12 -0
  18. package/dist/cli/cli.js +186 -0
  19. package/dist/cli/commands/build.command.d.ts +3 -0
  20. package/dist/cli/commands/build.command.js +23 -0
  21. package/dist/cli/commands/config.command.d.ts +6 -0
  22. package/dist/cli/commands/config.command.js +284 -0
  23. package/dist/cli/commands/generate.command.d.ts +8 -0
  24. package/dist/cli/commands/generate.command.js +180 -0
  25. package/dist/cli/commands/init.command.d.ts +7 -0
  26. package/dist/cli/commands/init.command.js +380 -0
  27. package/dist/cli/commands/migrate.command.d.ts +7 -0
  28. package/dist/cli/commands/migrate.command.js +116 -0
  29. package/dist/cli/commands/serve.command.d.ts +6 -0
  30. package/dist/cli/commands/serve.command.js +31 -0
  31. package/dist/cli/templates/controller.template.d.ts +1 -0
  32. package/dist/cli/templates/controller.template.js +52 -0
  33. package/dist/cli/templates/entity.template.d.ts +1 -0
  34. package/dist/cli/templates/entity.template.js +23 -0
  35. package/dist/cli/templates/repository.template.d.ts +1 -0
  36. package/dist/cli/templates/repository.template.js +43 -0
  37. package/dist/cli/templates/service.template.d.ts +1 -0
  38. package/dist/cli/templates/service.template.js +43 -0
  39. package/dist/cli/utils/file-generator.d.ts +9 -0
  40. package/dist/cli/utils/file-generator.js +67 -0
  41. package/dist/cli/utils/logger.d.ts +14 -0
  42. package/dist/cli/utils/logger.js +49 -0
  43. package/dist/controllers/health.controller.d.ts +13 -0
  44. package/dist/controllers/health.controller.js +50 -0
  45. package/dist/core/config/config-loader.d.ts +31 -0
  46. package/dist/core/config/config-loader.js +98 -0
  47. package/dist/core/container/di-container.d.ts +9 -0
  48. package/dist/core/container/di-container.js +37 -0
  49. package/dist/core/decorators/auth-guard.decorator.d.ts +3 -0
  50. package/dist/core/decorators/auth-guard.decorator.js +18 -0
  51. package/dist/core/decorators/autowire.decorator.d.ts +3 -0
  52. package/dist/core/decorators/autowire.decorator.js +17 -0
  53. package/dist/core/decorators/controller.decorator.d.ts +4 -0
  54. package/dist/core/decorators/controller.decorator.js +16 -0
  55. package/dist/core/decorators/injectable.decorator.d.ts +3 -0
  56. package/dist/core/decorators/injectable.decorator.js +14 -0
  57. package/dist/core/decorators/middleware.decorator.d.ts +3 -0
  58. package/dist/core/decorators/middleware.decorator.js +20 -0
  59. package/dist/core/decorators/repository.decorator.d.ts +1 -0
  60. package/dist/core/decorators/repository.decorator.js +7 -0
  61. package/dist/core/decorators/route.decorator.d.ts +14 -0
  62. package/dist/core/decorators/route.decorator.js +32 -0
  63. package/dist/core/decorators/service.decorator.d.ts +1 -0
  64. package/dist/core/decorators/service.decorator.js +7 -0
  65. package/dist/core/openai/openai-client.d.ts +12 -0
  66. package/dist/core/openai/openai-client.js +93 -0
  67. package/dist/database/data-source.d.ts +4 -0
  68. package/dist/database/data-source.js +26 -0
  69. package/dist/entities/session.entity.d.ts +9 -0
  70. package/dist/entities/session.entity.js +45 -0
  71. package/dist/entities/user.entity.d.ts +10 -0
  72. package/dist/entities/user.entity.js +48 -0
  73. package/dist/middlewares/logging.middleware.d.ts +2 -0
  74. package/dist/middlewares/logging.middleware.js +28 -0
  75. package/dist/repositories/session.repository.d.ts +9 -0
  76. package/dist/repositories/session.repository.js +50 -0
  77. package/dist/repositories/user.repository.d.ts +10 -0
  78. package/dist/repositories/user.repository.js +43 -0
  79. package/dist/server.d.ts +1 -0
  80. package/dist/server.js +30 -0
  81. package/dist/services/health.service.d.ts +13 -0
  82. package/dist/services/health.service.js +44 -0
  83. package/package.json +46 -0
  84. package/readme.md +120 -0
  85. package/src/app.ts +121 -0
  86. package/src/auth/auth.controller.ts +52 -0
  87. package/src/auth/auth.middleware.ts +27 -0
  88. package/src/auth/auth.service.ts +110 -0
  89. package/src/auth/dto/login.dto.ts +11 -0
  90. package/src/cli/cli.ts +212 -0
  91. package/src/cli/commands/build.command.ts +24 -0
  92. package/src/cli/commands/config.command.ts +280 -0
  93. package/src/cli/commands/generate.command.ts +170 -0
  94. package/src/cli/commands/init.command.ts +395 -0
  95. package/src/cli/commands/migrate.command.ts +118 -0
  96. package/src/cli/commands/serve.command.ts +37 -0
  97. package/src/cli/templates/controller.template.ts +51 -0
  98. package/src/cli/templates/entity.template.ts +22 -0
  99. package/src/cli/templates/repository.template.ts +42 -0
  100. package/src/cli/templates/service.template.ts +42 -0
  101. package/src/cli/utils/file-generator.ts +37 -0
  102. package/src/cli/utils/logger.ts +52 -0
  103. package/src/controllers/health.controller.ts +24 -0
  104. package/src/core/config/config-loader.ts +98 -0
  105. package/src/core/container/di-container.ts +43 -0
  106. package/src/core/decorators/auth-guard.decorator.ts +15 -0
  107. package/src/core/decorators/autowire.decorator.ts +18 -0
  108. package/src/core/decorators/controller.decorator.ts +15 -0
  109. package/src/core/decorators/injectable.decorator.ts +13 -0
  110. package/src/core/decorators/middleware.decorator.ts +18 -0
  111. package/src/core/decorators/repository.decorator.ts +6 -0
  112. package/src/core/decorators/route.decorator.ts +33 -0
  113. package/src/core/decorators/service.decorator.ts +6 -0
  114. package/src/core/openai/openai-client.ts +99 -0
  115. package/src/database/data-source.ts +29 -0
  116. package/src/entities/session.entity.ts +25 -0
  117. package/src/entities/user.entity.ts +27 -0
  118. package/src/middlewares/logging.middleware.ts +28 -0
  119. package/src/repositories/session.repository.ts +42 -0
  120. package/src/repositories/user.repository.ts +37 -0
  121. package/src/server.ts +32 -0
  122. package/src/services/health.service.ts +29 -0
  123. 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');