create-sprint 0.0.54 → 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 +40 -51
- package/dist/index.js +54 -33
- package/package.json +1 -1
- package/src/generators.ts +40 -53
- package/src/index.ts +73 -47
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, 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
|
-
}
|
|
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, 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
|
-
}
|
|
233
|
+
return res.json(req.custom.user);
|
|
246
234
|
};
|
|
247
235
|
`;
|
|
248
236
|
}
|
|
@@ -271,7 +259,7 @@ 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
264
|
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
277
265
|
res.json({ token });
|
|
@@ -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) => {
|
|
@@ -313,6 +302,7 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
313
302
|
}
|
|
314
303
|
};
|
|
315
304
|
`;
|
|
305
|
+
}
|
|
316
306
|
}
|
|
317
307
|
export function getHomeSchema(language) {
|
|
318
308
|
if (language === "typescript") {
|
|
@@ -426,7 +416,7 @@ export default defineMiddleware({
|
|
|
426
416
|
|
|
427
417
|
const token = auth.replace("Bearer ", "");
|
|
428
418
|
|
|
429
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
419
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
430
420
|
|
|
431
421
|
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
432
422
|
|
|
@@ -499,8 +489,7 @@ CMD ["npm", "start"]
|
|
|
499
489
|
`;
|
|
500
490
|
}
|
|
501
491
|
export function getDockerCompose(language) {
|
|
502
|
-
return `
|
|
503
|
-
|
|
492
|
+
return `
|
|
504
493
|
services:
|
|
505
494
|
app:
|
|
506
495
|
build: .
|
|
@@ -508,7 +497,7 @@ services:
|
|
|
508
497
|
- "3000:3000"
|
|
509
498
|
environment:
|
|
510
499
|
- NODE_ENV=production
|
|
511
|
-
- PORT=
|
|
500
|
+
- PORT=5000
|
|
512
501
|
restart: unless-stopped
|
|
513
502
|
`;
|
|
514
503
|
}
|
|
@@ -600,7 +589,7 @@ initTelemetry({
|
|
|
600
589
|
|
|
601
590
|
initTelemetry({
|
|
602
591
|
provider: "discord",
|
|
603
|
-
webhookUrl: process.env.
|
|
592
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
604
593
|
});
|
|
605
594
|
`;
|
|
606
595
|
}
|
|
@@ -627,7 +616,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
627
616
|
|
|
628
617
|
initTelemetry({
|
|
629
618
|
provider: "discord",
|
|
630
|
-
webhookUrl: process.env.
|
|
619
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
631
620
|
});
|
|
632
621
|
`;
|
|
633
622
|
}
|
|
@@ -648,7 +637,7 @@ SENTRY_DSN=
|
|
|
648
637
|
else if (telemetry === "discord") {
|
|
649
638
|
env += `
|
|
650
639
|
# Discord Webhook URL for error notifications
|
|
651
|
-
|
|
640
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
652
641
|
`;
|
|
653
642
|
}
|
|
654
643
|
return env;
|
|
@@ -673,7 +662,7 @@ SENTRY_DSN=
|
|
|
673
662
|
else if (telemetry === "discord") {
|
|
674
663
|
env += `
|
|
675
664
|
# Discord Webhook URL
|
|
676
|
-
|
|
665
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
677
666
|
`;
|
|
678
667
|
}
|
|
679
668
|
return env;
|
|
@@ -695,7 +684,7 @@ SENTRY_DSN=
|
|
|
695
684
|
else if (telemetry === "discord") {
|
|
696
685
|
env += `
|
|
697
686
|
# Discord Webhook URL
|
|
698
|
-
|
|
687
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
699
688
|
`;
|
|
700
689
|
}
|
|
701
690
|
return env;
|
package/dist/index.js
CHANGED
|
@@ -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} && `;
|
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, 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
|
-
}
|
|
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, 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
|
-
}
|
|
251
|
+
return res.json(req.custom.user);
|
|
264
252
|
};
|
|
265
253
|
`;
|
|
266
254
|
}
|
|
@@ -290,7 +278,7 @@ 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
283
|
const token = signEncrypted(payload, privateKey, encryptionSecret, { expiresIn: "1h" });
|
|
296
284
|
res.json({ token });
|
|
@@ -299,8 +287,8 @@ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintRe
|
|
|
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) => {
|
|
@@ -332,7 +320,7 @@ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) =
|
|
|
332
320
|
}
|
|
333
321
|
};
|
|
334
322
|
`;
|
|
335
|
-
|
|
323
|
+
}
|
|
336
324
|
}
|
|
337
325
|
|
|
338
326
|
export function getHomeSchema(language: string) {
|
|
@@ -450,7 +438,7 @@ export default defineMiddleware({
|
|
|
450
438
|
|
|
451
439
|
const token = auth.replace("Bearer ", "");
|
|
452
440
|
|
|
453
|
-
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
441
|
+
const decoded = verifyEncrypted(token, publicKey, encryptionSecret);
|
|
454
442
|
|
|
455
443
|
if (!decoded) return res.status(403).json({ error: "Invalid token" });
|
|
456
444
|
|
|
@@ -525,8 +513,7 @@ CMD ["npm", "start"]
|
|
|
525
513
|
}
|
|
526
514
|
|
|
527
515
|
export function getDockerCompose(language: string) {
|
|
528
|
-
return `
|
|
529
|
-
|
|
516
|
+
return `
|
|
530
517
|
services:
|
|
531
518
|
app:
|
|
532
519
|
build: .
|
|
@@ -534,7 +521,7 @@ services:
|
|
|
534
521
|
- "3000:3000"
|
|
535
522
|
environment:
|
|
536
523
|
- NODE_ENV=production
|
|
537
|
-
- PORT=
|
|
524
|
+
- PORT=5000
|
|
538
525
|
restart: unless-stopped
|
|
539
526
|
`;
|
|
540
527
|
}
|
|
@@ -629,7 +616,7 @@ initTelemetry({
|
|
|
629
616
|
|
|
630
617
|
initTelemetry({
|
|
631
618
|
provider: "discord",
|
|
632
|
-
webhookUrl: process.env.
|
|
619
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
633
620
|
});
|
|
634
621
|
`;
|
|
635
622
|
}
|
|
@@ -658,7 +645,7 @@ import { initTelemetry } from "sprint-es/telemetry";
|
|
|
658
645
|
|
|
659
646
|
initTelemetry({
|
|
660
647
|
provider: "discord",
|
|
661
|
-
webhookUrl: process.env.
|
|
648
|
+
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
662
649
|
});
|
|
663
650
|
`;
|
|
664
651
|
}
|
|
@@ -681,7 +668,7 @@ SENTRY_DSN=
|
|
|
681
668
|
} else if (telemetry === "discord") {
|
|
682
669
|
env += `
|
|
683
670
|
# Discord Webhook URL for error notifications
|
|
684
|
-
|
|
671
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
685
672
|
`;
|
|
686
673
|
}
|
|
687
674
|
|
|
@@ -709,7 +696,7 @@ SENTRY_DSN=
|
|
|
709
696
|
} else if (telemetry === "discord") {
|
|
710
697
|
env += `
|
|
711
698
|
# Discord Webhook URL
|
|
712
|
-
|
|
699
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
713
700
|
`;
|
|
714
701
|
}
|
|
715
702
|
|
|
@@ -733,7 +720,7 @@ SENTRY_DSN=
|
|
|
733
720
|
} else if (telemetry === "discord") {
|
|
734
721
|
env += `
|
|
735
722
|
# Discord Webhook URL
|
|
736
|
-
|
|
723
|
+
DISCORD_TELEMETRY_WEBHOOK_URL=
|
|
737
724
|
`;
|
|
738
725
|
}
|
|
739
726
|
|
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,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
|
|