stackkit-cli 0.4.3 → 0.4.4
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/bin/stackkit.js +10 -1
- package/dist/commands/add.js +138 -1
- package/dist/types/index.d.ts +14 -2
- package/modules/auth/better-auth/files/lib/auth.ts +13 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
- package/modules/auth/better-auth/module.json +54 -0
- package/modules/auth/clerk/module.json +115 -0
- package/modules/database/mongoose-mongodb/files/lib/db.ts +44 -6
- package/modules/database/mongoose-mongodb/files/models/User.ts +39 -0
- package/modules/database/mongoose-mongodb/module.json +27 -12
- package/modules/database/prisma/files/lib/prisma.ts +6 -0
- package/modules/database/prisma/files/prisma/schema.prisma +8 -0
- package/modules/database/prisma/files/prisma.config.ts +12 -0
- package/modules/database/prisma/module.json +140 -0
- package/package.json +1 -1
- package/templates/express/.env.example +2 -10
- package/templates/express/package.json +12 -18
- package/templates/express/src/app.ts +9 -29
- package/templates/express/src/config/env.ts +3 -14
- package/templates/express/src/features/auth/auth.controller.ts +48 -0
- package/templates/express/src/features/auth/auth.route.ts +10 -0
- package/templates/express/src/features/auth/auth.service.ts +21 -0
- package/templates/express/src/middlewares/error.middleware.ts +2 -2
- package/templates/express/src/server.ts +1 -1
- package/templates/express/template.json +1 -5
- package/templates/express/tsconfig.json +0 -1
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
- package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
- package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
- package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-express/module.json +0 -61
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/module.json +0 -62
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
- package/modules/auth/better-auth-react/module.json +0 -28
- package/modules/auth/clerk-express/module.json +0 -34
- package/modules/auth/clerk-nextjs/module.json +0 -64
- package/modules/auth/clerk-react/module.json +0 -28
- package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
- package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-mongodb/module.json +0 -60
- package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
- package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-postgresql/module.json +0 -60
- /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
- /package/modules/auth/{clerk-express/files/lib → clerk/files/express}/auth.ts +0 -0
- /package/modules/auth/{clerk-nextjs/files/lib → clerk/files/nextjs}/auth-provider.tsx +0 -0
- /package/modules/auth/{clerk-nextjs/files → clerk/files/nextjs}/middleware.ts +0 -0
- /package/modules/auth/{clerk-react/files/lib → clerk/files/react}/auth-provider.tsx +0 -0
package/bin/stackkit.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
// eslint-disable-next-line
|
|
5
|
+
require("ts-node/register");
|
|
6
|
+
// eslint-disable-next-line
|
|
7
|
+
require("../src/index.ts");
|
|
8
|
+
} catch {
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
require("../dist/index.js");
|
|
11
|
+
}
|
package/dist/commands/add.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.addCommand = addCommand;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
8
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
10
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
@@ -27,6 +28,81 @@ async function addCommand(module, options) {
|
|
|
27
28
|
logger_1.logger.error(`Module "${module}" not found`);
|
|
28
29
|
process.exit(1);
|
|
29
30
|
}
|
|
31
|
+
// For database modules, ensure provider is selected
|
|
32
|
+
let selectedProvider = options.provider;
|
|
33
|
+
if (moduleMetadata.category === "database" && !selectedProvider) {
|
|
34
|
+
if (typeof moduleMetadata.dependencies === "object" &&
|
|
35
|
+
"providers" in moduleMetadata.dependencies) {
|
|
36
|
+
const providers = Object.keys(moduleMetadata.dependencies.providers || {});
|
|
37
|
+
if (providers.length > 0) {
|
|
38
|
+
const { provider } = await inquirer_1.default.prompt([
|
|
39
|
+
{
|
|
40
|
+
type: "list",
|
|
41
|
+
name: "provider",
|
|
42
|
+
message: "Select database provider:",
|
|
43
|
+
choices: providers.map((p) => ({ name: p, value: p })),
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
selectedProvider = provider;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Merge dependencies based on provider
|
|
51
|
+
const mergedDeps = {};
|
|
52
|
+
const mergedDevDeps = {};
|
|
53
|
+
if (typeof moduleMetadata.dependencies === "object" &&
|
|
54
|
+
!("common" in moduleMetadata.dependencies)) {
|
|
55
|
+
// Already flat
|
|
56
|
+
Object.assign(mergedDeps, moduleMetadata.dependencies);
|
|
57
|
+
}
|
|
58
|
+
else if (typeof moduleMetadata.dependencies === "object" &&
|
|
59
|
+
"common" in moduleMetadata.dependencies) {
|
|
60
|
+
Object.assign(mergedDeps, moduleMetadata.dependencies.common);
|
|
61
|
+
if (selectedProvider &&
|
|
62
|
+
typeof moduleMetadata.dependencies === "object" &&
|
|
63
|
+
"providers" in moduleMetadata.dependencies &&
|
|
64
|
+
typeof moduleMetadata.dependencies.providers === "object" &&
|
|
65
|
+
selectedProvider in moduleMetadata.dependencies.providers) {
|
|
66
|
+
Object.assign(mergedDeps, moduleMetadata.dependencies.providers[selectedProvider]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (moduleMetadata.devDependencies) {
|
|
70
|
+
if (typeof moduleMetadata.devDependencies === "object" &&
|
|
71
|
+
!("common" in moduleMetadata.devDependencies)) {
|
|
72
|
+
Object.assign(mergedDevDeps, moduleMetadata.devDependencies);
|
|
73
|
+
}
|
|
74
|
+
else if (typeof moduleMetadata.devDependencies === "object" &&
|
|
75
|
+
"common" in moduleMetadata.devDependencies) {
|
|
76
|
+
Object.assign(mergedDevDeps, moduleMetadata.devDependencies.common);
|
|
77
|
+
if (selectedProvider &&
|
|
78
|
+
typeof moduleMetadata.devDependencies === "object" &&
|
|
79
|
+
"providers" in moduleMetadata.devDependencies &&
|
|
80
|
+
typeof moduleMetadata.devDependencies.providers === "object" &&
|
|
81
|
+
selectedProvider in moduleMetadata.devDependencies.providers) {
|
|
82
|
+
Object.assign(mergedDevDeps, moduleMetadata.devDependencies.providers[selectedProvider]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Update metadata with merged deps
|
|
87
|
+
moduleMetadata.dependencies = mergedDeps;
|
|
88
|
+
moduleMetadata.devDependencies = mergedDevDeps;
|
|
89
|
+
// Set variables for replacements
|
|
90
|
+
const variables = {};
|
|
91
|
+
if (selectedProvider) {
|
|
92
|
+
variables.provider = selectedProvider;
|
|
93
|
+
if (selectedProvider === "postgresql") {
|
|
94
|
+
variables.connectionString = "postgresql://user:password@localhost:5432/mydb?schema=public";
|
|
95
|
+
}
|
|
96
|
+
else if (selectedProvider === "mongodb") {
|
|
97
|
+
variables.connectionString = "mongodb://localhost:27017/mydb";
|
|
98
|
+
}
|
|
99
|
+
else if (selectedProvider === "mysql") {
|
|
100
|
+
variables.connectionString = "mysql://user:password@localhost:3306/mydb";
|
|
101
|
+
}
|
|
102
|
+
else if (selectedProvider === "sqlite") {
|
|
103
|
+
variables.connectionString = "file:./dev.db";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
30
106
|
// Check if framework is supported
|
|
31
107
|
if (!moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
32
108
|
logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
|
|
@@ -54,6 +130,24 @@ async function addCommand(module, options) {
|
|
|
54
130
|
}
|
|
55
131
|
// Apply module patches
|
|
56
132
|
await applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, module, options);
|
|
133
|
+
// Apply framework patches
|
|
134
|
+
if (moduleMetadata.frameworkPatches && !options.dryRun) {
|
|
135
|
+
await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
|
|
136
|
+
}
|
|
137
|
+
// Run post-install commands
|
|
138
|
+
if (moduleMetadata.postInstall && moduleMetadata.postInstall.length > 0 && !options.dryRun) {
|
|
139
|
+
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
140
|
+
try {
|
|
141
|
+
for (const command of moduleMetadata.postInstall) {
|
|
142
|
+
(0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
|
|
143
|
+
}
|
|
144
|
+
postInstallSpinner.succeed("Post-install commands completed");
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
postInstallSpinner.fail("Failed to run post-install commands");
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
57
151
|
// Add dependencies
|
|
58
152
|
if (Object.keys(moduleMetadata.dependencies).length > 0 && options.install !== false) {
|
|
59
153
|
const deps = Object.entries(moduleMetadata.dependencies).map(([name, version]) => `${name}@${version}`);
|
|
@@ -78,8 +172,13 @@ async function addCommand(module, options) {
|
|
|
78
172
|
}
|
|
79
173
|
// Add environment variables
|
|
80
174
|
if (moduleMetadata.envVars.length > 0) {
|
|
175
|
+
// Replace variables in envVars
|
|
176
|
+
const processedEnvVars = moduleMetadata.envVars.map((envVar) => ({
|
|
177
|
+
...envVar,
|
|
178
|
+
value: envVar.value?.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match),
|
|
179
|
+
}));
|
|
81
180
|
if (!options.dryRun) {
|
|
82
|
-
await (0, env_editor_1.addEnvVariables)(projectRoot,
|
|
181
|
+
await (0, env_editor_1.addEnvVariables)(projectRoot, processedEnvVars, { force: options.force });
|
|
83
182
|
}
|
|
84
183
|
else {
|
|
85
184
|
logger_1.logger.log(` ${chalk_1.default.dim("~")} .env.example`);
|
|
@@ -208,3 +307,41 @@ async function findModulePath(modulesDir, moduleName, provider) {
|
|
|
208
307
|
}
|
|
209
308
|
return null;
|
|
210
309
|
}
|
|
310
|
+
async function applyFrameworkPatches(projectRoot, patches, framework) {
|
|
311
|
+
const frameworkKey = framework;
|
|
312
|
+
const frameworkPatches = patches[frameworkKey];
|
|
313
|
+
if (!frameworkPatches)
|
|
314
|
+
return;
|
|
315
|
+
for (const [filename, patchConfig] of Object.entries(frameworkPatches)) {
|
|
316
|
+
const filePath = path_1.default.join(projectRoot, filename);
|
|
317
|
+
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
318
|
+
const fileContent = await fs_extra_1.default.readJson(filePath);
|
|
319
|
+
if (patchConfig.merge) {
|
|
320
|
+
const merged = deepMerge(fileContent, patchConfig.merge);
|
|
321
|
+
await fs_extra_1.default.writeJson(filePath, merged, { spaces: 2 });
|
|
322
|
+
const relativePath = path_1.default.relative(projectRoot, filePath);
|
|
323
|
+
logger_1.logger.log(` ${chalk_1.default.blue("~")} ${relativePath}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function deepMerge(target, source) {
|
|
329
|
+
const output = { ...target };
|
|
330
|
+
for (const key in source) {
|
|
331
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
332
|
+
if (target[key]) {
|
|
333
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
output[key] = source[key];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else if (Array.isArray(source[key])) {
|
|
340
|
+
output[key] = Array.from(new Set([...(target[key] || []), ...source[key]]));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
output[key] = source[key];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return output;
|
|
347
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -12,10 +12,22 @@ export interface ModuleMetadata {
|
|
|
12
12
|
description: string;
|
|
13
13
|
category: "auth" | "database" | "ui" | "other";
|
|
14
14
|
supportedFrameworks: string[];
|
|
15
|
-
dependencies:
|
|
16
|
-
|
|
15
|
+
dependencies: {
|
|
16
|
+
common?: Record<string, string>;
|
|
17
|
+
providers?: Record<string, Record<string, string>>;
|
|
18
|
+
} | Record<string, string>;
|
|
19
|
+
devDependencies?: {
|
|
20
|
+
common?: Record<string, string>;
|
|
21
|
+
providers?: Record<string, Record<string, string>>;
|
|
22
|
+
} | Record<string, string>;
|
|
17
23
|
envVars: EnvVar[];
|
|
18
24
|
patches: ModulePatch[];
|
|
25
|
+
frameworkPatches?: Record<string, {
|
|
26
|
+
[file: string]: {
|
|
27
|
+
merge?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
postInstall?: string[];
|
|
19
31
|
}
|
|
20
32
|
export interface EnvVar {
|
|
21
33
|
key: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
|
|
3
|
+
{{databaseAdapter}}
|
|
4
|
+
emailAndPassword: {
|
|
5
|
+
enabled: true,
|
|
6
|
+
},
|
|
7
|
+
socialProviders: {
|
|
8
|
+
google: {
|
|
9
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
10
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Better Auth models
|
|
2
|
+
model User {
|
|
3
|
+
id String @id {{idDefault}}
|
|
4
|
+
name String
|
|
5
|
+
email String
|
|
6
|
+
emailVerified Boolean @default(false)
|
|
7
|
+
image String?
|
|
8
|
+
createdAt DateTime @default(now())
|
|
9
|
+
updatedAt DateTime @updatedAt
|
|
10
|
+
sessions Session[]
|
|
11
|
+
accounts Account[]
|
|
12
|
+
role String @default("USER")
|
|
13
|
+
|
|
14
|
+
@@unique([email])
|
|
15
|
+
@@map("user")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
model Session {
|
|
19
|
+
id String @id {{idDefault}}
|
|
20
|
+
expiresAt DateTime
|
|
21
|
+
token String @unique
|
|
22
|
+
createdAt DateTime @default(now())
|
|
23
|
+
updatedAt DateTime @updatedAt
|
|
24
|
+
ipAddress String?
|
|
25
|
+
userAgent String?
|
|
26
|
+
userId String {{userIdType}}
|
|
27
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
28
|
+
|
|
29
|
+
@@index([userId])
|
|
30
|
+
@@map("session")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
model Account {
|
|
34
|
+
id String @id {{idDefault}}
|
|
35
|
+
accountId String
|
|
36
|
+
providerId String
|
|
37
|
+
userId String {{userIdType}}
|
|
38
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
39
|
+
accessToken String?
|
|
40
|
+
refreshToken String?
|
|
41
|
+
idToken String?
|
|
42
|
+
accessTokenExpiresAt DateTime?
|
|
43
|
+
refreshTokenExpiresAt DateTime?
|
|
44
|
+
scope String?
|
|
45
|
+
password String?
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
updatedAt DateTime @updatedAt
|
|
48
|
+
|
|
49
|
+
@@index([userId])
|
|
50
|
+
@@map("account")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
model Verification {
|
|
54
|
+
id String @id {{idDefault}}
|
|
55
|
+
identifier String
|
|
56
|
+
value String
|
|
57
|
+
expiresAt DateTime
|
|
58
|
+
createdAt DateTime @default(now())
|
|
59
|
+
updatedAt DateTime @updatedAt
|
|
60
|
+
|
|
61
|
+
@@index([identifier])
|
|
62
|
+
@@map("verification")
|
|
63
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "better-auth",
|
|
3
|
+
"displayName": "Better Auth",
|
|
4
|
+
"description": "Modern authentication with Better Auth",
|
|
5
|
+
"category": "auth",
|
|
6
|
+
"provider": "better-auth",
|
|
7
|
+
"supportedFrameworks": ["nextjs", "express", "react-vite"],
|
|
8
|
+
"databaseAdapters": {
|
|
9
|
+
"prisma-postgresql": {
|
|
10
|
+
"adapterCode": "import { prisma } from \"{{dbImport}}\";\nimport { prismaAdapter } from \"@better-auth/prisma\";\n\nexport const auth = betterAuth({\n database: prismaAdapter(prisma, {\n provider: \"postgresql\",\n }),",
|
|
11
|
+
"schema": "files/schemas/prisma-schema.prisma",
|
|
12
|
+
"schemaDestination": "prisma/schema.prisma",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"better-auth": "^1.1.4"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"prisma-mongodb": {
|
|
18
|
+
"adapterCode": "import { prisma } from \"{{dbImport}}\";\nimport { prismaAdapter } from \"@better-auth/prisma\";\n\nexport const auth = betterAuth({\n database: prismaAdapter(prisma, {\n provider: \"mongodb\",\n }),",
|
|
19
|
+
"schema": "files/schemas/prisma-schema.prisma",
|
|
20
|
+
"schemaDestination": "prisma/schema.prisma",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"better-auth": "^1.1.4"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"mongoose-mongodb": {
|
|
26
|
+
"adapterCode": "import { client } from \"{{dbImport}}\";\nimport { mongodbAdapter } from \"better-auth/adapters/mongodb\";\n\nexport const auth = betterAuth({\n database: mongodbAdapter(client),",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"better-auth": "^1.1.4"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"envVars": [
|
|
33
|
+
{
|
|
34
|
+
"key": "BETTER_AUTH_SECRET",
|
|
35
|
+
"value": "",
|
|
36
|
+
"description": "Secret key for Better Auth. Generate with: openssl rand -base64 32",
|
|
37
|
+
"required": true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"key": "BETTER_AUTH_URL",
|
|
41
|
+
"value": "http://localhost:3000",
|
|
42
|
+
"description": "Base URL of your application (change in production)",
|
|
43
|
+
"required": true
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"patches": [
|
|
47
|
+
{
|
|
48
|
+
"type": "create-file",
|
|
49
|
+
"description": "Create Better Auth configuration",
|
|
50
|
+
"source": "files/lib/auth.ts",
|
|
51
|
+
"destination": "{{lib}}/auth.ts"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clerk",
|
|
3
|
+
"displayName": "Clerk",
|
|
4
|
+
"description": "Clerk Authentication",
|
|
5
|
+
"category": "auth",
|
|
6
|
+
"provider": "clerk",
|
|
7
|
+
"supportedFrameworks": ["nextjs", "express", "react-vite"],
|
|
8
|
+
"frameworkConfigs": {
|
|
9
|
+
"nextjs": {
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@clerk/nextjs": "^6.10.2"
|
|
12
|
+
},
|
|
13
|
+
"envVars": [
|
|
14
|
+
{
|
|
15
|
+
"key": "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
16
|
+
"value": "",
|
|
17
|
+
"description": "Clerk publishable key (from Clerk dashboard)",
|
|
18
|
+
"required": true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"key": "CLERK_SECRET_KEY",
|
|
22
|
+
"value": "",
|
|
23
|
+
"description": "Clerk secret key (from Clerk dashboard)",
|
|
24
|
+
"required": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"key": "NEXT_PUBLIC_CLERK_SIGN_IN_URL",
|
|
28
|
+
"value": "/sign-in",
|
|
29
|
+
"description": "Sign in page URL",
|
|
30
|
+
"required": true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"key": "NEXT_PUBLIC_CLERK_SIGN_UP_URL",
|
|
34
|
+
"value": "/sign-up",
|
|
35
|
+
"description": "Sign up page URL",
|
|
36
|
+
"required": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"key": "NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL",
|
|
40
|
+
"value": "/",
|
|
41
|
+
"description": "Redirect URL after sign in",
|
|
42
|
+
"required": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL",
|
|
46
|
+
"value": "/",
|
|
47
|
+
"description": "Redirect URL after sign up",
|
|
48
|
+
"required": true
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"patches": [
|
|
52
|
+
{
|
|
53
|
+
"type": "create-file",
|
|
54
|
+
"description": "Create Clerk auth provider",
|
|
55
|
+
"source": "nextjs/auth-provider.tsx",
|
|
56
|
+
"destination": "{{lib}}/auth-provider.tsx"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"type": "create-file",
|
|
60
|
+
"description": "Create Clerk middleware",
|
|
61
|
+
"source": "nextjs/middleware.ts",
|
|
62
|
+
"destination": "middleware.ts"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"express": {
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@clerk/express": "^1.3.0"
|
|
69
|
+
},
|
|
70
|
+
"envVars": [
|
|
71
|
+
{
|
|
72
|
+
"key": "CLERK_PUBLISHABLE_KEY",
|
|
73
|
+
"value": "",
|
|
74
|
+
"description": "Clerk publishable key (from Clerk dashboard)",
|
|
75
|
+
"required": true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"key": "CLERK_SECRET_KEY",
|
|
79
|
+
"value": "",
|
|
80
|
+
"description": "Clerk secret key (from Clerk dashboard)",
|
|
81
|
+
"required": true
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"patches": [
|
|
85
|
+
{
|
|
86
|
+
"type": "create-file",
|
|
87
|
+
"description": "Create Clerk auth utilities",
|
|
88
|
+
"source": "express/auth.ts",
|
|
89
|
+
"destination": "{{lib}}/auth.ts"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"react-vite": {
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@clerk/clerk-react": "^5.15.0"
|
|
96
|
+
},
|
|
97
|
+
"envVars": [
|
|
98
|
+
{
|
|
99
|
+
"key": "VITE_CLERK_PUBLISHABLE_KEY",
|
|
100
|
+
"value": "",
|
|
101
|
+
"description": "Clerk publishable key (from Clerk dashboard)",
|
|
102
|
+
"required": true
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"patches": [
|
|
106
|
+
{
|
|
107
|
+
"type": "create-file",
|
|
108
|
+
"description": "Create Clerk auth provider",
|
|
109
|
+
"source": "react/auth-provider.tsx",
|
|
110
|
+
"destination": "{{lib}}/auth-provider.tsx"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -1,28 +1,45 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
1
2
|
import mongoose from "mongoose";
|
|
2
3
|
|
|
3
|
-
const MONGODB_URI = process.env.
|
|
4
|
+
const MONGODB_URI = process.env.DATABASE_URL || "mongodb://localhost:27017/myapp";
|
|
4
5
|
|
|
5
6
|
if (!MONGODB_URI) {
|
|
6
|
-
throw new Error("Please define the
|
|
7
|
+
throw new Error("Please define the DATABASE_URL environment variable");
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
interface MongooseCache {
|
|
11
|
+
conn: typeof mongoose | null;
|
|
12
|
+
promise: Promise<typeof mongoose> | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
var mongoose: MongooseCache | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cached: MongooseCache = global.mongoose || { conn: null, promise: null };
|
|
10
20
|
|
|
11
|
-
if (!
|
|
12
|
-
|
|
21
|
+
if (!global.mongoose) {
|
|
22
|
+
global.mongoose = cached;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
async function connectDB() {
|
|
25
|
+
async function connectDB(): Promise<typeof mongoose> {
|
|
16
26
|
if (cached.conn) {
|
|
27
|
+
console.log("Using existing MongoDB connection");
|
|
17
28
|
return cached.conn;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
if (!cached.promise) {
|
|
21
32
|
const opts = {
|
|
22
33
|
bufferCommands: false,
|
|
34
|
+
maxPoolSize: 10, // Maintain up to 10 socket connections
|
|
35
|
+
serverSelectionTimeoutMS: 5000, // Keep trying to send operations for 5 seconds
|
|
36
|
+
socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
|
|
37
|
+
family: 4, // Use IPv4, skip trying IPv6
|
|
23
38
|
};
|
|
24
39
|
|
|
40
|
+
console.log("Creating new MongoDB connection");
|
|
25
41
|
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
|
|
42
|
+
console.log("MongoDB connected successfully");
|
|
26
43
|
return mongoose;
|
|
27
44
|
});
|
|
28
45
|
}
|
|
@@ -31,10 +48,31 @@ async function connectDB() {
|
|
|
31
48
|
cached.conn = await cached.promise;
|
|
32
49
|
} catch (e) {
|
|
33
50
|
cached.promise = null;
|
|
51
|
+
console.error("MongoDB connection error:", e);
|
|
34
52
|
throw e;
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
return cached.conn;
|
|
38
56
|
}
|
|
39
57
|
|
|
58
|
+
// Handle connection events
|
|
59
|
+
mongoose.connection.on("connected", () => {
|
|
60
|
+
console.log("Mongoose connected to MongoDB");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
mongoose.connection.on("error", (err) => {
|
|
64
|
+
console.error("Mongoose connection error:", err);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
mongoose.connection.on("disconnected", () => {
|
|
68
|
+
console.log("Mongoose disconnected");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Close connection on app termination
|
|
72
|
+
process.on("SIGINT", async () => {
|
|
73
|
+
await mongoose.connection.close();
|
|
74
|
+
console.log("MongoDB connection closed due to app termination");
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
40
78
|
export default connectDB;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import mongoose, { Document, Schema } from "mongoose";
|
|
2
|
+
|
|
3
|
+
export interface IUser extends Document {
|
|
4
|
+
name: string;
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const UserSchema: Schema = new Schema(
|
|
12
|
+
{
|
|
13
|
+
name: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
trim: true,
|
|
17
|
+
},
|
|
18
|
+
email: {
|
|
19
|
+
type: String,
|
|
20
|
+
required: true,
|
|
21
|
+
unique: true,
|
|
22
|
+
lowercase: true,
|
|
23
|
+
trim: true,
|
|
24
|
+
},
|
|
25
|
+
password: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
timestamps: true,
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Add indexes for better performance
|
|
36
|
+
UserSchema.index({ email: 1 });
|
|
37
|
+
UserSchema.index({ createdAt: -1 });
|
|
38
|
+
|
|
39
|
+
export default mongoose.models.User || mongoose.model<IUser>("User", UserSchema);
|
|
@@ -7,28 +7,35 @@
|
|
|
7
7
|
"database": "mongodb",
|
|
8
8
|
"supportedFrameworks": ["nextjs", "express"],
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"mongoose": "^8.8.4"
|
|
10
|
+
"mongoose": "^8.8.4",
|
|
11
|
+
"dotenv": "^17.2.3"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/mongoose": "^5.11.97"
|
|
15
|
+
},
|
|
16
|
+
"envVars": {
|
|
17
|
+
"common": [
|
|
18
|
+
{
|
|
19
|
+
"key": "DATABASE_URL",
|
|
20
|
+
"value": "mongodb://localhost:27017/myapp",
|
|
21
|
+
"description": "MongoDB connection string",
|
|
22
|
+
"required": true
|
|
23
|
+
}
|
|
24
|
+
]
|
|
11
25
|
},
|
|
12
|
-
"devDependencies": {},
|
|
13
|
-
"envVars": [
|
|
14
|
-
{
|
|
15
|
-
"key": "MONGODB_URI",
|
|
16
|
-
"value": "mongodb://localhost:27017/myapp",
|
|
17
|
-
"description": "MongoDB connection string",
|
|
18
|
-
"required": true
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
26
|
"frameworkPatches": {
|
|
22
27
|
"express": {
|
|
23
28
|
"tsconfig.json": {
|
|
24
29
|
"merge": {
|
|
25
30
|
"compilerOptions": {
|
|
31
|
+
"baseUrl": ".",
|
|
26
32
|
"paths": {
|
|
27
33
|
"@/*": ["./src/*"],
|
|
28
34
|
"@/models/*": ["./src/models/*"]
|
|
29
35
|
}
|
|
30
36
|
},
|
|
31
|
-
"include": ["src/**/*", "src/models/**/*"]
|
|
37
|
+
"include": ["src/**/*", "src/models/**/*"],
|
|
38
|
+
"exclude": ["node_modules", "dist"]
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
},
|
|
@@ -36,10 +43,12 @@
|
|
|
36
43
|
"tsconfig.json": {
|
|
37
44
|
"merge": {
|
|
38
45
|
"compilerOptions": {
|
|
46
|
+
"baseUrl": ".",
|
|
39
47
|
"paths": {
|
|
40
48
|
"@/models/*": ["./models/*"]
|
|
41
49
|
}
|
|
42
|
-
}
|
|
50
|
+
},
|
|
51
|
+
"exclude": ["node_modules", ".next", "out"]
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
}
|
|
@@ -50,6 +59,12 @@
|
|
|
50
59
|
"description": "Create MongoDB connection with Mongoose",
|
|
51
60
|
"source": "lib/db.ts",
|
|
52
61
|
"destination": "{{lib}}/db.ts"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"type": "create-file",
|
|
65
|
+
"description": "Create sample User model",
|
|
66
|
+
"source": "models/User.ts",
|
|
67
|
+
"destination": "{{models}}/User.ts"
|
|
53
68
|
}
|
|
54
69
|
]
|
|
55
70
|
}
|