stackkit 0.1.6 → 0.1.8
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/cli/add.js +2 -2
- package/dist/cli/create.js +4 -2
- package/dist/index.js +4 -1
- package/dist/lib/env/env-editor.js +2 -18
- package/dist/lib/generation/code-generator.js +110 -23
- package/dist/lib/generation/generator-utils.js +18 -5
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +2 -6
- package/modules/auth/authjs/files/lib/auth.ts +15 -29
- package/modules/auth/authjs/files/{schemas/prisma-schema.prisma → prisma/schema.prisma} +17 -11
- package/modules/auth/authjs/generator.json +49 -4
- package/modules/auth/better-auth/files/lib/auth.ts +8 -4
- package/modules/auth/better-auth/files/lib/email-service.ts +1 -2
- package/modules/auth/better-auth/files/prisma/schema.prisma +3 -3
- package/modules/auth/better-auth/generator.json +9 -8
- package/modules/database/mongoose/generator.json +9 -7
- package/modules/database/prisma/files/lib/prisma.ts +4 -4
- package/modules/database/prisma/files/prisma.config.ts +2 -2
- package/modules/database/prisma/generator.json +17 -12
- package/package.json +1 -1
- package/modules/auth/authjs/files/lib/auth-client.ts +0 -11
package/dist/cli/add.js
CHANGED
|
@@ -49,10 +49,10 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
49
49
|
if (module === "database" || module === "auth") {
|
|
50
50
|
if (!options?.provider) {
|
|
51
51
|
if (module === "database") {
|
|
52
|
-
throw new Error(
|
|
52
|
+
throw new Error("Provider is required for database. Use: `npx stackkit add database --provider <provider>`");
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
|
-
throw new Error(
|
|
55
|
+
throw new Error("Provider is required for auth. Use: `npx stackkit add auth --provider <provider>`");
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
if (module === "database") {
|
package/dist/cli/create.js
CHANGED
|
@@ -306,7 +306,8 @@ async function processGeneratorEnvVars(config, targetDir) {
|
|
|
306
306
|
const generator = await fs_extra_1.default.readJson(dbGeneratorPath);
|
|
307
307
|
if (generator.operations) {
|
|
308
308
|
for (const operation of generator.operations) {
|
|
309
|
-
if (operation.type === "add-env" &&
|
|
309
|
+
if (operation.type === "add-env" &&
|
|
310
|
+
(!operation.condition || checkCondition(operation.condition, config))) {
|
|
310
311
|
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
311
312
|
envVars.push({
|
|
312
313
|
key,
|
|
@@ -326,7 +327,8 @@ async function processGeneratorEnvVars(config, targetDir) {
|
|
|
326
327
|
const generator = await fs_extra_1.default.readJson(authGeneratorPath);
|
|
327
328
|
if (generator.operations) {
|
|
328
329
|
for (const operation of generator.operations) {
|
|
329
|
-
if (operation.type === "add-env" &&
|
|
330
|
+
if (operation.type === "add-env" &&
|
|
331
|
+
(!operation.condition || checkCondition(operation.condition, config))) {
|
|
330
332
|
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
331
333
|
envVars.push({
|
|
332
334
|
key,
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,14 @@ const add_1 = require("./cli/add");
|
|
|
7
7
|
const doctor_1 = require("./cli/doctor");
|
|
8
8
|
const list_1 = require("./cli/list");
|
|
9
9
|
const logger_1 = require("./lib/ui/logger");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, "../package.json"), "utf-8"));
|
|
10
13
|
const program = new commander_1.Command();
|
|
11
14
|
program
|
|
12
15
|
.name("stackkit")
|
|
13
16
|
.description("CLI for creating and managing StackKit projects")
|
|
14
|
-
.version(
|
|
17
|
+
.version(packageJson.version)
|
|
15
18
|
.configureHelp({
|
|
16
19
|
subcommandTerm: (cmd) => {
|
|
17
20
|
const name = cmd.name();
|
|
@@ -8,8 +8,6 @@ exports.removeEnvVariables = removeEnvVariables;
|
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const logger_1 = require("../ui/logger");
|
|
11
|
-
const ENV_MARKER_START = "# StackKit:";
|
|
12
|
-
const ENV_MARKER_END = "# End StackKit";
|
|
13
11
|
async function addEnvVariables(projectRoot, variables, options = {}) {
|
|
14
12
|
const envExamplePath = path_1.default.join(projectRoot, ".env.example");
|
|
15
13
|
const envPath = path_1.default.join(projectRoot, ".env");
|
|
@@ -59,14 +57,11 @@ async function appendToEnvFile(filePath, variables, fileType, options = {}) {
|
|
|
59
57
|
if (content && !content.endsWith("\n")) {
|
|
60
58
|
content += "\n";
|
|
61
59
|
}
|
|
62
|
-
//
|
|
63
|
-
content += "\n";
|
|
64
|
-
content += `${ENV_MARKER_START} Added by StackKit\n`;
|
|
60
|
+
// Append variables
|
|
65
61
|
for (const variable of newVariables) {
|
|
66
62
|
const value = fileType === "example" ? variable.value || "" : variable.value || "";
|
|
67
63
|
content += `${variable.key}=${value}\n`;
|
|
68
64
|
}
|
|
69
|
-
content += `${ENV_MARKER_END}\n`;
|
|
70
65
|
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
71
66
|
await fs_extra_1.default.writeFile(filePath, content, "utf-8");
|
|
72
67
|
}
|
|
@@ -86,23 +81,12 @@ async function removeFromEnvFile(filePath, keys) {
|
|
|
86
81
|
const content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
87
82
|
const lines = content.split("\n");
|
|
88
83
|
const newLines = [];
|
|
89
|
-
let inStackKitBlock = false;
|
|
90
84
|
for (const line of lines) {
|
|
91
|
-
if (line.includes(ENV_MARKER_START)) {
|
|
92
|
-
inStackKitBlock = true;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (line.includes(ENV_MARKER_END)) {
|
|
96
|
-
inStackKitBlock = false;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
85
|
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
100
86
|
if (match && keys.includes(match[1])) {
|
|
101
87
|
continue;
|
|
102
88
|
}
|
|
103
|
-
|
|
104
|
-
newLines.push(line);
|
|
105
|
-
}
|
|
89
|
+
newLines.push(line);
|
|
106
90
|
}
|
|
107
91
|
while (newLines.length > 0 && newLines[newLines.length - 1].trim() === "") {
|
|
108
92
|
newLines.pop();
|
|
@@ -175,7 +175,7 @@ class AdvancedCodeGenerator {
|
|
|
175
175
|
conditionMet = typeof actualVal === "string" && actualVal.endsWith(cleanExpectedVal);
|
|
176
176
|
break;
|
|
177
177
|
}
|
|
178
|
-
const contentToProcess = conditionMet ? blockContent :
|
|
178
|
+
const contentToProcess = conditionMet ? blockContent : elseContent || "";
|
|
179
179
|
return this.processTemplateRecursive(contentToProcess, context)
|
|
180
180
|
.replace(/^\n+/, "")
|
|
181
181
|
.replace(/\n+$/, "");
|
|
@@ -185,7 +185,7 @@ class AdvancedCodeGenerator {
|
|
|
185
185
|
const conditionParts = condition.split("==");
|
|
186
186
|
if (conditionParts.length === 2) {
|
|
187
187
|
const [varName, expectedValue] = conditionParts.map((s) => s.trim().replace(/['"]/g, ""));
|
|
188
|
-
const contentToProcess = context[varName] === expectedValue ? blockContent :
|
|
188
|
+
const contentToProcess = context[varName] === expectedValue ? blockContent : elseContent || "";
|
|
189
189
|
return this.processTemplateRecursive(contentToProcess, context)
|
|
190
190
|
.replace(/^\n+/, "")
|
|
191
191
|
.replace(/\n+$/, "");
|
|
@@ -195,7 +195,7 @@ class AdvancedCodeGenerator {
|
|
|
195
195
|
const [arrayName, item] = conditionFunc[0].split("(");
|
|
196
196
|
const itemValue = item.replace(")", "").replace(/['"]/g, "");
|
|
197
197
|
const array = context[arrayName] || [];
|
|
198
|
-
const contentToProcess = Array.isArray(array) && array.includes(itemValue) ? blockContent :
|
|
198
|
+
const contentToProcess = Array.isArray(array) && array.includes(itemValue) ? blockContent : elseContent || "";
|
|
199
199
|
return this.processTemplateRecursive(contentToProcess, context)
|
|
200
200
|
.replace(/^\n+/, "")
|
|
201
201
|
.replace(/\n+$/, "");
|
|
@@ -214,14 +214,14 @@ class AdvancedCodeGenerator {
|
|
|
214
214
|
const [, caseValue, caseContent] = caseMatch;
|
|
215
215
|
const cleanCaseValue = caseValue.trim().replace(/['"]/g, "");
|
|
216
216
|
if (cleanCaseValue === "default") {
|
|
217
|
-
defaultCase = caseContent;
|
|
217
|
+
defaultCase = caseContent.trim();
|
|
218
218
|
}
|
|
219
219
|
else if (actualVal === cleanCaseValue) {
|
|
220
|
-
result = caseContent;
|
|
220
|
+
result = caseContent.trim();
|
|
221
221
|
break;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
return result || defaultCase || "";
|
|
224
|
+
return (result || defaultCase || "").trim();
|
|
225
225
|
});
|
|
226
226
|
// Handle variable replacement with advanced expressions
|
|
227
227
|
content = content.replace(/\{\{([^}]+)\}\}/g, (match, varExpr) => {
|
|
@@ -378,6 +378,18 @@ class AdvancedCodeGenerator {
|
|
|
378
378
|
!relativePath.startsWith("node_modules/"));
|
|
379
379
|
},
|
|
380
380
|
});
|
|
381
|
+
// If template provides a .env.example, create a .env from it when
|
|
382
|
+
// the target project does not already have a .env file.
|
|
383
|
+
try {
|
|
384
|
+
const envExampleSrc = path.join(templatePath, ".env.example");
|
|
385
|
+
const envDest = path.join(outputPath, ".env");
|
|
386
|
+
if ((await fs.pathExists(envExampleSrc)) && !(await fs.pathExists(envDest))) {
|
|
387
|
+
await fs.copy(envExampleSrc, envDest);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
// ignore failures here — not critical
|
|
392
|
+
}
|
|
381
393
|
}
|
|
382
394
|
}
|
|
383
395
|
processOperationTemplates(operation, context) {
|
|
@@ -469,34 +481,107 @@ class AdvancedCodeGenerator {
|
|
|
469
481
|
switch (patchOp.type) {
|
|
470
482
|
case "add-import":
|
|
471
483
|
if (patchOp.imports) {
|
|
484
|
+
// Process imports and trim any accidental surrounding blank lines
|
|
472
485
|
const imports = patchOp.imports
|
|
473
486
|
.map((imp) => this.processTemplate(imp, context))
|
|
474
|
-
.join("\n")
|
|
475
|
-
|
|
487
|
+
.join("\n")
|
|
488
|
+
.replace(/^\n+/, "")
|
|
489
|
+
.replace(/\n+$/, "");
|
|
490
|
+
// Add imports at the top, after existing imports without introducing
|
|
491
|
+
// extra blank lines.
|
|
476
492
|
const lines = content.split("\n");
|
|
477
|
-
|
|
493
|
+
// Find the last import line index
|
|
494
|
+
let lastImportIndex = -1;
|
|
478
495
|
for (let i = 0; i < lines.length; i++) {
|
|
479
|
-
if (lines[i].trim().startsWith("import")
|
|
480
|
-
|
|
496
|
+
if (lines[i].trim().startsWith("import"))
|
|
497
|
+
lastImportIndex = i;
|
|
498
|
+
}
|
|
499
|
+
// Insert right after the last import. If there's a blank line
|
|
500
|
+
// immediately after the imports, overwrite that blank line to
|
|
501
|
+
// avoid introducing an extra empty line above the inserted imports.
|
|
502
|
+
const insertIndex = lastImportIndex === -1 ? 0 : lastImportIndex + 1;
|
|
503
|
+
// Only add imports that don't already exist in the file
|
|
504
|
+
const importLines = imports
|
|
505
|
+
.split("\n")
|
|
506
|
+
.map((l) => l.trim())
|
|
507
|
+
.filter(Boolean);
|
|
508
|
+
const newImportLines = importLines.filter((imp) => !lines.some((ln) => ln.trim() === imp));
|
|
509
|
+
if (newImportLines.length > 0) {
|
|
510
|
+
// Insert imports
|
|
511
|
+
if (insertIndex < lines.length && lines[insertIndex].trim() === "") {
|
|
512
|
+
lines.splice(insertIndex, 1, ...newImportLines);
|
|
481
513
|
}
|
|
482
514
|
else {
|
|
483
|
-
|
|
515
|
+
lines.splice(insertIndex, 0, ...newImportLines);
|
|
516
|
+
}
|
|
517
|
+
// After insertion, ensure exactly one blank line after the import block
|
|
518
|
+
// Find last import line index again
|
|
519
|
+
let lastIdx = -1;
|
|
520
|
+
for (let i = 0; i < lines.length; i++) {
|
|
521
|
+
if (lines[i].trim().startsWith("import"))
|
|
522
|
+
lastIdx = i;
|
|
523
|
+
}
|
|
524
|
+
const nextIdx = lastIdx + 1;
|
|
525
|
+
if (lastIdx !== -1) {
|
|
526
|
+
// Remove multiple blank lines after imports
|
|
527
|
+
let j = nextIdx;
|
|
528
|
+
while (j < lines.length && lines[j].trim() === "") {
|
|
529
|
+
j++;
|
|
530
|
+
}
|
|
531
|
+
// Ensure exactly one blank line after imports unless imports end at EOF
|
|
532
|
+
if (nextIdx < lines.length) {
|
|
533
|
+
lines.splice(nextIdx, j - nextIdx, "");
|
|
534
|
+
}
|
|
484
535
|
}
|
|
485
536
|
}
|
|
486
|
-
lines.splice(insertIndex, 0, imports);
|
|
487
537
|
content = lines.join("\n");
|
|
488
538
|
}
|
|
489
539
|
break;
|
|
490
540
|
case "add-code":
|
|
491
|
-
if (patchOp.code
|
|
541
|
+
if (patchOp.code) {
|
|
492
542
|
const processedCode = this.processTemplate(patchOp.code, context);
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
543
|
+
// Skip insertion if the exact code already exists in the file
|
|
544
|
+
const codeTrimmed = processedCode.trim();
|
|
545
|
+
if (codeTrimmed && content.includes(codeTrimmed)) {
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
// Insert after pattern if provided
|
|
549
|
+
if (patchOp.after) {
|
|
550
|
+
const afterPattern = this.processTemplate(patchOp.after, context);
|
|
551
|
+
const index = content.indexOf(afterPattern);
|
|
552
|
+
if (index !== -1) {
|
|
553
|
+
const left = content.slice(0, index + afterPattern.length);
|
|
554
|
+
const right = content.slice(index + afterPattern.length);
|
|
555
|
+
// Normalize code: trim surrounding newlines and ensure single trailing newline
|
|
556
|
+
let codeNormalized = processedCode.replace(/^\n+|\n+$/g, "") + "\n";
|
|
557
|
+
// If right already starts with a newline, avoid double-blank by
|
|
558
|
+
// removing trailing newline from codeNormalized so only one newline remains
|
|
559
|
+
const rightStartsWithNewline = right.startsWith("\n");
|
|
560
|
+
if (rightStartsWithNewline && codeNormalized.endsWith("\n")) {
|
|
561
|
+
codeNormalized = codeNormalized.replace(/\n+$/, "");
|
|
562
|
+
}
|
|
563
|
+
const leftNeedsNewline = !left.endsWith("\n");
|
|
564
|
+
content = left + (leftNeedsNewline ? "\n" : "") + codeNormalized + right;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// Insert before pattern if provided
|
|
568
|
+
if (patchOp.before) {
|
|
569
|
+
const beforePattern = this.processTemplate(patchOp.before, context);
|
|
570
|
+
const index = content.indexOf(beforePattern);
|
|
571
|
+
if (index !== -1) {
|
|
572
|
+
const left = content.slice(0, index);
|
|
573
|
+
const right = content.slice(index);
|
|
574
|
+
// Normalize code: trim surrounding newlines and ensure single trailing newline
|
|
575
|
+
let codeNormalized = processedCode.replace(/^\n+|\n+$/g, "") + "\n";
|
|
576
|
+
// If right already starts with a newline, avoid double-blank by
|
|
577
|
+
// removing trailing newline from codeNormalized so only one newline remains
|
|
578
|
+
const rightStartsWithNewline = right.startsWith("\n");
|
|
579
|
+
if (rightStartsWithNewline && codeNormalized.endsWith("\n")) {
|
|
580
|
+
codeNormalized = codeNormalized.replace(/\n+$/, "");
|
|
581
|
+
}
|
|
582
|
+
const leftNeedsNewline = !left.endsWith("\n");
|
|
583
|
+
content = left + (leftNeedsNewline ? "\n" : "") + codeNormalized + right;
|
|
584
|
+
}
|
|
500
585
|
}
|
|
501
586
|
}
|
|
502
587
|
break;
|
|
@@ -546,6 +631,8 @@ class AdvancedCodeGenerator {
|
|
|
546
631
|
}
|
|
547
632
|
}
|
|
548
633
|
}
|
|
634
|
+
// Normalize excessive blank lines introduced during patching
|
|
635
|
+
content = content.replace(/\n{3,}/g, "\n\n");
|
|
549
636
|
// Write back the modified content
|
|
550
637
|
await fs.writeFile(filePath, content, "utf-8");
|
|
551
638
|
}
|
|
@@ -620,8 +707,8 @@ class AdvancedCodeGenerator {
|
|
|
620
707
|
if ((type === "framework" && name === selectedModules.framework) ||
|
|
621
708
|
(type === "database" && name === selectedModules.database) ||
|
|
622
709
|
(type === "auth" && name === selectedModules.auth)) {
|
|
623
|
-
|
|
624
|
-
|
|
710
|
+
// Dependencies and devDependencies are now provided via `add-dependency`
|
|
711
|
+
// operations. Keep merging scripts from generator configs for now.
|
|
625
712
|
Object.assign(allScripts, generator.scripts);
|
|
626
713
|
}
|
|
627
714
|
}
|
|
@@ -90,11 +90,24 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
// Collect dependencies/devDependencies from add-dependency operations
|
|
94
|
+
if (generator.operations && Array.isArray(generator.operations)) {
|
|
95
|
+
for (const operation of generator.operations) {
|
|
96
|
+
if (operation.type === "add-dependency") {
|
|
97
|
+
if (operation.dependencies) {
|
|
98
|
+
metadata.dependencies = {
|
|
99
|
+
...metadata.dependencies,
|
|
100
|
+
...operation.dependencies,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (operation.devDependencies) {
|
|
104
|
+
metadata.devDependencies = {
|
|
105
|
+
...metadata.devDependencies,
|
|
106
|
+
...operation.devDependencies,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
98
111
|
}
|
|
99
112
|
if (generator.postInstall && Array.isArray(generator.postInstall)) {
|
|
100
113
|
metadata.postInstall = metadata.postInstall || [];
|
|
@@ -1,36 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { PrismaAdapter } from "@auth/prisma-adapter"
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import NextAuth from "next-auth"
|
|
2
|
+
import { PrismaAdapter } from "@auth/prisma-adapter"
|
|
3
|
+
import prisma from "@/lib/prisma"
|
|
4
|
+
import Google from "next-auth/providers/google"
|
|
5
|
+
import { encode, decode } from 'next-auth/jwt';
|
|
5
6
|
|
|
6
|
-
export const
|
|
7
|
+
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
7
8
|
adapter: PrismaAdapter(prisma),
|
|
8
9
|
providers: [
|
|
9
|
-
|
|
10
|
-
clientId: process.env.
|
|
11
|
-
clientSecret: process.env.
|
|
10
|
+
Google({
|
|
11
|
+
clientId: process.env.AUTH_GOOGLE_ID,
|
|
12
|
+
clientSecret: process.env.AUTH_GOOGLE_SECRET,
|
|
12
13
|
}),
|
|
13
14
|
],
|
|
14
|
-
session: {
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
callbacks: {
|
|
18
|
-
async jwt({ token, user }) {
|
|
19
|
-
if (user) {
|
|
20
|
-
token.id = user.id;
|
|
21
|
-
}
|
|
22
|
-
return token;
|
|
23
|
-
},
|
|
24
|
-
async session({ session, token }) {
|
|
25
|
-
if (token) {
|
|
26
|
-
session.user.id = token.id as string;
|
|
27
|
-
}
|
|
28
|
-
return session;
|
|
29
|
-
},
|
|
30
|
-
},
|
|
15
|
+
session: { strategy: "jwt" },
|
|
16
|
+
secret: process.env.AUTH_SECRET,
|
|
17
|
+
jwt: { encode, decode },
|
|
31
18
|
pages: {
|
|
32
|
-
signIn:
|
|
33
|
-
|
|
34
|
-
error: "/auth/error",
|
|
19
|
+
signIn: '/sign-in',
|
|
20
|
+
error: '/error',
|
|
35
21
|
},
|
|
36
|
-
}
|
|
22
|
+
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
{{#var defaultId = {{#if prismaProvider == "mongodb"}}@default(auto()) @map("_id") @db.ObjectId{{else}}@default(cuid()){{/if}}}}
|
|
2
2
|
model Account {
|
|
3
|
-
id String @id {{
|
|
4
|
-
userId String
|
|
3
|
+
id String @id {{defaultId}}
|
|
4
|
+
userId String @map("user_id")
|
|
5
5
|
type String
|
|
6
6
|
provider String
|
|
7
|
-
providerAccountId String
|
|
7
|
+
providerAccountId String @map("provider_account_id")
|
|
8
8
|
refresh_token String? @db.Text
|
|
9
9
|
access_token String? @db.Text
|
|
10
10
|
expires_at Int?
|
|
@@ -16,30 +16,36 @@ model Account {
|
|
|
16
16
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
17
17
|
|
|
18
18
|
@@unique([provider, providerAccountId])
|
|
19
|
+
@@map("accounts")
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
model Session {
|
|
22
|
-
id String @id {{
|
|
23
|
-
sessionToken String @unique
|
|
24
|
-
userId String {{
|
|
23
|
+
id String @id {{defaultId}}
|
|
24
|
+
sessionToken String @unique @map("session_token")
|
|
25
|
+
userId String {{#if prismaProvider == "mongodb"}} @db.ObjectId{{/if}} @map("user_id")
|
|
25
26
|
expires DateTime
|
|
26
27
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
28
|
+
|
|
29
|
+
@@map("sessions")
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
model User {
|
|
30
|
-
id String @id {{
|
|
33
|
+
id String @id {{defaultId}}
|
|
31
34
|
name String?
|
|
32
|
-
email String
|
|
33
|
-
emailVerified DateTime?
|
|
35
|
+
email String? @unique
|
|
36
|
+
emailVerified DateTime? @map("email_verified")
|
|
34
37
|
image String?
|
|
35
38
|
accounts Account[]
|
|
36
39
|
sessions Session[]
|
|
40
|
+
|
|
41
|
+
@@map("users")
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
model VerificationToken {
|
|
40
45
|
identifier String
|
|
41
|
-
token String
|
|
46
|
+
token String
|
|
42
47
|
expires DateTime
|
|
43
48
|
|
|
44
49
|
@@unique([identifier, token])
|
|
50
|
+
@@map("verification_tokens")
|
|
45
51
|
}
|
|
@@ -2,8 +2,53 @@
|
|
|
2
2
|
"name": "authjs",
|
|
3
3
|
"type": "auth",
|
|
4
4
|
"priority": 10,
|
|
5
|
-
"operations": [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
"operations": [
|
|
6
|
+
{
|
|
7
|
+
"type": "create-file",
|
|
8
|
+
"source": "lib/auth.ts",
|
|
9
|
+
"destination": "lib/auth.ts"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "create-file",
|
|
13
|
+
"source": "api/auth/[...nextauth]/route.ts",
|
|
14
|
+
"destination": "app/api/auth/[...nextauth]/route.ts"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "create-file",
|
|
18
|
+
"destination": "proxy.ts",
|
|
19
|
+
"content": "export { auth as middleware } from \"@/auth\""
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "patch-file",
|
|
23
|
+
"destination": "prisma/schema.prisma",
|
|
24
|
+
"condition": { "database": "prisma" },
|
|
25
|
+
"operations": [
|
|
26
|
+
{
|
|
27
|
+
"type": "add-to-bottom",
|
|
28
|
+
"source": "prisma/schema.prisma"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "add-dependency",
|
|
34
|
+
"condition": { "database": "prisma" },
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@auth/prisma-adapter": "^0.5.0"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "add-env",
|
|
41
|
+
"envVars": {
|
|
42
|
+
"AUTH_SECRET": "",
|
|
43
|
+
"AUTH_GOOGLE_ID": "",
|
|
44
|
+
"AUTH_GOOGLE_SECRET": ""
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "add-dependency",
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"next-auth": "^5.0.0-beta.30"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
9
54
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { betterAuth } from "better-auth";
|
|
1
|
+
import { betterAuth, env } from "better-auth";
|
|
2
2
|
import { sendEmail } from "./email/email-service";
|
|
3
3
|
import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
|
|
4
4
|
{{#switch database}}
|
|
@@ -13,6 +13,12 @@ import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
|
13
13
|
{{/switch}}
|
|
14
14
|
|
|
15
15
|
export async function initAuth() {
|
|
16
|
+
{{#if database == 'mongoose'}}
|
|
17
|
+
const mongooseInstance = await mongoose();
|
|
18
|
+
const client = mongooseInstance.connection.getClient();
|
|
19
|
+
const db = client.db();
|
|
20
|
+
{{/if}}
|
|
21
|
+
|
|
16
22
|
return betterAuth({
|
|
17
23
|
{{#switch database}}
|
|
18
24
|
{{#case prisma}}
|
|
@@ -21,12 +27,10 @@ return betterAuth({
|
|
|
21
27
|
}),
|
|
22
28
|
{{/case}}
|
|
23
29
|
{{#case mongoose}}
|
|
24
|
-
const mongooseInstance = await mongoose();
|
|
25
|
-
const client = mongooseInstance.connection.getClient();
|
|
26
|
-
const db = client.db();
|
|
27
30
|
database: mongodbAdapter(db, { client }),
|
|
28
31
|
{{/case}}
|
|
29
32
|
{{/switch}}
|
|
33
|
+
secret: env.BETTER_AUTH_SECRET,
|
|
30
34
|
user: {
|
|
31
35
|
additionalFields: {
|
|
32
36
|
role: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import nodemailer from "nodemailer";
|
|
2
2
|
|
|
3
3
|
// Create email transporter
|
|
4
|
-
const transporter = nodemailer.
|
|
4
|
+
const transporter = nodemailer.createTransport({
|
|
5
5
|
host: process.env.EMAIL_HOST,
|
|
6
6
|
port: parseInt(process.env.EMAIL_PORT || "587"),
|
|
7
7
|
secure: process.env.EMAIL_PORT === "465",
|
|
@@ -27,7 +27,6 @@ export const sendEmail = async ({ to, subject, text, html }: {
|
|
|
27
27
|
html,
|
|
28
28
|
});
|
|
29
29
|
} catch (error) {
|
|
30
|
-
// eslint-disable-next-line no-console
|
|
31
30
|
console.error("Email sending failed:", error);
|
|
32
31
|
throw error;
|
|
33
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{{#var defaultId = {{#if prismaProvider == mongodb}}@default(auto()) @map("_id") @db.ObjectId{{else}}@default(cuid()){{/if}}}}
|
|
1
|
+
{{#var defaultId = {{#if prismaProvider == "mongodb"}}@default(auto()) @map("_id") @db.ObjectId{{else}}@default(cuid()){{/if}}}}
|
|
2
2
|
model User {
|
|
3
3
|
id String @id {{defaultId}}
|
|
4
4
|
name String
|
|
@@ -23,7 +23,7 @@ model Session {
|
|
|
23
23
|
updatedAt DateTime @updatedAt
|
|
24
24
|
ipAddress String?
|
|
25
25
|
userAgent String?
|
|
26
|
-
userId String {{#if prismaProvider == mongodb}} @db.ObjectId{{/if}}
|
|
26
|
+
userId String {{#if prismaProvider == "mongodb"}} @db.ObjectId{{/if}}
|
|
27
27
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
28
28
|
|
|
29
29
|
@@index([userId])
|
|
@@ -34,7 +34,7 @@ model Account {
|
|
|
34
34
|
id String @id {{defaultId}}
|
|
35
35
|
accountId String
|
|
36
36
|
providerId String
|
|
37
|
-
userId String {{#if prismaProvider == mongodb}} @db.ObjectId{{/if}}
|
|
37
|
+
userId String {{#if prismaProvider == "mongodb"}} @db.ObjectId{{/if}}
|
|
38
38
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
39
39
|
accessToken String?
|
|
40
40
|
refreshToken String?
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"operations": [
|
|
52
52
|
{
|
|
53
53
|
"type": "add-import",
|
|
54
|
-
"imports": ["import { initAuth } from \"
|
|
54
|
+
"imports": ["import { initAuth } from \"../lib/auth\";"]
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"type": "add-code",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"operations": [
|
|
68
68
|
{
|
|
69
69
|
"type": "add-import",
|
|
70
|
-
"imports": ["import { auth } from \"
|
|
70
|
+
"imports": ["import { auth } from \"../lib/auth\";",
|
|
71
71
|
"import { toNodeHandler } from \"better-auth/node\";"]
|
|
72
72
|
},
|
|
73
73
|
{
|
|
@@ -97,12 +97,13 @@
|
|
|
97
97
|
"EMAIL_USER": "",
|
|
98
98
|
"EMAIL_PASS": "",
|
|
99
99
|
"EMAIL_FROM": "noreply@yourapp.com"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"type": "add-dependency",
|
|
104
|
+
"dependencies": {
|
|
105
|
+
"better-auth": "^1.4.12"
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
]
|
|
103
|
-
"dependencies": {
|
|
104
|
-
"better-auth": "^1.4.12"
|
|
105
|
-
},
|
|
106
|
-
"devDependencies": {},
|
|
107
|
-
"scripts": {}
|
|
108
|
+
]
|
|
108
109
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
{
|
|
7
7
|
"type": "create-file",
|
|
8
8
|
"source": "lib/mongoose.ts",
|
|
9
|
-
"destination": "
|
|
9
|
+
"destination": "lib/mongoose.ts"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"type": "create-file",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"operations": [
|
|
21
21
|
{
|
|
22
22
|
"type": "add-import",
|
|
23
|
-
"imports": ["import { mongoose } from \"
|
|
23
|
+
"imports": ["import { mongoose } from \"../lib/mongoose\";"]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"type": "add-code",
|
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
"envVars": {
|
|
35
35
|
"MONGODB_URI": "mongodb://localhost:27017/database_name"
|
|
36
36
|
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"type": "add-dependency",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"mongoose": "^8.8.0"
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
|
-
]
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"mongoose": "^8.8.0"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {}
|
|
44
|
+
]
|
|
43
45
|
}
|
|
@@ -6,7 +6,7 @@ const globalForPrisma = globalThis as unknown as {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
{{#switch prismaProvider}}
|
|
9
|
-
{{#case postgresql}}
|
|
9
|
+
{{#case "postgresql"}}
|
|
10
10
|
import { PrismaPg } from '@prisma/adapter-pg'
|
|
11
11
|
|
|
12
12
|
const connectionString = `${process.env.DATABASE_URL}`
|
|
@@ -16,13 +16,13 @@ const prisma = new PrismaClient({ adapter })
|
|
|
16
16
|
|
|
17
17
|
export { prisma }
|
|
18
18
|
{{/case}}
|
|
19
|
-
{{#case mongodb}}
|
|
19
|
+
{{#case "mongodb"}}
|
|
20
20
|
|
|
21
21
|
const prisma = new PrismaClient()
|
|
22
22
|
|
|
23
23
|
export { prisma }
|
|
24
24
|
{{/case}}
|
|
25
|
-
{{#case mysql}}
|
|
25
|
+
{{#case "mysql"}}
|
|
26
26
|
import { PrismaMariaDb } from '@prisma/adapter-mariadb';
|
|
27
27
|
|
|
28
28
|
const adapter = new PrismaMariaDb({
|
|
@@ -36,7 +36,7 @@ const prisma = new PrismaClient({ adapter });
|
|
|
36
36
|
|
|
37
37
|
export { prisma }
|
|
38
38
|
{{/case}}
|
|
39
|
-
{{#case sqlite}}
|
|
39
|
+
{{#case "sqlite"}}
|
|
40
40
|
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
|
|
41
41
|
|
|
42
42
|
const connectionString = `${process.env.DATABASE_URL}`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
{{#switch prismaProvider}}
|
|
3
|
-
{{#case mongodb}}
|
|
3
|
+
{{#case "mongodb"}}
|
|
4
4
|
import { defineConfig, env } from "prisma/config";
|
|
5
5
|
{{/case}}
|
|
6
6
|
{{#case default}}
|
|
@@ -14,7 +14,7 @@ export default defineConfig({
|
|
|
14
14
|
path: "prisma/migrations",
|
|
15
15
|
},
|
|
16
16
|
{{#switch prismaProvider}}
|
|
17
|
-
{{#case mongodb}}
|
|
17
|
+
{{#case "mongodb"}}
|
|
18
18
|
engine: "classic",
|
|
19
19
|
datasource: {
|
|
20
20
|
url: env('DATABASE_URL'),
|
|
@@ -91,17 +91,22 @@
|
|
|
91
91
|
"envVars": {
|
|
92
92
|
"DATABASE_URL": "mongodb://localhost:27017/database_name"
|
|
93
93
|
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"type": "add-dependency",
|
|
97
|
+
"dependencies": {
|
|
98
|
+
"dotenv": "^17.2.3"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"type": "add-script",
|
|
103
|
+
"scripts": {
|
|
104
|
+
"db:generate": "npx prisma generate",
|
|
105
|
+
"db:push": "npx prisma db push",
|
|
106
|
+
"db:seed": "tsx prisma/seed.ts",
|
|
107
|
+
"db:migrate": "npx prisma migrate dev",
|
|
108
|
+
"db:studio": "npx prisma studio"
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
|
-
]
|
|
96
|
-
"dependencies": {
|
|
97
|
-
"dotenv": "^17.2.3"
|
|
98
|
-
},
|
|
99
|
-
"devDependencies": {},
|
|
100
|
-
"scripts": {
|
|
101
|
-
"db:generate": "npx prisma generate",
|
|
102
|
-
"db:push": "npx prisma db push",
|
|
103
|
-
"db:seed": "tsx prisma/seed.ts",
|
|
104
|
-
"db:migrate": "npx prisma migrate dev",
|
|
105
|
-
"db:studio": "npx prisma studio"
|
|
106
|
-
}
|
|
111
|
+
]
|
|
107
112
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { getServerSession } from "next-auth/next";
|
|
2
|
-
import { authOptions } from "@/lib/auth";
|
|
3
|
-
|
|
4
|
-
export async function getSession() {
|
|
5
|
-
return await getServerSession(authOptions);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export async function getCurrentUser() {
|
|
9
|
-
const session = await getSession();
|
|
10
|
-
return session?.user;
|
|
11
|
-
}
|