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/src/app.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
|
|
2
|
+
import express, { Express, Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { DIContainer } from './core/container/di-container';
|
|
4
|
+
import { ConfigLoader } from './core/config/config-loader';
|
|
5
|
+
import { initializeDataSource, AppDataSource } from './database/data-source';
|
|
6
|
+
import { loggingMiddleware } from './middlewares/logging.middleware';
|
|
7
|
+
import { authMiddleware } from './auth/auth.middleware';
|
|
8
|
+
import {
|
|
9
|
+
CONTROLLER_METADATA,
|
|
10
|
+
CONTROLLER_PATH
|
|
11
|
+
} from './core/decorators/controller.decorator';
|
|
12
|
+
import {
|
|
13
|
+
ROUTE_METADATA
|
|
14
|
+
} from './core/decorators/route.decorator';
|
|
15
|
+
import {
|
|
16
|
+
AUTH_GUARD_METADATA
|
|
17
|
+
} from './core/decorators/auth-guard.decorator';
|
|
18
|
+
import {
|
|
19
|
+
MIDDLEWARE_METADATA
|
|
20
|
+
} from './core/decorators/middleware.decorator';
|
|
21
|
+
|
|
22
|
+
// Import all controllers, services, repositories
|
|
23
|
+
import './controllers/health.controller';
|
|
24
|
+
import './auth/auth.controller';
|
|
25
|
+
import './services/health.service';
|
|
26
|
+
import './repositories/user.repository';
|
|
27
|
+
import './repositories/session.repository';
|
|
28
|
+
import './auth/auth.service';
|
|
29
|
+
import './core/openai/openai-client';
|
|
30
|
+
|
|
31
|
+
export class FragmentApplication {
|
|
32
|
+
private app: Express;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
this.app = express();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async initialize(): Promise<Express> {
|
|
39
|
+
// Load configuration
|
|
40
|
+
ConfigLoader.load(true);
|
|
41
|
+
|
|
42
|
+
// Initialize database
|
|
43
|
+
const dataSource = initializeDataSource();
|
|
44
|
+
await dataSource.initialize();
|
|
45
|
+
console.log('✅ Database connected');
|
|
46
|
+
|
|
47
|
+
// Middleware
|
|
48
|
+
this.app.use(express.json());
|
|
49
|
+
this.app.use(express.urlencoded({ extended: true }));
|
|
50
|
+
this.app.use(loggingMiddleware);
|
|
51
|
+
|
|
52
|
+
// Register all controllers
|
|
53
|
+
this.registerControllers();
|
|
54
|
+
|
|
55
|
+
// Error handling
|
|
56
|
+
this.app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
57
|
+
console.error(err.stack);
|
|
58
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return this.app;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private registerControllers(): void {
|
|
65
|
+
const registeredClasses = DIContainer.getRegisteredClasses();
|
|
66
|
+
|
|
67
|
+
registeredClasses.forEach((controllerClass) => {
|
|
68
|
+
const isController = Reflect.getMetadata(CONTROLLER_METADATA, controllerClass);
|
|
69
|
+
if (!isController) return;
|
|
70
|
+
|
|
71
|
+
const basePath = Reflect.getMetadata(CONTROLLER_PATH, controllerClass) || '';
|
|
72
|
+
const routes = Reflect.getMetadata(ROUTE_METADATA, controllerClass) || [];
|
|
73
|
+
const classMiddlewares = Reflect.getMetadata(MIDDLEWARE_METADATA, controllerClass) || [];
|
|
74
|
+
const classAuthGuard = Reflect.getMetadata(AUTH_GUARD_METADATA, controllerClass);
|
|
75
|
+
|
|
76
|
+
const controllerInstance = DIContainer.resolve(controllerClass) as any;
|
|
77
|
+
|
|
78
|
+
routes.forEach((route: any) => {
|
|
79
|
+
const fullPath = basePath + route.path;
|
|
80
|
+
const handler = controllerInstance[route.handlerName].bind(controllerInstance);
|
|
81
|
+
|
|
82
|
+
const middlewares: any[] = [...classMiddlewares];
|
|
83
|
+
|
|
84
|
+
const methodAuthGuard = Reflect.getMetadata(
|
|
85
|
+
AUTH_GUARD_METADATA,
|
|
86
|
+
controllerClass,
|
|
87
|
+
route.handlerName
|
|
88
|
+
);
|
|
89
|
+
if (classAuthGuard || methodAuthGuard) {
|
|
90
|
+
middlewares.push(authMiddleware);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const methodMiddlewares = Reflect.getMetadata(
|
|
94
|
+
MIDDLEWARE_METADATA,
|
|
95
|
+
controllerClass,
|
|
96
|
+
route.handlerName
|
|
97
|
+
) || [];
|
|
98
|
+
middlewares.push(...methodMiddlewares);
|
|
99
|
+
|
|
100
|
+
const asyncHandler = async (req: Request, res: Response, next: NextFunction) => {
|
|
101
|
+
try {
|
|
102
|
+
const result = await handler(req, res, next);
|
|
103
|
+
if (result && !res.headersSent) {
|
|
104
|
+
res.json(result);
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
next(error);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
(this.app as any)[route.method](fullPath, ...middlewares, asyncHandler);
|
|
112
|
+
|
|
113
|
+
console.log(`📍 ${route.method.toUpperCase().padEnd(6)} ${fullPath}`);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getExpressApp(): Express {
|
|
119
|
+
return this.app;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
import { Controller } from '../core/decorators/controller.decorator';
|
|
3
|
+
import { Post, Get } from '../core/decorators/route.decorator';
|
|
4
|
+
import { Injectable } from '../core/decorators/injectable.decorator';
|
|
5
|
+
import { Autowire } from '../core/decorators/autowire.decorator';
|
|
6
|
+
import { AuthGuard } from '../core/decorators/auth-guard.decorator';
|
|
7
|
+
import { AuthService } from './auth.service';
|
|
8
|
+
import { Request, Response } from 'express';
|
|
9
|
+
|
|
10
|
+
@Controller('/api/auth')
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class AuthController {
|
|
13
|
+
constructor(
|
|
14
|
+
@Autowire() private authService: AuthService
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
@Post('/register')
|
|
18
|
+
async register(req: Request, res: Response) {
|
|
19
|
+
try {
|
|
20
|
+
const result = await this.authService.register(req.body);
|
|
21
|
+
return res.json(result);
|
|
22
|
+
} catch (error: any) {
|
|
23
|
+
return res.status(400).json({ error: error.message });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Post('/login')
|
|
28
|
+
async login(req: Request, res: Response) {
|
|
29
|
+
try {
|
|
30
|
+
const result = await this.authService.login(req.body);
|
|
31
|
+
return res.json(result);
|
|
32
|
+
} catch (error: any) {
|
|
33
|
+
return res.status(401).json({ error: error.message });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Post('/logout')
|
|
38
|
+
@AuthGuard()
|
|
39
|
+
async logout(req: Request, res: Response) {
|
|
40
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
41
|
+
if (token) {
|
|
42
|
+
await this.authService.logout(token);
|
|
43
|
+
}
|
|
44
|
+
return res.json({ message: 'Logged out successfully' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Get('/me')
|
|
48
|
+
@AuthGuard()
|
|
49
|
+
async getCurrentUser(req: Request, res: Response) {
|
|
50
|
+
return res.json({ user: (req as any).user });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { DIContainer } from '../core/container/di-container';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
|
|
6
|
+
export async function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
7
|
+
const authHeader = req.headers.authorization;
|
|
8
|
+
|
|
9
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
10
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const token = authHeader.replace('Bearer ', '');
|
|
14
|
+
const authService = DIContainer.resolve<AuthService>(AuthService);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const user = await authService.validateToken(token);
|
|
18
|
+
if (!user) {
|
|
19
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
(req as any).user = user;
|
|
23
|
+
next();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import * as bcrypt from 'bcrypt';
|
|
4
|
+
import { Service } from '../core/decorators/service.decorator';
|
|
5
|
+
import { Autowire } from '../core/decorators/autowire.decorator';
|
|
6
|
+
import { UserRepository } from '../repositories/user.repository';
|
|
7
|
+
import { SessionRepository } from '../repositories/session.repository';
|
|
8
|
+
import { LoginDTO, RegisterDTO } from './dto/login.dto';
|
|
9
|
+
import { ConfigLoader } from '../core/config/config-loader';
|
|
10
|
+
|
|
11
|
+
@Service()
|
|
12
|
+
export class AuthService {
|
|
13
|
+
constructor(
|
|
14
|
+
@Autowire() private userRepository: UserRepository,
|
|
15
|
+
@Autowire() private sessionRepository: SessionRepository
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
async register(dto: RegisterDTO): Promise<{ user: any; token: string }> {
|
|
19
|
+
const existingUser = await this.userRepository.findByEmail(dto.email);
|
|
20
|
+
if (existingUser) {
|
|
21
|
+
throw new Error('User already exists');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const passwordHash = await bcrypt.hash(dto.password, 10);
|
|
25
|
+
const user = await this.userRepository.create({
|
|
26
|
+
name: dto.name,
|
|
27
|
+
email: dto.email,
|
|
28
|
+
passwordHash
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const token = await this.createSession(user.id);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
user: { id: user.id, name: user.name, email: user.email },
|
|
35
|
+
token
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async login(dto: LoginDTO): Promise<{ user: any; token: string }> {
|
|
40
|
+
const user = await this.userRepository.findByEmail(dto.email);
|
|
41
|
+
if (!user) {
|
|
42
|
+
throw new Error('Invalid credentials');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isValidPassword = await bcrypt.compare(dto.password, user.passwordHash);
|
|
46
|
+
if (!isValidPassword) {
|
|
47
|
+
throw new Error('Invalid credentials');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const token = await this.createSession(user.id);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
user: { id: user.id, name: user.name, email: user.email },
|
|
54
|
+
token
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async logout(token: string): Promise<void> {
|
|
59
|
+
await this.sessionRepository.deleteByToken(token);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async validateToken(token: string): Promise<any> {
|
|
63
|
+
const session = await this.sessionRepository.findByToken(token);
|
|
64
|
+
if (!session) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
id: session.user.id,
|
|
70
|
+
name: session.user.name,
|
|
71
|
+
email: session.user.email
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async createSession(userId: number): Promise<string> {
|
|
76
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
77
|
+
const config = ConfigLoader.get();
|
|
78
|
+
|
|
79
|
+
// Parse token expiry (e.g., "7d" = 7 days)
|
|
80
|
+
const expiryMs = this.parseExpiry(config.auth.tokenExpiry);
|
|
81
|
+
const expiresAt = new Date(Date.now() + expiryMs);
|
|
82
|
+
|
|
83
|
+
await this.sessionRepository.create({
|
|
84
|
+
token,
|
|
85
|
+
userId,
|
|
86
|
+
expiresAt
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return token;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private parseExpiry(expiry: string): number {
|
|
93
|
+
const match = expiry.match(/^(\d+)([dhms])$/);
|
|
94
|
+
if (!match) {
|
|
95
|
+
return 7 * 24 * 60 * 60 * 1000; // default 7 days
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const value = parseInt(match[1]);
|
|
99
|
+
const unit = match[2];
|
|
100
|
+
|
|
101
|
+
const multipliers: { [key: string]: number } = {
|
|
102
|
+
's': 1000,
|
|
103
|
+
'm': 60 * 1000,
|
|
104
|
+
'h': 60 * 60 * 1000,
|
|
105
|
+
'd': 24 * 60 * 60 * 1000
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return value * multipliers[unit];
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/cli/cli.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { InitCommand } from './commands/init.command';
|
|
5
|
+
import { GenerateCommand } from './commands/generate.command';
|
|
6
|
+
import { ConfigCommand } from './commands/config.command';
|
|
7
|
+
import { ServeCommand } from './commands/serve.command';
|
|
8
|
+
import { BuildCommand } from './commands/build.command';
|
|
9
|
+
import { MigrateCommand } from './commands/migrate.command';
|
|
10
|
+
import { CLILogger } from './utils/logger';
|
|
11
|
+
|
|
12
|
+
export class CLI {
|
|
13
|
+
private program: Command;
|
|
14
|
+
private initCommand: InitCommand;
|
|
15
|
+
private generateCommand: GenerateCommand;
|
|
16
|
+
private configCommand: ConfigCommand;
|
|
17
|
+
private serveCommand: ServeCommand;
|
|
18
|
+
private buildCommand: BuildCommand;
|
|
19
|
+
private migrateCommand: MigrateCommand;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
this.program = new Command();
|
|
23
|
+
this.initCommand = new InitCommand();
|
|
24
|
+
this.generateCommand = new GenerateCommand();
|
|
25
|
+
this.configCommand = new ConfigCommand();
|
|
26
|
+
this.serveCommand = new ServeCommand();
|
|
27
|
+
this.buildCommand = new BuildCommand();
|
|
28
|
+
this.migrateCommand = new MigrateCommand();
|
|
29
|
+
|
|
30
|
+
this.setupCommands();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private setupCommands(): void {
|
|
34
|
+
this.program
|
|
35
|
+
.name('frg')
|
|
36
|
+
.description('Fragment Framework CLI - Build powerful TypeScript backends')
|
|
37
|
+
.version('1.0.0');
|
|
38
|
+
|
|
39
|
+
// ASCII Art Banner
|
|
40
|
+
this.program
|
|
41
|
+
.addHelpText('beforeAll', () => {
|
|
42
|
+
return chalk.cyan(`
|
|
43
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
44
|
+
║ ║
|
|
45
|
+
║ ███████╗██████╗ █████╗ ██████╗ ███╗ ███╗███████╗ ║
|
|
46
|
+
║ ██╔════╝██╔══██╗██╔══██╗██╔════╝ ████╗ ████║██╔════╝ ║
|
|
47
|
+
║ █████╗ ██████╔╝███████║██║ ███╗██╔████╔██║█████╗ ║
|
|
48
|
+
║ ██╔══╝ ██╔══██╗██╔══██║██║ ██║██║╚██╔╝██║██╔══╝ ║
|
|
49
|
+
║ ██║ ██║ ██║██║ ██║╚██████╔╝██║ ╚═╝ ██║███████╗ ║
|
|
50
|
+
║ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ║
|
|
51
|
+
║ ║
|
|
52
|
+
║ TypeScript Backend Framework (v1.0.0) ║
|
|
53
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
54
|
+
`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Init command
|
|
58
|
+
this.program
|
|
59
|
+
.command('init [project-name]')
|
|
60
|
+
.description('Initialize a new Fragment project')
|
|
61
|
+
.action(async (projectName) => {
|
|
62
|
+
await this.initCommand.execute(projectName);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Generate commands
|
|
66
|
+
const generate = this.program
|
|
67
|
+
.command('generate')
|
|
68
|
+
.alias('g')
|
|
69
|
+
.description('Generate application components');
|
|
70
|
+
|
|
71
|
+
generate
|
|
72
|
+
.command('controller [name]')
|
|
73
|
+
.alias('c')
|
|
74
|
+
.description('Generate a new controller')
|
|
75
|
+
.action(async (name) => {
|
|
76
|
+
await this.generateCommand.execute('controller', name);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
generate
|
|
80
|
+
.command('service [name]')
|
|
81
|
+
.alias('s')
|
|
82
|
+
.description('Generate a new service')
|
|
83
|
+
.action(async (name) => {
|
|
84
|
+
await this.generateCommand.execute('service', name);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
generate
|
|
88
|
+
.command('repository [name]')
|
|
89
|
+
.alias('r')
|
|
90
|
+
.description('Generate a new repository')
|
|
91
|
+
.action(async (name) => {
|
|
92
|
+
await this.generateCommand.execute('repository', name);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
generate
|
|
96
|
+
.command('entity [name]')
|
|
97
|
+
.alias('e')
|
|
98
|
+
.description('Generate a new entity')
|
|
99
|
+
.action(async (name) => {
|
|
100
|
+
await this.generateCommand.execute('entity', name);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
generate
|
|
104
|
+
.command('resource [name]')
|
|
105
|
+
.alias('res')
|
|
106
|
+
.description('Generate a complete resource (controller, service, repository, entity)')
|
|
107
|
+
.action(async (name) => {
|
|
108
|
+
await this.generateCommand.execute('resource', name);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Config commands
|
|
112
|
+
const config = this.program
|
|
113
|
+
.command('config')
|
|
114
|
+
.description('Configure Fragment application');
|
|
115
|
+
|
|
116
|
+
config
|
|
117
|
+
.command('ai')
|
|
118
|
+
.description('Configure OpenAI integration')
|
|
119
|
+
.action(async () => {
|
|
120
|
+
await this.configCommand.execute('ai');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
config
|
|
124
|
+
.command('db')
|
|
125
|
+
.alias('database')
|
|
126
|
+
.description('Configure database connection')
|
|
127
|
+
.action(async () => {
|
|
128
|
+
await this.configCommand.execute('db');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
config
|
|
132
|
+
.command('show')
|
|
133
|
+
.description('Show current configuration')
|
|
134
|
+
.action(async () => {
|
|
135
|
+
await this.configCommand.execute('show');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Serve command
|
|
139
|
+
this.program
|
|
140
|
+
.command('serve')
|
|
141
|
+
.alias('dev')
|
|
142
|
+
.description('Start development server')
|
|
143
|
+
.option('-p, --port <port>', 'Port number', parseInt)
|
|
144
|
+
.option('--no-watch', 'Disable file watching')
|
|
145
|
+
.action(async (options) => {
|
|
146
|
+
await this.serveCommand.execute(options);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Build command
|
|
150
|
+
this.program
|
|
151
|
+
.command('build')
|
|
152
|
+
.description('Build application for production')
|
|
153
|
+
.action(async () => {
|
|
154
|
+
await this.buildCommand.execute();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Migrate commands
|
|
158
|
+
const migrate = this.program
|
|
159
|
+
.command('migrate')
|
|
160
|
+
.alias('migration')
|
|
161
|
+
.description('Database migration commands');
|
|
162
|
+
|
|
163
|
+
migrate
|
|
164
|
+
.command('run')
|
|
165
|
+
.description('Run pending migrations')
|
|
166
|
+
.action(async () => {
|
|
167
|
+
await this.migrateCommand.execute('run');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
migrate
|
|
171
|
+
.command('generate')
|
|
172
|
+
.alias('create')
|
|
173
|
+
.description('Generate a new migration')
|
|
174
|
+
.action(async () => {
|
|
175
|
+
await this.migrateCommand.execute('generate');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
migrate
|
|
179
|
+
.command('revert')
|
|
180
|
+
.description('Revert last migration')
|
|
181
|
+
.action(async () => {
|
|
182
|
+
await this.migrateCommand.execute('revert');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
migrate
|
|
186
|
+
.command('show')
|
|
187
|
+
.alias('status')
|
|
188
|
+
.description('Show migration status')
|
|
189
|
+
.action(async () => {
|
|
190
|
+
await this.migrateCommand.execute('show');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Info command
|
|
194
|
+
this.program
|
|
195
|
+
.command('info')
|
|
196
|
+
.description('Display Fragment CLI information')
|
|
197
|
+
.action(() => {
|
|
198
|
+
CLILogger.box('Fragment Framework CLI', [
|
|
199
|
+
'Version: 1.0.0',
|
|
200
|
+
'TypeScript Backend Framework',
|
|
201
|
+
'OOP + Decorators + DI',
|
|
202
|
+
'',
|
|
203
|
+
'Documentation: https://fragment-framework.dev',
|
|
204
|
+
'Repository: https://github.com/fragment/framework'
|
|
205
|
+
]);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
run(args: string[]): void {
|
|
210
|
+
this.program.parse(['node', 'frg', ...args]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { CLILogger } from '../utils/logger';
|
|
4
|
+
|
|
5
|
+
export class BuildCommand {
|
|
6
|
+
async execute(): Promise<void> {
|
|
7
|
+
CLILogger.title('🔨 Building Fragment Application');
|
|
8
|
+
|
|
9
|
+
const spinner = CLILogger.spinner('Compiling TypeScript...');
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
execSync('tsc', { stdio: 'pipe' });
|
|
13
|
+
spinner.succeed('Build completed successfully');
|
|
14
|
+
|
|
15
|
+
CLILogger.success('Output directory: ./dist');
|
|
16
|
+
CLILogger.info('Run "node dist/server.js" to start the production server');
|
|
17
|
+
|
|
18
|
+
} catch (error: any) {
|
|
19
|
+
spinner.fail('Build failed');
|
|
20
|
+
CLILogger.error(error.message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|