zuro-cli 0.0.2-beta.15 → 0.0.2-beta.17
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/index.js +421 -213
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +422 -214
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -825,6 +825,20 @@ var ENV_CONFIGS = {
|
|
|
825
825
|
{ name: "BETTER_AUTH_URL", schema: "z.string().url()" }
|
|
826
826
|
]
|
|
827
827
|
},
|
|
828
|
+
"auth-jwt": {
|
|
829
|
+
envVars: {
|
|
830
|
+
JWT_ACCESS_SECRET: "your-jwt-access-secret-at-least-32-characters",
|
|
831
|
+
JWT_REFRESH_SECRET: "your-jwt-refresh-secret-at-least-32-characters",
|
|
832
|
+
JWT_ACCESS_EXPIRES_IN: "15m",
|
|
833
|
+
JWT_REFRESH_EXPIRES_IN: "7d"
|
|
834
|
+
},
|
|
835
|
+
schemaFields: [
|
|
836
|
+
{ name: "JWT_ACCESS_SECRET", schema: "z.string().min(32)" },
|
|
837
|
+
{ name: "JWT_REFRESH_SECRET", schema: "z.string().min(32)" },
|
|
838
|
+
{ name: "JWT_ACCESS_EXPIRES_IN", schema: "z.string().min(2)" },
|
|
839
|
+
{ name: "JWT_REFRESH_EXPIRES_IN", schema: "z.string().min(2)" }
|
|
840
|
+
]
|
|
841
|
+
},
|
|
828
842
|
mailer: {
|
|
829
843
|
envVars: {
|
|
830
844
|
SMTP_HOST: "smtp.example.com",
|
|
@@ -1309,57 +1323,93 @@ init_code_inject();
|
|
|
1309
1323
|
async function isAuthModuleInstalled(projectRoot, srcDir) {
|
|
1310
1324
|
return await import_fs_extra8.default.pathExists(import_path9.default.join(projectRoot, srcDir, "lib", "auth.ts"));
|
|
1311
1325
|
}
|
|
1312
|
-
async function
|
|
1326
|
+
async function detectInstalledAuthProvider(projectRoot, srcDir) {
|
|
1327
|
+
const authPath = import_path9.default.join(projectRoot, srcDir, "lib", "auth.ts");
|
|
1328
|
+
if (!await import_fs_extra8.default.pathExists(authPath)) {
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
const authContent = await import_fs_extra8.default.readFile(authPath, "utf-8");
|
|
1332
|
+
if (authContent.includes("better-auth")) {
|
|
1333
|
+
return "better-auth";
|
|
1334
|
+
}
|
|
1335
|
+
if (authContent.includes("jsonwebtoken") || authContent.includes("JWT_ACCESS_SECRET")) {
|
|
1336
|
+
return "jwt";
|
|
1337
|
+
}
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
async function injectAuthRoutes(projectRoot, srcDir, provider) {
|
|
1313
1341
|
const appPath = import_path9.default.join(projectRoot, srcDir, "app.ts");
|
|
1314
1342
|
if (!await import_fs_extra8.default.pathExists(appPath)) {
|
|
1315
1343
|
return false;
|
|
1316
1344
|
}
|
|
1317
1345
|
let appContent = await import_fs_extra8.default.readFile(appPath, "utf-8");
|
|
1318
|
-
const authHandlerImport = `import { toNodeHandler } from "better-auth/node";`;
|
|
1319
|
-
const authImport = `import { auth } from "./lib/auth";`;
|
|
1320
1346
|
const routeIndexUserImport = `import userRoutes from "./user.routes";`;
|
|
1347
|
+
const routeIndexAuthImport = `import authRoutes from "./auth.routes";`;
|
|
1321
1348
|
const appUserImport = `import userRoutes from "./routes/user.routes";`;
|
|
1349
|
+
const appAuthImport = `import authRoutes from "./routes/auth.routes";`;
|
|
1322
1350
|
let appModified = false;
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1351
|
+
if (provider === "better-auth") {
|
|
1352
|
+
const authHandlerImport = `import { toNodeHandler } from "better-auth/node";`;
|
|
1353
|
+
const authImport = `import { auth } from "./lib/auth";`;
|
|
1354
|
+
for (const importLine of [authHandlerImport, authImport]) {
|
|
1355
|
+
const next = appendImport(appContent, importLine);
|
|
1356
|
+
if (!next.inserted) {
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
if (next.source !== appContent) {
|
|
1360
|
+
appContent = next.source;
|
|
1361
|
+
appModified = true;
|
|
1362
|
+
}
|
|
1327
1363
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1364
|
+
const hasAuthMount = /toNodeHandler\(\s*auth\s*\)/.test(appContent) && /\/api\/auth/.test(appContent);
|
|
1365
|
+
if (!hasAuthMount) {
|
|
1366
|
+
const authMountLine = "app.all(/^\\/api\\/auth(?:\\/.*)?$/, toNodeHandler(auth));\n";
|
|
1367
|
+
const jsonIndex = appContent.search(/^\s*app\.use\(\s*express\.json\(\)\s*\);\s*$/m);
|
|
1368
|
+
let insertionIndex = jsonIndex;
|
|
1369
|
+
if (insertionIndex < 0) {
|
|
1370
|
+
const healthIndex = appContent.search(/^\s*app\.get\(\s*["']\/health["']\s*,/m);
|
|
1371
|
+
insertionIndex = healthIndex;
|
|
1372
|
+
}
|
|
1373
|
+
if (insertionIndex < 0) {
|
|
1374
|
+
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
1375
|
+
insertionIndex = exportMatch?.index ?? -1;
|
|
1376
|
+
}
|
|
1377
|
+
if (insertionIndex < 0) {
|
|
1378
|
+
return false;
|
|
1379
|
+
}
|
|
1380
|
+
appContent = appContent.slice(0, insertionIndex) + authMountLine + appContent.slice(insertionIndex);
|
|
1330
1381
|
appModified = true;
|
|
1331
1382
|
}
|
|
1332
1383
|
}
|
|
1333
|
-
const hasAuthMount = /toNodeHandler\(\s*auth\s*\)/.test(appContent) && /\/api\/auth/.test(appContent);
|
|
1334
|
-
if (!hasAuthMount) {
|
|
1335
|
-
const authMountLine = "app.all(/^\\/api\\/auth(?:\\/.*)?$/, toNodeHandler(auth));\n";
|
|
1336
|
-
const jsonIndex = appContent.search(/^\s*app\.use\(\s*express\.json\(\)\s*\);\s*$/m);
|
|
1337
|
-
let insertionIndex = jsonIndex;
|
|
1338
|
-
if (insertionIndex < 0) {
|
|
1339
|
-
const healthIndex = appContent.search(/^\s*app\.get\(\s*["']\/health["']\s*,/m);
|
|
1340
|
-
insertionIndex = healthIndex;
|
|
1341
|
-
}
|
|
1342
|
-
if (insertionIndex < 0) {
|
|
1343
|
-
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
1344
|
-
insertionIndex = exportMatch?.index ?? -1;
|
|
1345
|
-
}
|
|
1346
|
-
if (insertionIndex < 0) {
|
|
1347
|
-
return false;
|
|
1348
|
-
}
|
|
1349
|
-
appContent = appContent.slice(0, insertionIndex) + authMountLine + appContent.slice(insertionIndex);
|
|
1350
|
-
appModified = true;
|
|
1351
|
-
}
|
|
1352
1384
|
const routeIndexPath = import_path9.default.join(projectRoot, srcDir, "routes", "index.ts");
|
|
1353
1385
|
if (await import_fs_extra8.default.pathExists(routeIndexPath)) {
|
|
1354
1386
|
let routeContent = await import_fs_extra8.default.readFile(routeIndexPath, "utf-8");
|
|
1355
1387
|
let routeModified = false;
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1388
|
+
const routeImports = provider === "jwt" ? [routeIndexAuthImport, routeIndexUserImport] : [routeIndexUserImport];
|
|
1389
|
+
for (const importLine of routeImports) {
|
|
1390
|
+
const next = appendImport(routeContent, importLine);
|
|
1391
|
+
if (!next.inserted) {
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1394
|
+
if (next.source !== routeContent) {
|
|
1395
|
+
routeContent = next.source;
|
|
1396
|
+
routeModified = true;
|
|
1397
|
+
}
|
|
1359
1398
|
}
|
|
1360
|
-
if (
|
|
1361
|
-
|
|
1362
|
-
|
|
1399
|
+
if (provider === "jwt") {
|
|
1400
|
+
const hasAuthRoute = /rootRouter\.use\(\s*["']\/auth["']\s*,\s*authRoutes\s*\)/.test(routeContent);
|
|
1401
|
+
if (!hasAuthRoute) {
|
|
1402
|
+
const routeSetup = `
|
|
1403
|
+
// Auth routes
|
|
1404
|
+
rootRouter.use("/auth", authRoutes);
|
|
1405
|
+
`;
|
|
1406
|
+
const exportMatch = routeContent.match(/export default rootRouter;?\s*$/m);
|
|
1407
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1410
|
+
routeContent = routeContent.slice(0, exportMatch.index) + routeSetup + "\n" + routeContent.slice(exportMatch.index);
|
|
1411
|
+
routeModified = true;
|
|
1412
|
+
}
|
|
1363
1413
|
}
|
|
1364
1414
|
const hasUserRoute = /rootRouter\.use\(\s*["']\/users["']\s*,\s*userRoutes\s*\)/.test(routeContent);
|
|
1365
1415
|
if (!hasUserRoute) {
|
|
@@ -1378,6 +1428,29 @@ rootRouter.use("/users", userRoutes);
|
|
|
1378
1428
|
await import_fs_extra8.default.writeFile(routeIndexPath, routeContent);
|
|
1379
1429
|
}
|
|
1380
1430
|
} else {
|
|
1431
|
+
if (provider === "jwt") {
|
|
1432
|
+
const authImportResult = appendImport(appContent, appAuthImport);
|
|
1433
|
+
if (!authImportResult.inserted) {
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
if (authImportResult.source !== appContent) {
|
|
1437
|
+
appContent = authImportResult.source;
|
|
1438
|
+
appModified = true;
|
|
1439
|
+
}
|
|
1440
|
+
const hasAuthRoute = /app\.use\(\s*["']\/api\/auth["']\s*,\s*authRoutes\s*\)/.test(appContent);
|
|
1441
|
+
if (!hasAuthRoute) {
|
|
1442
|
+
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
1443
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
const routeSetup = `
|
|
1447
|
+
// Auth routes
|
|
1448
|
+
app.use("/api/auth", authRoutes);
|
|
1449
|
+
`;
|
|
1450
|
+
appContent = appContent.slice(0, exportMatch.index) + routeSetup + "\n" + appContent.slice(exportMatch.index);
|
|
1451
|
+
appModified = true;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1381
1454
|
const hasUserRoute = /app\.use\(\s*["']\/api\/users["']\s*,\s*userRoutes\s*\)/.test(appContent);
|
|
1382
1455
|
if (!hasUserRoute) {
|
|
1383
1456
|
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
@@ -1405,21 +1478,23 @@ app.use("/api/users", userRoutes);
|
|
|
1405
1478
|
}
|
|
1406
1479
|
return true;
|
|
1407
1480
|
}
|
|
1408
|
-
async function injectAuthDocs(projectRoot, srcDir) {
|
|
1481
|
+
async function injectAuthDocs(projectRoot, srcDir, provider) {
|
|
1409
1482
|
const openApiPath = import_path9.default.join(projectRoot, srcDir, "lib", "openapi.ts");
|
|
1410
1483
|
if (!await import_fs_extra8.default.pathExists(openApiPath)) {
|
|
1411
1484
|
return false;
|
|
1412
1485
|
}
|
|
1413
|
-
const
|
|
1486
|
+
const betterAuthMarker = "// ZURO_AUTH_DOCS_BETTER_AUTH";
|
|
1487
|
+
const jwtMarker = "// ZURO_AUTH_DOCS_JWT";
|
|
1488
|
+
const legacyMarker = "// ZURO_AUTH_DOCS";
|
|
1414
1489
|
let content = await import_fs_extra8.default.readFile(openApiPath, "utf-8");
|
|
1415
|
-
if (content.includes(
|
|
1490
|
+
if (content.includes(betterAuthMarker) || content.includes(jwtMarker) || content.includes(legacyMarker)) {
|
|
1416
1491
|
return true;
|
|
1417
1492
|
}
|
|
1418
1493
|
const moduleDocsEndMarker = "// ZURO_DOCS_MODULES_END";
|
|
1419
1494
|
if (!content.includes(moduleDocsEndMarker)) {
|
|
1420
1495
|
return false;
|
|
1421
1496
|
}
|
|
1422
|
-
const authBlock = `
|
|
1497
|
+
const authBlock = provider === "jwt" ? `
|
|
1423
1498
|
const authSignUpSchema = z.object({
|
|
1424
1499
|
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1425
1500
|
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
@@ -1431,13 +1506,169 @@ const authSignInSchema = z.object({
|
|
|
1431
1506
|
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1432
1507
|
});
|
|
1433
1508
|
|
|
1509
|
+
const authRefreshSchema = z.object({
|
|
1510
|
+
refreshToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
const authTokenSchema = z.object({
|
|
1514
|
+
accessToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1515
|
+
refreshToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1516
|
+
tokenType: z.literal("Bearer").openapi({ example: "Bearer" }),
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1434
1519
|
const authUserSchema = z.object({
|
|
1435
1520
|
id: z.string().openapi({ example: "user_123" }),
|
|
1436
1521
|
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1437
1522
|
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1438
1523
|
});
|
|
1439
1524
|
|
|
1440
|
-
${
|
|
1525
|
+
${jwtMarker}
|
|
1526
|
+
registry.registerPath({
|
|
1527
|
+
method: "post",
|
|
1528
|
+
path: "/api/auth/sign-up/email",
|
|
1529
|
+
tags: ["Auth"],
|
|
1530
|
+
summary: "Register user and issue JWT tokens",
|
|
1531
|
+
request: {
|
|
1532
|
+
body: {
|
|
1533
|
+
content: {
|
|
1534
|
+
"application/json": {
|
|
1535
|
+
schema: authSignUpSchema,
|
|
1536
|
+
},
|
|
1537
|
+
},
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
responses: {
|
|
1541
|
+
200: {
|
|
1542
|
+
description: "Registration successful",
|
|
1543
|
+
content: {
|
|
1544
|
+
"application/json": {
|
|
1545
|
+
schema: z.object({ user: authUserSchema, ...authTokenSchema.shape }),
|
|
1546
|
+
},
|
|
1547
|
+
},
|
|
1548
|
+
},
|
|
1549
|
+
409: { description: "Email already registered" },
|
|
1550
|
+
},
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
registry.registerPath({
|
|
1554
|
+
method: "post",
|
|
1555
|
+
path: "/api/auth/sign-in/email",
|
|
1556
|
+
tags: ["Auth"],
|
|
1557
|
+
summary: "Sign in and issue JWT tokens",
|
|
1558
|
+
request: {
|
|
1559
|
+
body: {
|
|
1560
|
+
content: {
|
|
1561
|
+
"application/json": {
|
|
1562
|
+
schema: authSignInSchema,
|
|
1563
|
+
},
|
|
1564
|
+
},
|
|
1565
|
+
},
|
|
1566
|
+
},
|
|
1567
|
+
responses: {
|
|
1568
|
+
200: {
|
|
1569
|
+
description: "Sign in successful",
|
|
1570
|
+
content: {
|
|
1571
|
+
"application/json": {
|
|
1572
|
+
schema: authTokenSchema,
|
|
1573
|
+
},
|
|
1574
|
+
},
|
|
1575
|
+
},
|
|
1576
|
+
401: { description: "Invalid credentials" },
|
|
1577
|
+
},
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
registry.registerPath({
|
|
1581
|
+
method: "post",
|
|
1582
|
+
path: "/api/auth/refresh",
|
|
1583
|
+
tags: ["Auth"],
|
|
1584
|
+
summary: "Exchange refresh token for new JWT tokens",
|
|
1585
|
+
request: {
|
|
1586
|
+
body: {
|
|
1587
|
+
content: {
|
|
1588
|
+
"application/json": {
|
|
1589
|
+
schema: authRefreshSchema,
|
|
1590
|
+
},
|
|
1591
|
+
},
|
|
1592
|
+
},
|
|
1593
|
+
},
|
|
1594
|
+
responses: {
|
|
1595
|
+
200: {
|
|
1596
|
+
description: "Tokens refreshed",
|
|
1597
|
+
content: {
|
|
1598
|
+
"application/json": {
|
|
1599
|
+
schema: authTokenSchema,
|
|
1600
|
+
},
|
|
1601
|
+
},
|
|
1602
|
+
},
|
|
1603
|
+
401: { description: "Invalid refresh token" },
|
|
1604
|
+
},
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
registry.registerPath({
|
|
1608
|
+
method: "post",
|
|
1609
|
+
path: "/api/auth/sign-out",
|
|
1610
|
+
tags: ["Auth"],
|
|
1611
|
+
summary: "Client-side sign out for JWT auth",
|
|
1612
|
+
responses: {
|
|
1613
|
+
200: { description: "Sign out successful" },
|
|
1614
|
+
},
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1617
|
+
registry.registerPath({
|
|
1618
|
+
method: "get",
|
|
1619
|
+
path: "/api/auth/get-session",
|
|
1620
|
+
tags: ["Auth"],
|
|
1621
|
+
summary: "Get current JWT session user",
|
|
1622
|
+
security: [{ bearerAuth: [] }],
|
|
1623
|
+
responses: {
|
|
1624
|
+
200: {
|
|
1625
|
+
description: "Current session",
|
|
1626
|
+
content: {
|
|
1627
|
+
"application/json": {
|
|
1628
|
+
schema: z.object({ user: authUserSchema.nullable() }),
|
|
1629
|
+
},
|
|
1630
|
+
},
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
registry.registerPath({
|
|
1636
|
+
method: "get",
|
|
1637
|
+
path: "/api/users/me",
|
|
1638
|
+
tags: ["Auth"],
|
|
1639
|
+
summary: "Get current authenticated user",
|
|
1640
|
+
security: [{ bearerAuth: [] }],
|
|
1641
|
+
responses: {
|
|
1642
|
+
200: {
|
|
1643
|
+
description: "Current user",
|
|
1644
|
+
content: {
|
|
1645
|
+
"application/json": {
|
|
1646
|
+
schema: z.object({ user: authUserSchema }),
|
|
1647
|
+
},
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1650
|
+
401: { description: "Not authenticated" },
|
|
1651
|
+
},
|
|
1652
|
+
});
|
|
1653
|
+
` : `
|
|
1654
|
+
const authSignUpSchema = z.object({
|
|
1655
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1656
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1657
|
+
name: z.string().min(1).optional().openapi({ example: "Dev User" }),
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
const authSignInSchema = z.object({
|
|
1661
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1662
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
const authUserSchema = z.object({
|
|
1666
|
+
id: z.string().openapi({ example: "user_123" }),
|
|
1667
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1668
|
+
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
${betterAuthMarker}
|
|
1441
1672
|
registry.registerPath({
|
|
1442
1673
|
method: "post",
|
|
1443
1674
|
path: "/api/auth/sign-up/email",
|
|
@@ -1514,7 +1745,27 @@ ${moduleDocsEndMarker}`);
|
|
|
1514
1745
|
async function promptAuthConfig(projectRoot, srcDir, options) {
|
|
1515
1746
|
const { isDocsModuleInstalled: isDocsModuleInstalled2 } = await Promise.resolve().then(() => (init_docs_handler(), docs_handler_exports));
|
|
1516
1747
|
const docsInstalled = await isDocsModuleInstalled2(projectRoot, srcDir);
|
|
1748
|
+
let authProvider = "better-auth";
|
|
1517
1749
|
let shouldInstallDocsForAuth = false;
|
|
1750
|
+
if (options.authProvider) {
|
|
1751
|
+
authProvider = options.authProvider;
|
|
1752
|
+
} else if (!options.yes) {
|
|
1753
|
+
const providerResponse = await (0, import_prompts3.default)({
|
|
1754
|
+
type: "select",
|
|
1755
|
+
name: "provider",
|
|
1756
|
+
message: "Choose auth provider:",
|
|
1757
|
+
choices: [
|
|
1758
|
+
{ title: "Better Auth (session + plugin ecosystem)", value: "better-auth" },
|
|
1759
|
+
{ title: "JWT (access/refresh tokens)", value: "jwt" }
|
|
1760
|
+
],
|
|
1761
|
+
initial: 0
|
|
1762
|
+
});
|
|
1763
|
+
if (!providerResponse.provider) {
|
|
1764
|
+
console.log(import_chalk5.default.yellow("Operation cancelled."));
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
authProvider = providerResponse.provider;
|
|
1768
|
+
}
|
|
1518
1769
|
if (!docsInstalled) {
|
|
1519
1770
|
if (options.yes) {
|
|
1520
1771
|
shouldInstallDocsForAuth = true;
|
|
@@ -1534,13 +1785,19 @@ async function promptAuthConfig(projectRoot, srcDir, options) {
|
|
|
1534
1785
|
}
|
|
1535
1786
|
const { detectInstalledDatabaseDialect: detectInstalledDatabaseDialect2 } = await Promise.resolve().then(() => (init_database_handler(), database_handler_exports));
|
|
1536
1787
|
const authDatabaseDialect = await detectInstalledDatabaseDialect2(projectRoot, srcDir);
|
|
1537
|
-
return { shouldInstallDocsForAuth, authDatabaseDialect };
|
|
1788
|
+
return { authProvider, shouldInstallDocsForAuth, authDatabaseDialect };
|
|
1538
1789
|
}
|
|
1539
|
-
function printAuthHints(generatedAuthSecret) {
|
|
1540
|
-
if (
|
|
1541
|
-
|
|
1790
|
+
function printAuthHints(generatedAuthSecret, provider) {
|
|
1791
|
+
if (provider === "better-auth") {
|
|
1792
|
+
if (generatedAuthSecret) {
|
|
1793
|
+
console.log(import_chalk5.default.yellow("\u2139 BETTER_AUTH_SECRET was generated automatically."));
|
|
1794
|
+
} else {
|
|
1795
|
+
console.log(import_chalk5.default.yellow("\u2139 Review BETTER_AUTH_SECRET and BETTER_AUTH_URL in .env."));
|
|
1796
|
+
}
|
|
1797
|
+
} else if (generatedAuthSecret) {
|
|
1798
|
+
console.log(import_chalk5.default.yellow("\u2139 JWT_ACCESS_SECRET and JWT_REFRESH_SECRET were generated automatically."));
|
|
1542
1799
|
} else {
|
|
1543
|
-
console.log(import_chalk5.default.yellow("\u2139 Review
|
|
1800
|
+
console.log(import_chalk5.default.yellow("\u2139 Review JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, and token TTLs in .env."));
|
|
1544
1801
|
}
|
|
1545
1802
|
console.log(import_chalk5.default.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
|
|
1546
1803
|
}
|
|
@@ -1793,27 +2050,15 @@ var UPLOAD_PRESETS = {
|
|
|
1793
2050
|
mimeTypes: ["video/mp4", "video/quicktime", "video/webm"],
|
|
1794
2051
|
maxFileSize: 100 * 1024 * 1024,
|
|
1795
2052
|
maxFiles: 1
|
|
1796
|
-
},
|
|
1797
|
-
mixed: {
|
|
1798
|
-
mimeTypes: [
|
|
1799
|
-
"image/jpeg",
|
|
1800
|
-
"image/png",
|
|
1801
|
-
"image/webp",
|
|
1802
|
-
"application/pdf",
|
|
1803
|
-
"text/plain",
|
|
1804
|
-
"video/mp4"
|
|
1805
|
-
],
|
|
1806
|
-
maxFileSize: 25 * 1024 * 1024,
|
|
1807
|
-
maxFiles: 5
|
|
1808
2053
|
}
|
|
1809
2054
|
};
|
|
1810
2055
|
function getUploadEnvSchemaFields(provider) {
|
|
1811
2056
|
const shared = [
|
|
1812
2057
|
{ name: "UPLOAD_PROVIDER", schema: `z.enum(["s3", "r2", "cloudinary"])` },
|
|
1813
2058
|
{ name: "UPLOAD_MODE", schema: `z.enum(["proxy", "direct", "large"])` },
|
|
1814
|
-
{ name: "UPLOAD_AUTH_MODE", schema: `z.enum(["required", "
|
|
2059
|
+
{ name: "UPLOAD_AUTH_MODE", schema: `z.enum(["required", "none"])` },
|
|
1815
2060
|
{ name: "UPLOAD_FILE_ACCESS", schema: `z.enum(["private", "public"])` },
|
|
1816
|
-
{ name: "UPLOAD_FILE_PRESET", schema: `z.enum(["image", "document", "video"
|
|
2061
|
+
{ name: "UPLOAD_FILE_PRESET", schema: `z.enum(["image", "document", "video"])` },
|
|
1817
2062
|
{ name: "UPLOAD_KEY_PREFIX", schema: "z.string().min(1)" },
|
|
1818
2063
|
{ name: "UPLOAD_ALLOWED_MIME", schema: "z.string().min(1)" },
|
|
1819
2064
|
{ name: "UPLOAD_MAX_FILE_SIZE", schema: "z.coerce.number().positive()" },
|
|
@@ -1848,103 +2093,24 @@ async function isAuthInstalled(projectRoot, srcDir) {
|
|
|
1848
2093
|
function hasDrizzleDatabase(config) {
|
|
1849
2094
|
return config?.database?.orm === "drizzle";
|
|
1850
2095
|
}
|
|
1851
|
-
|
|
1852
|
-
console.log(import_chalk7.default.dim(" Tip: Leave fields blank to use placeholders and configure later.\n"));
|
|
2096
|
+
function getProviderEnvDefaults(provider) {
|
|
1853
2097
|
if (provider === "cloudinary") {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
},
|
|
1861
|
-
{
|
|
1862
|
-
type: "text",
|
|
1863
|
-
name: "apiKey",
|
|
1864
|
-
message: "Cloudinary API key",
|
|
1865
|
-
initial: ""
|
|
1866
|
-
},
|
|
1867
|
-
{
|
|
1868
|
-
type: "password",
|
|
1869
|
-
name: "apiSecret",
|
|
1870
|
-
message: "Cloudinary API secret"
|
|
1871
|
-
},
|
|
1872
|
-
{
|
|
1873
|
-
type: "text",
|
|
1874
|
-
name: "folder",
|
|
1875
|
-
message: "Cloudinary folder",
|
|
1876
|
-
initial: "uploads"
|
|
1877
|
-
},
|
|
1878
|
-
{
|
|
1879
|
-
type: "text",
|
|
1880
|
-
name: "uploadPreset",
|
|
1881
|
-
message: "Cloudinary upload preset (optional)",
|
|
1882
|
-
initial: ""
|
|
1883
|
-
}
|
|
1884
|
-
]);
|
|
1885
|
-
if (response2.cloudName === void 0) {
|
|
1886
|
-
console.log(import_chalk7.default.yellow("Operation cancelled."));
|
|
1887
|
-
return null;
|
|
1888
|
-
}
|
|
1889
|
-
const values2 = {
|
|
1890
|
-
CLOUDINARY_CLOUD_NAME: response2.cloudName?.trim() || "your-cloud-name",
|
|
1891
|
-
CLOUDINARY_API_KEY: response2.apiKey?.trim() || "your-api-key",
|
|
1892
|
-
CLOUDINARY_API_SECRET: response2.apiSecret?.trim() || "your-api-secret",
|
|
1893
|
-
CLOUDINARY_FOLDER: response2.folder?.trim() || "uploads",
|
|
1894
|
-
CLOUDINARY_UPLOAD_PRESET: response2.uploadPreset?.trim() || ""
|
|
2098
|
+
return {
|
|
2099
|
+
CLOUDINARY_CLOUD_NAME: "your-cloud-name",
|
|
2100
|
+
CLOUDINARY_API_KEY: "your-api-key",
|
|
2101
|
+
CLOUDINARY_API_SECRET: "your-api-secret",
|
|
2102
|
+
CLOUDINARY_FOLDER: "uploads",
|
|
2103
|
+
CLOUDINARY_UPLOAD_PRESET: ""
|
|
1895
2104
|
};
|
|
1896
|
-
return values2;
|
|
1897
|
-
}
|
|
1898
|
-
const response = await (0, import_prompts5.default)([
|
|
1899
|
-
{
|
|
1900
|
-
type: "text",
|
|
1901
|
-
name: "bucket",
|
|
1902
|
-
message: `${provider.toUpperCase()} bucket name`,
|
|
1903
|
-
initial: ""
|
|
1904
|
-
},
|
|
1905
|
-
{
|
|
1906
|
-
type: "text",
|
|
1907
|
-
name: "region",
|
|
1908
|
-
message: `${provider.toUpperCase()} region`,
|
|
1909
|
-
initial: provider === "r2" ? "auto" : "us-east-1"
|
|
1910
|
-
},
|
|
1911
|
-
{
|
|
1912
|
-
type: "text",
|
|
1913
|
-
name: "endpoint",
|
|
1914
|
-
message: provider === "r2" ? "R2 S3 endpoint" : "Custom S3 endpoint (optional)",
|
|
1915
|
-
initial: provider === "r2" ? "https://<account-id>.r2.cloudflarestorage.com" : ""
|
|
1916
|
-
},
|
|
1917
|
-
{
|
|
1918
|
-
type: "text",
|
|
1919
|
-
name: "accessKeyId",
|
|
1920
|
-
message: "Access key ID",
|
|
1921
|
-
initial: ""
|
|
1922
|
-
},
|
|
1923
|
-
{
|
|
1924
|
-
type: "password",
|
|
1925
|
-
name: "secretAccessKey",
|
|
1926
|
-
message: "Secret access key"
|
|
1927
|
-
},
|
|
1928
|
-
{
|
|
1929
|
-
type: "text",
|
|
1930
|
-
name: "publicBaseUrl",
|
|
1931
|
-
message: "Public base URL (optional)",
|
|
1932
|
-
initial: ""
|
|
1933
|
-
}
|
|
1934
|
-
]);
|
|
1935
|
-
if (response.bucket === void 0) {
|
|
1936
|
-
console.log(import_chalk7.default.yellow("Operation cancelled."));
|
|
1937
|
-
return null;
|
|
1938
2105
|
}
|
|
1939
|
-
|
|
1940
|
-
UPLOAD_BUCKET:
|
|
1941
|
-
UPLOAD_REGION:
|
|
1942
|
-
UPLOAD_ENDPOINT:
|
|
1943
|
-
UPLOAD_ACCESS_KEY_ID:
|
|
1944
|
-
UPLOAD_SECRET_ACCESS_KEY:
|
|
1945
|
-
UPLOAD_PUBLIC_BASE_URL:
|
|
2106
|
+
return {
|
|
2107
|
+
UPLOAD_BUCKET: `your-${provider}-bucket`,
|
|
2108
|
+
UPLOAD_REGION: provider === "r2" ? "auto" : "us-east-1",
|
|
2109
|
+
UPLOAD_ENDPOINT: provider === "r2" ? "https://<account-id>.r2.cloudflarestorage.com" : "",
|
|
2110
|
+
UPLOAD_ACCESS_KEY_ID: "your-access-key-id",
|
|
2111
|
+
UPLOAD_SECRET_ACCESS_KEY: "your-secret-access-key",
|
|
2112
|
+
UPLOAD_PUBLIC_BASE_URL: ""
|
|
1946
2113
|
};
|
|
1947
|
-
return values;
|
|
1948
2114
|
}
|
|
1949
2115
|
function buildSharedEnvVars(provider, mode, authMode, access, preset, maxFileSize, maxFiles) {
|
|
1950
2116
|
return {
|
|
@@ -1995,10 +2161,9 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
1995
2161
|
message: "Who can upload?",
|
|
1996
2162
|
choices: [
|
|
1997
2163
|
{ title: "Authenticated only", value: "required" },
|
|
1998
|
-
{ title: "Optional auth", value: "optional" },
|
|
1999
2164
|
{ title: "Public", value: "none" }
|
|
2000
2165
|
],
|
|
2001
|
-
initial: authInstalled ? 0 :
|
|
2166
|
+
initial: authInstalled ? 0 : 1
|
|
2002
2167
|
},
|
|
2003
2168
|
{
|
|
2004
2169
|
type: "select",
|
|
@@ -2017,16 +2182,9 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
2017
2182
|
choices: [
|
|
2018
2183
|
{ title: "Image", value: "image" },
|
|
2019
2184
|
{ title: "Document", value: "document" },
|
|
2020
|
-
{ title: "Video", value: "video" }
|
|
2021
|
-
{ title: "Mixed", value: "mixed" }
|
|
2185
|
+
{ title: "Video", value: "video" }
|
|
2022
2186
|
],
|
|
2023
2187
|
initial: 0
|
|
2024
|
-
},
|
|
2025
|
-
{
|
|
2026
|
-
type: "confirm",
|
|
2027
|
-
name: "useDefaults",
|
|
2028
|
-
message: "Use recommended upload limits for this preset?",
|
|
2029
|
-
initial: true
|
|
2030
2188
|
}
|
|
2031
2189
|
]);
|
|
2032
2190
|
if (initial.provider === void 0) {
|
|
@@ -2043,7 +2201,7 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
2043
2201
|
console.log(import_chalk7.default.yellow("Use S3 or R2 for large uploads, or pick Proxy/Direct for Cloudinary.\n"));
|
|
2044
2202
|
return null;
|
|
2045
2203
|
}
|
|
2046
|
-
if (provider === "cloudinary" &&
|
|
2204
|
+
if (provider === "cloudinary" && preset === "document") {
|
|
2047
2205
|
const warning = await (0, import_prompts5.default)({
|
|
2048
2206
|
type: "confirm",
|
|
2049
2207
|
name: "continue",
|
|
@@ -2056,50 +2214,19 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
2056
2214
|
}
|
|
2057
2215
|
}
|
|
2058
2216
|
const presetDefaults = UPLOAD_PRESETS[preset];
|
|
2059
|
-
let maxFileSize = presetDefaults.maxFileSize;
|
|
2060
|
-
let maxFiles = presetDefaults.maxFiles;
|
|
2061
|
-
if (!initial.useDefaults) {
|
|
2062
|
-
const custom = await (0, import_prompts5.default)([
|
|
2063
|
-
{
|
|
2064
|
-
type: "number",
|
|
2065
|
-
name: "maxFileSizeMb",
|
|
2066
|
-
message: "Max file size (MB)",
|
|
2067
|
-
initial: Math.max(1, Math.round(presetDefaults.maxFileSize / (1024 * 1024))),
|
|
2068
|
-
min: 1
|
|
2069
|
-
},
|
|
2070
|
-
{
|
|
2071
|
-
type: "number",
|
|
2072
|
-
name: "maxFiles",
|
|
2073
|
-
message: "Max files per request",
|
|
2074
|
-
initial: presetDefaults.maxFiles,
|
|
2075
|
-
min: 1,
|
|
2076
|
-
max: 20
|
|
2077
|
-
}
|
|
2078
|
-
]);
|
|
2079
|
-
if (custom.maxFileSizeMb === void 0) {
|
|
2080
|
-
console.log(import_chalk7.default.yellow("Operation cancelled."));
|
|
2081
|
-
return null;
|
|
2082
|
-
}
|
|
2083
|
-
maxFileSize = Number(custom.maxFileSizeMb) * 1024 * 1024;
|
|
2084
|
-
maxFiles = Number(custom.maxFiles);
|
|
2085
|
-
}
|
|
2086
2217
|
let useDatabaseMetadata = false;
|
|
2087
2218
|
let shouldInstallDatabase = false;
|
|
2088
2219
|
const metadataPrompt = await (0, import_prompts5.default)({
|
|
2089
|
-
type: "
|
|
2220
|
+
type: "confirm",
|
|
2090
2221
|
name: "metadata",
|
|
2091
|
-
message: "
|
|
2092
|
-
|
|
2093
|
-
{ title: drizzleInstalled ? "Database" : "Install database + track uploads", value: "db" },
|
|
2094
|
-
{ title: "No metadata", value: "none" }
|
|
2095
|
-
],
|
|
2096
|
-
initial: 0
|
|
2222
|
+
message: drizzleInstalled ? "Store upload metadata in your database?" : "Install database and store upload metadata?",
|
|
2223
|
+
initial: true
|
|
2097
2224
|
});
|
|
2098
2225
|
if (metadataPrompt.metadata === void 0) {
|
|
2099
2226
|
console.log(import_chalk7.default.yellow("Operation cancelled."));
|
|
2100
2227
|
return null;
|
|
2101
2228
|
}
|
|
2102
|
-
if (metadataPrompt.metadata ===
|
|
2229
|
+
if (metadataPrompt.metadata === true) {
|
|
2103
2230
|
useDatabaseMetadata = true;
|
|
2104
2231
|
if (!projectConfig?.database) {
|
|
2105
2232
|
shouldInstallDatabase = true;
|
|
@@ -2135,10 +2262,6 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
2135
2262
|
}
|
|
2136
2263
|
shouldInstallAuth = true;
|
|
2137
2264
|
}
|
|
2138
|
-
const providerEnv = await promptCredentials(provider);
|
|
2139
|
-
if (!providerEnv) {
|
|
2140
|
-
return null;
|
|
2141
|
-
}
|
|
2142
2265
|
return {
|
|
2143
2266
|
provider,
|
|
2144
2267
|
mode,
|
|
@@ -2149,8 +2272,8 @@ async function promptUploadsConfig(projectRoot, srcDir) {
|
|
|
2149
2272
|
shouldInstallAuth,
|
|
2150
2273
|
shouldInstallDatabase,
|
|
2151
2274
|
envVars: {
|
|
2152
|
-
...buildSharedEnvVars(provider, mode, authMode, access, preset, maxFileSize, maxFiles),
|
|
2153
|
-
...
|
|
2275
|
+
...buildSharedEnvVars(provider, mode, authMode, access, preset, presetDefaults.maxFileSize, presetDefaults.maxFiles),
|
|
2276
|
+
...getProviderEnvDefaults(provider)
|
|
2154
2277
|
}
|
|
2155
2278
|
};
|
|
2156
2279
|
}
|
|
@@ -2386,6 +2509,7 @@ ${moduleDocsEndMarker}`);
|
|
|
2386
2509
|
function printUploadHints(result) {
|
|
2387
2510
|
console.log(import_chalk7.default.yellow("\u2139 Upload routes are mounted at: /api/uploads"));
|
|
2388
2511
|
console.log(import_chalk7.default.yellow(`\u2139 Provider: ${result.provider} \xB7 Mode: ${result.mode} \xB7 Access: ${result.access}`));
|
|
2512
|
+
console.log(import_chalk7.default.yellow("\u2139 Fill the generated upload env vars in .env before testing uploads."));
|
|
2389
2513
|
if (result.mode === "proxy") {
|
|
2390
2514
|
console.log(import_chalk7.default.yellow("\u2139 Reuse uploadSingle()/uploadArray() from src/lib/uploads/proxy.ts in your own form + file routes."));
|
|
2391
2515
|
}
|
|
@@ -2456,6 +2580,8 @@ var add = async (moduleName, options = {}) => {
|
|
|
2456
2580
|
let selectedDatabaseDialect = null;
|
|
2457
2581
|
let generatedAuthSecret = false;
|
|
2458
2582
|
let authDatabaseDialect = null;
|
|
2583
|
+
let authProvider = options.authProvider || "better-auth";
|
|
2584
|
+
let uploadAuthProvider = null;
|
|
2459
2585
|
let customSmtpVars;
|
|
2460
2586
|
let usedDefaultSmtp = false;
|
|
2461
2587
|
let mailerProvider = "smtp";
|
|
@@ -2482,6 +2608,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2482
2608
|
if (resolvedModuleName === "auth") {
|
|
2483
2609
|
const result = await promptAuthConfig(projectRoot, srcDir, options);
|
|
2484
2610
|
if (!result) return;
|
|
2611
|
+
authProvider = result.authProvider;
|
|
2485
2612
|
shouldInstallDocsForAuth = result.shouldInstallDocsForAuth;
|
|
2486
2613
|
authDatabaseDialect = result.authDatabaseDialect;
|
|
2487
2614
|
}
|
|
@@ -2508,6 +2635,11 @@ var add = async (moduleName, options = {}) => {
|
|
|
2508
2635
|
currentStep = "module dependency resolution";
|
|
2509
2636
|
await resolveDependencies(moduleDeps, projectRoot);
|
|
2510
2637
|
if (resolvedModuleName === "uploads" && uploadConfig) {
|
|
2638
|
+
const errorHandlerInstalled = import_fs_extra12.default.existsSync(import_path13.default.join(projectRoot, srcDir, "lib", "errors.ts"));
|
|
2639
|
+
if (!errorHandlerInstalled) {
|
|
2640
|
+
console.log(import_chalk8.default.blue("\n\u2139 Uploads needs the error-handler module. Installing error-handler..."));
|
|
2641
|
+
await add("error-handler");
|
|
2642
|
+
}
|
|
2511
2643
|
if (uploadConfig.shouldInstallDatabase) {
|
|
2512
2644
|
console.log(import_chalk8.default.blue("\n\u2139 Upload metadata needs a Drizzle database. Installing database module..."));
|
|
2513
2645
|
await add("database");
|
|
@@ -2517,6 +2649,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2517
2649
|
await add("auth", { yes: true });
|
|
2518
2650
|
}
|
|
2519
2651
|
uploadDatabaseDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
|
|
2652
|
+
uploadAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir);
|
|
2520
2653
|
if (uploadConfig.useDatabaseMetadata) {
|
|
2521
2654
|
if (uploadDatabaseDialect === "database-prisma-pg" || uploadDatabaseDialect === "database-prisma-mysql") {
|
|
2522
2655
|
spinner.fail("Uploads metadata currently supports Drizzle-based database setup only.");
|
|
@@ -2551,6 +2684,15 @@ var add = async (moduleName, options = {}) => {
|
|
|
2551
2684
|
devDeps = ["@types/nodemailer"];
|
|
2552
2685
|
}
|
|
2553
2686
|
}
|
|
2687
|
+
if (resolvedModuleName === "auth") {
|
|
2688
|
+
if (authProvider === "jwt") {
|
|
2689
|
+
runtimeDeps = ["jsonwebtoken", "bcryptjs"];
|
|
2690
|
+
devDeps = ["@types/jsonwebtoken", "@types/bcryptjs"];
|
|
2691
|
+
} else {
|
|
2692
|
+
runtimeDeps = ["better-auth"];
|
|
2693
|
+
devDeps = [];
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2554
2696
|
if (resolvedModuleName === "uploads" && uploadConfig) {
|
|
2555
2697
|
runtimeDeps = ["multer"];
|
|
2556
2698
|
devDeps = ["@types/multer"];
|
|
@@ -2569,11 +2711,46 @@ var add = async (moduleName, options = {}) => {
|
|
|
2569
2711
|
let fetchPath = file.path;
|
|
2570
2712
|
let expectedSha256 = file.sha256;
|
|
2571
2713
|
let expectedSize = file.size;
|
|
2714
|
+
if (resolvedModuleName === "auth" && authProvider === "better-auth" && (file.target === "routes/auth.routes.ts" || file.target === "controllers/auth.controller.ts")) {
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2572
2717
|
if (resolvedModuleName === "auth" && file.target === "db/schema/auth.ts" && authDatabaseDialect === "database-mysql") {
|
|
2573
2718
|
fetchPath = "express/db/schema/auth.mysql.ts";
|
|
2574
2719
|
expectedSha256 = void 0;
|
|
2575
2720
|
expectedSize = void 0;
|
|
2576
2721
|
}
|
|
2722
|
+
if (resolvedModuleName === "auth" && authProvider === "jwt") {
|
|
2723
|
+
if (file.target === "lib/auth.ts") {
|
|
2724
|
+
fetchPath = "express/lib/auth.jwt.ts";
|
|
2725
|
+
expectedSha256 = void 0;
|
|
2726
|
+
expectedSize = void 0;
|
|
2727
|
+
}
|
|
2728
|
+
if (file.target === "controllers/user.controller.ts") {
|
|
2729
|
+
fetchPath = "express/controllers/user.controller.jwt.ts";
|
|
2730
|
+
expectedSha256 = void 0;
|
|
2731
|
+
expectedSize = void 0;
|
|
2732
|
+
}
|
|
2733
|
+
if (file.target === "routes/user.routes.ts") {
|
|
2734
|
+
fetchPath = "express/routes/user.routes.jwt.ts";
|
|
2735
|
+
expectedSha256 = void 0;
|
|
2736
|
+
expectedSize = void 0;
|
|
2737
|
+
}
|
|
2738
|
+
if (file.target === "controllers/auth.controller.ts") {
|
|
2739
|
+
fetchPath = "express/controllers/auth.controller.jwt.ts";
|
|
2740
|
+
expectedSha256 = void 0;
|
|
2741
|
+
expectedSize = void 0;
|
|
2742
|
+
}
|
|
2743
|
+
if (file.target === "routes/auth.routes.ts") {
|
|
2744
|
+
fetchPath = "express/routes/auth.routes.jwt.ts";
|
|
2745
|
+
expectedSha256 = void 0;
|
|
2746
|
+
expectedSize = void 0;
|
|
2747
|
+
}
|
|
2748
|
+
if (file.target === "db/schema/auth.ts") {
|
|
2749
|
+
fetchPath = authDatabaseDialect === "database-mysql" ? "express/db/schema/auth.mysql.jwt.ts" : "express/db/schema/auth.jwt.ts";
|
|
2750
|
+
expectedSha256 = void 0;
|
|
2751
|
+
expectedSize = void 0;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2577
2754
|
if (resolvedModuleName === "mailer" && file.target === "lib/mailer.ts" && mailerProvider === "resend") {
|
|
2578
2755
|
fetchPath = "express/lib/mailer.resend.ts";
|
|
2579
2756
|
expectedSha256 = void 0;
|
|
@@ -2591,7 +2768,12 @@ var add = async (moduleName, options = {}) => {
|
|
|
2591
2768
|
expectedSize = void 0;
|
|
2592
2769
|
}
|
|
2593
2770
|
if (file.target === "middleware/upload-auth.ts") {
|
|
2594
|
-
|
|
2771
|
+
if (uploadConfig.authMode === "none") {
|
|
2772
|
+
fetchPath = "express/middleware/upload-auth.none.ts";
|
|
2773
|
+
} else {
|
|
2774
|
+
const providerSuffix = uploadAuthProvider === "jwt" ? "jwt" : "better-auth";
|
|
2775
|
+
fetchPath = `express/middleware/upload-auth.required.${providerSuffix}.ts`;
|
|
2776
|
+
}
|
|
2595
2777
|
expectedSha256 = void 0;
|
|
2596
2778
|
expectedSize = void 0;
|
|
2597
2779
|
}
|
|
@@ -2627,7 +2809,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2627
2809
|
spinner.succeed("Files generated");
|
|
2628
2810
|
if (resolvedModuleName === "auth") {
|
|
2629
2811
|
spinner.start("Configuring routes...");
|
|
2630
|
-
const injected = await injectAuthRoutes(projectRoot, srcDir);
|
|
2812
|
+
const injected = await injectAuthRoutes(projectRoot, srcDir, authProvider);
|
|
2631
2813
|
if (injected) {
|
|
2632
2814
|
spinner.succeed("Routes configured");
|
|
2633
2815
|
} else {
|
|
@@ -2636,7 +2818,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2636
2818
|
const docsInstalled = await isDocsModuleInstalled(projectRoot, srcDir);
|
|
2637
2819
|
if (docsInstalled) {
|
|
2638
2820
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2639
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2821
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, authProvider);
|
|
2640
2822
|
if (authDocsInjected) {
|
|
2641
2823
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2642
2824
|
} else {
|
|
@@ -2672,8 +2854,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2672
2854
|
}
|
|
2673
2855
|
const authInstalled = await isAuthModuleInstalled(projectRoot, srcDir);
|
|
2674
2856
|
if (authInstalled) {
|
|
2857
|
+
const installedAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir) || "better-auth";
|
|
2675
2858
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2676
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2859
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, installedAuthProvider);
|
|
2677
2860
|
if (authDocsInjected) {
|
|
2678
2861
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2679
2862
|
} else {
|
|
@@ -2717,6 +2900,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2717
2900
|
if (resolvedModuleName === "mailer" && mailerProvider === "resend") {
|
|
2718
2901
|
envConfigKey = "mailer-resend";
|
|
2719
2902
|
}
|
|
2903
|
+
if (resolvedModuleName === "auth" && authProvider === "jwt") {
|
|
2904
|
+
envConfigKey = "auth-jwt";
|
|
2905
|
+
}
|
|
2720
2906
|
const envConfig = ENV_CONFIGS[envConfigKey];
|
|
2721
2907
|
if (envConfig) {
|
|
2722
2908
|
currentStep = "environment configuration";
|
|
@@ -2729,10 +2915,23 @@ var add = async (moduleName, options = {}) => {
|
|
|
2729
2915
|
Object.assign(envVars, customSmtpVars);
|
|
2730
2916
|
}
|
|
2731
2917
|
if (resolvedModuleName === "auth") {
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2918
|
+
if (authProvider === "better-auth") {
|
|
2919
|
+
const hasExistingSecret = await hasEnvVariable(projectRoot, "BETTER_AUTH_SECRET");
|
|
2920
|
+
if (!hasExistingSecret) {
|
|
2921
|
+
envVars.BETTER_AUTH_SECRET = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
2922
|
+
generatedAuthSecret = true;
|
|
2923
|
+
}
|
|
2924
|
+
} else {
|
|
2925
|
+
const hasAccessSecret = await hasEnvVariable(projectRoot, "JWT_ACCESS_SECRET");
|
|
2926
|
+
if (!hasAccessSecret) {
|
|
2927
|
+
envVars.JWT_ACCESS_SECRET = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
2928
|
+
generatedAuthSecret = true;
|
|
2929
|
+
}
|
|
2930
|
+
const hasRefreshSecret = await hasEnvVariable(projectRoot, "JWT_REFRESH_SECRET");
|
|
2931
|
+
if (!hasRefreshSecret) {
|
|
2932
|
+
envVars.JWT_REFRESH_SECRET = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
2933
|
+
generatedAuthSecret = true;
|
|
2934
|
+
}
|
|
2736
2935
|
}
|
|
2737
2936
|
}
|
|
2738
2937
|
await updateEnvFile(projectRoot, envVars, true, {
|
|
@@ -2773,7 +2972,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2773
2972
|
printDatabaseHints(resolvedModuleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath);
|
|
2774
2973
|
}
|
|
2775
2974
|
if (resolvedModuleName === "auth") {
|
|
2776
|
-
printAuthHints(generatedAuthSecret);
|
|
2975
|
+
printAuthHints(generatedAuthSecret, authProvider);
|
|
2777
2976
|
}
|
|
2778
2977
|
if (resolvedModuleName === "mailer") {
|
|
2779
2978
|
printMailerHints(usedDefaultSmtp);
|
|
@@ -2802,6 +3001,15 @@ ${import_chalk8.default.bold("Retry:")}`);
|
|
|
2802
3001
|
var program = new import_commander.Command();
|
|
2803
3002
|
program.name("zuro-cli").description("Zuro CLI tool").version("0.0.1");
|
|
2804
3003
|
program.command("init").description("Initialize a new Zuro project").action(init);
|
|
2805
|
-
program.command("add <module>").description("Add a module to your project").
|
|
3004
|
+
program.command("add <module>").description("Add a module to your project").option(
|
|
3005
|
+
"--auth-provider <provider>",
|
|
3006
|
+
"Auth provider for auth module (better-auth|jwt)",
|
|
3007
|
+
(value) => {
|
|
3008
|
+
if (value === "better-auth" || value === "jwt") {
|
|
3009
|
+
return value;
|
|
3010
|
+
}
|
|
3011
|
+
throw new import_commander.InvalidArgumentError("auth-provider must be 'better-auth' or 'jwt'");
|
|
3012
|
+
}
|
|
3013
|
+
).option("-y, --yes", "Skip prompts and use defaults").action((module2, options) => add(module2, options));
|
|
2806
3014
|
program.parse(process.argv);
|
|
2807
3015
|
//# sourceMappingURL=index.js.map
|