mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
Add backend framework selection between hono, elysiajs
This commit is contained in:
5
.changeset/fruity-melons-argue.md
Normal file
5
.changeset/fruity-melons-argue.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-better-t-stack": minor
|
||||
---
|
||||
|
||||
Add option to choose elysiajs as backend framework
|
||||
@@ -7,10 +7,7 @@
|
||||
"bin": {
|
||||
"create-better-t-stack": "dist/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"template"
|
||||
],
|
||||
"files": ["dist", "template"],
|
||||
"keywords": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { ProjectConfig } from "./types";
|
||||
import { getUserPkgManager } from "./utils/get-package-manager";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const distPath = path.dirname(__filename);
|
||||
@@ -14,7 +15,7 @@ export const DEFAULT_CONFIG: ProjectConfig = {
|
||||
addons: [],
|
||||
examples: [],
|
||||
git: true,
|
||||
packageManager: "npm",
|
||||
packageManager: getUserPkgManager(),
|
||||
noInstall: false,
|
||||
turso: false,
|
||||
backendFramework: "hono",
|
||||
@@ -48,6 +49,15 @@ export const dependencyVersionMap = {
|
||||
"@types/node": "^22.13.11",
|
||||
|
||||
"@types/bun": "^1.2.6",
|
||||
|
||||
"@elysiajs/node": "^1.2.6",
|
||||
|
||||
"@elysiajs/cors": "^1.2.0",
|
||||
"@elysiajs/trpc": "^1.1.0",
|
||||
elysia: "^1.2.25",
|
||||
|
||||
"@hono/trpc-server": "^0.3.4",
|
||||
hono: "^4.7.5",
|
||||
} as const;
|
||||
|
||||
export type AvailableDependencies = keyof typeof dependencyVersionMap;
|
||||
|
||||
41
apps/cli/src/helpers/backend-framework-setup.ts
Normal file
41
apps/cli/src/helpers/backend-framework-setup.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import path from "node:path";
|
||||
import type { AvailableDependencies } from "../constants";
|
||||
import type { BackendFramework, Runtime } from "../types";
|
||||
import { addPackageDependency } from "../utils/add-package-deps";
|
||||
|
||||
export async function setupBackendDependencies(
|
||||
projectDir: string,
|
||||
framework: BackendFramework,
|
||||
runtime: Runtime,
|
||||
): Promise<void> {
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
|
||||
const dependencies: AvailableDependencies[] = [];
|
||||
const devDependencies: AvailableDependencies[] = [];
|
||||
|
||||
if (framework === "hono") {
|
||||
dependencies.push("hono", "@hono/trpc-server");
|
||||
|
||||
if (runtime === "node") {
|
||||
dependencies.push("@hono/node-server");
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
} else if (framework === "elysia") {
|
||||
dependencies.push("elysia", "@elysiajs/cors", "@elysiajs/trpc");
|
||||
|
||||
if (runtime === "node") {
|
||||
dependencies.push("@elysiajs/node");
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
}
|
||||
|
||||
if (runtime === "bun") {
|
||||
devDependencies.push("@types/bun");
|
||||
}
|
||||
|
||||
addPackageDependency({
|
||||
dependencies,
|
||||
devDependencies,
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import pc from "picocolors";
|
||||
import type { ProjectConfig } from "../types";
|
||||
import { setupAddons } from "./addons-setup";
|
||||
import { setupAuth } from "./auth-setup";
|
||||
import { setupBackendDependencies } from "./backend-framework-setup";
|
||||
import { createReadme } from "./create-readme";
|
||||
import { setupDatabase } from "./db-setup";
|
||||
import { setupEnvironmentVariables } from "./env-setup";
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
copyBaseTemplate,
|
||||
fixGitignoreFiles,
|
||||
setupAuthTemplate,
|
||||
setupBackendFramework,
|
||||
setupOrmTemplate,
|
||||
} from "./template-manager";
|
||||
|
||||
@@ -27,10 +29,14 @@ export async function createProject(options: ProjectConfig): Promise<string> {
|
||||
await fs.ensureDir(projectDir);
|
||||
|
||||
await copyBaseTemplate(projectDir);
|
||||
|
||||
await fixGitignoreFiles(projectDir);
|
||||
|
||||
await setupAuthTemplate(projectDir, options.auth);
|
||||
await setupBackendFramework(projectDir, options.backendFramework);
|
||||
await setupBackendDependencies(
|
||||
projectDir,
|
||||
options.backendFramework,
|
||||
options.runtime,
|
||||
);
|
||||
|
||||
await setupOrmTemplate(
|
||||
projectDir,
|
||||
@@ -39,15 +45,6 @@ export async function createProject(options: ProjectConfig): Promise<string> {
|
||||
options.auth,
|
||||
);
|
||||
|
||||
await setupRuntime(projectDir, options.runtime);
|
||||
|
||||
await setupExamples(
|
||||
projectDir,
|
||||
options.examples,
|
||||
options.orm,
|
||||
options.auth,
|
||||
);
|
||||
|
||||
await setupDatabase(
|
||||
projectDir,
|
||||
options.database,
|
||||
@@ -55,8 +52,24 @@ export async function createProject(options: ProjectConfig): Promise<string> {
|
||||
options.turso ?? options.database === "sqlite",
|
||||
);
|
||||
|
||||
await setupAuthTemplate(
|
||||
projectDir,
|
||||
options.auth,
|
||||
options.backendFramework,
|
||||
options.orm,
|
||||
options.database,
|
||||
);
|
||||
await setupAuth(projectDir, options.auth);
|
||||
|
||||
await setupRuntime(projectDir, options.runtime, options.backendFramework);
|
||||
|
||||
await setupExamples(
|
||||
projectDir,
|
||||
options.examples,
|
||||
options.orm,
|
||||
options.auth,
|
||||
);
|
||||
|
||||
await setupEnvironmentVariables(projectDir, options);
|
||||
|
||||
await initializeGit(projectDir, options.git);
|
||||
@@ -66,7 +79,6 @@ export async function createProject(options: ProjectConfig): Promise<string> {
|
||||
}
|
||||
|
||||
await updatePackageConfigurations(projectDir, options);
|
||||
|
||||
await createReadme(projectDir, options);
|
||||
|
||||
displayPostInstallInstructions(
|
||||
|
||||
@@ -33,9 +33,7 @@ export function displayPostInstallInstructions(
|
||||
? getLintingInstructions(runCmd)
|
||||
: "";
|
||||
|
||||
log.info(`${pc.cyan("Project created successfully!")}
|
||||
|
||||
${pc.bold("Next steps:")}
|
||||
log.info(`${pc.bold("Next steps:")}
|
||||
${pc.cyan("1.")} ${cdCmd}
|
||||
${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan(depsInstalled ? "2." : "3.")} ${runCmd} dev
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import type { Runtime } from "../types";
|
||||
import type { BackendFramework, Runtime } from "../types";
|
||||
import { addPackageDependency } from "../utils/add-package-deps";
|
||||
|
||||
export async function setupRuntime(
|
||||
projectDir: string,
|
||||
runtime: Runtime,
|
||||
backendFramework: BackendFramework,
|
||||
): Promise<void> {
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
const serverIndexPath = path.join(serverDir, "src/index.ts");
|
||||
@@ -13,9 +14,19 @@ export async function setupRuntime(
|
||||
const indexContent = await fs.readFile(serverIndexPath, "utf-8");
|
||||
|
||||
if (runtime === "bun") {
|
||||
await setupBunRuntime(serverDir, serverIndexPath, indexContent);
|
||||
await setupBunRuntime(
|
||||
serverDir,
|
||||
serverIndexPath,
|
||||
indexContent,
|
||||
backendFramework,
|
||||
);
|
||||
} else if (runtime === "node") {
|
||||
await setupNodeRuntime(serverDir, serverIndexPath, indexContent);
|
||||
await setupNodeRuntime(
|
||||
serverDir,
|
||||
serverIndexPath,
|
||||
indexContent,
|
||||
backendFramework,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +34,7 @@ async function setupBunRuntime(
|
||||
serverDir: string,
|
||||
serverIndexPath: string,
|
||||
indexContent: string,
|
||||
backendFramework: BackendFramework,
|
||||
): Promise<void> {
|
||||
const packageJsonPath = path.join(serverDir, "package.json");
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
@@ -40,7 +52,7 @@ async function setupBunRuntime(
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
if (!indexContent.includes("export default app")) {
|
||||
if (backendFramework === "hono") {
|
||||
const updatedContent = `${indexContent}\n\nexport default app;\n`;
|
||||
await fs.writeFile(serverIndexPath, updatedContent);
|
||||
}
|
||||
@@ -50,13 +62,8 @@ async function setupNodeRuntime(
|
||||
serverDir: string,
|
||||
serverIndexPath: string,
|
||||
indexContent: string,
|
||||
backendFramework: BackendFramework,
|
||||
): Promise<void> {
|
||||
addPackageDependency({
|
||||
dependencies: ["@hono/node-server"],
|
||||
devDependencies: ["tsx", "@types/node"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
const packageJsonPath = path.join(serverDir, "package.json");
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
@@ -68,27 +75,62 @@ async function setupNodeRuntime(
|
||||
|
||||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
||||
|
||||
const importLine = 'import { serve } from "@hono/node-server";\n';
|
||||
const serverCode = `
|
||||
addPackageDependency({
|
||||
devDependencies: ["tsx", "@types/node"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
if (backendFramework === "hono") {
|
||||
addPackageDependency({
|
||||
dependencies: ["@hono/node-server"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
const importLine = 'import { serve } from "@hono/node-server";\n';
|
||||
const serverCode = `
|
||||
serve(
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: 3000,
|
||||
},
|
||||
(info) => {
|
||||
console.log(\`Server is running on http://localhost:\${info.port}\`);
|
||||
},
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: 3000,
|
||||
},
|
||||
(info) => {
|
||||
console.log(\`Server is running on http://localhost:\${info.port}\`);
|
||||
},
|
||||
);\n`;
|
||||
|
||||
if (!indexContent.includes("@hono/node-server")) {
|
||||
const importEndIndex = indexContent.lastIndexOf("import");
|
||||
const importSection = indexContent.substring(0, importEndIndex);
|
||||
const restOfFile = indexContent.substring(importEndIndex);
|
||||
if (!indexContent.includes("@hono/node-server")) {
|
||||
const importEndIndex = indexContent.lastIndexOf("import");
|
||||
const importSection = indexContent.substring(0, importEndIndex);
|
||||
const restOfFile = indexContent.substring(importEndIndex);
|
||||
|
||||
const updatedContent = importSection + importLine + restOfFile + serverCode;
|
||||
await fs.writeFile(serverIndexPath, updatedContent);
|
||||
} else if (!indexContent.includes("serve(")) {
|
||||
const updatedContent = indexContent + serverCode;
|
||||
await fs.writeFile(serverIndexPath, updatedContent);
|
||||
const updatedContent =
|
||||
importSection + importLine + restOfFile + serverCode;
|
||||
await fs.writeFile(serverIndexPath, updatedContent);
|
||||
}
|
||||
} else if (backendFramework === "elysia") {
|
||||
addPackageDependency({
|
||||
dependencies: ["@elysiajs/node"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
if (!indexContent.includes("@elysiajs/node")) {
|
||||
const nodeImport = 'import { node } from "@elysiajs/node";\n';
|
||||
|
||||
const firstImportEnd = indexContent.indexOf(
|
||||
"\n",
|
||||
indexContent.indexOf("import"),
|
||||
);
|
||||
const before = indexContent.substring(0, firstImportEnd + 1);
|
||||
const after = indexContent.substring(firstImportEnd + 1);
|
||||
|
||||
let updatedContent = before + nodeImport + after;
|
||||
|
||||
updatedContent = updatedContent.replace(
|
||||
/const app = new Elysia\([^)]*\)/,
|
||||
"const app = new Elysia({ adapter: node() })",
|
||||
);
|
||||
|
||||
await fs.writeFile(serverIndexPath, updatedContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import { PKG_ROOT } from "../constants";
|
||||
import type { ProjectDatabase, ProjectOrm } from "../types";
|
||||
import type { BackendFramework, ProjectDatabase, ProjectOrm } from "../types";
|
||||
|
||||
export async function copyBaseTemplate(projectDir: string): Promise<void> {
|
||||
const templateDir = path.join(PKG_ROOT, "template/base");
|
||||
@@ -11,15 +11,13 @@ export async function copyBaseTemplate(projectDir: string): Promise<void> {
|
||||
await fs.copy(templateDir, projectDir);
|
||||
}
|
||||
|
||||
export async function setupAuthTemplate(
|
||||
export async function setupBackendFramework(
|
||||
projectDir: string,
|
||||
auth: boolean,
|
||||
framework: BackendFramework,
|
||||
): Promise<void> {
|
||||
if (!auth) return;
|
||||
|
||||
const authTemplateDir = path.join(PKG_ROOT, "template/with-auth");
|
||||
if (await fs.pathExists(authTemplateDir)) {
|
||||
await fs.copy(authTemplateDir, projectDir, { overwrite: true });
|
||||
const frameworkDir = path.join(PKG_ROOT, `template/with-${framework}`);
|
||||
if (await fs.pathExists(frameworkDir)) {
|
||||
await fs.copy(frameworkDir, projectDir, { overwrite: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,17 +34,83 @@ export async function setupOrmTemplate(
|
||||
if (await fs.pathExists(ormTemplateDir)) {
|
||||
await fs.copy(ormTemplateDir, projectDir, { overwrite: true });
|
||||
|
||||
const serverSrcPath = path.join(projectDir, "apps/server/src");
|
||||
const libPath = path.join(serverSrcPath, "lib");
|
||||
const withAuthLibPath = path.join(serverSrcPath, "with-auth-lib");
|
||||
|
||||
if (auth) {
|
||||
if (await fs.pathExists(withAuthLibPath)) {
|
||||
await fs.remove(libPath);
|
||||
await fs.move(withAuthLibPath, libPath);
|
||||
if (!auth) {
|
||||
if (orm === "prisma") {
|
||||
const authSchemaPath = path.join(
|
||||
projectDir,
|
||||
"apps/server/prisma/schema/auth.prisma",
|
||||
);
|
||||
if (await fs.pathExists(authSchemaPath)) {
|
||||
await fs.remove(authSchemaPath);
|
||||
}
|
||||
} else if (orm === "drizzle") {
|
||||
const authSchemaPath = path.join(
|
||||
projectDir,
|
||||
"apps/server/src/db/schema/auth.ts",
|
||||
);
|
||||
if (await fs.pathExists(authSchemaPath)) {
|
||||
await fs.remove(authSchemaPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function setupAuthTemplate(
|
||||
projectDir: string,
|
||||
auth: boolean,
|
||||
framework: BackendFramework,
|
||||
orm: ProjectOrm,
|
||||
database: ProjectDatabase,
|
||||
): Promise<void> {
|
||||
if (!auth) return;
|
||||
|
||||
const authTemplateDir = path.join(PKG_ROOT, "template/with-auth");
|
||||
if (await fs.pathExists(authTemplateDir)) {
|
||||
const clientAuthDir = path.join(authTemplateDir, "apps/client");
|
||||
const projectClientDir = path.join(projectDir, "apps/client");
|
||||
await fs.copy(clientAuthDir, projectClientDir, { overwrite: true });
|
||||
|
||||
const serverAuthDir = path.join(authTemplateDir, "apps/server/src");
|
||||
const projectServerDir = path.join(projectDir, "apps/server/src");
|
||||
|
||||
await fs.copy(
|
||||
path.join(serverAuthDir, "lib/trpc.ts"),
|
||||
path.join(projectServerDir, "lib/trpc.ts"),
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
await fs.copy(
|
||||
path.join(serverAuthDir, "routers/index.ts"),
|
||||
path.join(projectServerDir, "routers/index.ts"),
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
const contextFileName = `with-${framework}-context.ts`;
|
||||
await fs.copy(
|
||||
path.join(serverAuthDir, "lib", contextFileName),
|
||||
path.join(projectServerDir, "lib/context.ts"),
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
const indexFileName = `with-${framework}-index.ts`;
|
||||
await fs.copy(
|
||||
path.join(serverAuthDir, indexFileName),
|
||||
path.join(projectServerDir, "index.ts"),
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
const authLibFileName = getAuthLibDir(orm, database);
|
||||
const authLibSourceDir = path.join(serverAuthDir, authLibFileName);
|
||||
if (await fs.pathExists(authLibSourceDir)) {
|
||||
const files = await fs.readdir(authLibSourceDir);
|
||||
for (const file of files) {
|
||||
await fs.copy(
|
||||
path.join(authLibSourceDir, file),
|
||||
path.join(projectServerDir, "lib", file),
|
||||
{ overwrite: true },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await fs.remove(withAuthLibPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,3 +145,19 @@ function getOrmTemplateDir(orm: ProjectOrm, database: ProjectDatabase): string {
|
||||
|
||||
return "template/base";
|
||||
}
|
||||
|
||||
function getAuthLibDir(orm: ProjectOrm, database: ProjectDatabase): string {
|
||||
if (orm === "drizzle") {
|
||||
return database === "sqlite"
|
||||
? "with-drizzle-sqlite-lib"
|
||||
: "with-drizzle-postgres-lib";
|
||||
}
|
||||
|
||||
if (orm === "prisma") {
|
||||
return database === "sqlite"
|
||||
? "with-prisma-sqlite-lib"
|
||||
: "with-prisma-postgres-lib";
|
||||
}
|
||||
|
||||
throw new Error("Invalid ORM or database configuration for auth setup");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createProject } from "./helpers/create-project";
|
||||
import { installDependencies } from "./helpers/install-dependencies";
|
||||
import { gatherConfig } from "./prompts/config-prompts";
|
||||
import type {
|
||||
BackendFramework,
|
||||
ProjectAddons,
|
||||
ProjectConfig,
|
||||
ProjectExamples,
|
||||
@@ -56,6 +57,7 @@ async function main() {
|
||||
.option("--turso", "Set up Turso for SQLite database")
|
||||
.option("--no-turso", "Skip Turso setup for SQLite database")
|
||||
.option("--hono", "Use Hono backend framework")
|
||||
.option("--elysia", "Use Elysia backend framework")
|
||||
.option("--runtime <runtime>", "Specify runtime (bun or node)")
|
||||
.parse();
|
||||
|
||||
@@ -68,6 +70,10 @@ async function main() {
|
||||
const options = program.opts();
|
||||
const projectDirectory = program.args[0];
|
||||
|
||||
let backendFramework: BackendFramework | undefined;
|
||||
if (options.hono) backendFramework = "hono";
|
||||
if (options.elysia) backendFramework = "elysia";
|
||||
|
||||
const flagConfig: Partial<ProjectConfig> = {
|
||||
...(projectDirectory && { projectName: projectDirectory }),
|
||||
...(options.database === false && { database: "none" }),
|
||||
@@ -82,7 +88,7 @@ async function main() {
|
||||
...("git" in options && { git: options.git }),
|
||||
...("install" in options && { noInstall: !options.install }),
|
||||
...("turso" in options && { turso: options.turso }),
|
||||
...(options.hono && { backendFramework: "hono" }),
|
||||
...(backendFramework && { backendFramework }),
|
||||
...(options.runtime && { runtime: options.runtime as Runtime }),
|
||||
...((options.pwa ||
|
||||
options.tauri ||
|
||||
@@ -124,7 +130,11 @@ async function main() {
|
||||
database:
|
||||
options.database === false
|
||||
? "none"
|
||||
: (options.database ?? DEFAULT_CONFIG.database),
|
||||
: options.sqlite
|
||||
? "sqlite"
|
||||
: options.postgres
|
||||
? "postgres"
|
||||
: DEFAULT_CONFIG.database,
|
||||
orm:
|
||||
options.database === false
|
||||
? "none"
|
||||
@@ -133,12 +143,10 @@ async function main() {
|
||||
: options.prisma
|
||||
? "prisma"
|
||||
: DEFAULT_CONFIG.orm,
|
||||
auth: options.auth ?? DEFAULT_CONFIG.auth,
|
||||
git: options.git ?? DEFAULT_CONFIG.git,
|
||||
auth: "auth" in options ? options.auth : DEFAULT_CONFIG.auth,
|
||||
git: "git" in options ? options.git : DEFAULT_CONFIG.git,
|
||||
noInstall:
|
||||
"noInstall" in options
|
||||
? options.noInstall
|
||||
: DEFAULT_CONFIG.noInstall,
|
||||
"install" in options ? !options.install : DEFAULT_CONFIG.noInstall,
|
||||
packageManager:
|
||||
flagConfig.packageManager ?? DEFAULT_CONFIG.packageManager,
|
||||
addons: flagConfig.addons?.length
|
||||
@@ -153,9 +161,7 @@ async function main() {
|
||||
: flagConfig.database === "sqlite"
|
||||
? DEFAULT_CONFIG.turso
|
||||
: false,
|
||||
backendFramework: options.hono
|
||||
? "hono"
|
||||
: DEFAULT_CONFIG.backendFramework,
|
||||
backendFramework: backendFramework ?? DEFAULT_CONFIG.backendFramework,
|
||||
runtime: options.runtime
|
||||
? (options.runtime as Runtime)
|
||||
: DEFAULT_CONFIG.runtime,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
import type { ProjectAddons } from "../types";
|
||||
|
||||
export async function getAddonsChoice(
|
||||
@@ -31,6 +32,7 @@ export async function getAddonsChoice(
|
||||
hint: "Add Git hooks with Husky, lint-staged (requires Biome)",
|
||||
},
|
||||
],
|
||||
initialValues: DEFAULT_CONFIG.addons,
|
||||
required: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// import { cancel, isCancel, select } from "@clack/prompts";
|
||||
// import pc from "picocolors";
|
||||
import { cancel, isCancel, select } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
import type { BackendFramework } from "../types";
|
||||
|
||||
export async function getBackendFrameworkChoice(
|
||||
@@ -7,24 +8,27 @@ export async function getBackendFrameworkChoice(
|
||||
): Promise<BackendFramework> {
|
||||
if (backendFramework !== undefined) return backendFramework;
|
||||
|
||||
return "hono";
|
||||
const response = await select<BackendFramework>({
|
||||
message: "Which backend framework would you like to use?",
|
||||
options: [
|
||||
{
|
||||
value: "hono",
|
||||
label: "Hono",
|
||||
hint: "Lightweight, ultrafast web framework",
|
||||
},
|
||||
{
|
||||
value: "elysia",
|
||||
label: "Elysia",
|
||||
hint: "TypeScript framework with end-to-end type safety)",
|
||||
},
|
||||
],
|
||||
initialValue: DEFAULT_CONFIG.backendFramework,
|
||||
});
|
||||
|
||||
// const response = await select<BackendFramework>({
|
||||
// message: "Which backend framework would you like to use?",
|
||||
// options: [
|
||||
// {
|
||||
// value: "hono",
|
||||
// label: "Hono",
|
||||
// hint: "Lightweight, ultrafast web framework",
|
||||
// },
|
||||
// ],
|
||||
// initialValue: "hono",
|
||||
// });
|
||||
if (isCancel(response)) {
|
||||
cancel(pc.red("Operation cancelled"));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// if (isCancel(response)) {
|
||||
// cancel(pc.red("Operation cancelled"));
|
||||
// process.exit(0);
|
||||
// }
|
||||
|
||||
// return response;
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ export async function gatherConfig(
|
||||
projectName: async () => {
|
||||
return getProjectName(flags.projectName);
|
||||
},
|
||||
runtime: () => getRuntimeChoice(flags.runtime),
|
||||
backendFramework: () => getBackendFrameworkChoice(flags.backendFramework),
|
||||
runtime: () => getRuntimeChoice(flags.runtime),
|
||||
database: () => getDatabaseChoice(flags.database),
|
||||
orm: ({ results }) =>
|
||||
getORMChoice(flags.orm, results.database !== "none"),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cancel, isCancel, select } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
import type { ProjectDatabase } from "../types";
|
||||
|
||||
export async function getDatabaseChoice(
|
||||
@@ -26,7 +27,7 @@ export async function getDatabaseChoice(
|
||||
hint: "Traditional relational database",
|
||||
},
|
||||
],
|
||||
initialValue: "sqlite",
|
||||
initialValue: DEFAULT_CONFIG.database,
|
||||
});
|
||||
|
||||
if (isCancel(response)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cancel, isCancel, select } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
import type { ProjectOrm } from "../types";
|
||||
|
||||
export async function getORMChoice(
|
||||
@@ -23,7 +24,7 @@ export async function getORMChoice(
|
||||
hint: "Powerful, feature-rich ORM with schema migrations",
|
||||
},
|
||||
],
|
||||
initialValue: "drizzle",
|
||||
initialValue: DEFAULT_CONFIG.orm,
|
||||
});
|
||||
|
||||
if (isCancel(response)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cancel, isCancel, select } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
import type { Runtime } from "../types";
|
||||
|
||||
export async function getRuntimeChoice(runtime?: Runtime): Promise<Runtime> {
|
||||
@@ -19,7 +20,7 @@ export async function getRuntimeChoice(runtime?: Runtime): Promise<Runtime> {
|
||||
hint: "Traditional Node.js runtime",
|
||||
},
|
||||
],
|
||||
initialValue: "bun",
|
||||
initialValue: DEFAULT_CONFIG.runtime,
|
||||
});
|
||||
|
||||
if (isCancel(response)) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { cancel, confirm, isCancel } from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { DEFAULT_CONFIG } from "../constants";
|
||||
|
||||
export async function getTursoSetupChoice(turso?: boolean): Promise<boolean> {
|
||||
if (turso !== undefined) return turso;
|
||||
|
||||
const response = await confirm({
|
||||
message: "Set up a Turso database for this project?",
|
||||
initialValue: true,
|
||||
initialValue: DEFAULT_CONFIG.turso,
|
||||
});
|
||||
|
||||
if (isCancel(response)) {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
export type ProjectDatabase = "sqlite" | "postgres" | "none";
|
||||
export type ProjectOrm = "drizzle" | "prisma" | "none";
|
||||
export type PackageManager = "npm" | "pnpm" | "bun";
|
||||
export type ProjectAddons = "pwa" | "tauri" | "biome" | "husky";
|
||||
export type ProjectAddons = "pwa" | "biome" | "tauri" | "husky";
|
||||
export type BackendFramework = "hono" | "elysia";
|
||||
export type Runtime = "node" | "bun";
|
||||
export type ProjectExamples = "todo";
|
||||
export type BackendFramework = "hono";
|
||||
export type Runtime = "bun" | "node";
|
||||
|
||||
export interface ProjectConfig {
|
||||
projectName: string;
|
||||
backendFramework: BackendFramework;
|
||||
runtime: Runtime;
|
||||
database: ProjectDatabase;
|
||||
orm: ProjectOrm;
|
||||
auth: boolean;
|
||||
@@ -17,6 +19,4 @@ export interface ProjectConfig {
|
||||
packageManager: PackageManager;
|
||||
noInstall?: boolean;
|
||||
turso?: boolean;
|
||||
backendFramework: BackendFramework;
|
||||
runtime: Runtime;
|
||||
}
|
||||
|
||||
@@ -7,32 +7,55 @@ export function displayConfig(config: Partial<ProjectConfig>) {
|
||||
if (config.projectName) {
|
||||
configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`);
|
||||
}
|
||||
if (config.database) {
|
||||
|
||||
if (config.backendFramework !== undefined) {
|
||||
configDisplay.push(
|
||||
`${pc.blue("Backend Framework:")} ${config.backendFramework}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (config.runtime !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Runtime:")} ${config.runtime}`);
|
||||
}
|
||||
|
||||
if (config.database !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Database:")} ${config.database}`);
|
||||
}
|
||||
if (config.orm) {
|
||||
|
||||
if (config.orm !== undefined) {
|
||||
configDisplay.push(`${pc.blue("ORM:")} ${config.orm}`);
|
||||
}
|
||||
|
||||
if (config.auth !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`);
|
||||
}
|
||||
if (config.runtime) {
|
||||
configDisplay.push(`${pc.blue("Runtime:")} ${config.runtime}`);
|
||||
|
||||
if (config.addons !== undefined) {
|
||||
const addonsText =
|
||||
config.addons.length > 0 ? config.addons.join(", ") : "none";
|
||||
configDisplay.push(`${pc.blue("Addons:")} ${addonsText}`);
|
||||
}
|
||||
if (config.addons?.length) {
|
||||
configDisplay.push(`${pc.blue("Addons:")} ${config.addons.join(", ")}`);
|
||||
|
||||
if (config.examples !== undefined) {
|
||||
const examplesText =
|
||||
config.examples.length > 0 ? config.examples.join(", ") : "none";
|
||||
configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`);
|
||||
}
|
||||
|
||||
if (config.git !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`);
|
||||
}
|
||||
if (config.packageManager) {
|
||||
|
||||
if (config.packageManager !== undefined) {
|
||||
configDisplay.push(
|
||||
`${pc.blue("Package Manager:")} ${config.packageManager}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (config.noInstall !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Skip Install:")} ${config.noInstall}`);
|
||||
}
|
||||
|
||||
if (config.turso !== undefined) {
|
||||
configDisplay.push(`${pc.blue("Turso Setup:")} ${config.turso}`);
|
||||
}
|
||||
|
||||
@@ -5,42 +5,36 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
|
||||
if (config.database === "none") {
|
||||
flags.push("--no-database");
|
||||
} else if (config.database === "sqlite") {
|
||||
flags.push("--sqlite");
|
||||
} else if (config.database === "postgres") {
|
||||
flags.push("--postgres");
|
||||
}
|
||||
} else {
|
||||
flags.push(`--${config.database}`);
|
||||
|
||||
if (config.database !== "none") {
|
||||
if (config.orm === "drizzle") {
|
||||
flags.push("--drizzle");
|
||||
} else if (config.orm === "prisma") {
|
||||
flags.push("--prisma");
|
||||
if (config.orm) {
|
||||
flags.push(`--${config.orm}`);
|
||||
}
|
||||
|
||||
if (config.database === "sqlite") {
|
||||
flags.push(config.turso ? "--turso" : "--no-turso");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.auth) {
|
||||
flags.push("--auth");
|
||||
} else {
|
||||
flags.push("--no-auth");
|
||||
}
|
||||
flags.push(config.auth ? "--auth" : "--no-auth");
|
||||
|
||||
if (config.git) {
|
||||
flags.push("--git");
|
||||
} else {
|
||||
flags.push("--no-git");
|
||||
}
|
||||
flags.push(config.git ? "--git" : "--no-git");
|
||||
|
||||
if (config.noInstall) {
|
||||
flags.push("--no-install");
|
||||
} else {
|
||||
flags.push("--install");
|
||||
}
|
||||
flags.push(config.noInstall ? "--no-install" : "--install");
|
||||
|
||||
if (config.packageManager) {
|
||||
flags.push(`--${config.packageManager}`);
|
||||
}
|
||||
|
||||
if (config.backendFramework) {
|
||||
flags.push(`--${config.backendFramework}`);
|
||||
}
|
||||
|
||||
if (config.runtime) {
|
||||
flags.push(`--runtime ${config.runtime}`);
|
||||
}
|
||||
|
||||
if (config.addons.length > 0) {
|
||||
for (const addon of config.addons) {
|
||||
flags.push(`--${addon}`);
|
||||
@@ -55,21 +49,8 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
||||
flags.push("--no-examples");
|
||||
}
|
||||
|
||||
if (config.database === "sqlite") {
|
||||
if (config.turso) {
|
||||
flags.push("--turso");
|
||||
} else {
|
||||
flags.push("--no-turso");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.runtime) {
|
||||
flags.push(`--runtime ${config.runtime}`);
|
||||
}
|
||||
|
||||
const baseCommand = "npx create-better-t-stack";
|
||||
const projectName = config.projectName ? ` ${config.projectName}` : "";
|
||||
const flagString = flags.length > 0 ? ` ${flags.join(" ")}` : "";
|
||||
|
||||
return `${baseCommand}${projectName}${flagString}`;
|
||||
return `${baseCommand}${projectName} ${flags.join(" ")}`;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
"compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hono/trpc-server": "^0.3.4",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"hono": "^4.7.5",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist",
|
||||
"types": ["node"],
|
||||
"types": ["node", "bun"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
import type { Context as ElysiaContext } from "elysia";
|
||||
import { auth } from "./auth";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
hono: HonoContext;
|
||||
context: ElysiaContext;
|
||||
};
|
||||
|
||||
export async function createContext({ hono }: CreateContextOptions) {
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: hono.req.raw.headers,
|
||||
headers: context.request.headers,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -2,12 +2,12 @@ import type { Context as HonoContext } from "hono";
|
||||
import { auth } from "./auth";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
hono: HonoContext;
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ hono }: CreateContextOptions) {
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: hono.req.raw.headers,
|
||||
headers: context.req.raw.headers,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import * as schema from "../db/schema/auth";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema/auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
@@ -0,0 +1,38 @@
|
||||
import "dotenv/config";
|
||||
import { Elysia } from "elysia";
|
||||
import { cors } from "@elysiajs/cors";
|
||||
import { auth } from "./lib/auth";
|
||||
import { createContext } from "./lib/context";
|
||||
import { appRouter } from "./routers/index";
|
||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||
|
||||
const app = new Elysia()
|
||||
.use(
|
||||
cors({
|
||||
origin: process.env.CORS_ORIGIN || "",
|
||||
methods: ["GET", "POST", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization"],
|
||||
credentials: true,
|
||||
}),
|
||||
)
|
||||
.all("/api/auth/*", async (context) => {
|
||||
const { request } = context;
|
||||
if (["POST", "GET"].includes(request.method)) {
|
||||
return auth.handler(request);
|
||||
} else {
|
||||
context.error(405);
|
||||
}
|
||||
})
|
||||
.all("/trpc/*", async (context) => {
|
||||
const res = await fetchRequestHandler({
|
||||
endpoint: "/trpc",
|
||||
router: appRouter,
|
||||
req: context.request,
|
||||
createContext: () => createContext({ context }),
|
||||
});
|
||||
return res;
|
||||
})
|
||||
.get("/", () => "OK")
|
||||
.listen(3000, () => {
|
||||
console.log(`Server is running on http://localhost:3000`);
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import { trpcServer } from "@hono/trpc-server";
|
||||
import "dotenv/config";
|
||||
import { Hono } from "hono";
|
||||
@@ -27,8 +28,8 @@ app.use(
|
||||
"/trpc/*",
|
||||
trpcServer({
|
||||
router: appRouter,
|
||||
createContext: (_opts, hono) => {
|
||||
return createContext({ hono });
|
||||
createContext: (_opts, context) => {
|
||||
return createContext({ context });
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -1,44 +0,0 @@
|
||||
import { z } from "zod";
|
||||
import { router, publicProcedure } from "../lib/trpc";
|
||||
import { todo } from "../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../db";
|
||||
|
||||
export const todoRouter = router({
|
||||
getAll: publicProcedure.query(async () => {
|
||||
return await db.select().from(todo).all();
|
||||
}),
|
||||
|
||||
create: publicProcedure
|
||||
.input(z.object({ text: z.string().min(1) }))
|
||||
.mutation(async ({ input }) => {
|
||||
return await db
|
||||
.insert(todo)
|
||||
.values({
|
||||
text: input.text,
|
||||
})
|
||||
.returning()
|
||||
.get();
|
||||
}),
|
||||
|
||||
toggle: publicProcedure
|
||||
.input(z.object({ id: z.number(), completed: z.boolean() }))
|
||||
.mutation(async ({ input }) => {
|
||||
return await db
|
||||
.update(todo)
|
||||
.set({ completed: input.completed })
|
||||
.where(eq(todo.id, input.id))
|
||||
.returning()
|
||||
.get();
|
||||
}),
|
||||
|
||||
delete: publicProcedure
|
||||
.input(z.object({ id: z.number() }))
|
||||
.mutation(async ({ input }) => {
|
||||
return await db
|
||||
.delete(todo)
|
||||
.where(eq(todo.id, input.id))
|
||||
.returning()
|
||||
.get();
|
||||
}),
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
import { auth } from "./auth";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
hono: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ hono }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: hono.req.raw.headers,
|
||||
});
|
||||
|
||||
return {
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const t = initTRPC.context<Context>().create();
|
||||
|
||||
export const router = t.router;
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Authentication required",
|
||||
cause: "No session",
|
||||
});
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
session: ctx.session,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const t = initTRPC.context<Context>().create();
|
||||
|
||||
export const router = t.router;
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Authentication required",
|
||||
cause: "No session",
|
||||
});
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
session: ctx.session,
|
||||
},
|
||||
});
|
||||
});
|
||||
27
apps/cli/template/with-elysia/apps/server/src/index.ts
Normal file
27
apps/cli/template/with-elysia/apps/server/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import "dotenv/config";
|
||||
import { Elysia } from "elysia";
|
||||
import { cors } from "@elysiajs/cors";
|
||||
import { createContext } from "./lib/context";
|
||||
import { appRouter } from "./routers/index";
|
||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||
|
||||
const app = new Elysia()
|
||||
.use(
|
||||
cors({
|
||||
origin: process.env.CORS_ORIGIN || "",
|
||||
methods: ["GET", "POST", "OPTIONS"],
|
||||
}),
|
||||
)
|
||||
.all("/trpc/*", async (context) => {
|
||||
const res = await fetchRequestHandler({
|
||||
endpoint: "/trpc",
|
||||
router: appRouter,
|
||||
req: context.request,
|
||||
createContext: () => createContext({ context }),
|
||||
});
|
||||
return res;
|
||||
})
|
||||
.get("/", () => "OK")
|
||||
.listen(3000, () => {
|
||||
console.log(`Server is running on http://localhost:3000`);
|
||||
});
|
||||
13
apps/cli/template/with-elysia/apps/server/src/lib/context.ts
Normal file
13
apps/cli/template/with-elysia/apps/server/src/lib/context.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Context as ElysiaContext } from "elysia";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: ElysiaContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
@@ -22,8 +22,8 @@ app.use(
|
||||
"/trpc/*",
|
||||
trpcServer({
|
||||
router: appRouter,
|
||||
createContext: (_opts, hono) => {
|
||||
return createContext({ hono });
|
||||
createContext: (_opts, context) => {
|
||||
return createContext({ context });
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
hono: HonoContext;
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ hono }: CreateContextOptions) {
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
import { auth } from "./auth";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
hono: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ hono }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: hono.req.raw.headers,
|
||||
});
|
||||
|
||||
return {
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const t = initTRPC.context<Context>().create();
|
||||
|
||||
export const router = t.router;
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Authentication required",
|
||||
cause: "No session",
|
||||
});
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
session: ctx.session,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const t = initTRPC.context<Context>().create();
|
||||
|
||||
export const router = t.router;
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Authentication required",
|
||||
cause: "No session",
|
||||
});
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
session: ctx.session,
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user