create-sprint 0.0.54 → 0.0.58
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 +22 -55
- package/dist/index.js +56 -35
- package/package.json +1 -1
- package/src/generators.ts +22 -57
- package/src/index.ts +74 -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";
|
|
@@ -135,7 +135,7 @@ import { homeController, jwtValidateController } from "@/controllers/home";
|
|
|
135
135
|
const router = Router();
|
|
136
136
|
|
|
137
137
|
router.get("/", homeSchema, homeController);
|
|
138
|
-
router.post("/
|
|
138
|
+
router.post("/me", jwtValidateController);
|
|
139
139
|
|
|
140
140
|
export default router;
|
|
141
141
|
`;
|
|
@@ -147,7 +147,7 @@ import { homeController, jwtValidateController } from "../controllers/home.js";
|
|
|
147
147
|
const router = Router();
|
|
148
148
|
|
|
149
149
|
router.get("/", homeSchema, homeController);
|
|
150
|
-
router.post("/
|
|
150
|
+
router.post("/me", jwtValidateController);
|
|
151
151
|
|
|
152
152
|
export default router;
|
|
153
153
|
`;
|
|
@@ -193,24 +193,7 @@ export const homeController: Handler = (req: SprintRequest, res: SprintResponse)
|
|
|
193
193
|
};
|
|
194
194
|
|
|
195
195
|
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, encryptionSecret } = getJwtFromEnv();
|
|
204
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
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
|
-
}
|
|
196
|
+
return res.json(req.custom.user);
|
|
214
197
|
};
|
|
215
198
|
`;
|
|
216
199
|
}
|
|
@@ -225,24 +208,7 @@ export const homeController = (req: SprintRequest, res: SprintResponse) => {
|
|
|
225
208
|
};
|
|
226
209
|
|
|
227
210
|
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, encryptionSecret } = getJwtFromEnv();
|
|
236
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
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
|
-
}
|
|
211
|
+
return res.json(req.custom.user);
|
|
246
212
|
};
|
|
247
213
|
`;
|
|
248
214
|
}
|
|
@@ -271,7 +237,7 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
271
237
|
const { userId, role } = req.body || {};
|
|
272
238
|
|
|
273
239
|
try {
|
|
274
|
-
const { privateKey } = getJwtFromEnv();
|
|
240
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
275
241
|
const payload = { userId, role: role || "user" };
|
|
276
242
|
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
277
243
|
res.json({ token });
|
|
@@ -281,7 +247,8 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
281
247
|
};
|
|
282
248
|
`;
|
|
283
249
|
}
|
|
284
|
-
|
|
250
|
+
else {
|
|
251
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
285
252
|
import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
286
253
|
|
|
287
254
|
export const adminController = (req: SprintRequest, res: SprintResponse) => {
|
|
@@ -313,6 +280,7 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
313
280
|
}
|
|
314
281
|
};
|
|
315
282
|
`;
|
|
283
|
+
}
|
|
316
284
|
}
|
|
317
285
|
export function getHomeSchema(language) {
|
|
318
286
|
if (language === "typescript") {
|
|
@@ -370,13 +338,13 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
370
338
|
}
|
|
371
339
|
export function getInternalAuthMiddleware(language) {
|
|
372
340
|
if (language === "typescript") {
|
|
373
|
-
return `import { defineMiddleware } from "sprint-es";
|
|
341
|
+
return `import { defineMiddleware, SprintRequest, SprintResponse, NextFunction } from "sprint-es";
|
|
374
342
|
|
|
375
343
|
export default defineMiddleware({
|
|
376
344
|
name: "adminAuth",
|
|
377
345
|
priority: 10,
|
|
378
346
|
include: "/admin/**",
|
|
379
|
-
handler: (req, res, next) => {
|
|
347
|
+
handler: (req: SprintRequest, res: SprintResponse, next: NextFunction) => {
|
|
380
348
|
const auth = req.sprint.getAuthorization();
|
|
381
349
|
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
382
350
|
|
|
@@ -410,7 +378,7 @@ export default defineMiddleware({
|
|
|
410
378
|
}
|
|
411
379
|
export function getUserAuthMiddleware(language) {
|
|
412
380
|
if (language === "typescript") {
|
|
413
|
-
return `import { defineMiddleware } from "sprint-es";
|
|
381
|
+
return `import { defineMiddleware, SprintRequest, SprintResponse, NextFunction } from "sprint-es";
|
|
414
382
|
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
415
383
|
|
|
416
384
|
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
@@ -420,13 +388,13 @@ export default defineMiddleware({
|
|
|
420
388
|
priority: 10,
|
|
421
389
|
include: "/**",
|
|
422
390
|
exclude: "/admin/**",
|
|
423
|
-
handler: (req, res, next) => {
|
|
391
|
+
handler: (req: SprintRequest, res: SprintResponse, next: NextFunction) => {
|
|
424
392
|
const auth = req.sprint.getAuthorization();
|
|
425
393
|
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
426
394
|
|
|
427
395
|
const token = auth.replace("Bearer ", "");
|
|
428
396
|
|
|
429
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
397
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
430
398
|
|
|
431
399
|
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
432
400
|
|
|
@@ -499,8 +467,7 @@ CMD ["npm", "start"]
|
|
|
499
467
|
`;
|
|
500
468
|
}
|
|
501
469
|
export function getDockerCompose(language) {
|
|
502
|
-
return `
|
|
503
|
-
|
|
470
|
+
return `
|
|
504
471
|
services:
|
|
505
472
|
app:
|
|
506
473
|
build: .
|
|
@@ -508,7 +475,7 @@ services:
|
|
|
508
475
|
- "3000:3000"
|
|
509
476
|
environment:
|
|
510
477
|
- NODE_ENV=production
|
|
511
|
-
- PORT=
|
|
478
|
+
- PORT=5000
|
|
512
479
|
restart: unless-stopped
|
|
513
480
|
`;
|
|
514
481
|
}
|
|
@@ -600,7 +567,7 @@ initTelemetry({
|
|
|
600
567
|
|
|
601
568
|
initTelemetry({
|
|
602
569
|
provider: "discord",
|
|
603
|
-
webhookUrl: process.env.
|
|
570
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
604
571
|
});
|
|
605
572
|
`;
|
|
606
573
|
}
|
|
@@ -627,7 +594,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
627
594
|
|
|
628
595
|
initTelemetry({
|
|
629
596
|
provider: "discord",
|
|
630
|
-
webhookUrl: process.env.
|
|
597
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
631
598
|
});
|
|
632
599
|
`;
|
|
633
600
|
}
|
|
@@ -648,7 +615,7 @@ SENTRY_DSN=
|
|
|
648
615
|
else if (telemetry === "discord") {
|
|
649
616
|
env += `
|
|
650
617
|
# Discord Webhook URL for error notifications
|
|
651
|
-
|
|
618
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
652
619
|
`;
|
|
653
620
|
}
|
|
654
621
|
return env;
|
|
@@ -673,7 +640,7 @@ SENTRY_DSN=
|
|
|
673
640
|
else if (telemetry === "discord") {
|
|
674
641
|
env += `
|
|
675
642
|
# Discord Webhook URL
|
|
676
|
-
|
|
643
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
677
644
|
`;
|
|
678
645
|
}
|
|
679
646
|
return env;
|
|
@@ -695,7 +662,7 @@ SENTRY_DSN=
|
|
|
695
662
|
else if (telemetry === "discord") {
|
|
696
663
|
env += `
|
|
697
664
|
# Discord Webhook URL
|
|
698
|
-
|
|
665
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
699
666
|
`;
|
|
700
667
|
}
|
|
701
668
|
return env;
|
package/dist/index.js
CHANGED
|
@@ -16,50 +16,67 @@ 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
|
-
|
|
54
|
-
|
|
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
|
+
}
|
|
55
71
|
if (installDeps) {
|
|
56
72
|
const s2 = p.spinner();
|
|
57
73
|
s2.start("Installing dependencies");
|
|
58
74
|
try {
|
|
59
75
|
await new Promise((resolve, reject) => {
|
|
60
|
-
const child = spawn(
|
|
76
|
+
const child = spawn("npm", ["install"], {
|
|
61
77
|
cwd: targetDir,
|
|
62
|
-
stdio: "
|
|
78
|
+
stdio: "inherit",
|
|
79
|
+
shell: true
|
|
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} && `;
|
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") {
|
|
@@ -151,7 +151,7 @@ import { homeController, jwtValidateController } from "@/controllers/home";
|
|
|
151
151
|
const router = Router();
|
|
152
152
|
|
|
153
153
|
router.get("/", homeSchema, homeController);
|
|
154
|
-
router.post("/
|
|
154
|
+
router.post("/me", jwtValidateController);
|
|
155
155
|
|
|
156
156
|
export default router;
|
|
157
157
|
`;
|
|
@@ -163,7 +163,7 @@ import { homeController, jwtValidateController } from "../controllers/home.js";
|
|
|
163
163
|
const router = Router();
|
|
164
164
|
|
|
165
165
|
router.get("/", homeSchema, homeController);
|
|
166
|
-
router.post("/
|
|
166
|
+
router.post("/me", jwtValidateController);
|
|
167
167
|
|
|
168
168
|
export default router;
|
|
169
169
|
`;
|
|
@@ -211,24 +211,7 @@ export const homeController: Handler = (req: SprintRequest, res: SprintResponse)
|
|
|
211
211
|
};
|
|
212
212
|
|
|
213
213
|
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, encryptionSecret } = getJwtFromEnv();
|
|
222
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
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
|
-
}
|
|
214
|
+
return res.json(req.custom.user);
|
|
232
215
|
};
|
|
233
216
|
`;
|
|
234
217
|
}
|
|
@@ -243,24 +226,7 @@ export const homeController = (req: SprintRequest, res: SprintResponse) => {
|
|
|
243
226
|
};
|
|
244
227
|
|
|
245
228
|
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, encryptionSecret } = getJwtFromEnv();
|
|
254
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
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
|
-
}
|
|
229
|
+
return res.json(req.custom.user);
|
|
264
230
|
};
|
|
265
231
|
`;
|
|
266
232
|
}
|
|
@@ -290,7 +256,7 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
290
256
|
const { userId, role } = req.body || {};
|
|
291
257
|
|
|
292
258
|
try {
|
|
293
|
-
const { privateKey } = getJwtFromEnv();
|
|
259
|
+
const { privateKey, encryptionSecret } = getJwtFromEnv();
|
|
294
260
|
const payload = { userId, role: role || "user" };
|
|
295
261
|
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
296
262
|
res.json({ token });
|
|
@@ -299,8 +265,8 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
299
265
|
}
|
|
300
266
|
};
|
|
301
267
|
`;
|
|
302
|
-
}
|
|
303
|
-
|
|
268
|
+
} else {
|
|
269
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
304
270
|
import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
305
271
|
|
|
306
272
|
export const adminController = (req: SprintRequest, res: SprintResponse) => {
|
|
@@ -332,7 +298,7 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
332
298
|
}
|
|
333
299
|
};
|
|
334
300
|
`;
|
|
335
|
-
|
|
301
|
+
}
|
|
336
302
|
}
|
|
337
303
|
|
|
338
304
|
export function getHomeSchema(language: string) {
|
|
@@ -393,13 +359,13 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
393
359
|
|
|
394
360
|
export function getInternalAuthMiddleware(language: string) {
|
|
395
361
|
if (language === "typescript") {
|
|
396
|
-
return `import { defineMiddleware } from "sprint-es";
|
|
362
|
+
return `import { defineMiddleware, SprintRequest, SprintResponse, NextFunction } from "sprint-es";
|
|
397
363
|
|
|
398
364
|
export default defineMiddleware({
|
|
399
365
|
name: "adminAuth",
|
|
400
366
|
priority: 10,
|
|
401
367
|
include: "/admin/**",
|
|
402
|
-
handler: (req, res, next) => {
|
|
368
|
+
handler: (req: SprintRequest, res: SprintResponse, next: NextFunction) => {
|
|
403
369
|
const auth = req.sprint.getAuthorization();
|
|
404
370
|
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
405
371
|
|
|
@@ -434,7 +400,7 @@ export default defineMiddleware({
|
|
|
434
400
|
|
|
435
401
|
export function getUserAuthMiddleware(language: string) {
|
|
436
402
|
if (language === "typescript") {
|
|
437
|
-
return `import { defineMiddleware } from "sprint-es";
|
|
403
|
+
return `import { defineMiddleware, SprintRequest, SprintResponse, NextFunction } from "sprint-es";
|
|
438
404
|
import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
|
|
439
405
|
|
|
440
406
|
const { publicKey, encryptionSecret } = getJwtFromEnv();
|
|
@@ -444,13 +410,13 @@ export default defineMiddleware({
|
|
|
444
410
|
priority: 10,
|
|
445
411
|
include: "/**",
|
|
446
412
|
exclude: "/admin/**",
|
|
447
|
-
handler: (req, res, next) => {
|
|
413
|
+
handler: (req: SprintRequest, res: SprintResponse, next: NextFunction) => {
|
|
448
414
|
const auth = req.sprint.getAuthorization();
|
|
449
415
|
if (!auth) return res.status(401).json({ error: "No authorization header" });
|
|
450
416
|
|
|
451
417
|
const token = auth.replace("Bearer ", "");
|
|
452
418
|
|
|
453
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
419
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
454
420
|
|
|
455
421
|
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
456
422
|
|
|
@@ -525,8 +491,7 @@ CMD ["npm", "start"]
|
|
|
525
491
|
}
|
|
526
492
|
|
|
527
493
|
export function getDockerCompose(language: string) {
|
|
528
|
-
return `
|
|
529
|
-
|
|
494
|
+
return `
|
|
530
495
|
services:
|
|
531
496
|
app:
|
|
532
497
|
build: .
|
|
@@ -534,7 +499,7 @@ services:
|
|
|
534
499
|
- "3000:3000"
|
|
535
500
|
environment:
|
|
536
501
|
- NODE_ENV=production
|
|
537
|
-
- PORT=
|
|
502
|
+
- PORT=5000
|
|
538
503
|
restart: unless-stopped
|
|
539
504
|
`;
|
|
540
505
|
}
|
|
@@ -629,7 +594,7 @@ initTelemetry({
|
|
|
629
594
|
|
|
630
595
|
initTelemetry({
|
|
631
596
|
provider: "discord",
|
|
632
|
-
webhookUrl: process.env.
|
|
597
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
633
598
|
});
|
|
634
599
|
`;
|
|
635
600
|
}
|
|
@@ -658,7 +623,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
658
623
|
|
|
659
624
|
initTelemetry({
|
|
660
625
|
provider: "discord",
|
|
661
|
-
webhookUrl: process.env.
|
|
626
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
662
627
|
});
|
|
663
628
|
`;
|
|
664
629
|
}
|
|
@@ -681,7 +646,7 @@ SENTRY_DSN=
|
|
|
681
646
|
} else if (telemetry === "discord") {
|
|
682
647
|
env += `
|
|
683
648
|
# Discord Webhook URL for error notifications
|
|
684
|
-
|
|
649
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
685
650
|
`;
|
|
686
651
|
}
|
|
687
652
|
|
|
@@ -709,7 +674,7 @@ SENTRY_DSN=
|
|
|
709
674
|
} else if (telemetry === "discord") {
|
|
710
675
|
env += `
|
|
711
676
|
# Discord Webhook URL
|
|
712
|
-
|
|
677
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
713
678
|
`;
|
|
714
679
|
}
|
|
715
680
|
|
|
@@ -733,7 +698,7 @@ SENTRY_DSN=
|
|
|
733
698
|
} else if (telemetry === "discord") {
|
|
734
699
|
env += `
|
|
735
700
|
# Discord Webhook URL
|
|
736
|
-
|
|
701
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
737
702
|
`;
|
|
738
703
|
}
|
|
739
704
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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";
|
|
@@ -28,79 +28,104 @@ 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
|
-
|
|
84
|
-
|
|
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
|
+
}
|
|
85
105
|
|
|
86
106
|
if (installDeps) {
|
|
87
107
|
const s2 = p.spinner();
|
|
88
108
|
s2.start("Installing dependencies");
|
|
89
109
|
try {
|
|
90
110
|
await new Promise<void>((resolve, reject) => {
|
|
91
|
-
const child = spawn(
|
|
111
|
+
const child = spawn("npm", ["install"], {
|
|
92
112
|
cwd: targetDir,
|
|
93
|
-
stdio: "
|
|
113
|
+
stdio: "inherit",
|
|
114
|
+
shell: true
|
|
94
115
|
});
|
|
95
116
|
child.on("close", (code) => {
|
|
96
117
|
if (code === 0) resolve();
|
|
97
118
|
else reject(new Error(`npm install exited with code ${code}`));
|
|
98
119
|
});
|
|
99
|
-
child.on("error",
|
|
120
|
+
child.on("error", (err) => {
|
|
121
|
+
p.cancel(`Failed to run npm install: ${err.message}`);
|
|
122
|
+
reject(err);
|
|
123
|
+
});
|
|
100
124
|
});
|
|
101
125
|
s2.stop("Dependencies installed");
|
|
102
|
-
} catch {
|
|
126
|
+
} catch (err) {
|
|
103
127
|
s2.stop("Install failed — run npm install manually");
|
|
128
|
+
console.error(err);
|
|
104
129
|
}
|
|
105
130
|
}
|
|
106
131
|
|