stackkit 0.1.0
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/README.md +41 -0
- package/bin/stackkit.js +4 -0
- package/dist/cli/add.d.ts +8 -0
- package/dist/cli/add.js +313 -0
- package/dist/cli/create.d.ts +22 -0
- package/dist/cli/create.js +336 -0
- package/dist/cli/doctor.d.ts +7 -0
- package/dist/cli/doctor.js +569 -0
- package/dist/cli/list.d.ts +6 -0
- package/dist/cli/list.js +123 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +91 -0
- package/dist/lib/conversion/js-conversion.d.ts +1 -0
- package/dist/lib/conversion/js-conversion.js +244 -0
- package/dist/lib/database/database-config.d.ts +6 -0
- package/dist/lib/database/database-config.js +9 -0
- package/dist/lib/discovery/module-discovery.d.ts +62 -0
- package/dist/lib/discovery/module-discovery.js +188 -0
- package/dist/lib/env/env-editor.d.ts +9 -0
- package/dist/lib/env/env-editor.js +116 -0
- package/dist/lib/framework/framework-utils.d.ts +22 -0
- package/dist/lib/framework/framework-utils.js +74 -0
- package/dist/lib/fs/files.d.ts +14 -0
- package/dist/lib/fs/files.js +101 -0
- package/dist/lib/generation/code-generator.d.ts +83 -0
- package/dist/lib/generation/code-generator.js +681 -0
- package/dist/lib/git-utils.d.ts +1 -0
- package/dist/lib/git-utils.js +9 -0
- package/dist/lib/pm/package-manager.d.ts +5 -0
- package/dist/lib/pm/package-manager.js +69 -0
- package/dist/lib/project/detect.d.ts +4 -0
- package/dist/lib/project/detect.js +121 -0
- package/dist/lib/ui/logger.d.ts +16 -0
- package/dist/lib/ui/logger.js +59 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/types/index.js +2 -0
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
- package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
- package/modules/auth/authjs/files/lib/auth.ts +36 -0
- package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
- package/modules/auth/authjs/module.json +22 -0
- package/modules/auth/better-auth/files/api/auth/[...all]/route.ts +4 -0
- package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
- package/modules/auth/better-auth/files/lib/auth.ts +83 -0
- package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
- package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
- package/modules/auth/better-auth/files/prisma/schema.prisma +63 -0
- package/modules/auth/better-auth/generator.json +78 -0
- package/modules/auth/better-auth/module.json +37 -0
- package/modules/database/mongoose/files/lib/db.ts +63 -0
- package/modules/database/mongoose/files/models/User.ts +34 -0
- package/modules/database/mongoose/generator.json +24 -0
- package/modules/database/mongoose/module.json +15 -0
- package/modules/database/prisma/files/lib/prisma.ts +45 -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/generator.json +43 -0
- package/modules/database/prisma/module.json +17 -0
- package/package.json +83 -0
- package/templates/express/.env.example +2 -0
- package/templates/express/eslint.config.cjs +42 -0
- package/templates/express/package.json +33 -0
- package/templates/express/src/app.ts +51 -0
- package/templates/express/src/config/env.ts +12 -0
- package/templates/express/src/features/health/health.controller.ts +18 -0
- package/templates/express/src/features/health/health.route.ts +9 -0
- package/templates/express/src/features/health/health.service.ts +6 -0
- package/templates/express/src/middlewares/error.middleware.ts +18 -0
- package/templates/express/src/server.ts +8 -0
- package/templates/express/template.json +27 -0
- package/templates/express/tsconfig.json +30 -0
- package/templates/nextjs/README.md +52 -0
- package/templates/nextjs/app/favicon.ico +0 -0
- package/templates/nextjs/app/globals.css +26 -0
- package/templates/nextjs/app/layout.tsx +30 -0
- package/templates/nextjs/app/page.tsx +57 -0
- package/templates/nextjs/eslint.config.mjs +18 -0
- package/templates/nextjs/lib/env.ts +8 -0
- package/templates/nextjs/next.config.ts +7 -0
- package/templates/nextjs/package.json +27 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/file.svg +1 -0
- package/templates/nextjs/public/globe.svg +1 -0
- package/templates/nextjs/public/next.svg +1 -0
- package/templates/nextjs/public/vercel.svg +1 -0
- package/templates/nextjs/public/window.svg +1 -0
- package/templates/nextjs/template.json +34 -0
- package/templates/nextjs/tsconfig.json +34 -0
- package/templates/react/.env.example +1 -0
- package/templates/react/.prettierignore +4 -0
- package/templates/react/.prettierrc +9 -0
- package/templates/react/README.md +56 -0
- package/templates/react/eslint.config.js +23 -0
- package/templates/react/index.html +14 -0
- package/templates/react/package.json +44 -0
- package/templates/react/public/vite.svg +1 -0
- package/templates/react/src/api/client.ts +47 -0
- package/templates/react/src/assets/react.svg +1 -0
- package/templates/react/src/components/ErrorBoundary.tsx +51 -0
- package/templates/react/src/components/Layout.tsx +13 -0
- package/templates/react/src/components/Loading.tsx +8 -0
- package/templates/react/src/components/SEO.tsx +49 -0
- package/templates/react/src/config/constants.ts +5 -0
- package/templates/react/src/hooks/index.ts +64 -0
- package/templates/react/src/index.css +1 -0
- package/templates/react/src/lib/queryClient.ts +12 -0
- package/templates/react/src/main.tsx +22 -0
- package/templates/react/src/pages/About.tsx +78 -0
- package/templates/react/src/pages/Home.tsx +49 -0
- package/templates/react/src/pages/NotFound.tsx +24 -0
- package/templates/react/src/router.tsx +21 -0
- package/templates/react/src/types/api.d.ts +20 -0
- package/templates/react/src/utils/helpers.ts +51 -0
- package/templates/react/src/utils/storage.ts +35 -0
- package/templates/react/src/vite-env.d.ts +11 -0
- package/templates/react/template.json +38 -0
- package/templates/react/tsconfig.app.json +28 -0
- package/templates/react/tsconfig.json +4 -0
- package/templates/react/tsconfig.node.json +26 -0
- package/templates/react/vite.config.ts +7 -0
- package/templates/react-vite/README.md +56 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initGit = initGit;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
async function initGit(cwd) {
|
|
6
|
+
(0, child_process_1.execSync)("git init", { cwd, stdio: "pipe" });
|
|
7
|
+
(0, child_process_1.execSync)("git add .", { cwd, stdio: "pipe" });
|
|
8
|
+
(0, child_process_1.execSync)('git commit -m "Initial commit"', { cwd, stdio: "pipe" });
|
|
9
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
|
|
2
|
+
export declare function detectPackageManager(cwd: string): Promise<PackageManager>;
|
|
3
|
+
export declare function installDependencies(cwd: string, pm: PackageManager): Promise<void>;
|
|
4
|
+
export declare function addDependencies(cwd: string, pm: PackageManager, packages: string[], dev?: boolean): Promise<void>;
|
|
5
|
+
export declare function initGit(cwd: string): Promise<void>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectPackageManager = detectPackageManager;
|
|
7
|
+
exports.installDependencies = installDependencies;
|
|
8
|
+
exports.addDependencies = addDependencies;
|
|
9
|
+
exports.initGit = initGit;
|
|
10
|
+
const detect_package_manager_1 = require("detect-package-manager");
|
|
11
|
+
const execa_1 = __importDefault(require("execa"));
|
|
12
|
+
const logger_1 = require("../ui/logger");
|
|
13
|
+
async function detectPackageManager(cwd) {
|
|
14
|
+
try {
|
|
15
|
+
const pm = await (0, detect_package_manager_1.detect)({ cwd });
|
|
16
|
+
return pm;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return "npm";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function installDependencies(cwd, pm) {
|
|
23
|
+
const args = [];
|
|
24
|
+
if (pm === "npm") {
|
|
25
|
+
args.push("install");
|
|
26
|
+
}
|
|
27
|
+
else if (pm === "yarn") {
|
|
28
|
+
args.push("install");
|
|
29
|
+
}
|
|
30
|
+
else if (pm === "pnpm") {
|
|
31
|
+
args.push("install");
|
|
32
|
+
}
|
|
33
|
+
else if (pm === "bun") {
|
|
34
|
+
args.push("install");
|
|
35
|
+
}
|
|
36
|
+
await (0, execa_1.default)(pm, args, { cwd, stdio: "pipe" });
|
|
37
|
+
}
|
|
38
|
+
async function addDependencies(cwd, pm, packages, dev = false) {
|
|
39
|
+
if (packages.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
const spinner = logger_1.logger.startSpinner(`Adding ${dev ? "dev " : ""}dependencies: ${packages.join(", ")}...`);
|
|
42
|
+
try {
|
|
43
|
+
const args = [];
|
|
44
|
+
if (pm === "npm") {
|
|
45
|
+
args.push("install", dev ? "--save-dev" : "--save", ...packages);
|
|
46
|
+
}
|
|
47
|
+
else if (pm === "yarn") {
|
|
48
|
+
args.push("add", dev ? "--dev" : "", ...packages);
|
|
49
|
+
}
|
|
50
|
+
else if (pm === "pnpm") {
|
|
51
|
+
args.push("add", dev ? "-D" : "", ...packages);
|
|
52
|
+
}
|
|
53
|
+
else if (pm === "bun") {
|
|
54
|
+
// bun uses `bun add` and `-d` for dev dependencies
|
|
55
|
+
args.push("add", ...(dev ? ["-d"] : []), ...packages);
|
|
56
|
+
}
|
|
57
|
+
await (0, execa_1.default)(pm, args.filter(Boolean), { cwd, stdio: "pipe" });
|
|
58
|
+
spinner.succeed(`Dependencies added successfully`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
spinner.fail(`Failed to add dependencies`);
|
|
62
|
+
throw new Error(`Failed to add dependencies: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function initGit(cwd) {
|
|
66
|
+
await (0, execa_1.default)("git", ["init"], { cwd });
|
|
67
|
+
await (0, execa_1.default)("git", ["add", "."], { cwd });
|
|
68
|
+
await (0, execa_1.default)("git", ["commit", "-m", "Initial commit from StackKit"], { cwd });
|
|
69
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ProjectInfo } from "../../types";
|
|
2
|
+
export declare function detectProjectInfo(targetDir: string): Promise<ProjectInfo>;
|
|
3
|
+
export declare function getRouterBasePath(projectInfo: ProjectInfo): string;
|
|
4
|
+
export declare function getLibPath(projectInfo: ProjectInfo): string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectProjectInfo = detectProjectInfo;
|
|
7
|
+
exports.getRouterBasePath = getRouterBasePath;
|
|
8
|
+
exports.getLibPath = getLibPath;
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
async function detectProjectInfo(targetDir) {
|
|
12
|
+
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
13
|
+
if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
|
|
14
|
+
throw new Error("No package.json found. This does not appear to be a Node.js project.");
|
|
15
|
+
}
|
|
16
|
+
const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
|
|
17
|
+
// Detect framework
|
|
18
|
+
const isNextJs = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
19
|
+
const isExpress = packageJson.dependencies?.express || packageJson.devDependencies?.express;
|
|
20
|
+
const isReact = packageJson.dependencies?.react || packageJson.devDependencies?.react;
|
|
21
|
+
const isVite = packageJson.dependencies?.vite || packageJson.devDependencies?.vite;
|
|
22
|
+
let framework;
|
|
23
|
+
if (isNextJs) {
|
|
24
|
+
framework = "nextjs";
|
|
25
|
+
}
|
|
26
|
+
else if (isExpress) {
|
|
27
|
+
framework = "express";
|
|
28
|
+
}
|
|
29
|
+
else if (isReact && isVite) {
|
|
30
|
+
framework = "react";
|
|
31
|
+
}
|
|
32
|
+
else if (isReact) {
|
|
33
|
+
framework = "react";
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
framework = "unknown";
|
|
37
|
+
}
|
|
38
|
+
if (framework === "unknown") {
|
|
39
|
+
throw new Error("Only Next.js, Express, and React projects are currently supported.");
|
|
40
|
+
}
|
|
41
|
+
// Detect router type (only for Next.js)
|
|
42
|
+
let router = "unknown";
|
|
43
|
+
if (framework === "nextjs") {
|
|
44
|
+
const appDirExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "app"));
|
|
45
|
+
const pagesDirExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "pages"));
|
|
46
|
+
const srcAppDirExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "src", "app"));
|
|
47
|
+
const srcPagesDirExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "src", "pages"));
|
|
48
|
+
if (appDirExists || srcAppDirExists) {
|
|
49
|
+
router = "app";
|
|
50
|
+
}
|
|
51
|
+
else if (pagesDirExists || srcPagesDirExists) {
|
|
52
|
+
router = "pages";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Detect TypeScript vs JavaScript
|
|
56
|
+
const tsconfigExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "tsconfig.json"));
|
|
57
|
+
const jsconfigExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "jsconfig.json"));
|
|
58
|
+
let language;
|
|
59
|
+
if (tsconfigExists) {
|
|
60
|
+
language = "ts";
|
|
61
|
+
}
|
|
62
|
+
else if (jsconfigExists) {
|
|
63
|
+
language = "js";
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Default to TypeScript if neither exists
|
|
67
|
+
language = "ts";
|
|
68
|
+
}
|
|
69
|
+
// Detect package manager
|
|
70
|
+
const yarnLockExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "yarn.lock"));
|
|
71
|
+
const pnpmLockExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "pnpm-lock.yaml"));
|
|
72
|
+
const bunLockExists = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "bun.lockb"));
|
|
73
|
+
let packageManager = "npm";
|
|
74
|
+
if (pnpmLockExists) {
|
|
75
|
+
packageManager = "pnpm";
|
|
76
|
+
}
|
|
77
|
+
else if (yarnLockExists) {
|
|
78
|
+
packageManager = "yarn";
|
|
79
|
+
}
|
|
80
|
+
else if (bunLockExists) {
|
|
81
|
+
packageManager = "bun";
|
|
82
|
+
}
|
|
83
|
+
// Check for existing integrations
|
|
84
|
+
const hasAuth = !!(packageJson.dependencies?.["next-auth"] ||
|
|
85
|
+
packageJson.dependencies?.["better-auth"] ||
|
|
86
|
+
packageJson.dependencies?.["@auth/core"] ||
|
|
87
|
+
packageJson.dependencies?.["@kinde-oss/kinde-auth-nextjs"] ||
|
|
88
|
+
packageJson.dependencies?.["passport"]);
|
|
89
|
+
const hasPrisma = !!(packageJson.dependencies?.["@prisma/client"] || packageJson.devDependencies?.["prisma"]);
|
|
90
|
+
const hasDatabase = hasPrisma ||
|
|
91
|
+
!!(packageJson.dependencies?.["mongoose"] ||
|
|
92
|
+
packageJson.dependencies?.["typeorm"] ||
|
|
93
|
+
packageJson.dependencies?.["drizzle-orm"]);
|
|
94
|
+
return {
|
|
95
|
+
framework,
|
|
96
|
+
router,
|
|
97
|
+
language,
|
|
98
|
+
packageManager,
|
|
99
|
+
hasAuth,
|
|
100
|
+
hasPrisma,
|
|
101
|
+
hasDatabase,
|
|
102
|
+
rootDir: targetDir,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function getRouterBasePath(projectInfo) {
|
|
106
|
+
const srcExists = fs_extra_1.default.existsSync(path_1.default.join(projectInfo.rootDir, "src"));
|
|
107
|
+
if (projectInfo.router === "app") {
|
|
108
|
+
return srcExists ? "src/app" : "app";
|
|
109
|
+
}
|
|
110
|
+
else if (projectInfo.router === "pages") {
|
|
111
|
+
return srcExists ? "src/pages" : "pages";
|
|
112
|
+
}
|
|
113
|
+
throw new Error("Unknown router type");
|
|
114
|
+
}
|
|
115
|
+
function getLibPath(projectInfo) {
|
|
116
|
+
if (projectInfo.framework === "express") {
|
|
117
|
+
return "src/lib";
|
|
118
|
+
}
|
|
119
|
+
const srcExists = fs_extra_1.default.existsSync(path_1.default.join(projectInfo.rootDir, "src"));
|
|
120
|
+
return srcExists ? "src/lib" : "lib";
|
|
121
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Ora } from "ora";
|
|
2
|
+
export declare class Logger {
|
|
3
|
+
private spinner;
|
|
4
|
+
info(message: string): void;
|
|
5
|
+
success(message: string): void;
|
|
6
|
+
error(message: string): void;
|
|
7
|
+
warn(message: string): void;
|
|
8
|
+
log(message: string): void;
|
|
9
|
+
newLine(): void;
|
|
10
|
+
startSpinner(text: string): Ora;
|
|
11
|
+
stopSpinner(success?: boolean, text?: string): void;
|
|
12
|
+
updateSpinner(text: string): void;
|
|
13
|
+
header(text: string): void;
|
|
14
|
+
footer(): void;
|
|
15
|
+
}
|
|
16
|
+
export declare const logger: Logger;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = exports.Logger = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
class Logger {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.spinner = null;
|
|
12
|
+
}
|
|
13
|
+
info(message) {
|
|
14
|
+
process.stdout.write(chalk_1.default.blue("ℹ") + " " + message + "\n");
|
|
15
|
+
}
|
|
16
|
+
success(message) {
|
|
17
|
+
process.stdout.write(chalk_1.default.green("✔") + " " + message + "\n");
|
|
18
|
+
}
|
|
19
|
+
error(message) {
|
|
20
|
+
process.stderr.write(chalk_1.default.red("✖") + " " + message + "\n");
|
|
21
|
+
}
|
|
22
|
+
warn(message) {
|
|
23
|
+
process.stdout.write(chalk_1.default.yellow("⚠") + " " + message + "\n");
|
|
24
|
+
}
|
|
25
|
+
log(message) {
|
|
26
|
+
process.stdout.write(message + "\n");
|
|
27
|
+
}
|
|
28
|
+
newLine() {
|
|
29
|
+
process.stdout.write("\n");
|
|
30
|
+
}
|
|
31
|
+
startSpinner(text) {
|
|
32
|
+
this.spinner = (0, ora_1.default)(text).start();
|
|
33
|
+
return this.spinner;
|
|
34
|
+
}
|
|
35
|
+
stopSpinner(success = true, text) {
|
|
36
|
+
if (this.spinner) {
|
|
37
|
+
if (success) {
|
|
38
|
+
this.spinner.succeed(text);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.spinner.fail(text);
|
|
42
|
+
}
|
|
43
|
+
this.spinner = null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
updateSpinner(text) {
|
|
47
|
+
if (this.spinner) {
|
|
48
|
+
this.spinner.text = text;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
header(text) {
|
|
52
|
+
process.stdout.write(chalk_1.default.bold.cyan(text) + "\n");
|
|
53
|
+
}
|
|
54
|
+
footer() {
|
|
55
|
+
process.stdout.write("\n");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.Logger = Logger;
|
|
59
|
+
exports.logger = new Logger();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface TemplateMetadata {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
description: string;
|
|
5
|
+
tags: string[];
|
|
6
|
+
defaultPackageManager: "pnpm" | "npm" | "yarn" | "bun";
|
|
7
|
+
features: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ModuleMetadata {
|
|
10
|
+
name: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
description: string;
|
|
13
|
+
category: "auth" | "database" | "ui" | "other";
|
|
14
|
+
supportedFrameworks: string[];
|
|
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>;
|
|
23
|
+
envVars: EnvVar[];
|
|
24
|
+
patches: ModulePatch[];
|
|
25
|
+
frameworkPatches?: Record<string, {
|
|
26
|
+
[file: string]: {
|
|
27
|
+
merge?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
postInstall?: string[];
|
|
31
|
+
databaseAdapters?: {
|
|
32
|
+
common?: {
|
|
33
|
+
dependencies?: Record<string, string>;
|
|
34
|
+
devDependencies?: Record<string, string>;
|
|
35
|
+
};
|
|
36
|
+
providers?: Record<string, Record<string, {
|
|
37
|
+
dependencies?: Record<string, string>;
|
|
38
|
+
devDependencies?: Record<string, string>;
|
|
39
|
+
}>>;
|
|
40
|
+
};
|
|
41
|
+
frameworkConfigs?: Record<string, {
|
|
42
|
+
dependencies?: Record<string, string>;
|
|
43
|
+
devDependencies?: Record<string, string>;
|
|
44
|
+
patches?: ModulePatch[];
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface EnvVar {
|
|
48
|
+
key: string;
|
|
49
|
+
value?: string;
|
|
50
|
+
description: string;
|
|
51
|
+
required: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface ModulePatch {
|
|
54
|
+
type: "create-file" | "modify-json" | "append-env" | "inject-code";
|
|
55
|
+
description: string;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface CreateFilePatch extends ModulePatch {
|
|
59
|
+
type: "create-file";
|
|
60
|
+
source: string;
|
|
61
|
+
destination: string;
|
|
62
|
+
condition?: {
|
|
63
|
+
router?: "app" | "pages";
|
|
64
|
+
language?: "ts" | "js";
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export interface ModifyJsonPatch extends ModulePatch {
|
|
68
|
+
type: "modify-json";
|
|
69
|
+
file: string;
|
|
70
|
+
operations: {
|
|
71
|
+
path: string;
|
|
72
|
+
value: unknown;
|
|
73
|
+
merge?: boolean;
|
|
74
|
+
}[];
|
|
75
|
+
}
|
|
76
|
+
export interface ProjectInfo {
|
|
77
|
+
framework: "nextjs" | "express" | "react" | "unknown";
|
|
78
|
+
router: "app" | "pages" | "unknown";
|
|
79
|
+
language: "ts" | "js";
|
|
80
|
+
packageManager: "npm" | "yarn" | "pnpm" | "bun";
|
|
81
|
+
hasAuth: boolean;
|
|
82
|
+
hasPrisma: boolean;
|
|
83
|
+
hasDatabase: boolean;
|
|
84
|
+
rootDir: string;
|
|
85
|
+
}
|
|
86
|
+
export interface CLIOptions {
|
|
87
|
+
force?: boolean;
|
|
88
|
+
dryRun?: boolean;
|
|
89
|
+
yes?: boolean;
|
|
90
|
+
noInstall?: boolean;
|
|
91
|
+
pm?: "npm" | "yarn" | "pnpm" | "bun";
|
|
92
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NextAuthOptions } from "next-auth";
|
|
2
|
+
import { PrismaAdapter } from "@auth/prisma-adapter";
|
|
3
|
+
import { prisma } from "@/lib/prisma";
|
|
4
|
+
import GoogleProvider from "next-auth/providers/google";
|
|
5
|
+
|
|
6
|
+
export const authOptions: NextAuthOptions = {
|
|
7
|
+
adapter: PrismaAdapter(prisma),
|
|
8
|
+
providers: [
|
|
9
|
+
GoogleProvider({
|
|
10
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
11
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
12
|
+
}),
|
|
13
|
+
],
|
|
14
|
+
session: {
|
|
15
|
+
strategy: "jwt",
|
|
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
|
+
},
|
|
31
|
+
pages: {
|
|
32
|
+
signIn: "/auth/signin",
|
|
33
|
+
signOut: "/auth/signout",
|
|
34
|
+
error: "/auth/error",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
model Account {
|
|
3
|
+
id String @id {{idDefault}}
|
|
4
|
+
userId String {{userIdType}}
|
|
5
|
+
type String
|
|
6
|
+
provider String
|
|
7
|
+
providerAccountId String
|
|
8
|
+
refresh_token String? @db.Text
|
|
9
|
+
access_token String? @db.Text
|
|
10
|
+
expires_at Int?
|
|
11
|
+
token_type String?
|
|
12
|
+
scope String?
|
|
13
|
+
id_token String? @db.Text
|
|
14
|
+
session_state String?
|
|
15
|
+
|
|
16
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
17
|
+
|
|
18
|
+
@@unique([provider, providerAccountId])
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
model Session {
|
|
22
|
+
id String @id {{idDefault}}
|
|
23
|
+
sessionToken String @unique
|
|
24
|
+
userId String {{userIdType}}
|
|
25
|
+
expires DateTime
|
|
26
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
model User {
|
|
30
|
+
id String @id {{idDefault}}
|
|
31
|
+
name String?
|
|
32
|
+
email String @unique
|
|
33
|
+
emailVerified DateTime?
|
|
34
|
+
image String?
|
|
35
|
+
accounts Account[]
|
|
36
|
+
sessions Session[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
model VerificationToken {
|
|
40
|
+
identifier String
|
|
41
|
+
token String @unique
|
|
42
|
+
expires DateTime
|
|
43
|
+
|
|
44
|
+
@@unique([identifier, token])
|
|
45
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "authjs",
|
|
3
|
+
"displayName": "Auth.js",
|
|
4
|
+
"description": "Authentication with Auth.js (NextAuth.js v5)",
|
|
5
|
+
"category": "auth",
|
|
6
|
+
"provider": "authjs",
|
|
7
|
+
"supportedFrameworks": ["nextjs"],
|
|
8
|
+
"compatibility": {
|
|
9
|
+
"frameworks": ["nextjs"],
|
|
10
|
+
"databases": ["prisma"],
|
|
11
|
+
"languages": ["typescript", "javascript"],
|
|
12
|
+
"packageManagers": ["npm", "yarn", "pnpm", "bun"]
|
|
13
|
+
},
|
|
14
|
+
"databaseAdapters": {
|
|
15
|
+
"common": {
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@auth/prisma-adapter": "^2.7.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { sendEmail } from "./email/email-service";
|
|
3
|
+
import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
|
|
4
|
+
{{#if database=='prisma'}}
|
|
5
|
+
import { prisma } from "{{framework=='nextjs' ? '@/lib' : '.'}}/prisma";
|
|
6
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
7
|
+
{{/if}}
|
|
8
|
+
{{#if database=='mongoose'}}
|
|
9
|
+
import { mongoClient, db } from "{{framework=='nextjs' ? '@/lib' : '.'}}/db";
|
|
10
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
11
|
+
{{/if}}
|
|
12
|
+
|
|
13
|
+
export const auth = betterAuth({
|
|
14
|
+
{{#if database=='prisma'}}
|
|
15
|
+
database: prismaAdapter(prisma, {
|
|
16
|
+
provider: {{prismaProvider}},
|
|
17
|
+
}),
|
|
18
|
+
{{/if}}
|
|
19
|
+
{{#if database=='mongoose'}}
|
|
20
|
+
database: mongodbAdapter(db),
|
|
21
|
+
{{/if}}
|
|
22
|
+
user: {
|
|
23
|
+
additionalFields: {
|
|
24
|
+
role: {
|
|
25
|
+
type: "string",
|
|
26
|
+
defaultValue: "USER",
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
emailAndPassword: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
requireEmailVerification: true,
|
|
34
|
+
},
|
|
35
|
+
socialProviders: {
|
|
36
|
+
google: {
|
|
37
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
38
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
emailVerification: {
|
|
42
|
+
sendVerificationEmail: async ({ user, url }) => {
|
|
43
|
+
const { html, text } = getVerificationEmailTemplate(user, url);
|
|
44
|
+
await sendEmail({
|
|
45
|
+
to: user.email,
|
|
46
|
+
subject: "Verify Your Email Address",
|
|
47
|
+
text,
|
|
48
|
+
html,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
sendOnSignIn: true,
|
|
52
|
+
},
|
|
53
|
+
password: {
|
|
54
|
+
reset: {
|
|
55
|
+
sendResetEmail: async ({ user, url }) => {
|
|
56
|
+
const { html, text } = getPasswordResetEmailTemplate(user, url);
|
|
57
|
+
await sendEmail({
|
|
58
|
+
to: user.email,
|
|
59
|
+
subject: "Reset Your Password",
|
|
60
|
+
text,
|
|
61
|
+
html,
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
rateLimit: {
|
|
67
|
+
window: 10, // 10 seconds
|
|
68
|
+
max: 100, // max requests per window
|
|
69
|
+
},
|
|
70
|
+
account: {
|
|
71
|
+
accountLinking: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
trustedProviders: ["google"],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
session: {
|
|
77
|
+
cookieCache: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
},
|
|
80
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
81
|
+
updateAge: 60 * 60 * 24, // 1 day
|
|
82
|
+
}
|
|
83
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
|
|
3
|
+
// Create email transporter
|
|
4
|
+
const transporter = nodemailer.createTransporter({
|
|
5
|
+
host: process.env.EMAIL_HOST,
|
|
6
|
+
port: parseInt(process.env.EMAIL_PORT || "587"),
|
|
7
|
+
secure: process.env.EMAIL_PORT === "465",
|
|
8
|
+
auth: {
|
|
9
|
+
user: process.env.EMAIL_USER,
|
|
10
|
+
pass: process.env.EMAIL_PASS,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Send email function
|
|
15
|
+
export const sendEmail = async ({ to, subject, text, html }: {
|
|
16
|
+
to: string;
|
|
17
|
+
subject: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
html?: string;
|
|
20
|
+
}) => {
|
|
21
|
+
try {
|
|
22
|
+
await transporter.sendMail({
|
|
23
|
+
from: process.env.EMAIL_FROM,
|
|
24
|
+
to,
|
|
25
|
+
subject,
|
|
26
|
+
text,
|
|
27
|
+
html,
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.error("Email sending failed:", error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|