create-sprint 0.0.34 → 0.0.38

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,8 +1,10 @@
1
+ import { randomBytes } from "node:crypto";
2
+ export function generateJWT_SECRET() {
3
+ return randomBytes(32).toString("hex");
4
+ }
1
5
  export function getTypeScriptPackageJson(name, telemetry) {
2
6
  const deps = {
3
- "sprint-es": "^0.0.35",
4
- "node-cron": "^3.0.3",
5
- dotenv: "^17.0.0",
7
+ "sprint-es": "^0.0.37",
6
8
  };
7
9
  const devDeps = {
8
10
  "@types/node": "^22.0.0",
@@ -24,6 +26,7 @@ export function getTypeScriptPackageJson(name, telemetry) {
24
26
  build: "sprint-es build",
25
27
  start: "sprint-es start",
26
28
  dev: "sprint-es dev",
29
+ "generate:keys": "sprint-es generate-keys"
27
30
  },
28
31
  dependencies: deps,
29
32
  devDependencies: devDeps,
@@ -31,9 +34,7 @@ export function getTypeScriptPackageJson(name, telemetry) {
31
34
  }
32
35
  export function getJavaScriptPackageJson(name, telemetry) {
33
36
  const deps = {
34
- "sprint-es": "^0.0.35",
35
- "node-cron": "^3.0.3",
36
- dotenv: "^17.0.0",
37
+ "sprint-es": "^0.0.37",
37
38
  };
38
39
  if (telemetry === "sentry" || telemetry === "glitchtip") {
39
40
  deps["@sentry/node"] = "^8.0.0";
@@ -51,6 +52,7 @@ export function getJavaScriptPackageJson(name, telemetry) {
51
52
  build: "sprint-es build",
52
53
  start: "sprint-es start",
53
54
  dev: "sprint-es dev",
55
+ "generate:keys": "sprint-es generate-keys"
54
56
  },
55
57
  dependencies: deps,
56
58
  };
@@ -147,24 +149,28 @@ export function getAdminRoute(language) {
147
149
  if (language === "typescript") {
148
150
  return `import { Router } from "sprint-es";
149
151
  import { adminSchema } from "@/schemas/admin";
150
- import { adminController, adminUsersController } from "@/controllers/admin";
152
+ import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "@/controllers/admin";
151
153
 
152
154
  const router = Router();
153
155
 
154
156
  router.get("/", adminSchema, adminController);
155
157
  router.get("/users", adminSchema, adminUsersController);
158
+ router.post("/jwt/generate", jwtGenerateController);
159
+ router.post("/jwt/validate", jwtValidateController);
156
160
 
157
161
  export default router;
158
162
  `;
159
163
  }
160
164
  return `import { Router } from "sprint-es";
161
165
  import { adminSchema } from "../schemas/admin.js";
162
- import { adminController, adminUsersController } from "../controllers/admin.js";
166
+ import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "../controllers/admin.js";
163
167
 
164
168
  const router = Router();
165
169
 
166
170
  router.get("/", adminSchema, adminController);
167
171
  router.get("/users", adminSchema, adminUsersController);
172
+ router.post("/jwt/generate", jwtGenerateController);
173
+ router.post("/jwt/validate", jwtValidateController);
168
174
 
169
175
  export default router;
170
176
  `;
@@ -194,6 +200,9 @@ export const homeController = (req, res) => {
194
200
  export function getAdminController(language) {
195
201
  if (language === "typescript") {
196
202
  return `import { Handler } from "sprint-es";
203
+ import { signEncrypted, verifyEncrypted } from "sprint-es/jwt";
204
+
205
+ const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key-change-in-production";
197
206
 
198
207
  export const adminController: Handler = (req, res) => {
199
208
  res.json({
@@ -210,9 +219,41 @@ export const adminUsersController: Handler = (req, res) => {
210
219
  ]
211
220
  });
212
221
  };
222
+
223
+ export const jwtGenerateController: Handler = (req, res) => {
224
+ const { userId, role } = req.body || {};
225
+
226
+ if (!userId) {
227
+ return res.status(400).json({ error: "userId is required" });
228
+ }
229
+
230
+ const payload = { userId, role: role || "user" };
231
+ const token = signEncrypted(payload, JWT_SECRET, { expiresIn: "1h" });
232
+
233
+ res.json({ token });
234
+ };
235
+
236
+ export const jwtValidateController: Handler = (req, res) => {
237
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
238
+
239
+ if (!token) {
240
+ return res.status(401).json({ error: "No token provided" });
241
+ }
242
+
243
+ const decoded = verifyEncrypted(token, JWT_SECRET);
244
+
245
+ if (!decoded) {
246
+ return res.status(401).json({ error: "Invalid token" });
247
+ }
248
+
249
+ res.json({ valid: true, payload: decoded });
250
+ };
213
251
  `;
214
252
  }
215
253
  return `import { Handler } from "sprint-es";
254
+ import { signEncrypted, verifyEncrypted } from "sprint-es/jwt";
255
+
256
+ const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key-change-in-production";
216
257
 
217
258
  export const adminController = (req, res) => {
218
259
  res.json({
@@ -229,6 +270,35 @@ export const adminUsersController = (req, res) => {
229
270
  ]
230
271
  });
231
272
  };
273
+
274
+ export const jwtGenerateController = (req, res) => {
275
+ const { userId, role } = req.body || {};
276
+
277
+ if (!userId) {
278
+ return res.status(400).json({ error: "userId is required" });
279
+ }
280
+
281
+ const payload = { userId, role: role || "user" };
282
+ const token = signEncrypted(payload, JWT_SECRET, { expiresIn: "1h" });
283
+
284
+ res.json({ token });
285
+ };
286
+
287
+ export const jwtValidateController = (req, res) => {
288
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
289
+
290
+ if (!token) {
291
+ return res.status(401).json({ error: "No token provided" });
292
+ }
293
+
294
+ const decoded = verifyEncrypted(token, JWT_SECRET);
295
+
296
+ if (!decoded) {
297
+ return res.status(401).json({ error: "Invalid token" });
298
+ }
299
+
300
+ res.json({ valid: true, payload: decoded });
301
+ };
232
302
  `;
233
303
  }
234
304
  export function getHomeSchema(language) {
@@ -280,13 +350,13 @@ export default defineMiddleware({
280
350
  priority: 10,
281
351
  include: "/admin/**",
282
352
  handler: (req, res, next) => {
283
- const authHeader = req.headers.authorization;
353
+ const auth = req.sprint.getAuthorization();
284
354
 
285
- if (!authHeader) {
355
+ if (!auth) {
286
356
  return res.status(401).json({ error: "No authorization header" });
287
357
  }
288
358
 
289
- const token = authHeader.replace("Bearer ", "");
359
+ const token = auth.replace("Bearer ", "");
290
360
 
291
361
  if (token !== "admin-token") {
292
362
  return res.status(403).json({ error: "Invalid token" });
@@ -304,13 +374,13 @@ export default defineMiddleware({
304
374
  priority: 10,
305
375
  include: "/admin/**",
306
376
  handler: (req, res, next) => {
307
- const authHeader = req.headers.authorization;
377
+ const auth = req.sprint.getAuthorization();
308
378
 
309
- if (!authHeader) {
379
+ if (!auth) {
310
380
  return res.status(401).json({ error: "No authorization header" });
311
381
  }
312
382
 
313
- const token = authHeader.replace("Bearer ", "");
383
+ const token = auth.replace("Bearer ", "");
314
384
 
315
385
  if (token !== "admin-token") {
316
386
  return res.status(403).json({ error: "Invalid token" });
@@ -511,8 +581,10 @@ DISCORD_WEBHOOK_URL=
511
581
  return env;
512
582
  }
513
583
  export function getEnvDevelopment(telemetry) {
584
+ const jwtSecret = generateJWT_SECRET();
514
585
  let env = `NODE_ENV=development
515
586
  PORT=3000
587
+ JWT_SECRET=${jwtSecret}
516
588
  `;
517
589
  if (telemetry === "sentry" || telemetry === "glitchtip") {
518
590
  env += `
@@ -529,8 +601,10 @@ DISCORD_WEBHOOK_URL=
529
601
  return env;
530
602
  }
531
603
  export function getEnvProduction(telemetry) {
604
+ const jwtSecret = generateJWT_SECRET();
532
605
  let env = `NODE_ENV=production
533
606
  PORT=3000
607
+ JWT_SECRET=${jwtSecret}
534
608
  `;
535
609
  if (telemetry === "sentry" || telemetry === "glitchtip") {
536
610
  env += `
package/dist/index.js CHANGED
@@ -193,8 +193,8 @@ async function createProject(projectName, language, telemetryArg, useDockerArg)
193
193
  await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
194
194
  await writeFile(join(targetDir, ".env.development.example"), getEnvDevelopment(telemetry));
195
195
  await writeFile(join(targetDir, ".env.production.example"), getEnvProduction(telemetry));
196
- await writeFile(join(targetDir, ".env.development"), "");
197
- await writeFile(join(targetDir, ".env.production"), "");
196
+ await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(telemetry));
197
+ await writeFile(join(targetDir, ".env.production"), getEnvProduction(telemetry));
198
198
  await writeFile(join(targetDir, ".gitignore"), getGitignore());
199
199
  if (useDocker) {
200
200
  await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sprint",
3
- "version": "0.0.34",
3
+ "version": "0.0.38",
4
4
  "description": "Create a new Sprint API project",
5
5
  "type": "module",
6
6
  "bin": {
package/src/generators.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import { randomBytes } from "node:crypto";
2
+
3
+ export function generateJWT_SECRET(): string {
4
+ return randomBytes(32).toString("hex");
5
+ }
6
+
1
7
  export function getTypeScriptPackageJson(name: string, telemetry: string) {
2
8
  const deps: Record<string, string> = {
3
- "sprint-es": "^0.0.35",
4
- "node-cron": "^3.0.3",
5
- dotenv: "^17.0.0",
9
+ "sprint-es": "^0.0.37",
6
10
  };
7
11
 
8
12
  const devDeps: Record<string, string> = {
@@ -26,6 +30,7 @@ export function getTypeScriptPackageJson(name: string, telemetry: string) {
26
30
  build: "sprint-es build",
27
31
  start: "sprint-es start",
28
32
  dev: "sprint-es dev",
33
+ "generate:keys": "sprint-es generate-keys"
29
34
  },
30
35
  dependencies: deps,
31
36
  devDependencies: devDeps,
@@ -34,9 +39,7 @@ export function getTypeScriptPackageJson(name: string, telemetry: string) {
34
39
 
35
40
  export function getJavaScriptPackageJson(name: string, telemetry: string) {
36
41
  const deps: Record<string, string> = {
37
- "sprint-es": "^0.0.35",
38
- "node-cron": "^3.0.3",
39
- dotenv: "^17.0.0",
42
+ "sprint-es": "^0.0.37",
40
43
  };
41
44
 
42
45
  if (telemetry === "sentry" || telemetry === "glitchtip") {
@@ -55,6 +58,7 @@ export function getJavaScriptPackageJson(name: string, telemetry: string) {
55
58
  build: "sprint-es build",
56
59
  start: "sprint-es start",
57
60
  dev: "sprint-es dev",
61
+ "generate:keys": "sprint-es generate-keys"
58
62
  },
59
63
  dependencies: deps,
60
64
  };
@@ -157,24 +161,28 @@ export function getAdminRoute(language: string) {
157
161
  if (language === "typescript") {
158
162
  return `import { Router } from "sprint-es";
159
163
  import { adminSchema } from "@/schemas/admin";
160
- import { adminController, adminUsersController } from "@/controllers/admin";
164
+ import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "@/controllers/admin";
161
165
 
162
166
  const router = Router();
163
167
 
164
168
  router.get("/", adminSchema, adminController);
165
169
  router.get("/users", adminSchema, adminUsersController);
170
+ router.post("/jwt/generate", jwtGenerateController);
171
+ router.post("/jwt/validate", jwtValidateController);
166
172
 
167
173
  export default router;
168
174
  `;
169
175
  }
170
176
  return `import { Router } from "sprint-es";
171
177
  import { adminSchema } from "../schemas/admin.js";
172
- import { adminController, adminUsersController } from "../controllers/admin.js";
178
+ import { adminController, adminUsersController, jwtGenerateController, jwtValidateController } from "../controllers/admin.js";
173
179
 
174
180
  const router = Router();
175
181
 
176
182
  router.get("/", adminSchema, adminController);
177
183
  router.get("/users", adminSchema, adminUsersController);
184
+ router.post("/jwt/generate", jwtGenerateController);
185
+ router.post("/jwt/validate", jwtValidateController);
178
186
 
179
187
  export default router;
180
188
  `;
@@ -206,6 +214,9 @@ export const homeController = (req, res) => {
206
214
  export function getAdminController(language: string) {
207
215
  if (language === "typescript") {
208
216
  return `import { Handler } from "sprint-es";
217
+ import { signEncrypted, verifyEncrypted } from "sprint-es/jwt";
218
+
219
+ const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key-change-in-production";
209
220
 
210
221
  export const adminController: Handler = (req, res) => {
211
222
  res.json({
@@ -222,9 +233,41 @@ export const adminUsersController: Handler = (req, res) => {
222
233
  ]
223
234
  });
224
235
  };
236
+
237
+ export const jwtGenerateController: Handler = (req, res) => {
238
+ const { userId, role } = req.body || {};
239
+
240
+ if (!userId) {
241
+ return res.status(400).json({ error: "userId is required" });
242
+ }
243
+
244
+ const payload = { userId, role: role || "user" };
245
+ const token = signEncrypted(payload, JWT_SECRET, { expiresIn: "1h" });
246
+
247
+ res.json({ token });
248
+ };
249
+
250
+ export const jwtValidateController: Handler = (req, res) => {
251
+ const token = req.sprint?.getAuthorization() || req.headers.authorization;
252
+
253
+ if (!token) {
254
+ return res.status(401).json({ error: "No token provided" });
255
+ }
256
+
257
+ const decoded = verifyEncrypted(token, JWT_SECRET);
258
+
259
+ if (!decoded) {
260
+ return res.status(401).json({ error: "Invalid token" });
261
+ }
262
+
263
+ res.json({ valid: true, payload: decoded });
264
+ };
225
265
  `;
226
266
  }
227
267
  return `import { Handler } from "sprint-es";
268
+ import { signEncrypted, verifyEncrypted } from "sprint-es/jwt";
269
+
270
+ const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key-change-in-production";
228
271
 
229
272
  export const adminController = (req, res) => {
230
273
  res.json({
@@ -241,6 +284,35 @@ export const adminUsersController = (req, res) => {
241
284
  ]
242
285
  });
243
286
  };
287
+
288
+ export const jwtGenerateController = (req, res) => {
289
+ const { userId, role } = req.body || {};
290
+
291
+ if (!userId) {
292
+ return res.status(400).json({ error: "userId is required" });
293
+ }
294
+
295
+ const payload = { userId, role: role || "user" };
296
+ const token = signEncrypted(payload, JWT_SECRET, { expiresIn: "1h" });
297
+
298
+ res.json({ token });
299
+ };
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
+ const decoded = verifyEncrypted(token, JWT_SECRET);
309
+
310
+ if (!decoded) {
311
+ return res.status(401).json({ error: "Invalid token" });
312
+ }
313
+
314
+ res.json({ valid: true, payload: decoded });
315
+ };
244
316
  `;
245
317
  }
246
318
 
@@ -295,13 +367,13 @@ export default defineMiddleware({
295
367
  priority: 10,
296
368
  include: "/admin/**",
297
369
  handler: (req, res, next) => {
298
- const authHeader = req.headers.authorization;
370
+ const auth = req.sprint.getAuthorization();
299
371
 
300
- if (!authHeader) {
372
+ if (!auth) {
301
373
  return res.status(401).json({ error: "No authorization header" });
302
374
  }
303
375
 
304
- const token = authHeader.replace("Bearer ", "");
376
+ const token = auth.replace("Bearer ", "");
305
377
 
306
378
  if (token !== "admin-token") {
307
379
  return res.status(403).json({ error: "Invalid token" });
@@ -319,13 +391,13 @@ export default defineMiddleware({
319
391
  priority: 10,
320
392
  include: "/admin/**",
321
393
  handler: (req, res, next) => {
322
- const authHeader = req.headers.authorization;
394
+ const auth = req.sprint.getAuthorization();
323
395
 
324
- if (!authHeader) {
396
+ if (!auth) {
325
397
  return res.status(401).json({ error: "No authorization header" });
326
398
  }
327
399
 
328
- const token = authHeader.replace("Bearer ", "");
400
+ const token = auth.replace("Bearer ", "");
329
401
 
330
402
  if (token !== "admin-token") {
331
403
  return res.status(403).json({ error: "Invalid token" });
@@ -537,8 +609,10 @@ DISCORD_WEBHOOK_URL=
537
609
  }
538
610
 
539
611
  export function getEnvDevelopment(telemetry: string) {
612
+ const jwtSecret = generateJWT_SECRET();
540
613
  let env = `NODE_ENV=development
541
614
  PORT=3000
615
+ JWT_SECRET=${jwtSecret}
542
616
  `;
543
617
 
544
618
  if (telemetry === "sentry" || telemetry === "glitchtip") {
@@ -557,8 +631,10 @@ DISCORD_WEBHOOK_URL=
557
631
  }
558
632
 
559
633
  export function getEnvProduction(telemetry: string) {
634
+ const jwtSecret = generateJWT_SECRET();
560
635
  let env = `NODE_ENV=production
561
636
  PORT=3000
637
+ JWT_SECRET=${jwtSecret}
562
638
  `;
563
639
 
564
640
  if (telemetry === "sentry" || telemetry === "glitchtip") {
package/src/index.ts CHANGED
@@ -232,8 +232,8 @@ async function createProject(
232
232
  await writeFile(join(targetDir, ".env.development.example"), getEnvDevelopment(telemetry));
233
233
  await writeFile(join(targetDir, ".env.production.example"), getEnvProduction(telemetry));
234
234
 
235
- await writeFile(join(targetDir, ".env.development"), "");
236
- await writeFile(join(targetDir, ".env.production"), "");
235
+ await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(telemetry));
236
+ await writeFile(join(targetDir, ".env.production"), getEnvProduction(telemetry));
237
237
 
238
238
  await writeFile(join(targetDir, ".gitignore"), getGitignore());
239
239