create-frootjs 1.0.15 → 1.0.16
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/dist/addons/testing.js +2 -2
- package/dist/addons/testing.js.map +1 -1
- package/dist/generator.js +1 -1
- package/dist/generator.js.map +1 -1
- package/dist/index.js +1 -7
- package/dist/index.js.map +1 -1
- package/dist/templates/minimal.js +54 -54
- package/dist/templates/modular.js +261 -261
- package/dist/templates/mvc.js +196 -196
- package/dist/templates/openapi.js +168 -168
- package/dist/templates/streaming.js +176 -176
- package/dist/templates/websocket.js +163 -163
- package/dist/ui.d.ts +4 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +106 -34
- package/dist/ui.js.map +1 -1
- package/package.json +2 -2
- package/dist/templates/fullstack.d.ts +0 -7
- package/dist/templates/fullstack.d.ts.map +0 -1
- package/dist/templates/fullstack.js +0 -225
- package/dist/templates/fullstack.js.map +0 -1
|
@@ -20,7 +20,7 @@ export const modularTemplate = {
|
|
|
20
20
|
test: 'node --test tests/*.test.ts',
|
|
21
21
|
},
|
|
22
22
|
dependencies: {
|
|
23
|
-
frootjs: '^1.0.
|
|
23
|
+
frootjs: '^1.0.6',
|
|
24
24
|
},
|
|
25
25
|
devDependencies: {
|
|
26
26
|
'@types/node': '^22.10.0',
|
|
@@ -30,278 +30,278 @@ export const modularTemplate = {
|
|
|
30
30
|
}, null, 2) + '\n',
|
|
31
31
|
'tsconfig.json': COMMON_TSCONFIG,
|
|
32
32
|
'.gitignore': COMMON_GITIGNORE,
|
|
33
|
-
'README.md': `# ${projectName}
|
|
34
|
-
|
|
35
|
-
Froot application built with the **Modular Architecture (NestJS-style)** pattern.
|
|
36
|
-
|
|
37
|
-
## Directory Structure
|
|
38
|
-
|
|
39
|
-
\`\`\`
|
|
40
|
-
src/
|
|
41
|
-
├── common/ # Cross-cutting concerns
|
|
42
|
-
│ ├── guards/ # Auth & role guards
|
|
43
|
-
│ │ └── auth.guard.ts
|
|
44
|
-
│ └── filters/ # Global error filters
|
|
45
|
-
│ └── error.filter.ts
|
|
46
|
-
├── modules/ # Feature modules
|
|
47
|
-
│ ├── auth/ # Authentication module (JWT & login)
|
|
48
|
-
│ │ ├── auth.routes.ts
|
|
49
|
-
│ │ └── auth.service.ts
|
|
50
|
-
│ ├── users/ # Users management module
|
|
51
|
-
│ │ ├── users.routes.ts
|
|
52
|
-
│ │ ├── users.service.ts
|
|
53
|
-
│ │ └── users.types.ts
|
|
54
|
-
│ └── products/ # Products catalog module
|
|
55
|
-
│ ├── products.routes.ts
|
|
56
|
-
│ ├── products.service.ts
|
|
57
|
-
│ └── products.types.ts
|
|
58
|
-
├── app.ts # Root application module orchestrator
|
|
59
|
-
└── server.ts # Server bootstrap
|
|
60
|
-
\`\`\`
|
|
61
|
-
|
|
62
|
-
## Getting Started
|
|
63
|
-
|
|
64
|
-
\`\`\`bash
|
|
65
|
-
# 1. Install dependencies
|
|
66
|
-
pnpm install
|
|
67
|
-
|
|
68
|
-
# 2. Run in development mode
|
|
69
|
-
pnpm run dev
|
|
70
|
-
|
|
71
|
-
# 3. Build for production
|
|
72
|
-
pnpm run build
|
|
73
|
-
pnpm start
|
|
74
|
-
\`\`\`
|
|
33
|
+
'README.md': `# ${projectName}
|
|
34
|
+
|
|
35
|
+
Froot application built with the **Modular Architecture (NestJS-style)** pattern.
|
|
36
|
+
|
|
37
|
+
## Directory Structure
|
|
38
|
+
|
|
39
|
+
\`\`\`
|
|
40
|
+
src/
|
|
41
|
+
├── common/ # Cross-cutting concerns
|
|
42
|
+
│ ├── guards/ # Auth & role guards
|
|
43
|
+
│ │ └── auth.guard.ts
|
|
44
|
+
│ └── filters/ # Global error filters
|
|
45
|
+
│ └── error.filter.ts
|
|
46
|
+
├── modules/ # Feature modules
|
|
47
|
+
│ ├── auth/ # Authentication module (JWT & login)
|
|
48
|
+
│ │ ├── auth.routes.ts
|
|
49
|
+
│ │ └── auth.service.ts
|
|
50
|
+
│ ├── users/ # Users management module
|
|
51
|
+
│ │ ├── users.routes.ts
|
|
52
|
+
│ │ ├── users.service.ts
|
|
53
|
+
│ │ └── users.types.ts
|
|
54
|
+
│ └── products/ # Products catalog module
|
|
55
|
+
│ ├── products.routes.ts
|
|
56
|
+
│ ├── products.service.ts
|
|
57
|
+
│ └── products.types.ts
|
|
58
|
+
├── app.ts # Root application module orchestrator
|
|
59
|
+
└── server.ts # Server bootstrap
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
## Getting Started
|
|
63
|
+
|
|
64
|
+
\`\`\`bash
|
|
65
|
+
# 1. Install dependencies
|
|
66
|
+
pnpm install
|
|
67
|
+
|
|
68
|
+
# 2. Run in development mode
|
|
69
|
+
pnpm run dev
|
|
70
|
+
|
|
71
|
+
# 3. Build for production
|
|
72
|
+
pnpm run build
|
|
73
|
+
pnpm start
|
|
74
|
+
\`\`\`
|
|
75
75
|
`,
|
|
76
|
-
'src/common/guards/auth.guard.ts': `import type { Context, MiddlewareHandler } from 'frootjs';
|
|
77
|
-
import { verifyJwt } from 'frootjs/jwt';
|
|
78
|
-
|
|
79
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
80
|
-
|
|
81
|
-
export interface AuthContextVariables {
|
|
82
|
-
user: {
|
|
83
|
-
id: string;
|
|
84
|
-
email: string;
|
|
85
|
-
role: string;
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const authGuard: MiddlewareHandler = async (c, next) => {
|
|
90
|
-
const authHeader = c.req.header('authorization');
|
|
91
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
92
|
-
return c.json({ error: 'Unauthorized: missing or invalid authorization header' }, 401);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const token = authHeader.slice(7).trim();
|
|
96
|
-
try {
|
|
97
|
-
const payload = await verifyJwt(token, JWT_SECRET);
|
|
98
|
-
c.set('user', payload);
|
|
99
|
-
await next();
|
|
100
|
-
} catch {
|
|
101
|
-
return c.json({ error: 'Unauthorized: token is invalid or expired' }, 401);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
76
|
+
'src/common/guards/auth.guard.ts': `import type { Context, MiddlewareHandler } from 'frootjs';
|
|
77
|
+
import { verifyJwt } from 'frootjs/jwt';
|
|
78
|
+
|
|
79
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
80
|
+
|
|
81
|
+
export interface AuthContextVariables {
|
|
82
|
+
user: {
|
|
83
|
+
id: string;
|
|
84
|
+
email: string;
|
|
85
|
+
role: string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const authGuard: MiddlewareHandler = async (c, next) => {
|
|
90
|
+
const authHeader = c.req.header('authorization');
|
|
91
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
92
|
+
return c.json({ error: 'Unauthorized: missing or invalid authorization header' }, 401);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const token = authHeader.slice(7).trim();
|
|
96
|
+
try {
|
|
97
|
+
const payload = await verifyJwt(token, JWT_SECRET);
|
|
98
|
+
c.set('user', payload);
|
|
99
|
+
await next();
|
|
100
|
+
} catch {
|
|
101
|
+
return c.json({ error: 'Unauthorized: token is invalid or expired' }, 401);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
104
|
`,
|
|
105
|
-
'src/common/filters/error.filter.ts': `import type { Context } from 'frootjs';
|
|
106
|
-
|
|
107
|
-
export function errorHandler(err: unknown, c: Context): Response {
|
|
108
|
-
console.error('[ErrorFilter] Caught unhandled error:', err);
|
|
109
|
-
|
|
110
|
-
if (err instanceof Error) {
|
|
111
|
-
return c.json(
|
|
112
|
-
{
|
|
113
|
-
error: err.name,
|
|
114
|
-
message: err.message,
|
|
115
|
-
statusCode: 500,
|
|
116
|
-
},
|
|
117
|
-
500
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return c.json(
|
|
122
|
-
{
|
|
123
|
-
error: 'Internal Server Error',
|
|
124
|
-
message: 'An unexpected error occurred',
|
|
125
|
-
statusCode: 500,
|
|
126
|
-
},
|
|
127
|
-
500
|
|
128
|
-
);
|
|
129
|
-
}
|
|
105
|
+
'src/common/filters/error.filter.ts': `import type { Context } from 'frootjs';
|
|
106
|
+
|
|
107
|
+
export function errorHandler(err: unknown, c: Context): Response {
|
|
108
|
+
console.error('[ErrorFilter] Caught unhandled error:', err);
|
|
109
|
+
|
|
110
|
+
if (err instanceof Error) {
|
|
111
|
+
return c.json(
|
|
112
|
+
{
|
|
113
|
+
error: err.name,
|
|
114
|
+
message: err.message,
|
|
115
|
+
statusCode: 500,
|
|
116
|
+
},
|
|
117
|
+
500
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return c.json(
|
|
122
|
+
{
|
|
123
|
+
error: 'Internal Server Error',
|
|
124
|
+
message: 'An unexpected error occurred',
|
|
125
|
+
statusCode: 500,
|
|
126
|
+
},
|
|
127
|
+
500
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
130
|
`,
|
|
131
|
-
'src/modules/auth/auth.service.ts': `import { signJwt } from 'frootjs/jwt';
|
|
132
|
-
|
|
133
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
134
|
-
|
|
135
|
-
export class AuthService {
|
|
136
|
-
static async createToken(user: { id: string; email: string; role: string }): Promise<string> {
|
|
137
|
-
return signJwt(
|
|
138
|
-
{
|
|
139
|
-
sub: user.id,
|
|
140
|
-
email: user.email,
|
|
141
|
-
role: user.role,
|
|
142
|
-
},
|
|
143
|
-
JWT_SECRET,
|
|
144
|
-
{ expiresIn: 60 * 60 * 24 }
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
131
|
+
'src/modules/auth/auth.service.ts': `import { signJwt } from 'frootjs/jwt';
|
|
132
|
+
|
|
133
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
134
|
+
|
|
135
|
+
export class AuthService {
|
|
136
|
+
static async createToken(user: { id: string; email: string; role: string }): Promise<string> {
|
|
137
|
+
return signJwt(
|
|
138
|
+
{
|
|
139
|
+
sub: user.id,
|
|
140
|
+
email: user.email,
|
|
141
|
+
role: user.role,
|
|
142
|
+
},
|
|
143
|
+
JWT_SECRET,
|
|
144
|
+
{ expiresIn: 60 * 60 * 24 }
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
148
|
`,
|
|
149
|
-
'src/modules/auth/auth.routes.ts': `import { Froot } from 'frootjs';
|
|
150
|
-
import { AuthService } from './auth.service.js';
|
|
151
|
-
|
|
152
|
-
export const authModule = new Froot();
|
|
153
|
-
|
|
154
|
-
authModule.post('/auth/login', async (c) => {
|
|
155
|
-
const body = await c.req.json<{ email?: string; password?: string }>();
|
|
156
|
-
if (!body.email || !body.password) {
|
|
157
|
-
return c.json({ error: 'Email and password required' }, 400);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Demo validation: any credentials pass for showcase
|
|
161
|
-
const user = {
|
|
162
|
-
id: 'usr_demo',
|
|
163
|
-
email: body.email,
|
|
164
|
-
role: 'admin',
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const token = await AuthService.createToken(user);
|
|
168
|
-
return c.json({
|
|
169
|
-
token,
|
|
170
|
-
user,
|
|
171
|
-
message: 'Login successful',
|
|
172
|
-
});
|
|
173
|
-
});
|
|
149
|
+
'src/modules/auth/auth.routes.ts': `import { Froot } from 'frootjs';
|
|
150
|
+
import { AuthService } from './auth.service.js';
|
|
151
|
+
|
|
152
|
+
export const authModule = new Froot();
|
|
153
|
+
|
|
154
|
+
authModule.post('/auth/login', async (c) => {
|
|
155
|
+
const body = await c.req.json<{ email?: string; password?: string }>();
|
|
156
|
+
if (!body.email || !body.password) {
|
|
157
|
+
return c.json({ error: 'Email and password required' }, 400);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Demo validation: any credentials pass for showcase
|
|
161
|
+
const user = {
|
|
162
|
+
id: 'usr_demo',
|
|
163
|
+
email: body.email,
|
|
164
|
+
role: 'admin',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const token = await AuthService.createToken(user);
|
|
168
|
+
return c.json({
|
|
169
|
+
token,
|
|
170
|
+
user,
|
|
171
|
+
message: 'Login successful',
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
174
|
`,
|
|
175
|
-
'src/modules/users/users.types.ts': `export interface UserProfile {
|
|
176
|
-
id: string;
|
|
177
|
-
name: string;
|
|
178
|
-
email: string;
|
|
179
|
-
role: string;
|
|
180
|
-
}
|
|
175
|
+
'src/modules/users/users.types.ts': `export interface UserProfile {
|
|
176
|
+
id: string;
|
|
177
|
+
name: string;
|
|
178
|
+
email: string;
|
|
179
|
+
role: string;
|
|
180
|
+
}
|
|
181
181
|
`,
|
|
182
|
-
'src/modules/users/users.service.ts': `import type { UserProfile } from './users.types.js';
|
|
183
|
-
|
|
184
|
-
const mockUsers: UserProfile[] = [
|
|
185
|
-
{ id: 'usr_1', name: 'Ada Lovelace', email: 'ada@froot.dev', role: 'admin' },
|
|
186
|
-
{ id: 'usr_2', name: 'Alan Turing', email: 'alan@froot.dev', role: 'engineer' },
|
|
187
|
-
];
|
|
188
|
-
|
|
189
|
-
export class UsersService {
|
|
190
|
-
static getAll(): UserProfile[] {
|
|
191
|
-
return mockUsers;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
static getById(id?: string): UserProfile | undefined {
|
|
195
|
-
if (!id) return undefined;
|
|
196
|
-
return mockUsers.find((u) => u.id === id);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
182
|
+
'src/modules/users/users.service.ts': `import type { UserProfile } from './users.types.js';
|
|
183
|
+
|
|
184
|
+
const mockUsers: UserProfile[] = [
|
|
185
|
+
{ id: 'usr_1', name: 'Ada Lovelace', email: 'ada@froot.dev', role: 'admin' },
|
|
186
|
+
{ id: 'usr_2', name: 'Alan Turing', email: 'alan@froot.dev', role: 'engineer' },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
export class UsersService {
|
|
190
|
+
static getAll(): UserProfile[] {
|
|
191
|
+
return mockUsers;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static getById(id?: string): UserProfile | undefined {
|
|
195
|
+
if (!id) return undefined;
|
|
196
|
+
return mockUsers.find((u) => u.id === id);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
199
|
`,
|
|
200
|
-
'src/modules/users/users.routes.ts': `import { Froot } from 'frootjs';
|
|
201
|
-
import { UsersService } from './users.service.js';
|
|
202
|
-
import { authGuard } from '../../common/guards/auth.guard.js';
|
|
203
|
-
|
|
204
|
-
export const usersModule = new Froot();
|
|
205
|
-
|
|
206
|
-
// Public route: list users
|
|
207
|
-
usersModule.get('/users', (c) => {
|
|
208
|
-
return c.json(UsersService.getAll());
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Protected route: user details (guarded by JWT)
|
|
212
|
-
usersModule.get('/users/:id', authGuard, (c) => {
|
|
213
|
-
const id = c.req.param('id');
|
|
214
|
-
const user = UsersService.getById(id);
|
|
215
|
-
if (!user) {
|
|
216
|
-
return c.json({ error: 'User not found' }, 404);
|
|
217
|
-
}
|
|
218
|
-
return c.json(user);
|
|
219
|
-
});
|
|
200
|
+
'src/modules/users/users.routes.ts': `import { Froot } from 'frootjs';
|
|
201
|
+
import { UsersService } from './users.service.js';
|
|
202
|
+
import { authGuard } from '../../common/guards/auth.guard.js';
|
|
203
|
+
|
|
204
|
+
export const usersModule = new Froot();
|
|
205
|
+
|
|
206
|
+
// Public route: list users
|
|
207
|
+
usersModule.get('/users', (c) => {
|
|
208
|
+
return c.json(UsersService.getAll());
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Protected route: user details (guarded by JWT)
|
|
212
|
+
usersModule.get('/users/:id', authGuard, (c) => {
|
|
213
|
+
const id = c.req.param('id');
|
|
214
|
+
const user = UsersService.getById(id);
|
|
215
|
+
if (!user) {
|
|
216
|
+
return c.json({ error: 'User not found' }, 404);
|
|
217
|
+
}
|
|
218
|
+
return c.json(user);
|
|
219
|
+
});
|
|
220
220
|
`,
|
|
221
|
-
'src/modules/products/products.types.ts': `export interface Product {
|
|
222
|
-
id: string;
|
|
223
|
-
title: string;
|
|
224
|
-
price: number;
|
|
225
|
-
inventory: number;
|
|
226
|
-
}
|
|
221
|
+
'src/modules/products/products.types.ts': `export interface Product {
|
|
222
|
+
id: string;
|
|
223
|
+
title: string;
|
|
224
|
+
price: number;
|
|
225
|
+
inventory: number;
|
|
226
|
+
}
|
|
227
227
|
`,
|
|
228
|
-
'src/modules/products/products.service.ts': `import type { Product } from './products.types.js';
|
|
229
|
-
|
|
230
|
-
const productsTable: Product[] = [
|
|
231
|
-
{ id: 'prod_1', title: 'High-Performance Microservice', price: 99.0, inventory: 50 },
|
|
232
|
-
{ id: 'prod_2', title: 'Real-Time Edge Gateway', price: 149.0, inventory: 25 },
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
export class ProductsService {
|
|
236
|
-
static list(): Product[] {
|
|
237
|
-
return productsTable;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
static find(id?: string): Product | undefined {
|
|
241
|
-
if (!id) return undefined;
|
|
242
|
-
return productsTable.find((p) => p.id === id);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
228
|
+
'src/modules/products/products.service.ts': `import type { Product } from './products.types.js';
|
|
229
|
+
|
|
230
|
+
const productsTable: Product[] = [
|
|
231
|
+
{ id: 'prod_1', title: 'High-Performance Microservice', price: 99.0, inventory: 50 },
|
|
232
|
+
{ id: 'prod_2', title: 'Real-Time Edge Gateway', price: 149.0, inventory: 25 },
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
export class ProductsService {
|
|
236
|
+
static list(): Product[] {
|
|
237
|
+
return productsTable;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static find(id?: string): Product | undefined {
|
|
241
|
+
if (!id) return undefined;
|
|
242
|
+
return productsTable.find((p) => p.id === id);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
245
|
`,
|
|
246
|
-
'src/modules/products/products.routes.ts': `import { Froot } from 'frootjs';
|
|
247
|
-
import { ProductsService } from './products.service.js';
|
|
248
|
-
|
|
249
|
-
export const productsModule = new Froot();
|
|
250
|
-
|
|
251
|
-
productsModule.get('/products', (c) => {
|
|
252
|
-
return c.json(ProductsService.list());
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
productsModule.get('/products/:id', (c) => {
|
|
256
|
-
const id = c.req.param('id');
|
|
257
|
-
const product = ProductsService.find(id);
|
|
258
|
-
if (!product) {
|
|
259
|
-
return c.json({ error: 'Product not found' }, 404);
|
|
260
|
-
}
|
|
261
|
-
return c.json(product);
|
|
262
|
-
});
|
|
246
|
+
'src/modules/products/products.routes.ts': `import { Froot } from 'frootjs';
|
|
247
|
+
import { ProductsService } from './products.service.js';
|
|
248
|
+
|
|
249
|
+
export const productsModule = new Froot();
|
|
250
|
+
|
|
251
|
+
productsModule.get('/products', (c) => {
|
|
252
|
+
return c.json(ProductsService.list());
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
productsModule.get('/products/:id', (c) => {
|
|
256
|
+
const id = c.req.param('id');
|
|
257
|
+
const product = ProductsService.find(id);
|
|
258
|
+
if (!product) {
|
|
259
|
+
return c.json({ error: 'Product not found' }, 404);
|
|
260
|
+
}
|
|
261
|
+
return c.json(product);
|
|
262
|
+
});
|
|
263
263
|
`,
|
|
264
|
-
'src/app.ts': `import { Froot } from 'frootjs';
|
|
265
|
-
import { cors } from 'frootjs/cors';
|
|
266
|
-
import { errorHandler } from './common/filters/error.filter.js';
|
|
267
|
-
import { authModule } from './modules/auth/auth.routes.js';
|
|
268
|
-
import { usersModule } from './modules/users/users.routes.js';
|
|
269
|
-
import { productsModule } from './modules/products/products.routes.js';
|
|
270
|
-
|
|
271
|
-
export const app = new Froot();
|
|
272
|
-
|
|
273
|
-
// Global middleware & filters
|
|
274
|
-
app.use(cors());
|
|
275
|
-
app.onError(errorHandler);
|
|
276
|
-
|
|
277
|
-
// Mount feature modules under /api prefix
|
|
278
|
-
app.route('/api', authModule);
|
|
279
|
-
app.route('/api', usersModule);
|
|
280
|
-
app.route('/api', productsModule);
|
|
281
|
-
|
|
282
|
-
// Root & Health check
|
|
283
|
-
app.get('/', (c) => {
|
|
284
|
-
return c.json({
|
|
285
|
-
message: 'Welcome to Froot Modular API',
|
|
286
|
-
health: '/health',
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
app.get('/health', (c) => {
|
|
291
|
-
return c.json({
|
|
292
|
-
status: 'ok',
|
|
293
|
-
uptime: process.uptime(),
|
|
294
|
-
modules: ['auth', 'users', 'products'],
|
|
295
|
-
});
|
|
296
|
-
});
|
|
264
|
+
'src/app.ts': `import { Froot } from 'frootjs';
|
|
265
|
+
import { cors } from 'frootjs/cors';
|
|
266
|
+
import { errorHandler } from './common/filters/error.filter.js';
|
|
267
|
+
import { authModule } from './modules/auth/auth.routes.js';
|
|
268
|
+
import { usersModule } from './modules/users/users.routes.js';
|
|
269
|
+
import { productsModule } from './modules/products/products.routes.js';
|
|
270
|
+
|
|
271
|
+
export const app = new Froot();
|
|
272
|
+
|
|
273
|
+
// Global middleware & filters
|
|
274
|
+
app.use(cors());
|
|
275
|
+
app.onError(errorHandler);
|
|
276
|
+
|
|
277
|
+
// Mount feature modules under /api prefix
|
|
278
|
+
app.route('/api', authModule);
|
|
279
|
+
app.route('/api', usersModule);
|
|
280
|
+
app.route('/api', productsModule);
|
|
281
|
+
|
|
282
|
+
// Root & Health check
|
|
283
|
+
app.get('/', (c) => {
|
|
284
|
+
return c.json({
|
|
285
|
+
message: 'Welcome to Froot Modular API',
|
|
286
|
+
health: '/health',
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
app.get('/health', (c) => {
|
|
291
|
+
return c.json({
|
|
292
|
+
status: 'ok',
|
|
293
|
+
uptime: process.uptime(),
|
|
294
|
+
modules: ['auth', 'users', 'products'],
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
297
|
`,
|
|
298
|
-
'src/server.ts': `import { app } from './app.js';
|
|
299
|
-
|
|
300
|
-
const PORT = Number(process.env.PORT) || 3000;
|
|
301
|
-
|
|
302
|
-
app.listen(PORT, () => {
|
|
303
|
-
console.log(\`Modular Froot Server running on http://localhost:\${PORT}\`);
|
|
304
|
-
});
|
|
298
|
+
'src/server.ts': `import { app } from './app.js';
|
|
299
|
+
|
|
300
|
+
const PORT = Number(process.env.PORT) || 3000;
|
|
301
|
+
|
|
302
|
+
app.listen(PORT, () => {
|
|
303
|
+
console.log(\`Modular Froot Server running on http://localhost:\${PORT}\`);
|
|
304
|
+
});
|
|
305
305
|
`,
|
|
306
306
|
};
|
|
307
307
|
},
|