zuro-cli 0.0.2-beta.16 → 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 +390 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +391 -55
- 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,179 @@ 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" ? `
|
|
1498
|
+
const authSignUpSchema = z.object({
|
|
1499
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1500
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1501
|
+
name: z.string().min(1).optional().openapi({ example: "Dev User" }),
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
const authSignInSchema = z.object({
|
|
1505
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1506
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1507
|
+
});
|
|
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
|
+
|
|
1519
|
+
const authUserSchema = z.object({
|
|
1520
|
+
id: z.string().openapi({ example: "user_123" }),
|
|
1521
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1522
|
+
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1523
|
+
});
|
|
1524
|
+
|
|
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
|
+
` : `
|
|
1423
1654
|
const authSignUpSchema = z.object({
|
|
1424
1655
|
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1425
1656
|
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
@@ -1437,7 +1668,7 @@ const authUserSchema = z.object({
|
|
|
1437
1668
|
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1438
1669
|
});
|
|
1439
1670
|
|
|
1440
|
-
${
|
|
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
|
}
|
|
@@ -2323,6 +2580,8 @@ var add = async (moduleName, options = {}) => {
|
|
|
2323
2580
|
let selectedDatabaseDialect = null;
|
|
2324
2581
|
let generatedAuthSecret = false;
|
|
2325
2582
|
let authDatabaseDialect = null;
|
|
2583
|
+
let authProvider = options.authProvider || "better-auth";
|
|
2584
|
+
let uploadAuthProvider = null;
|
|
2326
2585
|
let customSmtpVars;
|
|
2327
2586
|
let usedDefaultSmtp = false;
|
|
2328
2587
|
let mailerProvider = "smtp";
|
|
@@ -2349,6 +2608,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2349
2608
|
if (resolvedModuleName === "auth") {
|
|
2350
2609
|
const result = await promptAuthConfig(projectRoot, srcDir, options);
|
|
2351
2610
|
if (!result) return;
|
|
2611
|
+
authProvider = result.authProvider;
|
|
2352
2612
|
shouldInstallDocsForAuth = result.shouldInstallDocsForAuth;
|
|
2353
2613
|
authDatabaseDialect = result.authDatabaseDialect;
|
|
2354
2614
|
}
|
|
@@ -2389,6 +2649,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2389
2649
|
await add("auth", { yes: true });
|
|
2390
2650
|
}
|
|
2391
2651
|
uploadDatabaseDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
|
|
2652
|
+
uploadAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir);
|
|
2392
2653
|
if (uploadConfig.useDatabaseMetadata) {
|
|
2393
2654
|
if (uploadDatabaseDialect === "database-prisma-pg" || uploadDatabaseDialect === "database-prisma-mysql") {
|
|
2394
2655
|
spinner.fail("Uploads metadata currently supports Drizzle-based database setup only.");
|
|
@@ -2423,6 +2684,15 @@ var add = async (moduleName, options = {}) => {
|
|
|
2423
2684
|
devDeps = ["@types/nodemailer"];
|
|
2424
2685
|
}
|
|
2425
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
|
+
}
|
|
2426
2696
|
if (resolvedModuleName === "uploads" && uploadConfig) {
|
|
2427
2697
|
runtimeDeps = ["multer"];
|
|
2428
2698
|
devDeps = ["@types/multer"];
|
|
@@ -2441,11 +2711,46 @@ var add = async (moduleName, options = {}) => {
|
|
|
2441
2711
|
let fetchPath = file.path;
|
|
2442
2712
|
let expectedSha256 = file.sha256;
|
|
2443
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
|
+
}
|
|
2444
2717
|
if (resolvedModuleName === "auth" && file.target === "db/schema/auth.ts" && authDatabaseDialect === "database-mysql") {
|
|
2445
2718
|
fetchPath = "express/db/schema/auth.mysql.ts";
|
|
2446
2719
|
expectedSha256 = void 0;
|
|
2447
2720
|
expectedSize = void 0;
|
|
2448
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
|
+
}
|
|
2449
2754
|
if (resolvedModuleName === "mailer" && file.target === "lib/mailer.ts" && mailerProvider === "resend") {
|
|
2450
2755
|
fetchPath = "express/lib/mailer.resend.ts";
|
|
2451
2756
|
expectedSha256 = void 0;
|
|
@@ -2463,7 +2768,12 @@ var add = async (moduleName, options = {}) => {
|
|
|
2463
2768
|
expectedSize = void 0;
|
|
2464
2769
|
}
|
|
2465
2770
|
if (file.target === "middleware/upload-auth.ts") {
|
|
2466
|
-
|
|
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
|
+
}
|
|
2467
2777
|
expectedSha256 = void 0;
|
|
2468
2778
|
expectedSize = void 0;
|
|
2469
2779
|
}
|
|
@@ -2499,7 +2809,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2499
2809
|
spinner.succeed("Files generated");
|
|
2500
2810
|
if (resolvedModuleName === "auth") {
|
|
2501
2811
|
spinner.start("Configuring routes...");
|
|
2502
|
-
const injected = await injectAuthRoutes(projectRoot, srcDir);
|
|
2812
|
+
const injected = await injectAuthRoutes(projectRoot, srcDir, authProvider);
|
|
2503
2813
|
if (injected) {
|
|
2504
2814
|
spinner.succeed("Routes configured");
|
|
2505
2815
|
} else {
|
|
@@ -2508,7 +2818,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2508
2818
|
const docsInstalled = await isDocsModuleInstalled(projectRoot, srcDir);
|
|
2509
2819
|
if (docsInstalled) {
|
|
2510
2820
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2511
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2821
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, authProvider);
|
|
2512
2822
|
if (authDocsInjected) {
|
|
2513
2823
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2514
2824
|
} else {
|
|
@@ -2544,8 +2854,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2544
2854
|
}
|
|
2545
2855
|
const authInstalled = await isAuthModuleInstalled(projectRoot, srcDir);
|
|
2546
2856
|
if (authInstalled) {
|
|
2857
|
+
const installedAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir) || "better-auth";
|
|
2547
2858
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2548
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2859
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, installedAuthProvider);
|
|
2549
2860
|
if (authDocsInjected) {
|
|
2550
2861
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2551
2862
|
} else {
|
|
@@ -2589,6 +2900,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2589
2900
|
if (resolvedModuleName === "mailer" && mailerProvider === "resend") {
|
|
2590
2901
|
envConfigKey = "mailer-resend";
|
|
2591
2902
|
}
|
|
2903
|
+
if (resolvedModuleName === "auth" && authProvider === "jwt") {
|
|
2904
|
+
envConfigKey = "auth-jwt";
|
|
2905
|
+
}
|
|
2592
2906
|
const envConfig = ENV_CONFIGS[envConfigKey];
|
|
2593
2907
|
if (envConfig) {
|
|
2594
2908
|
currentStep = "environment configuration";
|
|
@@ -2601,10 +2915,23 @@ var add = async (moduleName, options = {}) => {
|
|
|
2601
2915
|
Object.assign(envVars, customSmtpVars);
|
|
2602
2916
|
}
|
|
2603
2917
|
if (resolvedModuleName === "auth") {
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
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
|
+
}
|
|
2608
2935
|
}
|
|
2609
2936
|
}
|
|
2610
2937
|
await updateEnvFile(projectRoot, envVars, true, {
|
|
@@ -2645,7 +2972,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2645
2972
|
printDatabaseHints(resolvedModuleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath);
|
|
2646
2973
|
}
|
|
2647
2974
|
if (resolvedModuleName === "auth") {
|
|
2648
|
-
printAuthHints(generatedAuthSecret);
|
|
2975
|
+
printAuthHints(generatedAuthSecret, authProvider);
|
|
2649
2976
|
}
|
|
2650
2977
|
if (resolvedModuleName === "mailer") {
|
|
2651
2978
|
printMailerHints(usedDefaultSmtp);
|
|
@@ -2674,6 +3001,15 @@ ${import_chalk8.default.bold("Retry:")}`);
|
|
|
2674
3001
|
var program = new import_commander.Command();
|
|
2675
3002
|
program.name("zuro-cli").description("Zuro CLI tool").version("0.0.1");
|
|
2676
3003
|
program.command("init").description("Initialize a new Zuro project").action(init);
|
|
2677
|
-
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));
|
|
2678
3014
|
program.parse(process.argv);
|
|
2679
3015
|
//# sourceMappingURL=index.js.map
|