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/base.ts
ADDED
|
@@ -0,0 +1,1810 @@
|
|
|
1
|
+
// // ============================================================================
|
|
2
|
+
// // PROJECT STRUCTURE
|
|
3
|
+
// // ============================================================================
|
|
4
|
+
// /*
|
|
5
|
+
// fragment/
|
|
6
|
+
// âââ bin/
|
|
7
|
+
// â âââ frg.ts
|
|
8
|
+
// âââ src/
|
|
9
|
+
// â âââ cli/
|
|
10
|
+
// â â âââ commands/
|
|
11
|
+
// â â â âââ init.command.ts
|
|
12
|
+
// â â â âââ generate.command.ts
|
|
13
|
+
// â â â âââ config.command.ts
|
|
14
|
+
// â â â âââ serve.command.ts
|
|
15
|
+
// â â â âââ build.command.ts
|
|
16
|
+
// â â â âââ migrate.command.ts
|
|
17
|
+
// â â âââ templates/
|
|
18
|
+
// â â â âââ controller.template.ts
|
|
19
|
+
// â â â âââ service.template.ts
|
|
20
|
+
// â â â âââ repository.template.ts
|
|
21
|
+
// â â â âââ entity.template.ts
|
|
22
|
+
// â â âââ utils/
|
|
23
|
+
// â â â âââ logger.ts
|
|
24
|
+
// â â â âââ file-generator.ts
|
|
25
|
+
// â â âââ cli.ts
|
|
26
|
+
// â âââ core/
|
|
27
|
+
// â â âââ decorators/
|
|
28
|
+
// â â âââ container/
|
|
29
|
+
// â â âââ config/
|
|
30
|
+
// â â âââ openai/
|
|
31
|
+
// â âââ auth/
|
|
32
|
+
// â âââ entities/
|
|
33
|
+
// â âââ repositories/
|
|
34
|
+
// â âââ services/
|
|
35
|
+
// â âââ controllers/
|
|
36
|
+
// â âââ middlewares/
|
|
37
|
+
// â âââ database/
|
|
38
|
+
// â âââ app.ts
|
|
39
|
+
// â âââ server.ts
|
|
40
|
+
// âââ config/
|
|
41
|
+
// â âââ fragment.yaml
|
|
42
|
+
// â âââ fragment.lock.yaml
|
|
43
|
+
// âââ package.json
|
|
44
|
+
// âââ tsconfig.json
|
|
45
|
+
// âââ .env.example
|
|
46
|
+
// */
|
|
47
|
+
|
|
48
|
+
// // ============================================================================
|
|
49
|
+
// // FILE: bin/frg.ts
|
|
50
|
+
// // ============================================================================
|
|
51
|
+
// #!/usr/bin/env node
|
|
52
|
+
// import { CLI } from '../src/cli/cli';
|
|
53
|
+
|
|
54
|
+
// const cli = new CLI();
|
|
55
|
+
// cli.run(process.argv.slice(2));
|
|
56
|
+
|
|
57
|
+
// // ============================================================================
|
|
58
|
+
// // FILE: src/cli/utils/logger.ts
|
|
59
|
+
// // ============================================================================
|
|
60
|
+
// import chalk from 'chalk';
|
|
61
|
+
// import ora, { Ora } from 'ora';
|
|
62
|
+
|
|
63
|
+
// export class CLILogger {
|
|
64
|
+
// static success(message: string): void {
|
|
65
|
+
// console.log(chalk.green('â'), message);
|
|
66
|
+
// }
|
|
67
|
+
|
|
68
|
+
// static error(message: string): void {
|
|
69
|
+
// console.log(chalk.red('â'), message);
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
// static info(message: string): void {
|
|
73
|
+
// console.log(chalk.blue('âš'), message);
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// static warning(message: string): void {
|
|
77
|
+
// console.log(chalk.yellow('â '), message);
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// static title(message: string): void {
|
|
81
|
+
// console.log(chalk.bold.cyan(`\n${message}\n`));
|
|
82
|
+
// }
|
|
83
|
+
|
|
84
|
+
// static section(message: string): void {
|
|
85
|
+
// console.log(chalk.bold(`\n${message}`));
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// static spinner(text: string): Ora {
|
|
89
|
+
// return ora(text).start();
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
// static box(title: string, content: string[]): void {
|
|
93
|
+
// const maxLength = Math.max(title.length, ...content.map(c => c.length)) + 4;
|
|
94
|
+
// const border = 'â'.repeat(maxLength);
|
|
95
|
+
|
|
96
|
+
// console.log(chalk.cyan(`â${border}â`));
|
|
97
|
+
// console.log(chalk.cyan('â') + chalk.bold(` ${title.padEnd(maxLength)} `) + chalk.cyan('â'));
|
|
98
|
+
// console.log(chalk.cyan(`â ${border}âŖ`));
|
|
99
|
+
// content.forEach(line => {
|
|
100
|
+
// console.log(chalk.cyan('â') + ` ${line.padEnd(maxLength)} ` + chalk.cyan('â'));
|
|
101
|
+
// });
|
|
102
|
+
// console.log(chalk.cyan(`â${border}â`));
|
|
103
|
+
// }
|
|
104
|
+
|
|
105
|
+
// static table(data: { [key: string]: string }): void {
|
|
106
|
+
// const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));
|
|
107
|
+
// Object.entries(data).forEach(([key, value]) => {
|
|
108
|
+
// console.log(` ${chalk.cyan(key.padEnd(maxKeyLength))} : ${value}`);
|
|
109
|
+
// });
|
|
110
|
+
// }
|
|
111
|
+
// }
|
|
112
|
+
|
|
113
|
+
// // ============================================================================
|
|
114
|
+
// // FILE: src/cli/utils/file-generator.ts
|
|
115
|
+
// // ============================================================================
|
|
116
|
+
// import * as fs from 'fs';
|
|
117
|
+
// import * as path from 'path';
|
|
118
|
+
|
|
119
|
+
// export class FileGenerator {
|
|
120
|
+
// static createDirectory(dirPath: string): void {
|
|
121
|
+
// if (!fs.existsSync(dirPath)) {
|
|
122
|
+
// fs.mkdirSync(dirPath, { recursive: true });
|
|
123
|
+
// }
|
|
124
|
+
// }
|
|
125
|
+
|
|
126
|
+
// static writeFile(filePath: string, content: string): void {
|
|
127
|
+
// const dir = path.dirname(filePath);
|
|
128
|
+
// this.createDirectory(dir);
|
|
129
|
+
// fs.writeFileSync(filePath, content, 'utf-8');
|
|
130
|
+
// }
|
|
131
|
+
|
|
132
|
+
// static fileExists(filePath: string): boolean {
|
|
133
|
+
// return fs.existsSync(filePath);
|
|
134
|
+
// }
|
|
135
|
+
|
|
136
|
+
// static readFile(filePath: string): string {
|
|
137
|
+
// return fs.readFileSync(filePath, 'utf-8');
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// static toCamelCase(str: string): string {
|
|
141
|
+
// return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
142
|
+
// }
|
|
143
|
+
|
|
144
|
+
// static toPascalCase(str: string): string {
|
|
145
|
+
// const camel = this.toCamelCase(str);
|
|
146
|
+
// return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
147
|
+
// }
|
|
148
|
+
|
|
149
|
+
// static toKebabCase(str: string): string {
|
|
150
|
+
// return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
151
|
+
// }
|
|
152
|
+
// }
|
|
153
|
+
|
|
154
|
+
// // ============================================================================
|
|
155
|
+
// // FILE: src/cli/templates/controller.template.ts
|
|
156
|
+
// // ============================================================================
|
|
157
|
+
// export function controllerTemplate(name: string, route: string): string {
|
|
158
|
+
// const className = name.endsWith('Controller') ? name : `${name}Controller`;
|
|
159
|
+
// const serviceName = name.replace('Controller', '') + 'Service';
|
|
160
|
+
|
|
161
|
+
// return `import { Controller } from '../core/decorators/controller.decorator';
|
|
162
|
+
// import { Get, Post, Put, Delete } from '../core/decorators/route.decorator';
|
|
163
|
+
// import { Injectable } from '../core/decorators/injectable.decorator';
|
|
164
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
165
|
+
// import { ${serviceName} } from '../services/${name.toLowerCase()}.service';
|
|
166
|
+
// import { Request, Response } from 'express';
|
|
167
|
+
|
|
168
|
+
// @Controller('${route}')
|
|
169
|
+
// @Injectable()
|
|
170
|
+
// export class ${className} {
|
|
171
|
+
// constructor(
|
|
172
|
+
// @Autowire() private ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}: ${serviceName}
|
|
173
|
+
// ) {}
|
|
174
|
+
|
|
175
|
+
// @Get('/')
|
|
176
|
+
// async findAll(req: Request, res: Response) {
|
|
177
|
+
// const items = await this.${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.findAll();
|
|
178
|
+
// return res.json(items);
|
|
179
|
+
// }
|
|
180
|
+
|
|
181
|
+
// @Get('/:id')
|
|
182
|
+
// async findOne(req: Request, res: Response) {
|
|
183
|
+
// const item = await this.${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.findById(parseInt(req.params.id));
|
|
184
|
+
// return res.json(item);
|
|
185
|
+
// }
|
|
186
|
+
|
|
187
|
+
// @Post('/')
|
|
188
|
+
// async create(req: Request, res: Response) {
|
|
189
|
+
// const item = await this.${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.create(req.body);
|
|
190
|
+
// return res.status(201).json(item);
|
|
191
|
+
// }
|
|
192
|
+
|
|
193
|
+
// @Put('/:id')
|
|
194
|
+
// async update(req: Request, res: Response) {
|
|
195
|
+
// const item = await this.${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.update(parseInt(req.params.id), req.body);
|
|
196
|
+
// return res.json(item);
|
|
197
|
+
// }
|
|
198
|
+
|
|
199
|
+
// @Delete('/:id')
|
|
200
|
+
// async delete(req: Request, res: Response) {
|
|
201
|
+
// await this.${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.delete(parseInt(req.params.id));
|
|
202
|
+
// return res.status(204).send();
|
|
203
|
+
// }
|
|
204
|
+
// }
|
|
205
|
+
// `;
|
|
206
|
+
// }
|
|
207
|
+
|
|
208
|
+
// // ============================================================================
|
|
209
|
+
// // FILE: src/cli/templates/service.template.ts
|
|
210
|
+
// // ============================================================================
|
|
211
|
+
// export function serviceTemplate(name: string): string {
|
|
212
|
+
// const className = name.endsWith('Service') ? name : `${name}Service`;
|
|
213
|
+
// const repoName = name.replace('Service', '') + 'Repository';
|
|
214
|
+
|
|
215
|
+
// return `import { Service } from '../core/decorators/service.decorator';
|
|
216
|
+
// import { Autowire } from '../core/decorators/autowire.decorator';
|
|
217
|
+
// import { ${repoName} } from '../repositories/${name.toLowerCase()}.repository';
|
|
218
|
+
|
|
219
|
+
// @Service()
|
|
220
|
+
// export class ${className} {
|
|
221
|
+
// constructor(
|
|
222
|
+
// @Autowire() private ${repoName.charAt(0).toLowerCase() + repoName.slice(1)}: ${repoName}
|
|
223
|
+
// ) {}
|
|
224
|
+
|
|
225
|
+
// async findAll() {
|
|
226
|
+
// // Implement business logic
|
|
227
|
+
// return [];
|
|
228
|
+
// }
|
|
229
|
+
|
|
230
|
+
// async findById(id: number) {
|
|
231
|
+
// // Implement business logic
|
|
232
|
+
// return null;
|
|
233
|
+
// }
|
|
234
|
+
|
|
235
|
+
// async create(data: any) {
|
|
236
|
+
// // Implement business logic
|
|
237
|
+
// return data;
|
|
238
|
+
// }
|
|
239
|
+
|
|
240
|
+
// async update(id: number, data: any) {
|
|
241
|
+
// // Implement business logic
|
|
242
|
+
// return data;
|
|
243
|
+
// }
|
|
244
|
+
|
|
245
|
+
// async delete(id: number) {
|
|
246
|
+
// // Implement business logic
|
|
247
|
+
// return true;
|
|
248
|
+
// }
|
|
249
|
+
// }
|
|
250
|
+
// `;
|
|
251
|
+
// }
|
|
252
|
+
|
|
253
|
+
// // ============================================================================
|
|
254
|
+
// // FILE: src/cli/templates/repository.template.ts
|
|
255
|
+
// // ============================================================================
|
|
256
|
+
// export function repositoryTemplate(name: string, entityName: string): string {
|
|
257
|
+
// const className = name.endsWith('Repository') ? name : `${name}Repository`;
|
|
258
|
+
|
|
259
|
+
// return `import { Repository as TypeORMRepository } from 'typeorm';
|
|
260
|
+
// import { Repository } from '../core/decorators/repository.decorator';
|
|
261
|
+
// import { ${entityName} } from '../entities/${entityName.toLowerCase()}.entity';
|
|
262
|
+
// import { AppDataSource } from '../database/data-source';
|
|
263
|
+
|
|
264
|
+
// @Repository()
|
|
265
|
+
// export class ${className} {
|
|
266
|
+
// private repository: TypeORMRepository<${entityName}>;
|
|
267
|
+
|
|
268
|
+
// constructor() {
|
|
269
|
+
// this.repository = AppDataSource.getRepository(${entityName});
|
|
270
|
+
// }
|
|
271
|
+
|
|
272
|
+
// async findAll(): Promise<${entityName}[]> {
|
|
273
|
+
// return await this.repository.find();
|
|
274
|
+
// }
|
|
275
|
+
|
|
276
|
+
// async findById(id: number): Promise<${entityName} | null> {
|
|
277
|
+
// return await this.repository.findOne({ where: { id } });
|
|
278
|
+
// }
|
|
279
|
+
|
|
280
|
+
// async create(data: Partial<${entityName}>): Promise<${entityName}> {
|
|
281
|
+
// const entity = this.repository.create(data);
|
|
282
|
+
// return await this.repository.save(entity);
|
|
283
|
+
// }
|
|
284
|
+
|
|
285
|
+
// async update(id: number, data: Partial<${entityName}>): Promise<${entityName} | null> {
|
|
286
|
+
// await this.repository.update(id, data);
|
|
287
|
+
// return await this.findById(id);
|
|
288
|
+
// }
|
|
289
|
+
|
|
290
|
+
// async delete(id: number): Promise<boolean> {
|
|
291
|
+
// const result = await this.repository.delete(id);
|
|
292
|
+
// return result.affected ? result.affected > 0 : false;
|
|
293
|
+
// }
|
|
294
|
+
// }
|
|
295
|
+
// `;
|
|
296
|
+
// }
|
|
297
|
+
|
|
298
|
+
// // ============================================================================
|
|
299
|
+
// // FILE: src/cli/templates/entity.template.ts
|
|
300
|
+
// // ============================================================================
|
|
301
|
+
// export function entityTemplate(name: string): string {
|
|
302
|
+
// const className = name.endsWith('Entity') ? name.replace('Entity', '') : name;
|
|
303
|
+
|
|
304
|
+
// return `import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
|
305
|
+
|
|
306
|
+
// @Entity('${className.toLowerCase()}s')
|
|
307
|
+
// export class ${className} {
|
|
308
|
+
// @PrimaryGeneratedColumn()
|
|
309
|
+
// id!: number;
|
|
310
|
+
|
|
311
|
+
// @Column({ length: 255 })
|
|
312
|
+
// name!: string;
|
|
313
|
+
|
|
314
|
+
// @CreateDateColumn()
|
|
315
|
+
// createdAt!: Date;
|
|
316
|
+
|
|
317
|
+
// @UpdateDateColumn()
|
|
318
|
+
// updatedAt!: Date;
|
|
319
|
+
// }
|
|
320
|
+
// `;
|
|
321
|
+
// }
|
|
322
|
+
|
|
323
|
+
// // ============================================================================
|
|
324
|
+
// // FILE: src/cli/commands/init.command.ts
|
|
325
|
+
// // ============================================================================
|
|
326
|
+
// import * as fs from 'fs';
|
|
327
|
+
// import * as path from 'path';
|
|
328
|
+
// import { execSync } from 'child_process';
|
|
329
|
+
// import { CLILogger } from '../utils/logger';
|
|
330
|
+
// import { FileGenerator } from '../utils/file-generator';
|
|
331
|
+
// import inquirer from 'inquirer';
|
|
332
|
+
|
|
333
|
+
// export class InitCommand {
|
|
334
|
+
// async execute(projectName?: string): Promise<void> {
|
|
335
|
+
// CLILogger.title('đˇ Fragment Framework Initializer');
|
|
336
|
+
|
|
337
|
+
// const answers = await inquirer.prompt([
|
|
338
|
+
// {
|
|
339
|
+
// type: 'input',
|
|
340
|
+
// name: 'projectName',
|
|
341
|
+
// message: 'Project name:',
|
|
342
|
+
// default: projectName || 'my-fragment-app',
|
|
343
|
+
// validate: (input: string) => {
|
|
344
|
+
// if (!/^[a-z0-9-]+$/.test(input)) {
|
|
345
|
+
// return 'Project name must contain only lowercase letters, numbers, and hyphens';
|
|
346
|
+
// }
|
|
347
|
+
// return true;
|
|
348
|
+
// }
|
|
349
|
+
// },
|
|
350
|
+
// {
|
|
351
|
+
// type: 'list',
|
|
352
|
+
// name: 'database',
|
|
353
|
+
// message: 'Select database:',
|
|
354
|
+
// choices: ['PostgreSQL', 'MySQL', 'SQLite', 'MongoDB'],
|
|
355
|
+
// default: 'PostgreSQL'
|
|
356
|
+
// },
|
|
357
|
+
// {
|
|
358
|
+
// type: 'confirm',
|
|
359
|
+
// name: 'useOpenAI',
|
|
360
|
+
// message: 'Enable OpenAI integration?',
|
|
361
|
+
// default: true
|
|
362
|
+
// },
|
|
363
|
+
// {
|
|
364
|
+
// type: 'confirm',
|
|
365
|
+
// name: 'useAuth',
|
|
366
|
+
// message: 'Include authentication system?',
|
|
367
|
+
// default: true
|
|
368
|
+
// },
|
|
369
|
+
// {
|
|
370
|
+
// type: 'list',
|
|
371
|
+
// name: 'packageManager',
|
|
372
|
+
// message: 'Package manager:',
|
|
373
|
+
// choices: ['npm', 'yarn', 'pnpm'],
|
|
374
|
+
// default: 'npm'
|
|
375
|
+
// }
|
|
376
|
+
// ]);
|
|
377
|
+
|
|
378
|
+
// const projectPath = path.join(process.cwd(), answers.projectName);
|
|
379
|
+
|
|
380
|
+
// if (fs.existsSync(projectPath)) {
|
|
381
|
+
// CLILogger.error(`Directory ${answers.projectName} already exists`);
|
|
382
|
+
// return;
|
|
383
|
+
// }
|
|
384
|
+
|
|
385
|
+
// const spinner = CLILogger.spinner('Creating project structure...');
|
|
386
|
+
|
|
387
|
+
// try {
|
|
388
|
+
// // Create project directory
|
|
389
|
+
// FileGenerator.createDirectory(projectPath);
|
|
390
|
+
|
|
391
|
+
// // Create directory structure
|
|
392
|
+
// const dirs = [
|
|
393
|
+
// 'src/controllers',
|
|
394
|
+
// 'src/services',
|
|
395
|
+
// 'src/repositories',
|
|
396
|
+
// 'src/entities',
|
|
397
|
+
// 'src/middlewares',
|
|
398
|
+
// 'src/core/decorators',
|
|
399
|
+
// 'src/core/container',
|
|
400
|
+
// 'src/core/config',
|
|
401
|
+
// 'src/database',
|
|
402
|
+
// 'config'
|
|
403
|
+
// ];
|
|
404
|
+
|
|
405
|
+
// if (answers.useOpenAI) {
|
|
406
|
+
// dirs.push('src/core/openai');
|
|
407
|
+
// }
|
|
408
|
+
|
|
409
|
+
// if (answers.useAuth) {
|
|
410
|
+
// dirs.push('src/auth', 'src/auth/dto');
|
|
411
|
+
// }
|
|
412
|
+
|
|
413
|
+
// dirs.forEach(dir => {
|
|
414
|
+
// FileGenerator.createDirectory(path.join(projectPath, dir));
|
|
415
|
+
// });
|
|
416
|
+
|
|
417
|
+
// spinner.text = 'Generating configuration files...';
|
|
418
|
+
|
|
419
|
+
// // Generate package.json
|
|
420
|
+
// const packageJson = {
|
|
421
|
+
// name: answers.projectName,
|
|
422
|
+
// version: '1.0.0',
|
|
423
|
+
// description: 'Fragment Framework Application',
|
|
424
|
+
// main: 'dist/server.js',
|
|
425
|
+
// scripts: {
|
|
426
|
+
// dev: 'ts-node-dev --respawn --transpile-only src/server.ts',
|
|
427
|
+
// build: 'tsc',
|
|
428
|
+
// start: 'node dist/server.js',
|
|
429
|
+
// 'frg:generate': 'frg generate',
|
|
430
|
+
// 'frg:migrate': 'frg migrate'
|
|
431
|
+
// },
|
|
432
|
+
// dependencies: {
|
|
433
|
+
// express: '^4.18.2',
|
|
434
|
+
// typeorm: '^0.3.17',
|
|
435
|
+
// 'reflect-metadata': '^0.1.13',
|
|
436
|
+
// bcrypt: '^5.1.1',
|
|
437
|
+
// 'js-yaml': '^4.1.0',
|
|
438
|
+
// pino: '^8.16.0',
|
|
439
|
+
// 'pino-pretty': '^10.2.3'
|
|
440
|
+
// },
|
|
441
|
+
// devDependencies: {
|
|
442
|
+
// '@types/express': '^4.17.20',
|
|
443
|
+
// '@types/node': '^20.9.0',
|
|
444
|
+
// '@types/bcrypt': '^5.0.2',
|
|
445
|
+
// '@types/js-yaml': '^4.0.9',
|
|
446
|
+
// typescript: '^5.2.2',
|
|
447
|
+
// 'ts-node': '^10.9.1',
|
|
448
|
+
// 'ts-node-dev': '^2.0.0'
|
|
449
|
+
// }
|
|
450
|
+
// };
|
|
451
|
+
|
|
452
|
+
// // Add database driver
|
|
453
|
+
// const dbDrivers: { [key: string]: string } = {
|
|
454
|
+
// 'PostgreSQL': 'pg@^8.11.3',
|
|
455
|
+
// 'MySQL': 'mysql2@^3.6.5',
|
|
456
|
+
// 'SQLite': 'sqlite3@^5.1.6',
|
|
457
|
+
// 'MongoDB': 'mongodb@^6.3.0'
|
|
458
|
+
// };
|
|
459
|
+
// packageJson.dependencies = {
|
|
460
|
+
// ...packageJson.dependencies,
|
|
461
|
+
// [dbDrivers[answers.database].split('@')[0]]: dbDrivers[answers.database].split('@')[1]
|
|
462
|
+
// };
|
|
463
|
+
|
|
464
|
+
// FileGenerator.writeFile(
|
|
465
|
+
// path.join(projectPath, 'package.json'),
|
|
466
|
+
// JSON.stringify(packageJson, null, 2)
|
|
467
|
+
// );
|
|
468
|
+
|
|
469
|
+
// // Generate tsconfig.json
|
|
470
|
+
// const tsConfig = {
|
|
471
|
+
// compilerOptions: {
|
|
472
|
+
// target: 'ES2020',
|
|
473
|
+
// module: 'commonjs',
|
|
474
|
+
// lib: ['ES2020'],
|
|
475
|
+
// outDir: './dist',
|
|
476
|
+
// rootDir: './src',
|
|
477
|
+
// strict: true,
|
|
478
|
+
// esModuleInterop: true,
|
|
479
|
+
// skipLibCheck: true,
|
|
480
|
+
// forceConsistentCasingInFileNames: true,
|
|
481
|
+
// resolveJsonModule: true,
|
|
482
|
+
// experimentalDecorators: true,
|
|
483
|
+
// emitDecoratorMetadata: true,
|
|
484
|
+
// moduleResolution: 'node',
|
|
485
|
+
// allowSyntheticDefaultImports: true
|
|
486
|
+
// },
|
|
487
|
+
// include: ['src/**/*'],
|
|
488
|
+
// exclude: ['node_modules', 'dist']
|
|
489
|
+
// };
|
|
490
|
+
|
|
491
|
+
// FileGenerator.writeFile(
|
|
492
|
+
// path.join(projectPath, 'tsconfig.json'),
|
|
493
|
+
// JSON.stringify(tsConfig, null, 2)
|
|
494
|
+
// );
|
|
495
|
+
|
|
496
|
+
// // Generate fragment.yaml
|
|
497
|
+
// const dbConfig = this.getDatabaseConfig(answers.database);
|
|
498
|
+
// const fragmentConfig = `app:
|
|
499
|
+
// name: ${answers.projectName}
|
|
500
|
+
// port: 3000
|
|
501
|
+
// env: development
|
|
502
|
+
|
|
503
|
+
// database:
|
|
504
|
+
// ${dbConfig}
|
|
505
|
+
|
|
506
|
+
// ${answers.useOpenAI ? `openai:
|
|
507
|
+
// apiKey: \${OPENAI_API_KEY}
|
|
508
|
+
// model: gpt-4
|
|
509
|
+
// ` : ''}
|
|
510
|
+
// ${answers.useAuth ? `auth:
|
|
511
|
+
// tokenExpiry: 7d
|
|
512
|
+
// sessionExpiry: 30d
|
|
513
|
+
// ` : ''}`;
|
|
514
|
+
|
|
515
|
+
// FileGenerator.writeFile(
|
|
516
|
+
// path.join(projectPath, 'config/fragment.yaml'),
|
|
517
|
+
// fragmentConfig
|
|
518
|
+
// );
|
|
519
|
+
|
|
520
|
+
// // Generate .env.example
|
|
521
|
+
// let envExample = `NODE_ENV=development
|
|
522
|
+
// PORT=3000
|
|
523
|
+
|
|
524
|
+
// ${this.getEnvTemplate(answers.database)}`;
|
|
525
|
+
|
|
526
|
+
// if (answers.useOpenAI) {
|
|
527
|
+
// envExample += '\nOPENAI_API_KEY=sk-your-openai-api-key';
|
|
528
|
+
// }
|
|
529
|
+
|
|
530
|
+
// FileGenerator.writeFile(
|
|
531
|
+
// path.join(projectPath, '.env.example'),
|
|
532
|
+
// envExample
|
|
533
|
+
// );
|
|
534
|
+
|
|
535
|
+
// // Generate .gitignore
|
|
536
|
+
// const gitignore = `node_modules/
|
|
537
|
+
// dist/
|
|
538
|
+
// .env
|
|
539
|
+
// *.log
|
|
540
|
+
// .DS_Store
|
|
541
|
+
// config/fragment.lock.yaml`;
|
|
542
|
+
|
|
543
|
+
// FileGenerator.writeFile(
|
|
544
|
+
// path.join(projectPath, '.gitignore'),
|
|
545
|
+
// gitignore
|
|
546
|
+
// );
|
|
547
|
+
|
|
548
|
+
// // Generate README.md
|
|
549
|
+
// const readme = this.generateReadme(answers.projectName, answers);
|
|
550
|
+
// FileGenerator.writeFile(
|
|
551
|
+
// path.join(projectPath, 'README.md'),
|
|
552
|
+
// readme
|
|
553
|
+
// );
|
|
554
|
+
|
|
555
|
+
// spinner.text = 'Copying core framework files...';
|
|
556
|
+
|
|
557
|
+
// // Copy all core files (decorators, container, config, etc.)
|
|
558
|
+
// await this.copyFrameworkCore(projectPath, answers);
|
|
559
|
+
|
|
560
|
+
// spinner.succeed('Project structure created');
|
|
561
|
+
|
|
562
|
+
// // Install dependencies
|
|
563
|
+
// const installSpinner = CLILogger.spinner(`Installing dependencies with ${answers.packageManager}...`);
|
|
564
|
+
|
|
565
|
+
// try {
|
|
566
|
+
// execSync(`cd ${projectPath} && ${answers.packageManager} install`, {
|
|
567
|
+
// stdio: 'pipe'
|
|
568
|
+
// });
|
|
569
|
+
// installSpinner.succeed('Dependencies installed');
|
|
570
|
+
// } catch (error) {
|
|
571
|
+
// installSpinner.fail('Failed to install dependencies');
|
|
572
|
+
// CLILogger.warning('You can install them manually by running:');
|
|
573
|
+
// console.log(` cd ${answers.projectName} && ${answers.packageManager} install`);
|
|
574
|
+
// }
|
|
575
|
+
|
|
576
|
+
// // Success message
|
|
577
|
+
// CLILogger.box('đ Project Created Successfully!', [
|
|
578
|
+
// `Project: ${answers.projectName}`,
|
|
579
|
+
// `Location: ${projectPath}`,
|
|
580
|
+
// `Database: ${answers.database}`,
|
|
581
|
+
// `OpenAI: ${answers.useOpenAI ? 'Enabled' : 'Disabled'}`,
|
|
582
|
+
// `Auth: ${answers.useAuth ? 'Enabled' : 'Disabled'}`
|
|
583
|
+
// ]);
|
|
584
|
+
|
|
585
|
+
// console.log(chalk.bold('\nNext steps:'));
|
|
586
|
+
// console.log(` 1. cd ${answers.projectName}`);
|
|
587
|
+
// console.log(` 2. cp .env.example .env`);
|
|
588
|
+
// console.log(` 3. Configure your database in .env`);
|
|
589
|
+
// if (answers.useOpenAI) {
|
|
590
|
+
// console.log(` 4. Add your OpenAI API key to .env`);
|
|
591
|
+
// }
|
|
592
|
+
// console.log(` ${answers.useOpenAI ? '5' : '4'}. ${answers.packageManager} run dev`);
|
|
593
|
+
|
|
594
|
+
// CLILogger.section('Available Commands:');
|
|
595
|
+
// CLILogger.table({
|
|
596
|
+
// 'frg generate controller <name>': 'Generate a new controller',
|
|
597
|
+
// 'frg generate service <name>': 'Generate a new service',
|
|
598
|
+
// 'frg generate resource <name>': 'Generate controller, service, repository & entity',
|
|
599
|
+
// 'frg config ai': 'Configure OpenAI settings',
|
|
600
|
+
// 'frg migrate': 'Run database migrations',
|
|
601
|
+
// 'frg serve': 'Start development server'
|
|
602
|
+
// });
|
|
603
|
+
|
|
604
|
+
// } catch (error: any) {
|
|
605
|
+
// spinner.fail('Failed to create project');
|
|
606
|
+
// CLILogger.error(error.message);
|
|
607
|
+
// }
|
|
608
|
+
// }
|
|
609
|
+
|
|
610
|
+
// private getDatabaseConfig(database: string): string {
|
|
611
|
+
// const configs: { [key: string]: string } = {
|
|
612
|
+
// 'PostgreSQL': ` type: postgres
|
|
613
|
+
// host: \${DB_HOST:localhost}
|
|
614
|
+
// port: \${DB_PORT:5432}
|
|
615
|
+
// username: \${DB_USERNAME:postgres}
|
|
616
|
+
// password: \${DB_PASSWORD:postgres}
|
|
617
|
+
// database: \${DB_DATABASE:fragment_db}
|
|
618
|
+
// synchronize: true
|
|
619
|
+
// logging: false`,
|
|
620
|
+
// 'MySQL': ` type: mysql
|
|
621
|
+
// host: \${DB_HOST:localhost}
|
|
622
|
+
// port: \${DB_PORT:3306}
|
|
623
|
+
// username: \${DB_USERNAME:root}
|
|
624
|
+
// password: \${DB_PASSWORD:root}
|
|
625
|
+
// database: \${DB_DATABASE:fragment_db}
|
|
626
|
+
// synchronize: true
|
|
627
|
+
// logging: false`,
|
|
628
|
+
// 'SQLite': ` type: sqlite
|
|
629
|
+
// database: \${DB_DATABASE:database.sqlite}
|
|
630
|
+
// synchronize: true
|
|
631
|
+
// logging: false`,
|
|
632
|
+
// 'MongoDB': ` type: mongodb
|
|
633
|
+
// host: \${DB_HOST:localhost}
|
|
634
|
+
// port: \${DB_PORT:27017}
|
|
635
|
+
// database: \${DB_DATABASE:fragment_db}
|
|
636
|
+
// synchronize: true
|
|
637
|
+
// logging: false`
|
|
638
|
+
// };
|
|
639
|
+
// return configs[database];
|
|
640
|
+
// }
|
|
641
|
+
|
|
642
|
+
// private getEnvTemplate(database: string): string {
|
|
643
|
+
// if (database === 'SQLite') {
|
|
644
|
+
// return 'DB_DATABASE=database.sqlite';
|
|
645
|
+
// }
|
|
646
|
+
// return `DB_HOST=localhost
|
|
647
|
+
// DB_PORT=${database === 'PostgreSQL' ? '5432' : database === 'MySQL' ? '3306' : '27017'}
|
|
648
|
+
// DB_USERNAME=${database === 'PostgreSQL' ? 'postgres' : 'root'}
|
|
649
|
+
// DB_PASSWORD=${database === 'PostgreSQL' ? 'postgres' : 'root'}
|
|
650
|
+
// DB_DATABASE=fragment_db`;
|
|
651
|
+
// }
|
|
652
|
+
|
|
653
|
+
// private generateReadme(projectName: string, answers: any): string {
|
|
654
|
+
// return `# ${projectName}
|
|
655
|
+
|
|
656
|
+
// A Fragment Framework application.
|
|
657
|
+
|
|
658
|
+
// ## Getting Started
|
|
659
|
+
|
|
660
|
+
// \`\`\`bash
|
|
661
|
+
// # Install dependencies
|
|
662
|
+
// ${answers.packageManager} install
|
|
663
|
+
|
|
664
|
+
// # Configure environment
|
|
665
|
+
// cp .env.example .env
|
|
666
|
+
|
|
667
|
+
// # Start development server
|
|
668
|
+
// ${answers.packageManager} run dev
|
|
669
|
+
// \`\`\`
|
|
670
|
+
|
|
671
|
+
// ## Fragment CLI Commands
|
|
672
|
+
|
|
673
|
+
// \`\`\`bash
|
|
674
|
+
// # Generate resources
|
|
675
|
+
// frg generate controller <name> # Generate controller
|
|
676
|
+
// frg generate service <name> # Generate service
|
|
677
|
+
// frg generate repository <name> # Generate repository
|
|
678
|
+
// frg generate entity <name> # Generate entity
|
|
679
|
+
// frg generate resource <name> # Generate all of the above
|
|
680
|
+
|
|
681
|
+
// # Configuration
|
|
682
|
+
// frg config ai # Configure OpenAI settings
|
|
683
|
+
// frg config db # Configure database settings
|
|
684
|
+
|
|
685
|
+
// # Database
|
|
686
|
+
// frg migrate # Run migrations
|
|
687
|
+
|
|
688
|
+
// # Development
|
|
689
|
+
// frg serve # Start development server
|
|
690
|
+
// frg build # Build for production
|
|
691
|
+
// \`\`\`
|
|
692
|
+
|
|
693
|
+
// ## Project Structure
|
|
694
|
+
|
|
695
|
+
// \`\`\`
|
|
696
|
+
// src/
|
|
697
|
+
// âââ controllers/ # HTTP request handlers
|
|
698
|
+
// âââ services/ # Business logic
|
|
699
|
+
// âââ repositories/ # Data access layer
|
|
700
|
+
// âââ entities/ # Database models
|
|
701
|
+
// âââ middlewares/ # Express middlewares
|
|
702
|
+
// âââ core/ # Framework core
|
|
703
|
+
// \`\`\`
|
|
704
|
+
|
|
705
|
+
// ## Documentation
|
|
706
|
+
|
|
707
|
+
// Visit [Fragment Framework Documentation](https://fragment-framework.dev) for more information.
|
|
708
|
+
// `;
|
|
709
|
+
// }
|
|
710
|
+
|
|
711
|
+
// private async copyFrameworkCore(projectPath: string, answers: any): Promise<void> {
|
|
712
|
+
// // This would copy all the core framework files
|
|
713
|
+
// // For brevity, I'll show the structure
|
|
714
|
+
// // In a real implementation, these would be actual file copies
|
|
715
|
+
|
|
716
|
+
// CLILogger.info('Core framework files copied');
|
|
717
|
+
// }
|
|
718
|
+
// }
|
|
719
|
+
|
|
720
|
+
// // ============================================================================
|
|
721
|
+
// // FILE: src/cli/commands/generate.command.ts
|
|
722
|
+
// // ============================================================================
|
|
723
|
+
// import * as path from 'path';
|
|
724
|
+
// import { CLILogger } from '../utils/logger';
|
|
725
|
+
// import { FileGenerator } from '../utils/file-generator';
|
|
726
|
+
// import {
|
|
727
|
+
// controllerTemplate,
|
|
728
|
+
// serviceTemplate,
|
|
729
|
+
// repositoryTemplate,
|
|
730
|
+
// entityTemplate
|
|
731
|
+
// } from '../templates';
|
|
732
|
+
// import inquirer from 'inquirer';
|
|
733
|
+
|
|
734
|
+
// export class GenerateCommand {
|
|
735
|
+
// async execute(type: string, name?: string): Promise<void> {
|
|
736
|
+
// if (!name) {
|
|
737
|
+
// const answer = await inquirer.prompt([
|
|
738
|
+
// {
|
|
739
|
+
// type: 'input',
|
|
740
|
+
// name: 'name',
|
|
741
|
+
// message: `Enter ${type} name:`,
|
|
742
|
+
// validate: (input: string) => {
|
|
743
|
+
// if (!input) return 'Name is required';
|
|
744
|
+
// return true;
|
|
745
|
+
// }
|
|
746
|
+
// }
|
|
747
|
+
// ]);
|
|
748
|
+
// name = answer.name;
|
|
749
|
+
// }
|
|
750
|
+
|
|
751
|
+
// const pascalName = FileGenerator.toPascalCase(name);
|
|
752
|
+
// const kebabName = FileGenerator.toKebabCase(name);
|
|
753
|
+
|
|
754
|
+
// switch (type) {
|
|
755
|
+
// case 'controller':
|
|
756
|
+
// await this.generateController(pascalName, kebabName);
|
|
757
|
+
// break;
|
|
758
|
+
// case 'service':
|
|
759
|
+
// await this.generateService(pascalName, kebabName);
|
|
760
|
+
// break;
|
|
761
|
+
// case 'repository':
|
|
762
|
+
// await this.generateRepository(pascalName, kebabName);
|
|
763
|
+
// break;
|
|
764
|
+
// case 'entity':
|
|
765
|
+
// await this.generateEntity(pascalName, kebabName);
|
|
766
|
+
// break;
|
|
767
|
+
// case 'resource':
|
|
768
|
+
// await this.generateResource(pascalName, kebabName);
|
|
769
|
+
// break;
|
|
770
|
+
// default:
|
|
771
|
+
// CLILogger.error(`Unknown type: ${type}`);
|
|
772
|
+
// CLILogger.info('Available types: controller, service, repository, entity, resource');
|
|
773
|
+
// }
|
|
774
|
+
// }
|
|
775
|
+
|
|
776
|
+
// private async generateController(name: string, fileName: string): Promise<void> {
|
|
777
|
+
// const answers = await inquirer.prompt([
|
|
778
|
+
// {
|
|
779
|
+
// type: 'input',
|
|
780
|
+
// name: 'route',
|
|
781
|
+
// message: 'Route path:',
|
|
782
|
+
// default: `/api/${fileName}s`
|
|
783
|
+
// }
|
|
784
|
+
// ]);
|
|
785
|
+
|
|
786
|
+
// const filePath = path.join(process.cwd(), 'src/controllers', `${fileName}.controller.ts`);
|
|
787
|
+
|
|
788
|
+
// if (FileGenerator.fileExists(filePath)) {
|
|
789
|
+
// CLILogger.error(`Controller ${name} already exists`);
|
|
790
|
+
// return;
|
|
791
|
+
// }
|
|
792
|
+
|
|
793
|
+
// const content = controllerTemplate(name, answers.route);
|
|
794
|
+
// FileGenerator.writeFile(filePath, content);
|
|
795
|
+
|
|
796
|
+
// CLILogger.success(`Controller created: src/controllers/${fileName}.controller.ts`);
|
|
797
|
+
// CLILogger.info(`Route: ${answers.route}`);
|
|
798
|
+
// }
|
|
799
|
+
|
|
800
|
+
// private async generateService(name: string, fileName: string): Promise<void> {
|
|
801
|
+
// const filePath = path.join(process.cwd(), 'src/services', `${fileName}.service.ts`);
|
|
802
|
+
|
|
803
|
+
// if (FileGenerator.fileExists(filePath)) {
|
|
804
|
+
// CLILogger.error(`Service ${name} already exists`);
|
|
805
|
+
// return;
|
|
806
|
+
// }
|
|
807
|
+
|
|
808
|
+
// const content = serviceTemplate(name);
|
|
809
|
+
// FileGenerator.writeFile(filePath, content);
|
|
810
|
+
|
|
811
|
+
// CLILogger.success(`Service created: src/services/${fileName}.service.ts`);
|
|
812
|
+
// }
|
|
813
|
+
|
|
814
|
+
// private async generateRepository(name: string, fileName: string): Promise<void> {
|
|
815
|
+
// const answers = await inquirer.prompt([
|
|
816
|
+
// {
|
|
817
|
+
// type: 'input',
|
|
818
|
+
// name: 'entity',
|
|
819
|
+
// message: 'Entity name:',
|
|
820
|
+
// default: name.replace('Repository', '')
|
|
821
|
+
// }
|
|
822
|
+
// ]);
|
|
823
|
+
|
|
824
|
+
// const filePath = path.join(process.cwd(), 'src/repositories', `${fileName}.repository.ts`);
|
|
825
|
+
|
|
826
|
+
// if (FileGenerator.fileExists(filePath)) {
|
|
827
|
+
// CLILogger.error(`Repository ${name} already exists`);
|
|
828
|
+
// return;
|
|
829
|
+
// }
|
|
830
|
+
|
|
831
|
+
// const content = repositoryTemplate(name, answers.entity);
|
|
832
|
+
// FileGenerator.writeFile(filePath, content);
|
|
833
|
+
|
|
834
|
+
// CLILogger.success(`Repository created: src/repositories/${fileName}.repository.ts`);
|
|
835
|
+
// }
|
|
836
|
+
|
|
837
|
+
// private async generateEntity(name: string, fileName: string): Promise<void> {
|
|
838
|
+
// const filePath = path.join(process.cwd(), 'src/entities', `${fileName}.entity.ts`);
|
|
839
|
+
|
|
840
|
+
// if (FileGenerator.fileExists(filePath)) {
|
|
841
|
+
// CLILogger.error(`Entity ${name} already exists`);
|
|
842
|
+
// return;
|
|
843
|
+
// }
|
|
844
|
+
|
|
845
|
+
// const content = entityTemplate(name);
|
|
846
|
+
// FileGenerator.writeFile(filePath, content);
|
|
847
|
+
|
|
848
|
+
// CLILogger.success(`Entity created: src/entities/${fileName}.entity.ts`);
|
|
849
|
+
// }
|
|
850
|
+
|
|
851
|
+
// private async generateResource(name: string, fileName: string): Promise<void> {
|
|
852
|
+
// CLILogger.title(`đˇ Generating resource: ${name}`);
|
|
853
|
+
|
|
854
|
+
// const spinner = CLILogger.spinner('Generating files...');
|
|
855
|
+
|
|
856
|
+
// try {
|
|
857
|
+
// // Generate entity
|
|
858
|
+
// const entityPath = path.join(process.cwd(), 'src/entities', `${fileName}.entity.ts`);
|
|
859
|
+
// FileGenerator.writeFile(entityPath, entityTemplate(name));
|
|
860
|
+
// spinner.text = 'Entity created...';
|
|
861
|
+
|
|
862
|
+
// // Generate repository
|
|
863
|
+
// const repoPath = path.join(process.cwd(), 'src/repositories', `${fileName}.repository.ts`);
|
|
864
|
+
// FileGenerator.writeFile(repoPath, repositoryTemplate(name + 'Repository', name));
|
|
865
|
+
// spinner.text = 'Repository created...';
|
|
866
|
+
|
|
867
|
+
// // Generate service
|
|
868
|
+
// const servicePath = path.join(process.cwd(), 'src/services', `${fileName}.service.ts`);
|
|
869
|
+
// FileGenerator.writeFile(servicePath, serviceTemplate(name + 'Service'));
|
|
870
|
+
// spinner.text = 'Service created...';
|
|
871
|
+
|
|
872
|
+
// // Generate controller
|
|
873
|
+
// const controllerPath = path.join(process.cwd(), 'src/controllers', `${fileName}.controller.ts`);
|
|
874
|
+
// FileGenerator.writeFile(controllerPath, controllerTemplate(name + 'Controller', `/api/${fileName}s`));
|
|
875
|
+
// spinner.text = 'Controller created...';
|
|
876
|
+
|
|
877
|
+
// spinner.succeed('Resource generated successfully');
|
|
878
|
+
|
|
879
|
+
// CLILogger.box('đĻ Generated Files', [
|
|
880
|
+
// `Entity: src/entities/${fileName}.entity.ts`,
|
|
881
|
+
// `Repository: src/repositories/${fileName}.repository.ts`,
|
|
882
|
+
// `Service: src/services/${fileName}.service.ts`,
|
|
883
|
+
// `Controller: src/controllers/${fileName}.controller.ts`
|
|
884
|
+
// ]);
|
|
885
|
+
|
|
886
|
+
// CLILogger.info(`\nRoute available at: /api/${fileName}s`);
|
|
887
|
+
|
|
888
|
+
// } catch (error: any) {
|
|
889
|
+
// spinner.fail('Failed to generate resource');
|
|
890
|
+
// CLILogger.error(error.message);
|
|
891
|
+
// }
|
|
892
|
+
// }
|
|
893
|
+
// }
|
|
894
|
+
|
|
895
|
+
// // ============================================================================
|
|
896
|
+
// // FILE: src/cli/commands/config.command.ts
|
|
897
|
+
// // ============================================================================
|
|
898
|
+
// import * as fs from 'fs';
|
|
899
|
+
// import * as path from 'path';
|
|
900
|
+
// import * as yaml from 'js-yaml';
|
|
901
|
+
// import { CLILogger } from '../utils/logger';
|
|
902
|
+
// import { FileGenerator } from '../utils/file-generator';
|
|
903
|
+
// import inquirer from 'inquirer';
|
|
904
|
+
|
|
905
|
+
// export class ConfigCommand {
|
|
906
|
+
// async execute(target: string): Promise<void> {
|
|
907
|
+
// switch (target) {
|
|
908
|
+
// case 'ai':
|
|
909
|
+
// await this.configureAI();
|
|
910
|
+
// break;
|
|
911
|
+
// case 'db':
|
|
912
|
+
// case 'database':
|
|
913
|
+
// await this.configureDatabase();
|
|
914
|
+
// break;
|
|
915
|
+
// case 'show':
|
|
916
|
+
// await this.showConfig();
|
|
917
|
+
// break;
|
|
918
|
+
// default:
|
|
919
|
+
// CLILogger.error(`Unknown config target: ${target}`);
|
|
920
|
+
// CLILogger.info('Available targets: ai, db, show');
|
|
921
|
+
// }
|
|
922
|
+
// }
|
|
923
|
+
|
|
924
|
+
// private async configureAI(): Promise<void> {
|
|
925
|
+
// CLILogger.title('đ¤ OpenAI Configuration');
|
|
926
|
+
|
|
927
|
+
// const answers = await inquirer.prompt([
|
|
928
|
+
// {
|
|
929
|
+
// type: 'password',
|
|
930
|
+
// name: 'apiKey',
|
|
931
|
+
// message: 'OpenAI API Key:',
|
|
932
|
+
// mask: '*',
|
|
933
|
+
// validate: (input: string) => {
|
|
934
|
+
// if (!input || !input.startsWith('sk-')) {
|
|
935
|
+
// return 'Please enter a valid OpenAI API key (starts with sk-)';
|
|
936
|
+
// }
|
|
937
|
+
// return true;
|
|
938
|
+
// }
|
|
939
|
+
// },
|
|
940
|
+
// {
|
|
941
|
+
// type: 'list',
|
|
942
|
+
// name: 'model',
|
|
943
|
+
// message: 'Default model:',
|
|
944
|
+
// choices: [
|
|
945
|
+
// 'gpt-4-turbo',
|
|
946
|
+
// 'gpt-4',
|
|
947
|
+
// 'gpt-3.5-turbo',
|
|
948
|
+
// 'claude-sonnet-4-5-20250929',
|
|
949
|
+
// 'claude-opus-4-1-20250514'
|
|
950
|
+
// ],
|
|
951
|
+
// default: 'gpt-4'
|
|
952
|
+
// },
|
|
953
|
+
// {
|
|
954
|
+
// type: 'confirm',
|
|
955
|
+
// name: 'saveToEnv',
|
|
956
|
+
// message: 'Save API key to .env file?',
|
|
957
|
+
// default: true
|
|
958
|
+
// }
|
|
959
|
+
// ]);
|
|
960
|
+
|
|
961
|
+
// const configPath = path.join(process.cwd(), 'config/fragment.yaml');
|
|
962
|
+
|
|
963
|
+
// if (!FileGenerator.fileExists(configPath)) {
|
|
964
|
+
// CLILogger.error('fragment.yaml not found. Are you in a Fragment project?');
|
|
965
|
+
// return;
|
|
966
|
+
// }
|
|
967
|
+
|
|
968
|
+
// try {
|
|
969
|
+
// const configContent = FileGenerator.readFile(configPath);
|
|
970
|
+
// const config: any = yaml.load(configContent);
|
|
971
|
+
|
|
972
|
+
// if (!config.openai) {
|
|
973
|
+
// config.openai = {};
|
|
974
|
+
// }
|
|
975
|
+
|
|
976
|
+
// config.openai.apiKey = answers.saveToEnv ? '${OPENAI_API_KEY}' : answers.apiKey;
|
|
977
|
+
// config.openai.model = answers.model;
|
|
978
|
+
|
|
979
|
+
// FileGenerator.writeFile(configPath, yaml.dump(config));
|
|
980
|
+
|
|
981
|
+
// if (answers.saveToEnv) {
|
|
982
|
+
// const envPath = path.join(process.cwd(), '.env');
|
|
983
|
+
// let envContent = '';
|
|
984
|
+
|
|
985
|
+
// if (FileGenerator.fileExists(envPath)) {
|
|
986
|
+
// envContent = FileGenerator.readFile(envPath);
|
|
987
|
+
// }
|
|
988
|
+
|
|
989
|
+
// // Update or add OPENAI_API_KEY
|
|
990
|
+
// if (envContent.includes('OPENAI_API_KEY=')) {
|
|
991
|
+
// envContent = envContent.replace(
|
|
992
|
+
// /OPENAI_API_KEY=.*/,
|
|
993
|
+
// `OPENAI_API_KEY=${answers.apiKey}`
|
|
994
|
+
// );
|
|
995
|
+
// } else {
|
|
996
|
+
// envContent += `\nOPENAI_API_KEY=${answers.apiKey}`;
|
|
997
|
+
// }
|
|
998
|
+
|
|
999
|
+
// FileGenerator.writeFile(envPath, envContent);
|
|
1000
|
+
// CLILogger.success('API key saved to .env file');
|
|
1001
|
+
// }
|
|
1002
|
+
|
|
1003
|
+
// CLILogger.success('OpenAI configuration updated');
|
|
1004
|
+
// CLILogger.table({
|
|
1005
|
+
// 'Model': answers.model,
|
|
1006
|
+
// 'API Key': answers.saveToEnv ? 'Stored in .env' : 'Stored in config'
|
|
1007
|
+
// });
|
|
1008
|
+
|
|
1009
|
+
// } catch (error: any) {
|
|
1010
|
+
// CLILogger.error(`Failed to update configuration: ${error.message}`);
|
|
1011
|
+
// }
|
|
1012
|
+
// }
|
|
1013
|
+
|
|
1014
|
+
// private async configureDatabase(): Promise<void> {
|
|
1015
|
+
// CLILogger.title('đī¸ Database Configuration');
|
|
1016
|
+
|
|
1017
|
+
// const answers = await inquirer.prompt([
|
|
1018
|
+
// {
|
|
1019
|
+
// type: 'list',
|
|
1020
|
+
// name: 'type',
|
|
1021
|
+
// message: 'Database type:',
|
|
1022
|
+
// choices: ['postgres', 'mysql', 'sqlite', 'mongodb']
|
|
1023
|
+
// },
|
|
1024
|
+
// {
|
|
1025
|
+
// type: 'input',
|
|
1026
|
+
// name: 'host',
|
|
1027
|
+
// message: 'Host:',
|
|
1028
|
+
// default: 'localhost',
|
|
1029
|
+
// when: (answers) => answers.type !== 'sqlite'
|
|
1030
|
+
// },
|
|
1031
|
+
// {
|
|
1032
|
+
// type: 'input',
|
|
1033
|
+
// name: 'port',
|
|
1034
|
+
// message: 'Port:',
|
|
1035
|
+
// default: (answers: any) => {
|
|
1036
|
+
// const ports: any = { postgres: 5432, mysql: 3306, mongodb: 27017 };
|
|
1037
|
+
// return ports[answers.type] || 5432;
|
|
1038
|
+
// },
|
|
1039
|
+
// when: (answers) => answers.type !== 'sqlite'
|
|
1040
|
+
// },
|
|
1041
|
+
// {
|
|
1042
|
+
// type: 'input',
|
|
1043
|
+
// name: 'username',
|
|
1044
|
+
// message: 'Username:',
|
|
1045
|
+
// default: (answers: any) => answers.type === 'postgres' ? 'postgres' : 'root',
|
|
1046
|
+
// when: (answers) => answers.type !== 'sqlite'
|
|
1047
|
+
// },
|
|
1048
|
+
// {
|
|
1049
|
+
// type: 'password',
|
|
1050
|
+
// name: 'password',
|
|
1051
|
+
// message: 'Password:',
|
|
1052
|
+
// mask: '*',
|
|
1053
|
+
// when: (answers) => answers.type !== 'sqlite'
|
|
1054
|
+
// },
|
|
1055
|
+
// {
|
|
1056
|
+
// type: 'input',
|
|
1057
|
+
// name: 'database',
|
|
1058
|
+
// message: 'Database name:',
|
|
1059
|
+
// default: 'fragment_db'
|
|
1060
|
+
// },
|
|
1061
|
+
// {
|
|
1062
|
+
// type: 'confirm',
|
|
1063
|
+
// name: 'synchronize',
|
|
1064
|
+
// message: 'Auto-synchronize schema? (not recommended for production)',
|
|
1065
|
+
// default: true
|
|
1066
|
+
// }
|
|
1067
|
+
// ]);
|
|
1068
|
+
|
|
1069
|
+
// const configPath = path.join(process.cwd(), 'config/fragment.yaml');
|
|
1070
|
+
|
|
1071
|
+
// try {
|
|
1072
|
+
// const configContent = FileGenerator.readFile(configPath);
|
|
1073
|
+
// const config: any = yaml.load(configContent);
|
|
1074
|
+
|
|
1075
|
+
// config.database = {
|
|
1076
|
+
// type: answers.type,
|
|
1077
|
+
// ...(answers.type !== 'sqlite' && {
|
|
1078
|
+
// host: '${DB_HOST:' + answers.host + '}',
|
|
1079
|
+
// port: '${DB_PORT:' + answers.port + '}',
|
|
1080
|
+
// username: '${DB_USERNAME:' + answers.username + '}',
|
|
1081
|
+
// password: '${DB_PASSWORD}'
|
|
1082
|
+
// }),
|
|
1083
|
+
// database: '${DB_DATABASE:' + answers.database + '}',
|
|
1084
|
+
// synchronize: answers.synchronize,
|
|
1085
|
+
// logging: false
|
|
1086
|
+
// };
|
|
1087
|
+
|
|
1088
|
+
// FileGenerator.writeFile(configPath, yaml.dump(config));
|
|
1089
|
+
|
|
1090
|
+
// // Update .env
|
|
1091
|
+
// const envPath = path.join(process.cwd(), '.env');
|
|
1092
|
+
// let envContent = FileGenerator.fileExists(envPath) ? FileGenerator.readFile(envPath) : '';
|
|
1093
|
+
|
|
1094
|
+
// const envVars: any = {
|
|
1095
|
+
// DB_HOST: answers.host,
|
|
1096
|
+
// DB_PORT: answers.port,
|
|
1097
|
+
// DB_USERNAME: answers.username,
|
|
1098
|
+
// DB_PASSWORD: answers.password,
|
|
1099
|
+
// DB_DATABASE: answers.database
|
|
1100
|
+
// };
|
|
1101
|
+
|
|
1102
|
+
// Object.entries(envVars).forEach(([key, value]) => {
|
|
1103
|
+
// if (value !== undefined) {
|
|
1104
|
+
// if (envContent.includes(`${key}=`)) {
|
|
1105
|
+
// envContent = envContent.replace(new RegExp(`${key}=.*`), `${key}=${value}`);
|
|
1106
|
+
// } else {
|
|
1107
|
+
// envContent += `\n${key}=${value}`;
|
|
1108
|
+
// }
|
|
1109
|
+
// }
|
|
1110
|
+
// });
|
|
1111
|
+
|
|
1112
|
+
// FileGenerator.writeFile(envPath, envContent.trim());
|
|
1113
|
+
|
|
1114
|
+
// CLILogger.success('Database configuration updated');
|
|
1115
|
+
// CLILogger.table({
|
|
1116
|
+
// 'Type': answers.type,
|
|
1117
|
+
// 'Host': answers.host || 'N/A',
|
|
1118
|
+
// 'Database': answers.database,
|
|
1119
|
+
// 'Synchronize': answers.synchronize ? 'Yes' : 'No'
|
|
1120
|
+
// });
|
|
1121
|
+
|
|
1122
|
+
// } catch (error: any) {
|
|
1123
|
+
// CLILogger.error(`Failed to update configuration: ${error.message}`);
|
|
1124
|
+
// }
|
|
1125
|
+
// }
|
|
1126
|
+
|
|
1127
|
+
// private async showConfig(): Promise<void> {
|
|
1128
|
+
// CLILogger.title('đ Current Configuration');
|
|
1129
|
+
|
|
1130
|
+
// const configPath = path.join(process.cwd(), 'config/fragment.yaml');
|
|
1131
|
+
|
|
1132
|
+
// if (!FileGenerator.fileExists(configPath)) {
|
|
1133
|
+
// CLILogger.error('fragment.yaml not found');
|
|
1134
|
+
// return;
|
|
1135
|
+
// }
|
|
1136
|
+
|
|
1137
|
+
// try {
|
|
1138
|
+
// const configContent = FileGenerator.readFile(configPath);
|
|
1139
|
+
// const config: any = yaml.load(configContent);
|
|
1140
|
+
|
|
1141
|
+
// CLILogger.section('Application');
|
|
1142
|
+
// CLILogger.table({
|
|
1143
|
+
// 'Name': config.app?.name || 'N/A',
|
|
1144
|
+
// 'Port': config.app?.port || 'N/A',
|
|
1145
|
+
// 'Environment': config.app?.env || 'N/A'
|
|
1146
|
+
// });
|
|
1147
|
+
|
|
1148
|
+
// CLILogger.section('Database');
|
|
1149
|
+
// CLILogger.table({
|
|
1150
|
+
// 'Type': config.database?.type || 'N/A',
|
|
1151
|
+
// 'Host': config.database?.host || 'N/A',
|
|
1152
|
+
// 'Database': config.database?.database || 'N/A',
|
|
1153
|
+
// 'Synchronize': config.database?.synchronize ? 'Yes' : 'No'
|
|
1154
|
+
// });
|
|
1155
|
+
|
|
1156
|
+
// if (config.openai) {
|
|
1157
|
+
// CLILogger.section('OpenAI');
|
|
1158
|
+
// CLILogger.table({
|
|
1159
|
+
// 'Model': config.openai.model || 'N/A',
|
|
1160
|
+
// 'API Key': config.openai.apiKey?.includes('${') ? 'From environment' : 'Configured'
|
|
1161
|
+
// });
|
|
1162
|
+
// }
|
|
1163
|
+
|
|
1164
|
+
// if (config.auth) {
|
|
1165
|
+
// CLILogger.section('Authentication');
|
|
1166
|
+
// CLILogger.table({
|
|
1167
|
+
// 'Token Expiry': config.auth.tokenExpiry || 'N/A',
|
|
1168
|
+
// 'Session Expiry': config.auth.sessionExpiry || 'N/A'
|
|
1169
|
+
// });
|
|
1170
|
+
// }
|
|
1171
|
+
|
|
1172
|
+
// } catch (error: any) {
|
|
1173
|
+
// CLILogger.error(`Failed to read configuration: ${error.message}`);
|
|
1174
|
+
// }
|
|
1175
|
+
// }
|
|
1176
|
+
// }
|
|
1177
|
+
|
|
1178
|
+
// // ============================================================================
|
|
1179
|
+
// // FILE: src/cli/commands/serve.command.ts
|
|
1180
|
+
// // ============================================================================
|
|
1181
|
+
// import { spawn } from 'child_process';
|
|
1182
|
+
// import { CLILogger } from '../utils/logger';
|
|
1183
|
+
|
|
1184
|
+
// export class ServeCommand {
|
|
1185
|
+
// async execute(options: { port?: number; watch?: boolean }): Promise<void> {
|
|
1186
|
+
// CLILogger.title('đ Starting Fragment Application');
|
|
1187
|
+
|
|
1188
|
+
// const port = options.port || process.env.PORT || 3000;
|
|
1189
|
+
// const args = ['--respawn', '--transpile-only'];
|
|
1190
|
+
|
|
1191
|
+
// if (options.watch !== false) {
|
|
1192
|
+
// args.push('--watch');
|
|
1193
|
+
// }
|
|
1194
|
+
|
|
1195
|
+
// args.push('src/server.ts');
|
|
1196
|
+
|
|
1197
|
+
// const env = { ...process.env, PORT: port.toString() };
|
|
1198
|
+
|
|
1199
|
+
// CLILogger.info(`Starting server on port ${port}...`);
|
|
1200
|
+
|
|
1201
|
+
// const child = spawn('ts-node-dev', args, {
|
|
1202
|
+
// env,
|
|
1203
|
+
// stdio: 'inherit'
|
|
1204
|
+
// });
|
|
1205
|
+
|
|
1206
|
+
// child.on('error', (error) => {
|
|
1207
|
+
// CLILogger.error(`Failed to start server: ${error.message}`);
|
|
1208
|
+
// });
|
|
1209
|
+
|
|
1210
|
+
// child.on('close', (code) => {
|
|
1211
|
+
// if (code !== 0) {
|
|
1212
|
+
// CLILogger.error(`Server exited with code ${code}`);
|
|
1213
|
+
// }
|
|
1214
|
+
// });
|
|
1215
|
+
// }
|
|
1216
|
+
// }
|
|
1217
|
+
|
|
1218
|
+
// // ============================================================================
|
|
1219
|
+
// // FILE: src/cli/commands/build.command.ts
|
|
1220
|
+
// // ============================================================================
|
|
1221
|
+
// import { execSync } from 'child_process';
|
|
1222
|
+
// import { CLILogger } from '../utils/logger';
|
|
1223
|
+
|
|
1224
|
+
// export class BuildCommand {
|
|
1225
|
+
// async execute(): Promise<void> {
|
|
1226
|
+
// CLILogger.title('đ¨ Building Fragment Application');
|
|
1227
|
+
|
|
1228
|
+
// const spinner = CLILogger.spinner('Compiling TypeScript...');
|
|
1229
|
+
|
|
1230
|
+
// try {
|
|
1231
|
+
// execSync('tsc', { stdio: 'pipe' });
|
|
1232
|
+
// spinner.succeed('Build completed successfully');
|
|
1233
|
+
|
|
1234
|
+
// CLILogger.success('Output directory: ./dist');
|
|
1235
|
+
// CLILogger.info('Run "node dist/server.js" to start the production server');
|
|
1236
|
+
|
|
1237
|
+
// } catch (error: any) {
|
|
1238
|
+
// spinner.fail('Build failed');
|
|
1239
|
+
// CLILogger.error(error.message);
|
|
1240
|
+
// process.exit(1);
|
|
1241
|
+
// }
|
|
1242
|
+
// }
|
|
1243
|
+
// }
|
|
1244
|
+
|
|
1245
|
+
// // ============================================================================
|
|
1246
|
+
// // FILE: src/cli/commands/migrate.command.ts
|
|
1247
|
+
// // ============================================================================
|
|
1248
|
+
// import { execSync } from 'child_process';
|
|
1249
|
+
// import { CLILogger } from '../utils/logger';
|
|
1250
|
+
// import inquirer from 'inquirer';
|
|
1251
|
+
|
|
1252
|
+
// export class MigrateCommand {
|
|
1253
|
+
// async execute(action?: string): Promise<void> {
|
|
1254
|
+
// if (!action) {
|
|
1255
|
+
// const answer = await inquirer.prompt([
|
|
1256
|
+
// {
|
|
1257
|
+
// type: 'list',
|
|
1258
|
+
// name: 'action',
|
|
1259
|
+
// message: 'Select migration action:',
|
|
1260
|
+
// choices: [
|
|
1261
|
+
// { name: 'Run pending migrations', value: 'run' },
|
|
1262
|
+
// { name: 'Generate new migration', value: 'generate' },
|
|
1263
|
+
// { name: 'Revert last migration', value: 'revert' },
|
|
1264
|
+
// { name: 'Show migration status', value: 'show' }
|
|
1265
|
+
// ]
|
|
1266
|
+
// }
|
|
1267
|
+
// ]);
|
|
1268
|
+
// action = answer.action;
|
|
1269
|
+
// }
|
|
1270
|
+
|
|
1271
|
+
// switch (action) {
|
|
1272
|
+
// case 'run':
|
|
1273
|
+
// await this.runMigrations();
|
|
1274
|
+
// break;
|
|
1275
|
+
// case 'generate':
|
|
1276
|
+
// await this.generateMigration();
|
|
1277
|
+
// break;
|
|
1278
|
+
// case 'revert':
|
|
1279
|
+
// await this.revertMigration();
|
|
1280
|
+
// break;
|
|
1281
|
+
// case 'show':
|
|
1282
|
+
// await this.showMigrations();
|
|
1283
|
+
// break;
|
|
1284
|
+
// default:
|
|
1285
|
+
// CLILogger.error(`Unknown action: ${action}`);
|
|
1286
|
+
// }
|
|
1287
|
+
// }
|
|
1288
|
+
|
|
1289
|
+
// private async runMigrations(): Promise<void> {
|
|
1290
|
+
// CLILogger.title('đĻ Running Migrations');
|
|
1291
|
+
// const spinner = CLILogger.spinner('Executing migrations...');
|
|
1292
|
+
|
|
1293
|
+
// try {
|
|
1294
|
+
// execSync('npm run typeorm migration:run', { stdio: 'pipe' });
|
|
1295
|
+
// spinner.succeed('Migrations executed successfully');
|
|
1296
|
+
// } catch (error: any) {
|
|
1297
|
+
// spinner.fail('Migration failed');
|
|
1298
|
+
// CLILogger.error(error.message);
|
|
1299
|
+
// }
|
|
1300
|
+
// }
|
|
1301
|
+
|
|
1302
|
+
// private async generateMigration(): Promise<void> {
|
|
1303
|
+
// const answer = await inquirer.prompt([
|
|
1304
|
+
// {
|
|
1305
|
+
// type: 'input',
|
|
1306
|
+
// name: 'name',
|
|
1307
|
+
// message: 'Migration name:',
|
|
1308
|
+
// validate: (input: string) => {
|
|
1309
|
+
// if (!input) return 'Name is required';
|
|
1310
|
+
// return true;
|
|
1311
|
+
// }
|
|
1312
|
+
// }
|
|
1313
|
+
// ]);
|
|
1314
|
+
|
|
1315
|
+
// CLILogger.title(`đ Generating Migration: ${answer.name}`);
|
|
1316
|
+
// const spinner = CLILogger.spinner('Creating migration file...');
|
|
1317
|
+
|
|
1318
|
+
// try {
|
|
1319
|
+
// execSync(`npm run typeorm migration:generate -- -n ${answer.name}`, { stdio: 'pipe' });
|
|
1320
|
+
// spinner.succeed('Migration generated successfully');
|
|
1321
|
+
// } catch (error: any) {
|
|
1322
|
+
// spinner.fail('Failed to generate migration');
|
|
1323
|
+
// CLILogger.error(error.message);
|
|
1324
|
+
// }
|
|
1325
|
+
// }
|
|
1326
|
+
|
|
1327
|
+
// private async revertMigration(): Promise<void> {
|
|
1328
|
+
// const confirm = await inquirer.prompt([
|
|
1329
|
+
// {
|
|
1330
|
+
// type: 'confirm',
|
|
1331
|
+
// name: 'confirmed',
|
|
1332
|
+
// message: 'Are you sure you want to revert the last migration?',
|
|
1333
|
+
// default: false
|
|
1334
|
+
// }
|
|
1335
|
+
// ]);
|
|
1336
|
+
|
|
1337
|
+
// if (!confirm.confirmed) {
|
|
1338
|
+
// CLILogger.info('Migration revert cancelled');
|
|
1339
|
+
// return;
|
|
1340
|
+
// }
|
|
1341
|
+
|
|
1342
|
+
// CLILogger.title('âĒ Reverting Migration');
|
|
1343
|
+
// const spinner = CLILogger.spinner('Reverting last migration...');
|
|
1344
|
+
|
|
1345
|
+
// try {
|
|
1346
|
+
// execSync('npm run typeorm migration:revert', { stdio: 'pipe' });
|
|
1347
|
+
// spinner.succeed('Migration reverted successfully');
|
|
1348
|
+
// } catch (error: any) {
|
|
1349
|
+
// spinner.fail('Failed to revert migration');
|
|
1350
|
+
// CLILogger.error(error.message);
|
|
1351
|
+
// }
|
|
1352
|
+
// }
|
|
1353
|
+
|
|
1354
|
+
// private async showMigrations(): Promise<void> {
|
|
1355
|
+
// CLILogger.title('đ Migration Status');
|
|
1356
|
+
|
|
1357
|
+
// try {
|
|
1358
|
+
// const output = execSync('npm run typeorm migration:show', { encoding: 'utf-8' });
|
|
1359
|
+
// console.log(output);
|
|
1360
|
+
// } catch (error: any) {
|
|
1361
|
+
// CLILogger.error('Failed to show migrations');
|
|
1362
|
+
// }
|
|
1363
|
+
// }
|
|
1364
|
+
// }
|
|
1365
|
+
|
|
1366
|
+
// // ============================================================================
|
|
1367
|
+
// // FILE: src/cli/cli.ts
|
|
1368
|
+
// // ============================================================================
|
|
1369
|
+
// import chalk from 'chalk';
|
|
1370
|
+
// import { Command } from 'commander';
|
|
1371
|
+
// import { InitCommand } from './commands/init.command';
|
|
1372
|
+
// import { GenerateCommand } from './commands/generate.command';
|
|
1373
|
+
// import { ConfigCommand } from './commands/config.command';
|
|
1374
|
+
// import { ServeCommand } from './commands/serve.command';
|
|
1375
|
+
// import { BuildCommand } from './commands/build.command';
|
|
1376
|
+
// import { MigrateCommand } from './commands/migrate.command';
|
|
1377
|
+
// import { CLILogger } from './utils/logger';
|
|
1378
|
+
|
|
1379
|
+
// export class CLI {
|
|
1380
|
+
// private program: Command;
|
|
1381
|
+
// private initCommand: InitCommand;
|
|
1382
|
+
// private generateCommand: GenerateCommand;
|
|
1383
|
+
// private configCommand: ConfigCommand;
|
|
1384
|
+
// private serveCommand: ServeCommand;
|
|
1385
|
+
// private buildCommand: BuildCommand;
|
|
1386
|
+
// private migrateCommand: MigrateCommand;
|
|
1387
|
+
|
|
1388
|
+
// constructor() {
|
|
1389
|
+
// this.program = new Command();
|
|
1390
|
+
// this.initCommand = new InitCommand();
|
|
1391
|
+
// this.generateCommand = new GenerateCommand();
|
|
1392
|
+
// this.configCommand = new ConfigCommand();
|
|
1393
|
+
// this.serveCommand = new ServeCommand();
|
|
1394
|
+
// this.buildCommand = new BuildCommand();
|
|
1395
|
+
// this.migrateCommand = new MigrateCommand();
|
|
1396
|
+
|
|
1397
|
+
// this.setupCommands();
|
|
1398
|
+
// }
|
|
1399
|
+
|
|
1400
|
+
// private setupCommands(): void {
|
|
1401
|
+
// this.program
|
|
1402
|
+
// .name('frg')
|
|
1403
|
+
// .description('Fragment Framework CLI - Build powerful TypeScript backends')
|
|
1404
|
+
// .version('1.0.0');
|
|
1405
|
+
|
|
1406
|
+
// // ASCII Art Banner
|
|
1407
|
+
// this.program
|
|
1408
|
+
// .addHelpText('beforeAll', () => {
|
|
1409
|
+
// return chalk.cyan(`
|
|
1410
|
+
// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
1411
|
+
// â â
|
|
1412
|
+
// â âââââââââââââââ ââââââ âââââââ ââââ ââââââââââââ â
|
|
1413
|
+
// â ââââââââââââââââââââââââââââââââ âââââ âââââââââââââ â
|
|
1414
|
+
// â ââââââ âââââââââââââââââââ âââââââââââââââââââââ â
|
|
1415
|
+
// â ââââââ âââââââââââââââââââ ââââââââââââââââââââ â
|
|
1416
|
+
// â âââ âââ ââââââ âââââââââââââââ âââ âââââââââââ â
|
|
1417
|
+
// â âââ âââ ââââââ âââ âââââââ âââ âââââââââââ â
|
|
1418
|
+
// â â
|
|
1419
|
+
// â TypeScript Backend Framework (v1.0.0) â
|
|
1420
|
+
// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
1421
|
+
// `);
|
|
1422
|
+
// });
|
|
1423
|
+
|
|
1424
|
+
// // Init command
|
|
1425
|
+
// this.program
|
|
1426
|
+
// .command('init [project-name]')
|
|
1427
|
+
// .description('Initialize a new Fragment project')
|
|
1428
|
+
// .action(async (projectName) => {
|
|
1429
|
+
// await this.initCommand.execute(projectName);
|
|
1430
|
+
// });
|
|
1431
|
+
|
|
1432
|
+
// // Generate commands
|
|
1433
|
+
// const generate = this.program
|
|
1434
|
+
// .command('generate')
|
|
1435
|
+
// .alias('g')
|
|
1436
|
+
// .description('Generate application components');
|
|
1437
|
+
|
|
1438
|
+
// generate
|
|
1439
|
+
// .command('controller [name]')
|
|
1440
|
+
// .alias('c')
|
|
1441
|
+
// .description('Generate a new controller')
|
|
1442
|
+
// .action(async (name) => {
|
|
1443
|
+
// await this.generateCommand.execute('controller', name);
|
|
1444
|
+
// });
|
|
1445
|
+
|
|
1446
|
+
// generate
|
|
1447
|
+
// .command('service [name]')
|
|
1448
|
+
// .alias('s')
|
|
1449
|
+
// .description('Generate a new service')
|
|
1450
|
+
// .action(async (name) => {
|
|
1451
|
+
// await this.generateCommand.execute('service', name);
|
|
1452
|
+
// });
|
|
1453
|
+
|
|
1454
|
+
// generate
|
|
1455
|
+
// .command('repository [name]')
|
|
1456
|
+
// .alias('r')
|
|
1457
|
+
// .description('Generate a new repository')
|
|
1458
|
+
// .action(async (name) => {
|
|
1459
|
+
// await this.generateCommand.execute('repository', name);
|
|
1460
|
+
// });
|
|
1461
|
+
|
|
1462
|
+
// generate
|
|
1463
|
+
// .command('entity [name]')
|
|
1464
|
+
// .alias('e')
|
|
1465
|
+
// .description('Generate a new entity')
|
|
1466
|
+
// .action(async (name) => {
|
|
1467
|
+
// await this.generateCommand.execute('entity', name);
|
|
1468
|
+
// });
|
|
1469
|
+
|
|
1470
|
+
// generate
|
|
1471
|
+
// .command('resource [name]')
|
|
1472
|
+
// .alias('res')
|
|
1473
|
+
// .description('Generate a complete resource (controller, service, repository, entity)')
|
|
1474
|
+
// .action(async (name) => {
|
|
1475
|
+
// await this.generateCommand.execute('resource', name);
|
|
1476
|
+
// });
|
|
1477
|
+
|
|
1478
|
+
// // Config commands
|
|
1479
|
+
// const config = this.program
|
|
1480
|
+
// .command('config')
|
|
1481
|
+
// .description('Configure Fragment application');
|
|
1482
|
+
|
|
1483
|
+
// config
|
|
1484
|
+
// .command('ai')
|
|
1485
|
+
// .description('Configure OpenAI integration')
|
|
1486
|
+
// .action(async () => {
|
|
1487
|
+
// await this.configCommand.execute('ai');
|
|
1488
|
+
// });
|
|
1489
|
+
|
|
1490
|
+
// config
|
|
1491
|
+
// .command('db')
|
|
1492
|
+
// .alias('database')
|
|
1493
|
+
// .description('Configure database connection')
|
|
1494
|
+
// .action(async () => {
|
|
1495
|
+
// await this.configCommand.execute('db');
|
|
1496
|
+
// });
|
|
1497
|
+
|
|
1498
|
+
// config
|
|
1499
|
+
// .command('show')
|
|
1500
|
+
// .description('Show current configuration')
|
|
1501
|
+
// .action(async () => {
|
|
1502
|
+
// await this.configCommand.execute('show');
|
|
1503
|
+
// });
|
|
1504
|
+
|
|
1505
|
+
// // Serve command
|
|
1506
|
+
// this.program
|
|
1507
|
+
// .command('serve')
|
|
1508
|
+
// .alias('dev')
|
|
1509
|
+
// .description('Start development server')
|
|
1510
|
+
// .option('-p, --port <port>', 'Port number', parseInt)
|
|
1511
|
+
// .option('--no-watch', 'Disable file watching')
|
|
1512
|
+
// .action(async (options) => {
|
|
1513
|
+
// await this.serveCommand.execute(options);
|
|
1514
|
+
// });
|
|
1515
|
+
|
|
1516
|
+
// // Build command
|
|
1517
|
+
// this.program
|
|
1518
|
+
// .command('build')
|
|
1519
|
+
// .description('Build application for production')
|
|
1520
|
+
// .action(async () => {
|
|
1521
|
+
// await this.buildCommand.execute();
|
|
1522
|
+
// });
|
|
1523
|
+
|
|
1524
|
+
// // Migrate commands
|
|
1525
|
+
// const migrate = this.program
|
|
1526
|
+
// .command('migrate')
|
|
1527
|
+
// .alias('migration')
|
|
1528
|
+
// .description('Database migration commands');
|
|
1529
|
+
|
|
1530
|
+
// migrate
|
|
1531
|
+
// .command('run')
|
|
1532
|
+
// .description('Run pending migrations')
|
|
1533
|
+
// .action(async () => {
|
|
1534
|
+
// await this.migrateCommand.execute('run');
|
|
1535
|
+
// });
|
|
1536
|
+
|
|
1537
|
+
// migrate
|
|
1538
|
+
// .command('generate')
|
|
1539
|
+
// .alias('create')
|
|
1540
|
+
// .description('Generate a new migration')
|
|
1541
|
+
// .action(async () => {
|
|
1542
|
+
// await this.migrateCommand.execute('generate');
|
|
1543
|
+
// });
|
|
1544
|
+
|
|
1545
|
+
// migrate
|
|
1546
|
+
// .command('revert')
|
|
1547
|
+
// .description('Revert last migration')
|
|
1548
|
+
// .action(async () => {
|
|
1549
|
+
// await this.migrateCommand.execute('revert');
|
|
1550
|
+
// });
|
|
1551
|
+
|
|
1552
|
+
// migrate
|
|
1553
|
+
// .command('show')
|
|
1554
|
+
// .alias('status')
|
|
1555
|
+
// .description('Show migration status')
|
|
1556
|
+
// .action(async () => {
|
|
1557
|
+
// await this.migrateCommand.execute('show');
|
|
1558
|
+
// });
|
|
1559
|
+
|
|
1560
|
+
// // Info command
|
|
1561
|
+
// this.program
|
|
1562
|
+
// .command('info')
|
|
1563
|
+
// .description('Display Fragment CLI information')
|
|
1564
|
+
// .action(() => {
|
|
1565
|
+
// CLILogger.box('Fragment Framework CLI', [
|
|
1566
|
+
// 'Version: 1.0.0',
|
|
1567
|
+
// 'TypeScript Backend Framework',
|
|
1568
|
+
// 'OOP + Decorators + DI',
|
|
1569
|
+
// '',
|
|
1570
|
+
// 'Documentation: https://fragment-framework.dev',
|
|
1571
|
+
// 'Repository: https://github.com/fragment/framework'
|
|
1572
|
+
// ]);
|
|
1573
|
+
// });
|
|
1574
|
+
// }
|
|
1575
|
+
|
|
1576
|
+
// run(args: string[]): void {
|
|
1577
|
+
// this.program.parse(['node', 'frg', ...args]);
|
|
1578
|
+
// }
|
|
1579
|
+
// }
|
|
1580
|
+
|
|
1581
|
+
// // ============================================================================
|
|
1582
|
+
// // FILE: src/app.ts (Updated with Fragment branding)
|
|
1583
|
+
// // ============================================================================
|
|
1584
|
+
// import express, { Express, Request, Response, NextFunction } from 'express';
|
|
1585
|
+
// import { DIContainer } from './core/container/di-container';
|
|
1586
|
+
// import { ConfigLoader } from './core/config/config-loader';
|
|
1587
|
+
// import { initializeDataSource, AppDataSource } from './database/data-source';
|
|
1588
|
+
// import { loggingMiddleware } from './middlewares/logging.middleware';
|
|
1589
|
+
// import { authMiddleware } from './auth/auth.middleware';
|
|
1590
|
+
// import {
|
|
1591
|
+
// CONTROLLER_METADATA,
|
|
1592
|
+
// CONTROLLER_PATH
|
|
1593
|
+
// } from './core/decorators/controller.decorator';
|
|
1594
|
+
// import {
|
|
1595
|
+
// ROUTE_METADATA
|
|
1596
|
+
// } from './core/decorators/route.decorator';
|
|
1597
|
+
// import {
|
|
1598
|
+
// AUTH_GUARD_METADATA
|
|
1599
|
+
// } from './core/decorators/auth-guard.decorator';
|
|
1600
|
+
// import {
|
|
1601
|
+
// MIDDLEWARE_METADATA
|
|
1602
|
+
// } from './core/decorators/middleware.decorator';
|
|
1603
|
+
|
|
1604
|
+
// // Import all controllers, services, repositories
|
|
1605
|
+
// import './controllers/health.controller';
|
|
1606
|
+
// import './auth/auth.controller';
|
|
1607
|
+
// import './services/health.service';
|
|
1608
|
+
// import './repositories/user.repository';
|
|
1609
|
+
// import './repositories/session.repository';
|
|
1610
|
+
// import './auth/auth.service';
|
|
1611
|
+
// import './core/openai/openai-client';
|
|
1612
|
+
|
|
1613
|
+
// export class FragmentApplication {
|
|
1614
|
+
// private app: Express;
|
|
1615
|
+
|
|
1616
|
+
// constructor() {
|
|
1617
|
+
// this.app = express();
|
|
1618
|
+
// }
|
|
1619
|
+
|
|
1620
|
+
// async initialize(): Promise<Express> {
|
|
1621
|
+
// // Load configuration
|
|
1622
|
+
// ConfigLoader.load(true);
|
|
1623
|
+
|
|
1624
|
+
// // Initialize database
|
|
1625
|
+
// const dataSource = initializeDataSource();
|
|
1626
|
+
// await dataSource.initialize();
|
|
1627
|
+
// console.log('â
Database connected');
|
|
1628
|
+
|
|
1629
|
+
// // Middleware
|
|
1630
|
+
// this.app.use(express.json());
|
|
1631
|
+
// this.app.use(express.urlencoded({ extended: true }));
|
|
1632
|
+
// this.app.use(loggingMiddleware);
|
|
1633
|
+
|
|
1634
|
+
// // Register all controllers
|
|
1635
|
+
// this.registerControllers();
|
|
1636
|
+
|
|
1637
|
+
// // Error handling
|
|
1638
|
+
// this.app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
1639
|
+
// console.error(err.stack);
|
|
1640
|
+
// res.status(500).json({ error: 'Internal server error' });
|
|
1641
|
+
// });
|
|
1642
|
+
|
|
1643
|
+
// return this.app;
|
|
1644
|
+
// }
|
|
1645
|
+
|
|
1646
|
+
// private registerControllers(): void {
|
|
1647
|
+
// const registeredClasses = DIContainer.getRegisteredClasses();
|
|
1648
|
+
|
|
1649
|
+
// registeredClasses.forEach((controllerClass) => {
|
|
1650
|
+
// const isController = Reflect.getMetadata(CONTROLLER_METADATA, controllerClass);
|
|
1651
|
+
// if (!isController) return;
|
|
1652
|
+
|
|
1653
|
+
// const basePath = Reflect.getMetadata(CONTROLLER_PATH, controllerClass) || '';
|
|
1654
|
+
// const routes = Reflect.getMetadata(ROUTE_METADATA, controllerClass) || [];
|
|
1655
|
+
// const classMiddlewares = Reflect.getMetadata(MIDDLEWARE_METADATA, controllerClass) || [];
|
|
1656
|
+
// const classAuthGuard = Reflect.getMetadata(AUTH_GUARD_METADATA, controllerClass);
|
|
1657
|
+
|
|
1658
|
+
// const controllerInstance = DIContainer.resolve(controllerClass);
|
|
1659
|
+
|
|
1660
|
+
// routes.forEach((route: any) => {
|
|
1661
|
+
// const fullPath = basePath + route.path;
|
|
1662
|
+
// const handler = controllerInstance[route.handlerName].bind(controllerInstance);
|
|
1663
|
+
|
|
1664
|
+
// const middlewares: any[] = [...classMiddlewares];
|
|
1665
|
+
|
|
1666
|
+
// const methodAuthGuard = Reflect.getMetadata(
|
|
1667
|
+
// AUTH_GUARD_METADATA,
|
|
1668
|
+
// controllerClass,
|
|
1669
|
+
// route.handlerName
|
|
1670
|
+
// );
|
|
1671
|
+
// if (classAuthGuard || methodAuthGuard) {
|
|
1672
|
+
// middlewares.push(authMiddleware);
|
|
1673
|
+
// }
|
|
1674
|
+
|
|
1675
|
+
// const methodMiddlewares = Reflect.getMetadata(
|
|
1676
|
+
// MIDDLEWARE_METADATA,
|
|
1677
|
+
// controllerClass,
|
|
1678
|
+
// route.handlerName
|
|
1679
|
+
// ) || [];
|
|
1680
|
+
// middlewares.push(...methodMiddlewares);
|
|
1681
|
+
|
|
1682
|
+
// const asyncHandler = async (req: Request, res: Response, next: NextFunction) => {
|
|
1683
|
+
// try {
|
|
1684
|
+
// const result = await handler(req, res, next);
|
|
1685
|
+
// if (result && !res.headersSent) {
|
|
1686
|
+
// res.json(result);
|
|
1687
|
+
// }
|
|
1688
|
+
// } catch (error) {
|
|
1689
|
+
// next(error);
|
|
1690
|
+
// }
|
|
1691
|
+
// };
|
|
1692
|
+
|
|
1693
|
+
// (this.app as any)[route.method](fullPath, ...middlewares, asyncHandler);
|
|
1694
|
+
|
|
1695
|
+
// console.log(`đ ${route.method.toUpperCase().padEnd(6)} ${fullPath}`);
|
|
1696
|
+
// });
|
|
1697
|
+
// });
|
|
1698
|
+
// }
|
|
1699
|
+
|
|
1700
|
+
// getExpressApp(): Express {
|
|
1701
|
+
// return this.app;
|
|
1702
|
+
// }
|
|
1703
|
+
// }
|
|
1704
|
+
|
|
1705
|
+
// // ============================================================================
|
|
1706
|
+
// // FILE: src/server.ts (Updated)
|
|
1707
|
+
// // ============================================================================a
|
|
1708
|
+
|
|
1709
|
+
// // ============================================================================
|
|
1710
|
+
// // COMPLETE CLI USAGE GUIDE
|
|
1711
|
+
// // ============================================================================
|
|
1712
|
+
// /*
|
|
1713
|
+
|
|
1714
|
+
// ## Fragment CLI (FRG) - World-Class Commands
|
|
1715
|
+
|
|
1716
|
+
// ### đ¯ Initialize New Project
|
|
1717
|
+
// ```bash
|
|
1718
|
+
// frg init my-app # Create new project
|
|
1719
|
+
// frg init # Interactive mode
|
|
1720
|
+
// ```
|
|
1721
|
+
|
|
1722
|
+
// ### đī¸ Generate Code
|
|
1723
|
+
// ```bash
|
|
1724
|
+
// frg generate controller User # Generate controller
|
|
1725
|
+
// frg g c User # Short alias
|
|
1726
|
+
|
|
1727
|
+
// frg generate service User # Generate service
|
|
1728
|
+
// frg g s User # Short alias
|
|
1729
|
+
|
|
1730
|
+
// frg generate repository User # Generate repository
|
|
1731
|
+
// frg g r User # Short alias
|
|
1732
|
+
|
|
1733
|
+
// frg generate entity User # Generate entity
|
|
1734
|
+
// frg g e User # Short alias
|
|
1735
|
+
|
|
1736
|
+
// frg generate resource Product # Generate complete resource
|
|
1737
|
+
// frg g res Product # (controller, service, repository, entity)
|
|
1738
|
+
// ```
|
|
1739
|
+
|
|
1740
|
+
// ### âī¸ Configure
|
|
1741
|
+
// ```bash
|
|
1742
|
+
// frg config ai # Configure OpenAI
|
|
1743
|
+
// frg config db # Configure database
|
|
1744
|
+
// frg config show # Show current config
|
|
1745
|
+
// ```
|
|
1746
|
+
|
|
1747
|
+
// ### đ Development
|
|
1748
|
+
// ```bash
|
|
1749
|
+
// frg serve # Start dev server
|
|
1750
|
+
// frg dev # Alias for serve
|
|
1751
|
+
// frg serve --port 8080 # Custom port
|
|
1752
|
+
// frg serve --no-watch # Disable watch mode
|
|
1753
|
+
// ```
|
|
1754
|
+
|
|
1755
|
+
// ### đĻ Production
|
|
1756
|
+
// ```bash
|
|
1757
|
+
// frg build # Build for production
|
|
1758
|
+
// node dist/server.js # Run production build
|
|
1759
|
+
// ```
|
|
1760
|
+
|
|
1761
|
+
// ### đī¸ Database Migrations
|
|
1762
|
+
// ```bash
|
|
1763
|
+
// frg migrate run # Run pending migrations
|
|
1764
|
+
// frg migrate generate # Create new migration
|
|
1765
|
+
// frg migrate revert # Revert last migration
|
|
1766
|
+
// frg migrate show # Show migration status
|
|
1767
|
+
// ```
|
|
1768
|
+
|
|
1769
|
+
// ### âšī¸ Information
|
|
1770
|
+
// ```bash
|
|
1771
|
+
// frg info # Show CLI info
|
|
1772
|
+
// frg --help # Show help
|
|
1773
|
+
// frg --version # Show version
|
|
1774
|
+
// ```
|
|
1775
|
+
|
|
1776
|
+
// ## Example Workflow
|
|
1777
|
+
|
|
1778
|
+
// ```bash
|
|
1779
|
+
// # 1. Create new project
|
|
1780
|
+
// frg init my-blog-api
|
|
1781
|
+
|
|
1782
|
+
// # 2. Navigate to project
|
|
1783
|
+
// cd my-blog-api
|
|
1784
|
+
|
|
1785
|
+
// # 3. Configure OpenAI
|
|
1786
|
+
// frg config ai
|
|
1787
|
+
|
|
1788
|
+
// # 4. Generate a complete resource
|
|
1789
|
+
// frg generate resource Post
|
|
1790
|
+
|
|
1791
|
+
// # 5. Start development server
|
|
1792
|
+
// frg serve
|
|
1793
|
+
|
|
1794
|
+
// # 6. Access your API
|
|
1795
|
+
// curl http://localhost:3000/api/posts
|
|
1796
|
+
// ```
|
|
1797
|
+
|
|
1798
|
+
// ## Features
|
|
1799
|
+
|
|
1800
|
+
// ⨠Interactive prompts with validation
|
|
1801
|
+
// ⨠Beautiful ASCII art and colored output
|
|
1802
|
+
// ⨠Progress spinners for long operations
|
|
1803
|
+
// ⨠Automatic dependency installation
|
|
1804
|
+
// ⨠Smart file generation with templates
|
|
1805
|
+
// ⨠Configuration management
|
|
1806
|
+
// ⨠Database migration tools
|
|
1807
|
+
// ⨠Hot-reload development server
|
|
1808
|
+
// ⨠Production build optimization
|
|
1809
|
+
|
|
1810
|
+
// */
|