create-sprint 0.0.46 → 0.0.52

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.
@@ -1,11 +1,11 @@
1
- import { generateKeyPairSync } from "node:crypto";
1
+ import * as crypto from "node:crypto";
2
2
  export function generateJWTKeys() {
3
- const { publicKey, privateKey } = generateKeyPairSync("ec", {
4
- namedCurve: "prime256v1",
3
+ const keys = crypto.generateKeyPairSync("rsa", {
4
+ modulusLength: 4096,
5
5
  publicKeyEncoding: { type: "spki", format: "pem" },
6
6
  privateKeyEncoding: { type: "pkcs8", format: "pem" }
7
7
  });
8
- return { publicKey, privateKey };
8
+ return keys;
9
9
  }
10
10
  export function getTypeScriptPackageJson(name, telemetry) {
11
11
  const deps = {
@@ -130,22 +130,24 @@ export function getHomeRoute(language) {
130
130
  if (language === "typescript") {
131
131
  return `import { Router } from "sprint-es";
132
132
  import { homeSchema } from "@/schemas/home";
133
- import { homeController } from "@/controllers/home";
133
+ import { homeController, jwtValidateController } from "@/controllers/home";
134
134
 
135
135
  const router = Router();
136
136
 
137
137
  router.get("/", homeSchema, homeController);
138
+ router.post("/jwt/validate", jwtValidateController);
138
139
 
139
140
  export default router;
140
141
  `;
141
142
  }
142
143
  return `import { Router } from "sprint-es";
143
144
  import { homeSchema } from "../schemas/home.js";
144
- import { homeController } from "../controllers/home.js";
145
+ import { homeController, jwtValidateController } from "../controllers/home.js";
145
146
 
146
147
  const router = Router();
147
148
 
148
149
  router.get("/", homeSchema, homeController);
150
+ router.post("/jwt/validate", jwtValidateController);
149
151
 
150
152
  export default router;
151
153
  `;
@@ -153,68 +155,110 @@ export default router;
153
155
  export function getAdminRoute(language) {
154
156
  if (language === "typescript") {
155
157
  return `import { Router } from "sprint-es";
156
- import { adminSchema } from "@/schemas/admin";
157
- import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "@/controllers/admin";
158
+ import { adminSchema, jwtGenerateSchema } from "@/schemas/admin";
159
+ import { adminController, adminUsersController, jwtGenerateController } from "@/controllers/admin";
158
160
 
159
161
  const router = Router();
160
162
 
161
163
  router.get("/", adminSchema, adminController);
162
164
  router.get("/users", adminSchema, adminUsersController);
163
- router.post("/jwt/generate", jwtGenerateController);
164
- router.post("/jwt/validate", jwtValidateController);
165
+ router.post("/jwt/generate", jwtGenerateSchema, jwtGenerateController);
165
166
 
166
167
  export default router;
167
168
  `;
168
169
  }
169
170
  return `import { Router } from "sprint-es";
170
- import { adminSchema } from "../schemas/admin.js";
171
- import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "../controllers/admin.js";
171
+ import { adminSchema, jwtGenerateSchema } from "../schemas/admin.js";
172
+ import { adminController, adminUsersController, jwtGenerateController } from "../controllers/admin.js";
172
173
 
173
174
  const router = Router();
174
175
 
175
176
  router.get("/", adminSchema, adminController);
176
177
  router.get("/users", adminSchema, adminUsersController);
177
- router.post("/jwt/generate", jwtGenerateController);
178
- router.post("/jwt/validate", jwtValidateController);
178
+ router.post("/jwt/generate", jwtGenerateSchema, jwtGenerateController);
179
179
 
180
180
  export default router;
181
181
  `;
182
182
  }
183
183
  export function getHomeController(language) {
184
184
  if (language === "typescript") {
185
- return `import { Handler } from "sprint-es";
185
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
186
+ import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
186
187
 
187
- export const homeController: Handler = (req, res) => {
188
+ export const homeController: Handler = (req: SprintRequest, res: SprintResponse) => {
188
189
  res.json({
189
190
  message: "Hello World",
190
191
  status: "ok"
191
192
  });
192
193
  };
194
+
195
+ export const jwtValidateController: Handler = (req: SprintRequest, res: SprintResponse) => {
196
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
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
+ }
214
+ };
193
215
  `;
194
216
  }
195
- return `import { Handler } from "sprint-es";
217
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
218
+ import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
196
219
 
197
- export const homeController = (req, res) => {
220
+ export const homeController = (req: SprintRequest, res: SprintResponse) => {
198
221
  res.json({
199
222
  message: "Hello World",
200
223
  status: "ok"
201
224
  });
202
225
  };
226
+
227
+ export const jwtValidateController = (req: SprintRequest, res: SprintResponse) => {
228
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
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
+ }
246
+ };
203
247
  `;
204
248
  }
205
249
  export function getAdminController(language) {
206
250
  if (language === "typescript") {
207
- return `import { Handler } from "sprint-es";
208
- import { signEncrypted, verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
251
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
252
+ import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
209
253
 
210
- export const adminController: Handler = (req, res) => {
254
+ export const adminController: Handler = (req: SprintRequest, res: SprintResponse) => {
211
255
  res.json({
212
256
  message: "Admin Dashboard",
213
257
  status: "ok"
214
258
  });
215
259
  };
216
260
 
217
- export const adminUsersController: Handler = (req, res) => {
261
+ export const adminUsersController: Handler = (req: SprintRequest, res: SprintResponse) => {
218
262
  res.json({
219
263
  users: [
220
264
  { id: 1, name: "John Doe", role: "admin" },
@@ -223,13 +267,9 @@ export const adminUsersController: Handler = (req, res) => {
223
267
  });
224
268
  };
225
269
 
226
- export const jwtGenerateController: Handler = (req, res) => {
270
+ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintResponse) => {
227
271
  const { userId, role } = req.body || {};
228
272
 
229
- if (!userId) {
230
- return res.status(400).json({ error: "userId is required" });
231
- }
232
-
233
273
  try {
234
274
  const { privateKey } = getJwtFromEnv();
235
275
  const payload = { userId, role: role || "user" };
@@ -239,40 +279,19 @@ export const jwtGenerateController: Handler = (req, res) => {
239
279
  return res.status(500).json({ error: "JWT not configured" });
240
280
  }
241
281
  };
242
-
243
- export const jwtValidateController: Handler = (req, res) => {
244
- const token = req.sprint?.getAuthorization() || req.headers.authorization;
245
-
246
- if (!token) {
247
- return res.status(401).json({ error: "No token provided" });
248
- }
249
-
250
- try {
251
- const { publicKey } = getJwtFromEnv();
252
- const decoded = verifyEncrypted(token, publicKey);
253
-
254
- if (!decoded) {
255
- return res.status(401).json({ error: "Invalid token" });
256
- }
257
-
258
- res.json({ valid: true, payload: decoded });
259
- } catch (error) {
260
- return res.status(500).json({ error: "JWT not configured" });
261
- }
262
- };
263
282
  `;
264
283
  }
265
- return `import { Handler } from "sprint-es";
266
- import { signEncrypted, verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
284
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
285
+ import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
267
286
 
268
- export const adminController = (req, res) => {
287
+ export const adminController = (req: SprintRequest, res: SprintResponse) => {
269
288
  res.json({
270
289
  message: "Admin Dashboard",
271
290
  status: "ok"
272
291
  });
273
292
  };
274
293
 
275
- export const adminUsersController = (req, res) => {
294
+ export const adminUsersController = (req: SprintRequest, res: SprintResponse) => {
276
295
  res.json({
277
296
  users: [
278
297
  { id: 1, name: "John Doe", role: "admin" },
@@ -281,13 +300,9 @@ export const adminUsersController = (req, res) => {
281
300
  });
282
301
  };
283
302
 
284
- export const jwtGenerateController = (req, res) => {
303
+ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) => {
285
304
  const { userId, role } = req.body || {};
286
305
 
287
- if (!userId) {
288
- return res.status(400).json({ error: "userId is required" });
289
- }
290
-
291
306
  try {
292
307
  const { privateKey } = getJwtFromEnv();
293
308
  const payload = { userId, role: role || "user" };
@@ -297,27 +312,6 @@ export const jwtGenerateController = (req, res) => {
297
312
  return res.status(500).json({ error: "JWT not configured" });
298
313
  }
299
314
  };
300
-
301
- export const jwtValidateController = (req, res) => {
302
- const token = req.sprint?.getAuthorization() || req.headers.authorization;
303
-
304
- if (!token) {
305
- return res.status(401).json({ error: "No token provided" });
306
- }
307
-
308
- try {
309
- const { publicKey } = getJwtFromEnv();
310
- const decoded = verifyEncrypted(token, publicKey);
311
-
312
- if (!decoded) {
313
- return res.status(401).json({ error: "Invalid token" });
314
- }
315
-
316
- res.json({ valid: true, payload: decoded });
317
- } catch (error) {
318
- return res.status(500).json({ error: "JWT not configured" });
319
- }
320
- };
321
315
  `;
322
316
  }
323
317
  export function getHomeSchema(language) {
@@ -345,6 +339,13 @@ export const adminSchema = defineRouteSchema({
345
339
  email: z.string().email().optional()
346
340
  })
347
341
  });
342
+
343
+ export const jwtGenerateSchema = defineRouteSchema({
344
+ body: z.object({
345
+ userId: z.string().min(1),
346
+ role: z.string().optional()
347
+ })
348
+ });
348
349
  `;
349
350
  }
350
351
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
@@ -358,6 +359,13 @@ export const adminSchema = defineRouteSchema({
358
359
  email: z.string().email().optional()
359
360
  })
360
361
  });
362
+
363
+ export const jwtGenerateSchema = defineRouteSchema({
364
+ body: z.object({
365
+ userId: z.string().min(1),
366
+ role: z.string().optional()
367
+ })
368
+ });
361
369
  `;
362
370
  }
363
371
  export function getAuthMiddleware(language) {
@@ -580,7 +588,7 @@ initTelemetry({
580
588
  return config;
581
589
  }
582
590
  export function getEnvExample(telemetry) {
583
- let env = `PORT=3000
591
+ let env = `PORT=5000
584
592
 
585
593
  # Development: npm run dev (NODE_ENV=development)
586
594
  # Production: npm start (NODE_ENV=production)
@@ -599,12 +607,16 @@ DISCORD_WEBHOOK_URL=
599
607
  }
600
608
  return env;
601
609
  }
610
+ function envKey(key) {
611
+ return key;
612
+ }
602
613
  export function getEnvDevelopment(telemetry) {
603
- const { publicKey, privateKey } = generateJWTKeys();
614
+ const keys = generateJWTKeys();
604
615
  let env = `NODE_ENV=development
605
- PORT=3000
606
- JWT_PUBLIC_KEY=${publicKey.replace(/\n/g, "\\n")}
607
- JWT_PRIVATE_KEY=${privateKey.replace(/\n/g, "\\n")}
616
+ PORT=5000
617
+ JWT_PUBLIC_KEY='${keys.publicKey}'
618
+ JWT_PRIVATE_KEY='${keys.privateKey}'
619
+ JWT_ENCRYPTION_SECRET='${crypto.randomBytes(32).toString("hex")}'
608
620
  `;
609
621
  if (telemetry === "sentry" || telemetry === "glitchtip") {
610
622
  env += `
@@ -621,11 +633,12 @@ DISCORD_WEBHOOK_URL=
621
633
  return env;
622
634
  }
623
635
  export function getEnvProduction(telemetry) {
624
- const { publicKey, privateKey } = generateJWTKeys();
636
+ const keys = generateJWTKeys();
625
637
  let env = `NODE_ENV=production
626
- PORT=3000
627
- JWT_PUBLIC_KEY=${publicKey.replace(/\n/g, "\\n")}
628
- JWT_PRIVATE_KEY=${privateKey.replace(/\n/g, "\\n")}
638
+ PORT=5000
639
+ JWT_PUBLIC_KEY='${keys.publicKey}'
640
+ JWT_PRIVATE_KEY='${keys.privateKey}'
641
+ JWT_ENCRYPTION_SECRET='${crypto.randomBytes(32).toString("hex")}'
629
642
  `;
630
643
  if (telemetry === "sentry" || telemetry === "glitchtip") {
631
644
  env += `
package/dist/index.js CHANGED
@@ -1,13 +1,21 @@
1
1
  import { spawn } from "child_process";
2
2
  import { existsSync } from "fs";
3
- import { mkdir, writeFile } from "fs/promises";
3
+ import { mkdir, writeFile as fsWriteFile } from "fs/promises";
4
4
  import { join } from "path";
5
+ import color from "picocolors";
5
6
  import * as p from "@clack/prompts";
6
7
  import { validateProjectName } from "./validators.js";
7
8
  import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
9
+ export async function writeFile(path, content, options) {
10
+ if (typeof content === "string")
11
+ content = content.trimEnd();
12
+ await fsWriteFile(path, content, options);
13
+ }
14
+ ;
8
15
  export async function runCLI(args) {
9
16
  const options = parseArgs(args);
10
17
  p.intro("Sprint — Quickly API Framework");
18
+ p.intro(`${color.bgCyan(color.black(' create-sprint-app '))}`);
11
19
  const config = await p.group({
12
20
  projectName: () => p.text({
13
21
  message: "Project name:",
@@ -43,12 +51,13 @@ export async function runCLI(args) {
43
51
  await createProject(config.projectName, config.language, config.telemetry, config.docker);
44
52
  s.stop("Project created");
45
53
  const installDeps = await p.confirm({ message: "Install dependencies now?", initialValue: true });
54
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
46
55
  if (installDeps) {
47
56
  const s2 = p.spinner();
48
57
  s2.start("Installing dependencies");
49
58
  try {
50
59
  await new Promise((resolve, reject) => {
51
- const child = spawn("npm", ["install"], {
60
+ const child = spawn(npmCmd, ["install"], {
52
61
  cwd: targetDir,
53
62
  stdio: "pipe"
54
63
  });
@@ -58,6 +67,7 @@ export async function runCLI(args) {
58
67
  else
59
68
  reject(new Error(`npm install exited with code ${code}`));
60
69
  });
70
+ child.on("error", reject);
61
71
  });
62
72
  s2.stop("Dependencies installed");
63
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sprint",
3
- "version": "0.0.46",
3
+ "version": "0.0.52",
4
4
  "description": "Create a new Sprint API project",
5
5
  "type": "module",
6
6
  "bin": {
package/src/generators.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { generateKeyPairSync } from "node:crypto";
1
+ import * as crypto from "node:crypto";
2
2
 
3
3
  export interface JWTKeys {
4
4
  publicKey: string;
@@ -6,12 +6,12 @@ export interface JWTKeys {
6
6
  }
7
7
 
8
8
  export function generateJWTKeys(): JWTKeys {
9
- const { publicKey, privateKey } = generateKeyPairSync("ec", {
10
- namedCurve: "prime256v1",
9
+ const keys = crypto.generateKeyPairSync("rsa", {
10
+ modulusLength: 4096,
11
11
  publicKeyEncoding: { type: "spki", format: "pem" },
12
12
  privateKeyEncoding: { type: "pkcs8", format: "pem" }
13
- });
14
- return { publicKey, privateKey };
13
+ }) as unknown as { publicKey: string; privateKey: string };
14
+ return keys;
15
15
  }
16
16
 
17
17
  export function getTypeScriptPackageJson(name: string, telemetry: string) {
@@ -146,22 +146,24 @@ export function getHomeRoute(language: string) {
146
146
  if (language === "typescript") {
147
147
  return `import { Router } from "sprint-es";
148
148
  import { homeSchema } from "@/schemas/home";
149
- import { homeController } from "@/controllers/home";
149
+ import { homeController, jwtValidateController } from "@/controllers/home";
150
150
 
151
151
  const router = Router();
152
152
 
153
153
  router.get("/", homeSchema, homeController);
154
+ router.post("/jwt/validate", jwtValidateController);
154
155
 
155
156
  export default router;
156
157
  `;
157
158
  }
158
159
  return `import { Router } from "sprint-es";
159
160
  import { homeSchema } from "../schemas/home.js";
160
- import { homeController } from "../controllers/home.js";
161
+ import { homeController, jwtValidateController } from "../controllers/home.js";
161
162
 
162
163
  const router = Router();
163
164
 
164
165
  router.get("/", homeSchema, homeController);
166
+ router.post("/jwt/validate", jwtValidateController);
165
167
 
166
168
  export default router;
167
169
  `;
@@ -170,29 +172,27 @@ export default router;
170
172
  export function getAdminRoute(language: string) {
171
173
  if (language === "typescript") {
172
174
  return `import { Router } from "sprint-es";
173
- import { adminSchema } from "@/schemas/admin";
174
- import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "@/controllers/admin";
175
+ import { adminSchema, jwtGenerateSchema } from "@/schemas/admin";
176
+ import { adminController, adminUsersController, jwtGenerateController } from "@/controllers/admin";
175
177
 
176
178
  const router = Router();
177
179
 
178
180
  router.get("/", adminSchema, adminController);
179
181
  router.get("/users", adminSchema, adminUsersController);
180
- router.post("/jwt/generate", jwtGenerateController);
181
- router.post("/jwt/validate", jwtValidateController);
182
+ router.post("/jwt/generate", jwtGenerateSchema, jwtGenerateController);
182
183
 
183
184
  export default router;
184
185
  `;
185
186
  }
186
187
  return `import { Router } from "sprint-es";
187
- import { adminSchema } from "../schemas/admin.js";
188
- import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "../controllers/admin.js";
188
+ import { adminSchema, jwtGenerateSchema } from "../schemas/admin.js";
189
+ import { adminController, adminUsersController, jwtGenerateController } from "../controllers/admin.js";
189
190
 
190
191
  const router = Router();
191
192
 
192
193
  router.get("/", adminSchema, adminController);
193
194
  router.get("/users", adminSchema, adminUsersController);
194
- router.post("/jwt/generate", jwtGenerateController);
195
- router.post("/jwt/validate", jwtValidateController);
195
+ router.post("/jwt/generate", jwtGenerateSchema, jwtGenerateController);
196
196
 
197
197
  export default router;
198
198
  `;
@@ -200,40 +200,84 @@ export default router;
200
200
 
201
201
  export function getHomeController(language: string) {
202
202
  if (language === "typescript") {
203
- return `import { Handler } from "sprint-es";
203
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
204
+ import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
204
205
 
205
- export const homeController: Handler = (req, res) => {
206
+ export const homeController: Handler = (req: SprintRequest, res: SprintResponse) => {
206
207
  res.json({
207
208
  message: "Hello World",
208
209
  status: "ok"
209
210
  });
210
211
  };
212
+
213
+ export const jwtValidateController: Handler = (req: SprintRequest, res: SprintResponse) => {
214
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
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
+ }
232
+ };
211
233
  `;
212
234
  }
213
- return `import { Handler } from "sprint-es";
235
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
236
+ import { verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
214
237
 
215
- export const homeController = (req, res) => {
238
+ export const homeController = (req: SprintRequest, res: SprintResponse) => {
216
239
  res.json({
217
240
  message: "Hello World",
218
241
  status: "ok"
219
242
  });
220
243
  };
244
+
245
+ export const jwtValidateController = (req: SprintRequest, res: SprintResponse) => {
246
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
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
+ }
264
+ };
221
265
  `;
222
266
  }
223
267
 
224
268
  export function getAdminController(language: string) {
225
269
  if (language === "typescript") {
226
- return `import { Handler } from "sprint-es";
227
- import { signEncrypted, verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
270
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
271
+ import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
228
272
 
229
- export const adminController: Handler = (req, res) => {
273
+ export const adminController: Handler = (req: SprintRequest, res: SprintResponse) => {
230
274
  res.json({
231
275
  message: "Admin Dashboard",
232
276
  status: "ok"
233
277
  });
234
278
  };
235
279
 
236
- export const adminUsersController: Handler = (req, res) => {
280
+ export const adminUsersController: Handler = (req: SprintRequest, res: SprintResponse) => {
237
281
  res.json({
238
282
  users: [
239
283
  { id: 1, name: "John Doe", role: "admin" },
@@ -242,13 +286,9 @@ export const adminUsersController: Handler = (req, res) => {
242
286
  });
243
287
  };
244
288
 
245
- export const jwtGenerateController: Handler = (req, res) => {
289
+ export const jwtGenerateController: Handler = (req: SprintRequest, res: SprintResponse) => {
246
290
  const { userId, role } = req.body || {};
247
291
 
248
- if (!userId) {
249
- return res.status(400).json({ error: "userId is required" });
250
- }
251
-
252
292
  try {
253
293
  const { privateKey } = getJwtFromEnv();
254
294
  const payload = { userId, role: role || "user" };
@@ -258,40 +298,19 @@ export const jwtGenerateController: Handler = (req, res) => {
258
298
  return res.status(500).json({ error: "JWT not configured" });
259
299
  }
260
300
  };
261
-
262
- export const jwtValidateController: Handler = (req, res) => {
263
- const token = req.sprint?.getAuthorization() || req.headers.authorization;
264
-
265
- if (!token) {
266
- return res.status(401).json({ error: "No token provided" });
267
- }
268
-
269
- try {
270
- const { publicKey } = getJwtFromEnv();
271
- const decoded = verifyEncrypted(token, publicKey);
272
-
273
- if (!decoded) {
274
- return res.status(401).json({ error: "Invalid token" });
275
- }
276
-
277
- res.json({ valid: true, payload: decoded });
278
- } catch (error) {
279
- return res.status(500).json({ error: "JWT not configured" });
280
- }
281
- };
282
301
  `;
283
302
  }
284
- return `import { Handler } from "sprint-es";
285
- import { signEncrypted, verifyEncrypted, getJwtFromEnv } from "sprint-es/jwt";
303
+ return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
304
+ import { signEncrypted, getJwtFromEnv } from "sprint-es/jwt";
286
305
 
287
- export const adminController = (req, res) => {
306
+ export const adminController = (req: SprintRequest, res: SprintResponse) => {
288
307
  res.json({
289
308
  message: "Admin Dashboard",
290
309
  status: "ok"
291
310
  });
292
311
  };
293
312
 
294
- export const adminUsersController = (req, res) => {
313
+ export const adminUsersController = (req: SprintRequest, res: SprintResponse) => {
295
314
  res.json({
296
315
  users: [
297
316
  { id: 1, name: "John Doe", role: "admin" },
@@ -300,13 +319,9 @@ export const adminUsersController = (req, res) => {
300
319
  });
301
320
  };
302
321
 
303
- export const jwtGenerateController = (req, res) => {
322
+ export const jwtGenerateController = (req: SprintRequest, res: SprintResponse) => {
304
323
  const { userId, role } = req.body || {};
305
324
 
306
- if (!userId) {
307
- return res.status(400).json({ error: "userId is required" });
308
- }
309
-
310
325
  try {
311
326
  const { privateKey } = getJwtFromEnv();
312
327
  const payload = { userId, role: role || "user" };
@@ -316,28 +331,8 @@ export const jwtGenerateController = (req, res) => {
316
331
  return res.status(500).json({ error: "JWT not configured" });
317
332
  }
318
333
  };
319
-
320
- export const jwtValidateController = (req, res) => {
321
- const token = req.sprint?.getAuthorization() || req.headers.authorization;
322
-
323
- if (!token) {
324
- return res.status(401).json({ error: "No token provided" });
325
- }
326
-
327
- try {
328
- const { publicKey } = getJwtFromEnv();
329
- const decoded = verifyEncrypted(token, publicKey);
330
-
331
- if (!decoded) {
332
- return res.status(401).json({ error: "Invalid token" });
333
- }
334
-
335
- res.json({ valid: true, payload: decoded });
336
- } catch (error) {
337
- return res.status(500).json({ error: "JWT not configured" });
338
- }
339
- };
340
334
  `;
335
+
341
336
  }
342
337
 
343
338
  export function getHomeSchema(language: string) {
@@ -366,6 +361,13 @@ export const adminSchema = defineRouteSchema({
366
361
  email: z.string().email().optional()
367
362
  })
368
363
  });
364
+
365
+ export const jwtGenerateSchema = defineRouteSchema({
366
+ body: z.object({
367
+ userId: z.string().min(1),
368
+ role: z.string().optional()
369
+ })
370
+ });
369
371
  `;
370
372
  }
371
373
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
@@ -379,6 +381,13 @@ export const adminSchema = defineRouteSchema({
379
381
  email: z.string().email().optional()
380
382
  })
381
383
  });
384
+
385
+ export const jwtGenerateSchema = defineRouteSchema({
386
+ body: z.object({
387
+ userId: z.string().min(1),
388
+ role: z.string().optional()
389
+ })
390
+ });
382
391
  `;
383
392
  }
384
393
 
@@ -611,7 +620,7 @@ initTelemetry({
611
620
  }
612
621
 
613
622
  export function getEnvExample(telemetry: string) {
614
- let env = `PORT=3000
623
+ let env = `PORT=5000
615
624
 
616
625
  # Development: npm run dev (NODE_ENV=development)
617
626
  # Production: npm start (NODE_ENV=production)
@@ -632,12 +641,17 @@ DISCORD_WEBHOOK_URL=
632
641
  return env;
633
642
  }
634
643
 
644
+ function envKey(key: any): string {
645
+ return key;
646
+ }
647
+
635
648
  export function getEnvDevelopment(telemetry: string) {
636
- const { publicKey, privateKey } = generateJWTKeys();
649
+ const keys = generateJWTKeys();
637
650
  let env = `NODE_ENV=development
638
- PORT=3000
639
- JWT_PUBLIC_KEY=${publicKey.replace(/\n/g, "\\n")}
640
- JWT_PRIVATE_KEY=${privateKey.replace(/\n/g, "\\n")}
651
+ PORT=5000
652
+ JWT_PUBLIC_KEY='${keys.publicKey}'
653
+ JWT_PRIVATE_KEY='${keys.privateKey}'
654
+ JWT_ENCRYPTION_SECRET='${crypto.randomBytes(32).toString("hex")}'
641
655
  `;
642
656
 
643
657
  if (telemetry === "sentry" || telemetry === "glitchtip") {
@@ -656,11 +670,12 @@ DISCORD_WEBHOOK_URL=
656
670
  }
657
671
 
658
672
  export function getEnvProduction(telemetry: string) {
659
- const { publicKey, privateKey } = generateJWTKeys();
673
+ const keys = generateJWTKeys();
660
674
  let env = `NODE_ENV=production
661
- PORT=3000
662
- JWT_PUBLIC_KEY=${publicKey.replace(/\n/g, "\\n")}
663
- JWT_PRIVATE_KEY=${privateKey.replace(/\n/g, "\\n")}
675
+ PORT=5000
676
+ JWT_PUBLIC_KEY='${keys.publicKey}'
677
+ JWT_PRIVATE_KEY='${keys.privateKey}'
678
+ JWT_ENCRYPTION_SECRET='${crypto.randomBytes(32).toString("hex")}'
664
679
  `;
665
680
 
666
681
  if (telemetry === "sentry" || telemetry === "glitchtip") {
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { spawn } from "child_process";
2
- import { existsSync } from "fs";
3
- import { mkdir, writeFile } from "fs/promises";
2
+ import { existsSync, writeFileSync } from "fs";
3
+ import { mkdir, writeFile as fsWriteFile } from "fs/promises";
4
4
  import { join } from "path";
5
+ import color from "picocolors";
5
6
  import * as p from "@clack/prompts";
6
7
  import { validateProjectName } from "./validators.js";
7
8
  import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
@@ -15,11 +16,18 @@ export interface CLIOptions {
15
16
  skipPrompts?: boolean;
16
17
  }
17
18
 
19
+ export async function writeFile(path: string, content: string, options?: any) {
20
+ if (typeof content === "string") content = content.trimEnd();
21
+ await fsWriteFile(path, content, options);
22
+ };
23
+
18
24
  export async function runCLI(args: string[]) {
19
25
  const options = parseArgs(args);
20
26
 
21
27
  p.intro("Sprint — Quickly API Framework");
22
28
 
29
+ p.intro(`${color.bgCyan(color.black(' create-sprint-app '))}`);
30
+
23
31
  const config = await p.group(
24
32
  {
25
33
  projectName: () =>
@@ -73,13 +81,14 @@ export async function runCLI(args: string[]) {
73
81
  s.stop("Project created");
74
82
 
75
83
  const installDeps = await p.confirm({ message: "Install dependencies now?", initialValue: true });
84
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
76
85
 
77
86
  if (installDeps) {
78
87
  const s2 = p.spinner();
79
88
  s2.start("Installing dependencies");
80
89
  try {
81
90
  await new Promise<void>((resolve, reject) => {
82
- const child = spawn("npm", ["install"], {
91
+ const child = spawn(npmCmd, ["install"], {
83
92
  cwd: targetDir,
84
93
  stdio: "pipe"
85
94
  });
@@ -87,6 +96,7 @@ export async function runCLI(args: string[]) {
87
96
  if (code === 0) resolve();
88
97
  else reject(new Error(`npm install exited with code ${code}`));
89
98
  });
99
+ child.on("error", reject);
90
100
  });
91
101
  s2.stop("Dependencies installed");
92
102
  } catch {