create-sprint 0.0.52 → 0.0.56
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/generators.js +104 -69
- package/dist/index.js +57 -35
- package/package.json +1 -1
- package/src/generators.ts +105 -71
- package/src/index.ts +76 -49
package/dist/generators.js
CHANGED
|
@@ -9,7 +9,7 @@ export function generateJWTKeys() {
|
|
|
9
9
|
}
|
|
10
10
|
export function getTypeScriptPackageJson(name, telemetry) {
|
|
11
11
|
const deps = {
|
|
12
|
-
"sprint-es": "^0.0.
|
|
12
|
+
"sprint-es": "^0.0.48"
|
|
13
13
|
};
|
|
14
14
|
const devDeps = {
|
|
15
15
|
"@types/node": "^22.0.0",
|
|
@@ -39,7 +39,7 @@ export function getTypeScriptPackageJson(name, telemetry) {
|
|
|
39
39
|
}
|
|
40
40
|
export function getJavaScriptPackageJson(name, telemetry) {
|
|
41
41
|
const deps = {
|
|
42
|
-
"sprint-es": "^0.0.
|
|
42
|
+
"sprint-es": "^0.0.48"
|
|
43
43
|
};
|
|
44
44
|
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
45
45
|
deps["@sentry/node"] = "^8.0.0";
|
|
@@ -117,13 +117,35 @@ export default defineConfig({
|
|
|
117
117
|
export function getMainFile(language) {
|
|
118
118
|
if (language === "typescript") {
|
|
119
119
|
return `import Sprint from "sprint-es";
|
|
120
|
+
import homeRouter from "./routes/home";
|
|
121
|
+
import adminRouter from "./routes/admin";
|
|
122
|
+
import authInternalMiddleware from "./middlewares/auth.internal";
|
|
123
|
+
import authUserMiddleware from "./middlewares/auth.user";
|
|
120
124
|
|
|
121
125
|
const app = new Sprint();
|
|
126
|
+
|
|
127
|
+
app.use(authUserMiddleware);
|
|
128
|
+
app.use(authInternalMiddleware);
|
|
129
|
+
app.route("/", homeRouter);
|
|
130
|
+
app.route("/admin", adminRouter);
|
|
131
|
+
|
|
132
|
+
app.listen();
|
|
122
133
|
`;
|
|
123
134
|
}
|
|
124
135
|
return `import Sprint from "sprint-es";
|
|
136
|
+
import homeRouter from "./routes/home.js";
|
|
137
|
+
import adminRouter from "./routes/admin.js";
|
|
138
|
+
import authInternalMiddleware from "./middlewares/auth.internal.js";
|
|
139
|
+
import authUserMiddleware from "./middlewares/auth.user.js";
|
|
125
140
|
|
|
126
141
|
const app = new Sprint();
|
|
142
|
+
|
|
143
|
+
app.use(authUserMiddleware);
|
|
144
|
+
app.use(authInternalMiddleware);
|
|
145
|
+
app.route("/", homeRouter);
|
|
146
|
+
app.route("/admin", adminRouter);
|
|
147
|
+
|
|
148
|
+
app.listen();
|
|
127
149
|
`;
|
|
128
150
|
}
|
|
129
151
|
export function getHomeRoute(language) {
|
|
@@ -135,7 +157,7 @@ import { homeController, jwtValidateController } from "@/controllers/home";
|
|
|
135
157
|
const router = Router();
|
|
136
158
|
|
|
137
159
|
router.get("/", homeSchema, homeController);
|
|
138
|
-
router.post("/
|
|
160
|
+
router.post("/me", jwtValidateController);
|
|
139
161
|
|
|
140
162
|
export default router;
|
|
141
163
|
`;
|
|
@@ -147,7 +169,7 @@ import { homeController, jwtValidateController } from "../controllers/home.js";
|
|
|
147
169
|
const router = Router();
|
|
148
170
|
|
|
149
171
|
router.get("/", homeSchema, homeController);
|
|
150
|
-
router.post("/
|
|
172
|
+
router.post("/me", jwtValidateController);
|
|
151
173
|
|
|
152
174
|
export default router;
|
|
153
175
|
`;
|
|
@@ -193,24 +215,7 @@ export const homeController: Handler = (req: SprintRequest, res: SprintResponse)
|
|
|
193
215
|
};
|
|
194
216
|
|
|
195
217
|
export const jwtValidateController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (!token) {
|
|
199
|
-
return res.status(401).json({ error: "No token provided" });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
const { publicKey } = getJwtFromEnv();
|
|
204
|
-
const decoded = verifyEncrypted(token, publicKey);
|
|
205
|
-
|
|
206
|
-
if (!decoded) {
|
|
207
|
-
return res.status(401).json({ error: "Invalid token" });
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
res.json({ valid: true, payload: decoded });
|
|
211
|
-
} catch (error) {
|
|
212
|
-
return res.status(500).json({ error: "JWT not configured" });
|
|
213
|
-
}
|
|
218
|
+
return res.json(req.custom.user);
|
|
214
219
|
};
|
|
215
220
|
`;
|
|
216
221
|
}
|
|
@@ -225,24 +230,7 @@ export const homeController = (req: SprintRequest, res: SprintResponse) => {
|
|
|
225
230
|
};
|
|
226
231
|
|
|
227
232
|
export const jwtValidateController = (req: SprintRequest, res: SprintResponse) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!token) {
|
|
231
|
-
return res.status(401).json({ error: "No token provided" });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const { publicKey } = getJwtFromEnv();
|
|
236
|
-
const decoded = verifyEncrypted(token, publicKey);
|
|
237
|
-
|
|
238
|
-
if (!decoded) {
|
|
239
|
-
return res.status(401).json({ error: "Invalid token" });
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
res.json({ valid: true, payload: decoded });
|
|
243
|
-
} catch (error) {
|
|
244
|
-
return res.status(500).json({ error: "JWT not configured" });
|
|
245
|
-
}
|
|
233
|
+
return res.json(req.custom.user);
|
|
246
234
|
};
|
|
247
235
|
`;
|
|
248
236
|
}
|
|
@@ -271,9 +259,9 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
271
259
|
const { userId, role } = req.body || {};
|
|
272
260
|
|
|
273
261
|
try {
|
|
274
|
-
const { privateKey } = getJwtFromEnv();
|
|
262
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
275
263
|
const payload = { userId, role: role || "user" };
|
|
276
|
-
const token = signEncrypted(payload, privateKey, { expiresIn: "1h" });
|
|
264
|
+
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
277
265
|
res.json({ token });
|
|
278
266
|
} catch (error) {
|
|
279
267
|
return res.status(500).json({ error: "JWT not configured" });
|
|
@@ -281,7 +269,8 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
281
269
|
};
|
|
282
270
|
`;
|
|
283
271
|
}
|
|
284
|
-
|
|
272
|
+
else {
|
|
273
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
285
274
|
import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
286
275
|
|
|
287
276
|
export const adminController = (req: SprintRequest, res: SprintResponse) => {
|
|
@@ -304,15 +293,16 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
304
293
|
const { userId, role } = req.body || {};
|
|
305
294
|
|
|
306
295
|
try {
|
|
307
|
-
const { privateKey } = getJwtFromEnv();
|
|
296
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
308
297
|
const payload = { userId, role: role || "user" };
|
|
309
|
-
const token = signEncrypted(payload, privateKey, { expiresIn: "1h" });
|
|
298
|
+
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
310
299
|
res.json({ token });
|
|
311
300
|
} catch (error) {
|
|
312
301
|
return res.status(500).json({ error: "JWT not configured" });
|
|
313
302
|
}
|
|
314
303
|
};
|
|
315
304
|
`;
|
|
305
|
+
}
|
|
316
306
|
}
|
|
317
307
|
export function getHomeSchema(language) {
|
|
318
308
|
if (language === "typescript") {
|
|
@@ -368,26 +358,21 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
368
358
|
});
|
|
369
359
|
`;
|
|
370
360
|
}
|
|
371
|
-
export function
|
|
361
|
+
export function getInternalAuthMiddleware(language) {
|
|
372
362
|
if (language === "typescript") {
|
|
373
363
|
return `import { defineMiddleware } from "sprint-es";
|
|
374
364
|
|
|
375
365
|
export default defineMiddleware({
|
|
376
|
-
name: "
|
|
366
|
+
name: "adminAuth",
|
|
377
367
|
priority: 10,
|
|
378
368
|
include: "/admin/**",
|
|
379
369
|
handler: (req, res, next) => {
|
|
380
370
|
const auth = req.sprint.getAuthorization();
|
|
381
|
-
|
|
382
|
-
if (!auth) {
|
|
383
|
-
return res.status(401).json({ error: "No authorization header" });
|
|
384
|
-
}
|
|
371
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
385
372
|
|
|
386
373
|
const token = auth.replace("Bearer ", "");
|
|
387
374
|
|
|
388
|
-
if (token !== "admin-token") {
|
|
389
|
-
return res.status(403).json({ error: "Invalid token" });
|
|
390
|
-
}
|
|
375
|
+
if (token !== "admin-token") return res.status(403).json({ error: "Invalid token" });
|
|
391
376
|
|
|
392
377
|
next();
|
|
393
378
|
}
|
|
@@ -397,21 +382,72 @@ export default defineMiddleware({
|
|
|
397
382
|
return `import { defineMiddleware } from "sprint-es";
|
|
398
383
|
|
|
399
384
|
export default defineMiddleware({
|
|
400
|
-
name: "
|
|
385
|
+
name: "adminAuth",
|
|
401
386
|
priority: 10,
|
|
402
387
|
include: "/admin/**",
|
|
403
388
|
handler: (req, res, next) => {
|
|
404
389
|
const auth = req.sprint.getAuthorization();
|
|
390
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
391
|
+
|
|
392
|
+
const token = auth.replace("Bearer ", "");
|
|
405
393
|
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
394
|
+
if (token !== "admin-token") return res.status(403).json({ error: "Invalid token" });
|
|
395
|
+
|
|
396
|
+
next();
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
export function getUserAuthMiddleware(language) {
|
|
402
|
+
if (language === "typescript") {
|
|
403
|
+
return `import { defineMiddleware } from "sprint-es";
|
|
404
|
+
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
405
|
+
|
|
406
|
+
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
407
|
+
|
|
408
|
+
export default defineMiddleware({
|
|
409
|
+
name: "userAuth",
|
|
410
|
+
priority: 10,
|
|
411
|
+
include: "/**",
|
|
412
|
+
exclude: "/admin/**",
|
|
413
|
+
handler: (req, res, next) => {
|
|
414
|
+
const auth = req.sprint.getAuthorization();
|
|
415
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
409
416
|
|
|
410
417
|
const token = auth.replace("Bearer ", "");
|
|
411
418
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
419
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
420
|
+
|
|
421
|
+
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
422
|
+
|
|
423
|
+
req.custom.user = decoded;
|
|
424
|
+
|
|
425
|
+
next();
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
`;
|
|
429
|
+
}
|
|
430
|
+
return `import { defineMiddleware } from "sprint-es";
|
|
431
|
+
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
432
|
+
|
|
433
|
+
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
434
|
+
|
|
435
|
+
export default defineMiddleware({
|
|
436
|
+
name: "userAuth",
|
|
437
|
+
priority: 10,
|
|
438
|
+
include: "/**",
|
|
439
|
+
exclude: "/admin/**",
|
|
440
|
+
handler: (req, res, next) => {
|
|
441
|
+
const auth = req.sprint.getAuthorization();
|
|
442
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
443
|
+
|
|
444
|
+
const token = auth.replace("Bearer ", "");
|
|
445
|
+
|
|
446
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
447
|
+
|
|
448
|
+
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
449
|
+
|
|
450
|
+
req.custom.user = decoded;
|
|
415
451
|
|
|
416
452
|
next();
|
|
417
453
|
}
|
|
@@ -453,8 +489,7 @@ CMD ["npm", "start"]
|
|
|
453
489
|
`;
|
|
454
490
|
}
|
|
455
491
|
export function getDockerCompose(language) {
|
|
456
|
-
return `
|
|
457
|
-
|
|
492
|
+
return `
|
|
458
493
|
services:
|
|
459
494
|
app:
|
|
460
495
|
build: .
|
|
@@ -462,7 +497,7 @@ services:
|
|
|
462
497
|
- "3000:3000"
|
|
463
498
|
environment:
|
|
464
499
|
- NODE_ENV=production
|
|
465
|
-
- PORT=
|
|
500
|
+
- PORT=5000
|
|
466
501
|
restart: unless-stopped
|
|
467
502
|
`;
|
|
468
503
|
}
|
|
@@ -554,7 +589,7 @@ initTelemetry({
|
|
|
554
589
|
|
|
555
590
|
initTelemetry({
|
|
556
591
|
provider: "discord",
|
|
557
|
-
webhookUrl: process.env.
|
|
592
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
558
593
|
});
|
|
559
594
|
`;
|
|
560
595
|
}
|
|
@@ -581,7 +616,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
581
616
|
|
|
582
617
|
initTelemetry({
|
|
583
618
|
provider: "discord",
|
|
584
|
-
webhookUrl: process.env.
|
|
619
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
585
620
|
});
|
|
586
621
|
`;
|
|
587
622
|
}
|
|
@@ -602,7 +637,7 @@ SENTRY_DSN=
|
|
|
602
637
|
else if (telemetry === "discord") {
|
|
603
638
|
env += `
|
|
604
639
|
# Discord Webhook URL for error notifications
|
|
605
|
-
|
|
640
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
606
641
|
`;
|
|
607
642
|
}
|
|
608
643
|
return env;
|
|
@@ -627,7 +662,7 @@ SENTRY_DSN=
|
|
|
627
662
|
else if (telemetry === "discord") {
|
|
628
663
|
env += `
|
|
629
664
|
# Discord Webhook URL
|
|
630
|
-
|
|
665
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
631
666
|
`;
|
|
632
667
|
}
|
|
633
668
|
return env;
|
|
@@ -649,7 +684,7 @@ SENTRY_DSN=
|
|
|
649
684
|
else if (telemetry === "discord") {
|
|
650
685
|
env += `
|
|
651
686
|
# Discord Webhook URL
|
|
652
|
-
|
|
687
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
653
688
|
`;
|
|
654
689
|
}
|
|
655
690
|
return env;
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from "path";
|
|
|
5
5
|
import color from "picocolors";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { validateProjectName } from "./validators.js";
|
|
8
|
-
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController,
|
|
8
|
+
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
|
|
9
9
|
export async function writeFile(path, content, options) {
|
|
10
10
|
if (typeof content === "string")
|
|
11
11
|
content = content.trimEnd();
|
|
@@ -16,41 +16,58 @@ export async function runCLI(args) {
|
|
|
16
16
|
const options = parseArgs(args);
|
|
17
17
|
p.intro("Sprint — Quickly API Framework");
|
|
18
18
|
p.intro(`${color.bgCyan(color.black(' create-sprint-app '))}`);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
19
|
+
let config;
|
|
20
|
+
if (options.skipPrompts) {
|
|
21
|
+
config = {
|
|
22
|
+
projectName: options.projectName || "sprint-app",
|
|
23
|
+
language: options.language || "typescript",
|
|
24
|
+
telemetry: options.telemetry || "none",
|
|
25
|
+
docker: options.docker || false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
config = await p.group({
|
|
30
|
+
projectName: () => p.text({
|
|
31
|
+
message: "Project name:",
|
|
32
|
+
placeholder: "my-api",
|
|
33
|
+
validate: (v) => validateProjectName(v) || undefined,
|
|
34
|
+
}),
|
|
35
|
+
language: () => p.select({
|
|
36
|
+
message: "Language:",
|
|
37
|
+
options: [
|
|
38
|
+
{ value: "typescript", label: "TypeScript", hint: "recommended" },
|
|
39
|
+
{ value: "javascript", label: "JavaScript" },
|
|
40
|
+
],
|
|
41
|
+
}),
|
|
42
|
+
telemetry: () => p.select({
|
|
43
|
+
message: "Error tracking:",
|
|
44
|
+
options: [
|
|
45
|
+
{ value: "none", label: "None" },
|
|
46
|
+
{ value: "sentry", label: "Sentry", hint: "free tier available" },
|
|
47
|
+
{ value: "glitchtip", label: "GlitchTip", hint: "self-hostable" },
|
|
48
|
+
{ value: "discord", label: "Discord Webhook", hint: "sends to a channel" },
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false }),
|
|
52
|
+
}, {
|
|
53
|
+
onCancel: () => {
|
|
54
|
+
p.cancel("Cancelled.");
|
|
55
|
+
process.exit(0);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
48
59
|
const targetDir = config.projectName === "." ? process.cwd() : join(process.cwd(), config.projectName);
|
|
49
60
|
const s = p.spinner();
|
|
50
61
|
s.start("Creating project");
|
|
51
62
|
await createProject(config.projectName, config.language, config.telemetry, config.docker);
|
|
52
63
|
s.stop("Project created");
|
|
53
|
-
|
|
64
|
+
let installDeps = true;
|
|
65
|
+
if (options.skipInstall) {
|
|
66
|
+
installDeps = false;
|
|
67
|
+
}
|
|
68
|
+
else if (!options.skipPrompts) {
|
|
69
|
+
installDeps = await p.confirm({ message: "Install dependencies now?", initialValue: true });
|
|
70
|
+
}
|
|
54
71
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
55
72
|
if (installDeps) {
|
|
56
73
|
const s2 = p.spinner();
|
|
@@ -59,7 +76,7 @@ export async function runCLI(args) {
|
|
|
59
76
|
await new Promise((resolve, reject) => {
|
|
60
77
|
const child = spawn(npmCmd, ["install"], {
|
|
61
78
|
cwd: targetDir,
|
|
62
|
-
stdio: "
|
|
79
|
+
stdio: "inherit"
|
|
63
80
|
});
|
|
64
81
|
child.on("close", (code) => {
|
|
65
82
|
if (code === 0)
|
|
@@ -67,12 +84,16 @@ export async function runCLI(args) {
|
|
|
67
84
|
else
|
|
68
85
|
reject(new Error(`npm install exited with code ${code}`));
|
|
69
86
|
});
|
|
70
|
-
child.on("error",
|
|
87
|
+
child.on("error", (err) => {
|
|
88
|
+
p.cancel(`Failed to run npm install: ${err.message}`);
|
|
89
|
+
reject(err);
|
|
90
|
+
});
|
|
71
91
|
});
|
|
72
92
|
s2.stop("Dependencies installed");
|
|
73
93
|
}
|
|
74
|
-
catch {
|
|
94
|
+
catch (err) {
|
|
75
95
|
s2.stop("Install failed — run npm install manually");
|
|
96
|
+
console.error(err);
|
|
76
97
|
}
|
|
77
98
|
}
|
|
78
99
|
const cdCmd = config.projectName === "." ? "" : `cd ${config.projectName} && `;
|
|
@@ -158,7 +179,8 @@ async function createProject(projectName, language, telemetry, useDocker) {
|
|
|
158
179
|
await writeFile(join(srcDir, "routes", "admin." + (language === "typescript" ? "ts" : "js")), getAdminRoute(language));
|
|
159
180
|
await writeFile(join(srcDir, "controllers", "home." + (language === "typescript" ? "ts" : "js")), getHomeController(language));
|
|
160
181
|
await writeFile(join(srcDir, "controllers", "admin." + (language === "typescript" ? "ts" : "js")), getAdminController(language));
|
|
161
|
-
await writeFile(join(srcDir, "middlewares", "auth." + (language === "typescript" ? "ts" : "js")),
|
|
182
|
+
await writeFile(join(srcDir, "middlewares", "auth.internal." + (language === "typescript" ? "ts" : "js")), getInternalAuthMiddleware(language));
|
|
183
|
+
await writeFile(join(srcDir, "middlewares", "auth.user." + (language === "typescript" ? "ts" : "js")), getUserAuthMiddleware(language));
|
|
162
184
|
await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
|
|
163
185
|
await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
|
|
164
186
|
await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
|
package/package.json
CHANGED
package/src/generators.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function generateJWTKeys(): JWTKeys {
|
|
|
16
16
|
|
|
17
17
|
export function getTypeScriptPackageJson(name: string, telemetry: string) {
|
|
18
18
|
const deps: Record<string, string> = {
|
|
19
|
-
"sprint-es": "^0.0.
|
|
19
|
+
"sprint-es": "^0.0.48"
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const devDeps: Record<string, string> = {
|
|
@@ -49,7 +49,7 @@ export function getTypeScriptPackageJson(name: string, telemetry: string) {
|
|
|
49
49
|
|
|
50
50
|
export function getJavaScriptPackageJson(name: string, telemetry: string) {
|
|
51
51
|
const deps: Record<string, string> = {
|
|
52
|
-
"sprint-es": "^0.0.
|
|
52
|
+
"sprint-es": "^0.0.48"
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
@@ -131,14 +131,36 @@ export default defineConfig({
|
|
|
131
131
|
export function getMainFile(language: string) {
|
|
132
132
|
if (language === "typescript") {
|
|
133
133
|
return `import Sprint from "sprint-es";
|
|
134
|
+
import homeRouter from "./routes/home";
|
|
135
|
+
import adminRouter from "./routes/admin";
|
|
136
|
+
import authInternalMiddleware from "./middlewares/auth.internal";
|
|
137
|
+
import authUserMiddleware from "./middlewares/auth.user";
|
|
134
138
|
|
|
135
139
|
const app = new Sprint();
|
|
140
|
+
|
|
141
|
+
app.use(authUserMiddleware);
|
|
142
|
+
app.use(authInternalMiddleware);
|
|
143
|
+
app.route("/", homeRouter);
|
|
144
|
+
app.route("/admin", adminRouter);
|
|
145
|
+
|
|
146
|
+
app.listen();
|
|
136
147
|
`;
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
return `import Sprint from "sprint-es";
|
|
151
|
+
import homeRouter from "./routes/home.js";
|
|
152
|
+
import adminRouter from "./routes/admin.js";
|
|
153
|
+
import authInternalMiddleware from "./middlewares/auth.internal.js";
|
|
154
|
+
import authUserMiddleware from "./middlewares/auth.user.js";
|
|
140
155
|
|
|
141
156
|
const app = new Sprint();
|
|
157
|
+
|
|
158
|
+
app.use(authUserMiddleware);
|
|
159
|
+
app.use(authInternalMiddleware);
|
|
160
|
+
app.route("/", homeRouter);
|
|
161
|
+
app.route("/admin", adminRouter);
|
|
162
|
+
|
|
163
|
+
app.listen();
|
|
142
164
|
`;
|
|
143
165
|
}
|
|
144
166
|
|
|
@@ -151,7 +173,7 @@ import { homeController, jwtValidateController } from "@/controllers/home";
|
|
|
151
173
|
const router = Router();
|
|
152
174
|
|
|
153
175
|
router.get("/", homeSchema, homeController);
|
|
154
|
-
router.post("/
|
|
176
|
+
router.post("/me", jwtValidateController);
|
|
155
177
|
|
|
156
178
|
export default router;
|
|
157
179
|
`;
|
|
@@ -163,7 +185,7 @@ import { homeController, jwtValidateController } from "../controllers/home.js";
|
|
|
163
185
|
const router = Router();
|
|
164
186
|
|
|
165
187
|
router.get("/", homeSchema, homeController);
|
|
166
|
-
router.post("/
|
|
188
|
+
router.post("/me", jwtValidateController);
|
|
167
189
|
|
|
168
190
|
export default router;
|
|
169
191
|
`;
|
|
@@ -211,24 +233,7 @@ export const homeController: Handler = (req: SprintRequest, res: SprintResponse)
|
|
|
211
233
|
};
|
|
212
234
|
|
|
213
235
|
export const jwtValidateController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!token) {
|
|
217
|
-
return res.status(401).json({ error: "No token provided" });
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
const { publicKey } = getJwtFromEnv();
|
|
222
|
-
const decoded = verifyEncrypted(token, publicKey);
|
|
223
|
-
|
|
224
|
-
if (!decoded) {
|
|
225
|
-
return res.status(401).json({ error: "Invalid token" });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
res.json({ valid: true, payload: decoded });
|
|
229
|
-
} catch (error) {
|
|
230
|
-
return res.status(500).json({ error: "JWT not configured" });
|
|
231
|
-
}
|
|
236
|
+
return res.json(req.custom.user);
|
|
232
237
|
};
|
|
233
238
|
`;
|
|
234
239
|
}
|
|
@@ -243,24 +248,7 @@ export const homeController = (req: SprintRequest, res: SprintResponse) => {
|
|
|
243
248
|
};
|
|
244
249
|
|
|
245
250
|
export const jwtValidateController = (req: SprintRequest, res: SprintResponse) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!token) {
|
|
249
|
-
return res.status(401).json({ error: "No token provided" });
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
try {
|
|
253
|
-
const { publicKey } = getJwtFromEnv();
|
|
254
|
-
const decoded = verifyEncrypted(token, publicKey);
|
|
255
|
-
|
|
256
|
-
if (!decoded) {
|
|
257
|
-
return res.status(401).json({ error: "Invalid token" });
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
res.json({ valid: true, payload: decoded });
|
|
261
|
-
} catch (error) {
|
|
262
|
-
return res.status(500).json({ error: "JWT not configured" });
|
|
263
|
-
}
|
|
251
|
+
return res.json(req.custom.user);
|
|
264
252
|
};
|
|
265
253
|
`;
|
|
266
254
|
}
|
|
@@ -290,17 +278,17 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
290
278
|
const { userId, role } = req.body || {};
|
|
291
279
|
|
|
292
280
|
try {
|
|
293
|
-
const { privateKey } = getJwtFromEnv();
|
|
281
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
294
282
|
const payload = { userId, role: role || "user" };
|
|
295
|
-
const token = signEncrypted(payload, privateKey, { expiresIn: "1h" });
|
|
283
|
+
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
296
284
|
res.json({ token });
|
|
297
285
|
} catch (error) {
|
|
298
286
|
return res.status(500).json({ error: "JWT not configured" });
|
|
299
287
|
}
|
|
300
288
|
};
|
|
301
289
|
`;
|
|
302
|
-
}
|
|
303
|
-
|
|
290
|
+
} else {
|
|
291
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
304
292
|
import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
305
293
|
|
|
306
294
|
export const adminController = (req: SprintRequest, res: SprintResponse) => {
|
|
@@ -323,16 +311,16 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
323
311
|
const { userId, role } = req.body || {};
|
|
324
312
|
|
|
325
313
|
try {
|
|
326
|
-
const { privateKey } = getJwtFromEnv();
|
|
314
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
327
315
|
const payload = { userId, role: role || "user" };
|
|
328
|
-
const token = signEncrypted(payload, privateKey, { expiresIn: "1h" });
|
|
316
|
+
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
329
317
|
res.json({ token });
|
|
330
318
|
} catch (error) {
|
|
331
319
|
return res.status(500).json({ error: "JWT not configured" });
|
|
332
320
|
}
|
|
333
321
|
};
|
|
334
322
|
`;
|
|
335
|
-
|
|
323
|
+
}
|
|
336
324
|
}
|
|
337
325
|
|
|
338
326
|
export function getHomeSchema(language: string) {
|
|
@@ -391,26 +379,21 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
391
379
|
`;
|
|
392
380
|
}
|
|
393
381
|
|
|
394
|
-
export function
|
|
382
|
+
export function getInternalAuthMiddleware(language: string) {
|
|
395
383
|
if (language === "typescript") {
|
|
396
384
|
return `import { defineMiddleware } from "sprint-es";
|
|
397
385
|
|
|
398
386
|
export default defineMiddleware({
|
|
399
|
-
name: "
|
|
387
|
+
name: "adminAuth",
|
|
400
388
|
priority: 10,
|
|
401
389
|
include: "/admin/**",
|
|
402
390
|
handler: (req, res, next) => {
|
|
403
391
|
const auth = req.sprint.getAuthorization();
|
|
404
|
-
|
|
405
|
-
if (!auth) {
|
|
406
|
-
return res.status(401).json({ error: "No authorization header" });
|
|
407
|
-
}
|
|
392
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
408
393
|
|
|
409
394
|
const token = auth.replace("Bearer ", "");
|
|
410
395
|
|
|
411
|
-
if (token !== "admin-token") {
|
|
412
|
-
return res.status(403).json({ error: "Invalid token" });
|
|
413
|
-
}
|
|
396
|
+
if (token !== "admin-token") return res.status(403).json({ error: "Invalid token" });
|
|
414
397
|
|
|
415
398
|
next();
|
|
416
399
|
}
|
|
@@ -420,21 +403,73 @@ export default defineMiddleware({
|
|
|
420
403
|
return `import { defineMiddleware } from "sprint-es";
|
|
421
404
|
|
|
422
405
|
export default defineMiddleware({
|
|
423
|
-
name: "
|
|
406
|
+
name: "adminAuth",
|
|
424
407
|
priority: 10,
|
|
425
408
|
include: "/admin/**",
|
|
426
409
|
handler: (req, res, next) => {
|
|
427
410
|
const auth = req.sprint.getAuthorization();
|
|
411
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
412
|
+
|
|
413
|
+
const token = auth.replace("Bearer ", "");
|
|
428
414
|
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
|
|
415
|
+
if (token !== "admin-token") return res.status(403).json({ error: "Invalid token" });
|
|
416
|
+
|
|
417
|
+
next();
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function getUserAuthMiddleware(language: string) {
|
|
424
|
+
if (language === "typescript") {
|
|
425
|
+
return `import { defineMiddleware } from "sprint-es";
|
|
426
|
+
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
427
|
+
|
|
428
|
+
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
429
|
+
|
|
430
|
+
export default defineMiddleware({
|
|
431
|
+
name: "userAuth",
|
|
432
|
+
priority: 10,
|
|
433
|
+
include: "/**",
|
|
434
|
+
exclude: "/admin/**",
|
|
435
|
+
handler: (req, res, next) => {
|
|
436
|
+
const auth = req.sprint.getAuthorization();
|
|
437
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
432
438
|
|
|
433
439
|
const token = auth.replace("Bearer ", "");
|
|
434
440
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
441
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
442
|
+
|
|
443
|
+
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
444
|
+
|
|
445
|
+
req.custom.user = decoded;
|
|
446
|
+
|
|
447
|
+
next();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
`;
|
|
451
|
+
}
|
|
452
|
+
return `import { defineMiddleware } from "sprint-es";
|
|
453
|
+
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
454
|
+
|
|
455
|
+
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
456
|
+
|
|
457
|
+
export default defineMiddleware({
|
|
458
|
+
name: "userAuth",
|
|
459
|
+
priority: 10,
|
|
460
|
+
include: "/**",
|
|
461
|
+
exclude: "/admin/**",
|
|
462
|
+
handler: (req, res, next) => {
|
|
463
|
+
const auth = req.sprint.getAuthorization();
|
|
464
|
+
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
465
|
+
|
|
466
|
+
const token = auth.replace("Bearer ", "");
|
|
467
|
+
|
|
468
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
469
|
+
|
|
470
|
+
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
471
|
+
|
|
472
|
+
req.custom.user = decoded;
|
|
438
473
|
|
|
439
474
|
next();
|
|
440
475
|
}
|
|
@@ -478,8 +513,7 @@ CMD ["npm", "start"]
|
|
|
478
513
|
}
|
|
479
514
|
|
|
480
515
|
export function getDockerCompose(language: string) {
|
|
481
|
-
return `
|
|
482
|
-
|
|
516
|
+
return `
|
|
483
517
|
services:
|
|
484
518
|
app:
|
|
485
519
|
build: .
|
|
@@ -487,7 +521,7 @@ services:
|
|
|
487
521
|
- "3000:3000"
|
|
488
522
|
environment:
|
|
489
523
|
- NODE_ENV=production
|
|
490
|
-
- PORT=
|
|
524
|
+
- PORT=5000
|
|
491
525
|
restart: unless-stopped
|
|
492
526
|
`;
|
|
493
527
|
}
|
|
@@ -582,7 +616,7 @@ initTelemetry({
|
|
|
582
616
|
|
|
583
617
|
initTelemetry({
|
|
584
618
|
provider: "discord",
|
|
585
|
-
webhookUrl: process.env.
|
|
619
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
586
620
|
});
|
|
587
621
|
`;
|
|
588
622
|
}
|
|
@@ -611,7 +645,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
611
645
|
|
|
612
646
|
initTelemetry({
|
|
613
647
|
provider: "discord",
|
|
614
|
-
webhookUrl: process.env.
|
|
648
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
615
649
|
});
|
|
616
650
|
`;
|
|
617
651
|
}
|
|
@@ -634,7 +668,7 @@ SENTRY_DSN=
|
|
|
634
668
|
} else if (telemetry === "discord") {
|
|
635
669
|
env += `
|
|
636
670
|
# Discord Webhook URL for error notifications
|
|
637
|
-
|
|
671
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
638
672
|
`;
|
|
639
673
|
}
|
|
640
674
|
|
|
@@ -662,7 +696,7 @@ SENTRY_DSN=
|
|
|
662
696
|
} else if (telemetry === "discord") {
|
|
663
697
|
env += `
|
|
664
698
|
# Discord Webhook URL
|
|
665
|
-
|
|
699
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
666
700
|
`;
|
|
667
701
|
}
|
|
668
702
|
|
|
@@ -686,7 +720,7 @@ SENTRY_DSN=
|
|
|
686
720
|
} else if (telemetry === "discord") {
|
|
687
721
|
env += `
|
|
688
722
|
# Discord Webhook URL
|
|
689
|
-
|
|
723
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
690
724
|
`;
|
|
691
725
|
}
|
|
692
726
|
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
-
import { existsSync
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
3
|
import { mkdir, writeFile as fsWriteFile } from "fs/promises";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import color from "picocolors";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { validateProjectName } from "./validators.js";
|
|
8
|
-
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController,
|
|
8
|
+
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
|
|
9
9
|
|
|
10
10
|
export interface CLIOptions {
|
|
11
11
|
projectName?: string;
|
|
@@ -28,59 +28,81 @@ export async function runCLI(args: string[]) {
|
|
|
28
28
|
|
|
29
29
|
p.intro(`${color.bgCyan(color.black(' create-sprint-app '))}`);
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
31
|
+
let config: {
|
|
32
|
+
projectName: string;
|
|
33
|
+
language: "typescript" | "javascript";
|
|
34
|
+
telemetry: string;
|
|
35
|
+
docker: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (options.skipPrompts) {
|
|
39
|
+
config = {
|
|
40
|
+
projectName: options.projectName || "sprint-app",
|
|
41
|
+
language: options.language || "typescript",
|
|
42
|
+
telemetry: options.telemetry || "none",
|
|
43
|
+
docker: options.docker || false,
|
|
44
|
+
};
|
|
45
|
+
} else {
|
|
46
|
+
config = await p.group(
|
|
47
|
+
{
|
|
48
|
+
projectName: () =>
|
|
49
|
+
p.text({
|
|
50
|
+
message: "Project name:",
|
|
51
|
+
placeholder: "my-api",
|
|
52
|
+
validate: (v) => validateProjectName(v) || undefined,
|
|
53
|
+
}),
|
|
54
|
+
|
|
55
|
+
language: () =>
|
|
56
|
+
p.select({
|
|
57
|
+
message: "Language:",
|
|
58
|
+
options: [
|
|
59
|
+
{ value: "typescript", label: "TypeScript", hint: "recommended" },
|
|
60
|
+
{ value: "javascript", label: "JavaScript" },
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
telemetry: () =>
|
|
65
|
+
p.select({
|
|
66
|
+
message: "Error tracking:",
|
|
67
|
+
options: [
|
|
68
|
+
{ value: "none", label: "None" },
|
|
69
|
+
{ value: "sentry", label: "Sentry", hint: "free tier available" },
|
|
70
|
+
{ value: "glitchtip", label: "GlitchTip", hint: "self-hostable" },
|
|
71
|
+
{ value: "discord", label: "Discord Webhook", hint: "sends to a channel" },
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
|
|
75
|
+
docker: () =>
|
|
76
|
+
p.confirm({ message: "Add Docker support?", initialValue: false }),
|
|
67
77
|
},
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
{
|
|
79
|
+
onCancel: () => {
|
|
80
|
+
p.cancel("Cancelled.");
|
|
81
|
+
process.exit(0);
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
70
86
|
|
|
71
87
|
const targetDir = config.projectName === "." ? process.cwd() : join(process.cwd(), config.projectName);
|
|
72
88
|
|
|
73
89
|
const s = p.spinner();
|
|
74
90
|
s.start("Creating project");
|
|
75
91
|
await createProject(
|
|
76
|
-
config.projectName
|
|
77
|
-
config.language
|
|
78
|
-
config.telemetry
|
|
79
|
-
config.docker
|
|
92
|
+
config.projectName,
|
|
93
|
+
config.language,
|
|
94
|
+
config.telemetry,
|
|
95
|
+
config.docker,
|
|
80
96
|
);
|
|
81
97
|
s.stop("Project created");
|
|
82
98
|
|
|
83
|
-
|
|
99
|
+
let installDeps = true;
|
|
100
|
+
if (options.skipInstall) {
|
|
101
|
+
installDeps = false;
|
|
102
|
+
} else if (!options.skipPrompts) {
|
|
103
|
+
installDeps = await p.confirm({ message: "Install dependencies now?", initialValue: true }) as boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
85
107
|
|
|
86
108
|
if (installDeps) {
|
|
@@ -90,17 +112,21 @@ export async function runCLI(args: string[]) {
|
|
|
90
112
|
await new Promise<void>((resolve, reject) => {
|
|
91
113
|
const child = spawn(npmCmd, ["install"], {
|
|
92
114
|
cwd: targetDir,
|
|
93
|
-
stdio: "
|
|
115
|
+
stdio: "inherit"
|
|
94
116
|
});
|
|
95
117
|
child.on("close", (code) => {
|
|
96
118
|
if (code === 0) resolve();
|
|
97
119
|
else reject(new Error(`npm install exited with code ${code}`));
|
|
98
120
|
});
|
|
99
|
-
child.on("error",
|
|
121
|
+
child.on("error", (err) => {
|
|
122
|
+
p.cancel(`Failed to run npm install: ${err.message}`);
|
|
123
|
+
reject(err);
|
|
124
|
+
});
|
|
100
125
|
});
|
|
101
126
|
s2.stop("Dependencies installed");
|
|
102
|
-
} catch {
|
|
127
|
+
} catch (err) {
|
|
103
128
|
s2.stop("Install failed — run npm install manually");
|
|
129
|
+
console.error(err);
|
|
104
130
|
}
|
|
105
131
|
}
|
|
106
132
|
|
|
@@ -202,7 +228,8 @@ async function createProject(
|
|
|
202
228
|
await writeFile(join(srcDir, "controllers", "home." + (language === "typescript" ? "ts" : "js")), getHomeController(language));
|
|
203
229
|
await writeFile(join(srcDir, "controllers", "admin." + (language === "typescript" ? "ts" : "js")), getAdminController(language));
|
|
204
230
|
|
|
205
|
-
await writeFile(join(srcDir, "middlewares", "auth." + (language === "typescript" ? "ts" : "js")),
|
|
231
|
+
await writeFile(join(srcDir, "middlewares", "auth.internal." + (language === "typescript" ? "ts" : "js")), getInternalAuthMiddleware(language));
|
|
232
|
+
await writeFile(join(srcDir, "middlewares", "auth.user." + (language === "typescript" ? "ts" : "js")), getUserAuthMiddleware(language));
|
|
206
233
|
|
|
207
234
|
await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
|
|
208
235
|
await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
|