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.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-VMOTWTER.mjs";
|
|
21
21
|
|
|
22
22
|
// src/index.ts
|
|
23
|
-
import { Command } from "commander";
|
|
23
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
24
24
|
|
|
25
25
|
// src/commands/init.ts
|
|
26
26
|
import ora from "ora";
|
|
@@ -371,6 +371,20 @@ var ENV_CONFIGS = {
|
|
|
371
371
|
{ name: "BETTER_AUTH_URL", schema: "z.string().url()" }
|
|
372
372
|
]
|
|
373
373
|
},
|
|
374
|
+
"auth-jwt": {
|
|
375
|
+
envVars: {
|
|
376
|
+
JWT_ACCESS_SECRET: "your-jwt-access-secret-at-least-32-characters",
|
|
377
|
+
JWT_REFRESH_SECRET: "your-jwt-refresh-secret-at-least-32-characters",
|
|
378
|
+
JWT_ACCESS_EXPIRES_IN: "15m",
|
|
379
|
+
JWT_REFRESH_EXPIRES_IN: "7d"
|
|
380
|
+
},
|
|
381
|
+
schemaFields: [
|
|
382
|
+
{ name: "JWT_ACCESS_SECRET", schema: "z.string().min(32)" },
|
|
383
|
+
{ name: "JWT_REFRESH_SECRET", schema: "z.string().min(32)" },
|
|
384
|
+
{ name: "JWT_ACCESS_EXPIRES_IN", schema: "z.string().min(2)" },
|
|
385
|
+
{ name: "JWT_REFRESH_EXPIRES_IN", schema: "z.string().min(2)" }
|
|
386
|
+
]
|
|
387
|
+
},
|
|
374
388
|
mailer: {
|
|
375
389
|
envVars: {
|
|
376
390
|
SMTP_HOST: "smtp.example.com",
|
|
@@ -852,57 +866,93 @@ import chalk4 from "chalk";
|
|
|
852
866
|
async function isAuthModuleInstalled(projectRoot, srcDir) {
|
|
853
867
|
return await fs6.pathExists(path7.join(projectRoot, srcDir, "lib", "auth.ts"));
|
|
854
868
|
}
|
|
855
|
-
async function
|
|
869
|
+
async function detectInstalledAuthProvider(projectRoot, srcDir) {
|
|
870
|
+
const authPath = path7.join(projectRoot, srcDir, "lib", "auth.ts");
|
|
871
|
+
if (!await fs6.pathExists(authPath)) {
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
const authContent = await fs6.readFile(authPath, "utf-8");
|
|
875
|
+
if (authContent.includes("better-auth")) {
|
|
876
|
+
return "better-auth";
|
|
877
|
+
}
|
|
878
|
+
if (authContent.includes("jsonwebtoken") || authContent.includes("JWT_ACCESS_SECRET")) {
|
|
879
|
+
return "jwt";
|
|
880
|
+
}
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
async function injectAuthRoutes(projectRoot, srcDir, provider) {
|
|
856
884
|
const appPath = path7.join(projectRoot, srcDir, "app.ts");
|
|
857
885
|
if (!await fs6.pathExists(appPath)) {
|
|
858
886
|
return false;
|
|
859
887
|
}
|
|
860
888
|
let appContent = await fs6.readFile(appPath, "utf-8");
|
|
861
|
-
const authHandlerImport = `import { toNodeHandler } from "better-auth/node";`;
|
|
862
|
-
const authImport = `import { auth } from "./lib/auth";`;
|
|
863
889
|
const routeIndexUserImport = `import userRoutes from "./user.routes";`;
|
|
890
|
+
const routeIndexAuthImport = `import authRoutes from "./auth.routes";`;
|
|
864
891
|
const appUserImport = `import userRoutes from "./routes/user.routes";`;
|
|
892
|
+
const appAuthImport = `import authRoutes from "./routes/auth.routes";`;
|
|
865
893
|
let appModified = false;
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
894
|
+
if (provider === "better-auth") {
|
|
895
|
+
const authHandlerImport = `import { toNodeHandler } from "better-auth/node";`;
|
|
896
|
+
const authImport = `import { auth } from "./lib/auth";`;
|
|
897
|
+
for (const importLine of [authHandlerImport, authImport]) {
|
|
898
|
+
const next = appendImport(appContent, importLine);
|
|
899
|
+
if (!next.inserted) {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
if (next.source !== appContent) {
|
|
903
|
+
appContent = next.source;
|
|
904
|
+
appModified = true;
|
|
905
|
+
}
|
|
870
906
|
}
|
|
871
|
-
|
|
872
|
-
|
|
907
|
+
const hasAuthMount = /toNodeHandler\(\s*auth\s*\)/.test(appContent) && /\/api\/auth/.test(appContent);
|
|
908
|
+
if (!hasAuthMount) {
|
|
909
|
+
const authMountLine = "app.all(/^\\/api\\/auth(?:\\/.*)?$/, toNodeHandler(auth));\n";
|
|
910
|
+
const jsonIndex = appContent.search(/^\s*app\.use\(\s*express\.json\(\)\s*\);\s*$/m);
|
|
911
|
+
let insertionIndex = jsonIndex;
|
|
912
|
+
if (insertionIndex < 0) {
|
|
913
|
+
const healthIndex = appContent.search(/^\s*app\.get\(\s*["']\/health["']\s*,/m);
|
|
914
|
+
insertionIndex = healthIndex;
|
|
915
|
+
}
|
|
916
|
+
if (insertionIndex < 0) {
|
|
917
|
+
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
918
|
+
insertionIndex = exportMatch?.index ?? -1;
|
|
919
|
+
}
|
|
920
|
+
if (insertionIndex < 0) {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
appContent = appContent.slice(0, insertionIndex) + authMountLine + appContent.slice(insertionIndex);
|
|
873
924
|
appModified = true;
|
|
874
925
|
}
|
|
875
926
|
}
|
|
876
|
-
const hasAuthMount = /toNodeHandler\(\s*auth\s*\)/.test(appContent) && /\/api\/auth/.test(appContent);
|
|
877
|
-
if (!hasAuthMount) {
|
|
878
|
-
const authMountLine = "app.all(/^\\/api\\/auth(?:\\/.*)?$/, toNodeHandler(auth));\n";
|
|
879
|
-
const jsonIndex = appContent.search(/^\s*app\.use\(\s*express\.json\(\)\s*\);\s*$/m);
|
|
880
|
-
let insertionIndex = jsonIndex;
|
|
881
|
-
if (insertionIndex < 0) {
|
|
882
|
-
const healthIndex = appContent.search(/^\s*app\.get\(\s*["']\/health["']\s*,/m);
|
|
883
|
-
insertionIndex = healthIndex;
|
|
884
|
-
}
|
|
885
|
-
if (insertionIndex < 0) {
|
|
886
|
-
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
887
|
-
insertionIndex = exportMatch?.index ?? -1;
|
|
888
|
-
}
|
|
889
|
-
if (insertionIndex < 0) {
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
appContent = appContent.slice(0, insertionIndex) + authMountLine + appContent.slice(insertionIndex);
|
|
893
|
-
appModified = true;
|
|
894
|
-
}
|
|
895
927
|
const routeIndexPath = path7.join(projectRoot, srcDir, "routes", "index.ts");
|
|
896
928
|
if (await fs6.pathExists(routeIndexPath)) {
|
|
897
929
|
let routeContent = await fs6.readFile(routeIndexPath, "utf-8");
|
|
898
930
|
let routeModified = false;
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
931
|
+
const routeImports = provider === "jwt" ? [routeIndexAuthImport, routeIndexUserImport] : [routeIndexUserImport];
|
|
932
|
+
for (const importLine of routeImports) {
|
|
933
|
+
const next = appendImport(routeContent, importLine);
|
|
934
|
+
if (!next.inserted) {
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
937
|
+
if (next.source !== routeContent) {
|
|
938
|
+
routeContent = next.source;
|
|
939
|
+
routeModified = true;
|
|
940
|
+
}
|
|
902
941
|
}
|
|
903
|
-
if (
|
|
904
|
-
|
|
905
|
-
|
|
942
|
+
if (provider === "jwt") {
|
|
943
|
+
const hasAuthRoute = /rootRouter\.use\(\s*["']\/auth["']\s*,\s*authRoutes\s*\)/.test(routeContent);
|
|
944
|
+
if (!hasAuthRoute) {
|
|
945
|
+
const routeSetup = `
|
|
946
|
+
// Auth routes
|
|
947
|
+
rootRouter.use("/auth", authRoutes);
|
|
948
|
+
`;
|
|
949
|
+
const exportMatch = routeContent.match(/export default rootRouter;?\s*$/m);
|
|
950
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
routeContent = routeContent.slice(0, exportMatch.index) + routeSetup + "\n" + routeContent.slice(exportMatch.index);
|
|
954
|
+
routeModified = true;
|
|
955
|
+
}
|
|
906
956
|
}
|
|
907
957
|
const hasUserRoute = /rootRouter\.use\(\s*["']\/users["']\s*,\s*userRoutes\s*\)/.test(routeContent);
|
|
908
958
|
if (!hasUserRoute) {
|
|
@@ -921,6 +971,29 @@ rootRouter.use("/users", userRoutes);
|
|
|
921
971
|
await fs6.writeFile(routeIndexPath, routeContent);
|
|
922
972
|
}
|
|
923
973
|
} else {
|
|
974
|
+
if (provider === "jwt") {
|
|
975
|
+
const authImportResult = appendImport(appContent, appAuthImport);
|
|
976
|
+
if (!authImportResult.inserted) {
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
if (authImportResult.source !== appContent) {
|
|
980
|
+
appContent = authImportResult.source;
|
|
981
|
+
appModified = true;
|
|
982
|
+
}
|
|
983
|
+
const hasAuthRoute = /app\.use\(\s*["']\/api\/auth["']\s*,\s*authRoutes\s*\)/.test(appContent);
|
|
984
|
+
if (!hasAuthRoute) {
|
|
985
|
+
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
986
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
const routeSetup = `
|
|
990
|
+
// Auth routes
|
|
991
|
+
app.use("/api/auth", authRoutes);
|
|
992
|
+
`;
|
|
993
|
+
appContent = appContent.slice(0, exportMatch.index) + routeSetup + "\n" + appContent.slice(exportMatch.index);
|
|
994
|
+
appModified = true;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
924
997
|
const hasUserRoute = /app\.use\(\s*["']\/api\/users["']\s*,\s*userRoutes\s*\)/.test(appContent);
|
|
925
998
|
if (!hasUserRoute) {
|
|
926
999
|
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
@@ -948,21 +1021,23 @@ app.use("/api/users", userRoutes);
|
|
|
948
1021
|
}
|
|
949
1022
|
return true;
|
|
950
1023
|
}
|
|
951
|
-
async function injectAuthDocs(projectRoot, srcDir) {
|
|
1024
|
+
async function injectAuthDocs(projectRoot, srcDir, provider) {
|
|
952
1025
|
const openApiPath = path7.join(projectRoot, srcDir, "lib", "openapi.ts");
|
|
953
1026
|
if (!await fs6.pathExists(openApiPath)) {
|
|
954
1027
|
return false;
|
|
955
1028
|
}
|
|
956
|
-
const
|
|
1029
|
+
const betterAuthMarker = "// ZURO_AUTH_DOCS_BETTER_AUTH";
|
|
1030
|
+
const jwtMarker = "// ZURO_AUTH_DOCS_JWT";
|
|
1031
|
+
const legacyMarker = "// ZURO_AUTH_DOCS";
|
|
957
1032
|
let content = await fs6.readFile(openApiPath, "utf-8");
|
|
958
|
-
if (content.includes(
|
|
1033
|
+
if (content.includes(betterAuthMarker) || content.includes(jwtMarker) || content.includes(legacyMarker)) {
|
|
959
1034
|
return true;
|
|
960
1035
|
}
|
|
961
1036
|
const moduleDocsEndMarker = "// ZURO_DOCS_MODULES_END";
|
|
962
1037
|
if (!content.includes(moduleDocsEndMarker)) {
|
|
963
1038
|
return false;
|
|
964
1039
|
}
|
|
965
|
-
const authBlock = `
|
|
1040
|
+
const authBlock = provider === "jwt" ? `
|
|
966
1041
|
const authSignUpSchema = z.object({
|
|
967
1042
|
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
968
1043
|
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
@@ -974,13 +1049,169 @@ const authSignInSchema = z.object({
|
|
|
974
1049
|
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
975
1050
|
});
|
|
976
1051
|
|
|
1052
|
+
const authRefreshSchema = z.object({
|
|
1053
|
+
refreshToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
const authTokenSchema = z.object({
|
|
1057
|
+
accessToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1058
|
+
refreshToken: z.string().openapi({ example: "eyJhbGciOi..." }),
|
|
1059
|
+
tokenType: z.literal("Bearer").openapi({ example: "Bearer" }),
|
|
1060
|
+
});
|
|
1061
|
+
|
|
977
1062
|
const authUserSchema = z.object({
|
|
978
1063
|
id: z.string().openapi({ example: "user_123" }),
|
|
979
1064
|
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
980
1065
|
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
981
1066
|
});
|
|
982
1067
|
|
|
983
|
-
${
|
|
1068
|
+
${jwtMarker}
|
|
1069
|
+
registry.registerPath({
|
|
1070
|
+
method: "post",
|
|
1071
|
+
path: "/api/auth/sign-up/email",
|
|
1072
|
+
tags: ["Auth"],
|
|
1073
|
+
summary: "Register user and issue JWT tokens",
|
|
1074
|
+
request: {
|
|
1075
|
+
body: {
|
|
1076
|
+
content: {
|
|
1077
|
+
"application/json": {
|
|
1078
|
+
schema: authSignUpSchema,
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
},
|
|
1083
|
+
responses: {
|
|
1084
|
+
200: {
|
|
1085
|
+
description: "Registration successful",
|
|
1086
|
+
content: {
|
|
1087
|
+
"application/json": {
|
|
1088
|
+
schema: z.object({ user: authUserSchema, ...authTokenSchema.shape }),
|
|
1089
|
+
},
|
|
1090
|
+
},
|
|
1091
|
+
},
|
|
1092
|
+
409: { description: "Email already registered" },
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
registry.registerPath({
|
|
1097
|
+
method: "post",
|
|
1098
|
+
path: "/api/auth/sign-in/email",
|
|
1099
|
+
tags: ["Auth"],
|
|
1100
|
+
summary: "Sign in and issue JWT tokens",
|
|
1101
|
+
request: {
|
|
1102
|
+
body: {
|
|
1103
|
+
content: {
|
|
1104
|
+
"application/json": {
|
|
1105
|
+
schema: authSignInSchema,
|
|
1106
|
+
},
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
responses: {
|
|
1111
|
+
200: {
|
|
1112
|
+
description: "Sign in successful",
|
|
1113
|
+
content: {
|
|
1114
|
+
"application/json": {
|
|
1115
|
+
schema: authTokenSchema,
|
|
1116
|
+
},
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
401: { description: "Invalid credentials" },
|
|
1120
|
+
},
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
registry.registerPath({
|
|
1124
|
+
method: "post",
|
|
1125
|
+
path: "/api/auth/refresh",
|
|
1126
|
+
tags: ["Auth"],
|
|
1127
|
+
summary: "Exchange refresh token for new JWT tokens",
|
|
1128
|
+
request: {
|
|
1129
|
+
body: {
|
|
1130
|
+
content: {
|
|
1131
|
+
"application/json": {
|
|
1132
|
+
schema: authRefreshSchema,
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
responses: {
|
|
1138
|
+
200: {
|
|
1139
|
+
description: "Tokens refreshed",
|
|
1140
|
+
content: {
|
|
1141
|
+
"application/json": {
|
|
1142
|
+
schema: authTokenSchema,
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
401: { description: "Invalid refresh token" },
|
|
1147
|
+
},
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
registry.registerPath({
|
|
1151
|
+
method: "post",
|
|
1152
|
+
path: "/api/auth/sign-out",
|
|
1153
|
+
tags: ["Auth"],
|
|
1154
|
+
summary: "Client-side sign out for JWT auth",
|
|
1155
|
+
responses: {
|
|
1156
|
+
200: { description: "Sign out successful" },
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
registry.registerPath({
|
|
1161
|
+
method: "get",
|
|
1162
|
+
path: "/api/auth/get-session",
|
|
1163
|
+
tags: ["Auth"],
|
|
1164
|
+
summary: "Get current JWT session user",
|
|
1165
|
+
security: [{ bearerAuth: [] }],
|
|
1166
|
+
responses: {
|
|
1167
|
+
200: {
|
|
1168
|
+
description: "Current session",
|
|
1169
|
+
content: {
|
|
1170
|
+
"application/json": {
|
|
1171
|
+
schema: z.object({ user: authUserSchema.nullable() }),
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
},
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
registry.registerPath({
|
|
1179
|
+
method: "get",
|
|
1180
|
+
path: "/api/users/me",
|
|
1181
|
+
tags: ["Auth"],
|
|
1182
|
+
summary: "Get current authenticated user",
|
|
1183
|
+
security: [{ bearerAuth: [] }],
|
|
1184
|
+
responses: {
|
|
1185
|
+
200: {
|
|
1186
|
+
description: "Current user",
|
|
1187
|
+
content: {
|
|
1188
|
+
"application/json": {
|
|
1189
|
+
schema: z.object({ user: authUserSchema }),
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
},
|
|
1193
|
+
401: { description: "Not authenticated" },
|
|
1194
|
+
},
|
|
1195
|
+
});
|
|
1196
|
+
` : `
|
|
1197
|
+
const authSignUpSchema = z.object({
|
|
1198
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1199
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1200
|
+
name: z.string().min(1).optional().openapi({ example: "Dev User" }),
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
const authSignInSchema = z.object({
|
|
1204
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1205
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
const authUserSchema = z.object({
|
|
1209
|
+
id: z.string().openapi({ example: "user_123" }),
|
|
1210
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1211
|
+
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
${betterAuthMarker}
|
|
984
1215
|
registry.registerPath({
|
|
985
1216
|
method: "post",
|
|
986
1217
|
path: "/api/auth/sign-up/email",
|
|
@@ -1057,7 +1288,27 @@ ${moduleDocsEndMarker}`);
|
|
|
1057
1288
|
async function promptAuthConfig(projectRoot, srcDir, options) {
|
|
1058
1289
|
const { isDocsModuleInstalled: isDocsModuleInstalled2 } = await import("./docs.handler-JL3ZIVJQ.mjs");
|
|
1059
1290
|
const docsInstalled = await isDocsModuleInstalled2(projectRoot, srcDir);
|
|
1291
|
+
let authProvider = "better-auth";
|
|
1060
1292
|
let shouldInstallDocsForAuth = false;
|
|
1293
|
+
if (options.authProvider) {
|
|
1294
|
+
authProvider = options.authProvider;
|
|
1295
|
+
} else if (!options.yes) {
|
|
1296
|
+
const providerResponse = await prompts2({
|
|
1297
|
+
type: "select",
|
|
1298
|
+
name: "provider",
|
|
1299
|
+
message: "Choose auth provider:",
|
|
1300
|
+
choices: [
|
|
1301
|
+
{ title: "Better Auth (session + plugin ecosystem)", value: "better-auth" },
|
|
1302
|
+
{ title: "JWT (access/refresh tokens)", value: "jwt" }
|
|
1303
|
+
],
|
|
1304
|
+
initial: 0
|
|
1305
|
+
});
|
|
1306
|
+
if (!providerResponse.provider) {
|
|
1307
|
+
console.log(chalk4.yellow("Operation cancelled."));
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
authProvider = providerResponse.provider;
|
|
1311
|
+
}
|
|
1061
1312
|
if (!docsInstalled) {
|
|
1062
1313
|
if (options.yes) {
|
|
1063
1314
|
shouldInstallDocsForAuth = true;
|
|
@@ -1077,13 +1328,19 @@ async function promptAuthConfig(projectRoot, srcDir, options) {
|
|
|
1077
1328
|
}
|
|
1078
1329
|
const { detectInstalledDatabaseDialect: detectInstalledDatabaseDialect2 } = await import("./database.handler-5OUD6XJZ.mjs");
|
|
1079
1330
|
const authDatabaseDialect = await detectInstalledDatabaseDialect2(projectRoot, srcDir);
|
|
1080
|
-
return { shouldInstallDocsForAuth, authDatabaseDialect };
|
|
1331
|
+
return { authProvider, shouldInstallDocsForAuth, authDatabaseDialect };
|
|
1081
1332
|
}
|
|
1082
|
-
function printAuthHints(generatedAuthSecret) {
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1333
|
+
function printAuthHints(generatedAuthSecret, provider) {
|
|
1334
|
+
if (provider === "better-auth") {
|
|
1335
|
+
if (generatedAuthSecret) {
|
|
1336
|
+
console.log(chalk4.yellow("\u2139 BETTER_AUTH_SECRET was generated automatically."));
|
|
1337
|
+
} else {
|
|
1338
|
+
console.log(chalk4.yellow("\u2139 Review BETTER_AUTH_SECRET and BETTER_AUTH_URL in .env."));
|
|
1339
|
+
}
|
|
1340
|
+
} else if (generatedAuthSecret) {
|
|
1341
|
+
console.log(chalk4.yellow("\u2139 JWT_ACCESS_SECRET and JWT_REFRESH_SECRET were generated automatically."));
|
|
1085
1342
|
} else {
|
|
1086
|
-
console.log(chalk4.yellow("\u2139 Review
|
|
1343
|
+
console.log(chalk4.yellow("\u2139 Review JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, and token TTLs in .env."));
|
|
1087
1344
|
}
|
|
1088
1345
|
console.log(chalk4.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
|
|
1089
1346
|
}
|
|
@@ -1862,6 +2119,8 @@ var add = async (moduleName, options = {}) => {
|
|
|
1862
2119
|
let selectedDatabaseDialect = null;
|
|
1863
2120
|
let generatedAuthSecret = false;
|
|
1864
2121
|
let authDatabaseDialect = null;
|
|
2122
|
+
let authProvider = options.authProvider || "better-auth";
|
|
2123
|
+
let uploadAuthProvider = null;
|
|
1865
2124
|
let customSmtpVars;
|
|
1866
2125
|
let usedDefaultSmtp = false;
|
|
1867
2126
|
let mailerProvider = "smtp";
|
|
@@ -1888,6 +2147,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
1888
2147
|
if (resolvedModuleName === "auth") {
|
|
1889
2148
|
const result = await promptAuthConfig(projectRoot, srcDir, options);
|
|
1890
2149
|
if (!result) return;
|
|
2150
|
+
authProvider = result.authProvider;
|
|
1891
2151
|
shouldInstallDocsForAuth = result.shouldInstallDocsForAuth;
|
|
1892
2152
|
authDatabaseDialect = result.authDatabaseDialect;
|
|
1893
2153
|
}
|
|
@@ -1928,6 +2188,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
1928
2188
|
await add("auth", { yes: true });
|
|
1929
2189
|
}
|
|
1930
2190
|
uploadDatabaseDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
|
|
2191
|
+
uploadAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir);
|
|
1931
2192
|
if (uploadConfig.useDatabaseMetadata) {
|
|
1932
2193
|
if (uploadDatabaseDialect === "database-prisma-pg" || uploadDatabaseDialect === "database-prisma-mysql") {
|
|
1933
2194
|
spinner.fail("Uploads metadata currently supports Drizzle-based database setup only.");
|
|
@@ -1962,6 +2223,15 @@ var add = async (moduleName, options = {}) => {
|
|
|
1962
2223
|
devDeps = ["@types/nodemailer"];
|
|
1963
2224
|
}
|
|
1964
2225
|
}
|
|
2226
|
+
if (resolvedModuleName === "auth") {
|
|
2227
|
+
if (authProvider === "jwt") {
|
|
2228
|
+
runtimeDeps = ["jsonwebtoken", "bcryptjs"];
|
|
2229
|
+
devDeps = ["@types/jsonwebtoken", "@types/bcryptjs"];
|
|
2230
|
+
} else {
|
|
2231
|
+
runtimeDeps = ["better-auth"];
|
|
2232
|
+
devDeps = [];
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
1965
2235
|
if (resolvedModuleName === "uploads" && uploadConfig) {
|
|
1966
2236
|
runtimeDeps = ["multer"];
|
|
1967
2237
|
devDeps = ["@types/multer"];
|
|
@@ -1980,11 +2250,46 @@ var add = async (moduleName, options = {}) => {
|
|
|
1980
2250
|
let fetchPath = file.path;
|
|
1981
2251
|
let expectedSha256 = file.sha256;
|
|
1982
2252
|
let expectedSize = file.size;
|
|
2253
|
+
if (resolvedModuleName === "auth" && authProvider === "better-auth" && (file.target === "routes/auth.routes.ts" || file.target === "controllers/auth.controller.ts")) {
|
|
2254
|
+
continue;
|
|
2255
|
+
}
|
|
1983
2256
|
if (resolvedModuleName === "auth" && file.target === "db/schema/auth.ts" && authDatabaseDialect === "database-mysql") {
|
|
1984
2257
|
fetchPath = "express/db/schema/auth.mysql.ts";
|
|
1985
2258
|
expectedSha256 = void 0;
|
|
1986
2259
|
expectedSize = void 0;
|
|
1987
2260
|
}
|
|
2261
|
+
if (resolvedModuleName === "auth" && authProvider === "jwt") {
|
|
2262
|
+
if (file.target === "lib/auth.ts") {
|
|
2263
|
+
fetchPath = "express/lib/auth.jwt.ts";
|
|
2264
|
+
expectedSha256 = void 0;
|
|
2265
|
+
expectedSize = void 0;
|
|
2266
|
+
}
|
|
2267
|
+
if (file.target === "controllers/user.controller.ts") {
|
|
2268
|
+
fetchPath = "express/controllers/user.controller.jwt.ts";
|
|
2269
|
+
expectedSha256 = void 0;
|
|
2270
|
+
expectedSize = void 0;
|
|
2271
|
+
}
|
|
2272
|
+
if (file.target === "routes/user.routes.ts") {
|
|
2273
|
+
fetchPath = "express/routes/user.routes.jwt.ts";
|
|
2274
|
+
expectedSha256 = void 0;
|
|
2275
|
+
expectedSize = void 0;
|
|
2276
|
+
}
|
|
2277
|
+
if (file.target === "controllers/auth.controller.ts") {
|
|
2278
|
+
fetchPath = "express/controllers/auth.controller.jwt.ts";
|
|
2279
|
+
expectedSha256 = void 0;
|
|
2280
|
+
expectedSize = void 0;
|
|
2281
|
+
}
|
|
2282
|
+
if (file.target === "routes/auth.routes.ts") {
|
|
2283
|
+
fetchPath = "express/routes/auth.routes.jwt.ts";
|
|
2284
|
+
expectedSha256 = void 0;
|
|
2285
|
+
expectedSize = void 0;
|
|
2286
|
+
}
|
|
2287
|
+
if (file.target === "db/schema/auth.ts") {
|
|
2288
|
+
fetchPath = authDatabaseDialect === "database-mysql" ? "express/db/schema/auth.mysql.jwt.ts" : "express/db/schema/auth.jwt.ts";
|
|
2289
|
+
expectedSha256 = void 0;
|
|
2290
|
+
expectedSize = void 0;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
1988
2293
|
if (resolvedModuleName === "mailer" && file.target === "lib/mailer.ts" && mailerProvider === "resend") {
|
|
1989
2294
|
fetchPath = "express/lib/mailer.resend.ts";
|
|
1990
2295
|
expectedSha256 = void 0;
|
|
@@ -2002,7 +2307,12 @@ var add = async (moduleName, options = {}) => {
|
|
|
2002
2307
|
expectedSize = void 0;
|
|
2003
2308
|
}
|
|
2004
2309
|
if (file.target === "middleware/upload-auth.ts") {
|
|
2005
|
-
|
|
2310
|
+
if (uploadConfig.authMode === "none") {
|
|
2311
|
+
fetchPath = "express/middleware/upload-auth.none.ts";
|
|
2312
|
+
} else {
|
|
2313
|
+
const providerSuffix = uploadAuthProvider === "jwt" ? "jwt" : "better-auth";
|
|
2314
|
+
fetchPath = `express/middleware/upload-auth.required.${providerSuffix}.ts`;
|
|
2315
|
+
}
|
|
2006
2316
|
expectedSha256 = void 0;
|
|
2007
2317
|
expectedSize = void 0;
|
|
2008
2318
|
}
|
|
@@ -2038,7 +2348,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2038
2348
|
spinner.succeed("Files generated");
|
|
2039
2349
|
if (resolvedModuleName === "auth") {
|
|
2040
2350
|
spinner.start("Configuring routes...");
|
|
2041
|
-
const injected = await injectAuthRoutes(projectRoot, srcDir);
|
|
2351
|
+
const injected = await injectAuthRoutes(projectRoot, srcDir, authProvider);
|
|
2042
2352
|
if (injected) {
|
|
2043
2353
|
spinner.succeed("Routes configured");
|
|
2044
2354
|
} else {
|
|
@@ -2047,7 +2357,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2047
2357
|
const docsInstalled = await isDocsModuleInstalled(projectRoot, srcDir);
|
|
2048
2358
|
if (docsInstalled) {
|
|
2049
2359
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2050
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2360
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, authProvider);
|
|
2051
2361
|
if (authDocsInjected) {
|
|
2052
2362
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2053
2363
|
} else {
|
|
@@ -2083,8 +2393,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2083
2393
|
}
|
|
2084
2394
|
const authInstalled = await isAuthModuleInstalled(projectRoot, srcDir);
|
|
2085
2395
|
if (authInstalled) {
|
|
2396
|
+
const installedAuthProvider = await detectInstalledAuthProvider(projectRoot, srcDir) || "better-auth";
|
|
2086
2397
|
spinner.start("Adding auth endpoints to API docs...");
|
|
2087
|
-
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
2398
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir, installedAuthProvider);
|
|
2088
2399
|
if (authDocsInjected) {
|
|
2089
2400
|
spinner.succeed("Auth endpoints added to API docs");
|
|
2090
2401
|
} else {
|
|
@@ -2128,6 +2439,9 @@ var add = async (moduleName, options = {}) => {
|
|
|
2128
2439
|
if (resolvedModuleName === "mailer" && mailerProvider === "resend") {
|
|
2129
2440
|
envConfigKey = "mailer-resend";
|
|
2130
2441
|
}
|
|
2442
|
+
if (resolvedModuleName === "auth" && authProvider === "jwt") {
|
|
2443
|
+
envConfigKey = "auth-jwt";
|
|
2444
|
+
}
|
|
2131
2445
|
const envConfig = ENV_CONFIGS[envConfigKey];
|
|
2132
2446
|
if (envConfig) {
|
|
2133
2447
|
currentStep = "environment configuration";
|
|
@@ -2140,10 +2454,23 @@ var add = async (moduleName, options = {}) => {
|
|
|
2140
2454
|
Object.assign(envVars, customSmtpVars);
|
|
2141
2455
|
}
|
|
2142
2456
|
if (resolvedModuleName === "auth") {
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2457
|
+
if (authProvider === "better-auth") {
|
|
2458
|
+
const hasExistingSecret = await hasEnvVariable(projectRoot, "BETTER_AUTH_SECRET");
|
|
2459
|
+
if (!hasExistingSecret) {
|
|
2460
|
+
envVars.BETTER_AUTH_SECRET = randomBytes(32).toString("hex");
|
|
2461
|
+
generatedAuthSecret = true;
|
|
2462
|
+
}
|
|
2463
|
+
} else {
|
|
2464
|
+
const hasAccessSecret = await hasEnvVariable(projectRoot, "JWT_ACCESS_SECRET");
|
|
2465
|
+
if (!hasAccessSecret) {
|
|
2466
|
+
envVars.JWT_ACCESS_SECRET = randomBytes(32).toString("hex");
|
|
2467
|
+
generatedAuthSecret = true;
|
|
2468
|
+
}
|
|
2469
|
+
const hasRefreshSecret = await hasEnvVariable(projectRoot, "JWT_REFRESH_SECRET");
|
|
2470
|
+
if (!hasRefreshSecret) {
|
|
2471
|
+
envVars.JWT_REFRESH_SECRET = randomBytes(32).toString("hex");
|
|
2472
|
+
generatedAuthSecret = true;
|
|
2473
|
+
}
|
|
2147
2474
|
}
|
|
2148
2475
|
}
|
|
2149
2476
|
await updateEnvFile(projectRoot, envVars, true, {
|
|
@@ -2184,7 +2511,7 @@ var add = async (moduleName, options = {}) => {
|
|
|
2184
2511
|
printDatabaseHints(resolvedModuleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath);
|
|
2185
2512
|
}
|
|
2186
2513
|
if (resolvedModuleName === "auth") {
|
|
2187
|
-
printAuthHints(generatedAuthSecret);
|
|
2514
|
+
printAuthHints(generatedAuthSecret, authProvider);
|
|
2188
2515
|
}
|
|
2189
2516
|
if (resolvedModuleName === "mailer") {
|
|
2190
2517
|
printMailerHints(usedDefaultSmtp);
|
|
@@ -2213,6 +2540,15 @@ ${chalk7.bold("Retry:")}`);
|
|
|
2213
2540
|
var program = new Command();
|
|
2214
2541
|
program.name("zuro-cli").description("Zuro CLI tool").version("0.0.1");
|
|
2215
2542
|
program.command("init").description("Initialize a new Zuro project").action(init);
|
|
2216
|
-
program.command("add <module>").description("Add a module to your project").
|
|
2543
|
+
program.command("add <module>").description("Add a module to your project").option(
|
|
2544
|
+
"--auth-provider <provider>",
|
|
2545
|
+
"Auth provider for auth module (better-auth|jwt)",
|
|
2546
|
+
(value) => {
|
|
2547
|
+
if (value === "better-auth" || value === "jwt") {
|
|
2548
|
+
return value;
|
|
2549
|
+
}
|
|
2550
|
+
throw new InvalidArgumentError("auth-provider must be 'better-auth' or 'jwt'");
|
|
2551
|
+
}
|
|
2552
|
+
).option("-y, --yes", "Skip prompts and use defaults").action((module, options) => add(module, options));
|
|
2217
2553
|
program.parse(process.argv);
|
|
2218
2554
|
//# sourceMappingURL=index.mjs.map
|