Initial commit: Setup Bun, Elysia, Vite, React, TanStack Router, Mantine, and Biome
This commit is contained in:
10
src/utils/api-client.ts
Normal file
10
src/utils/api-client.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { edenTreaty } from "@elysiajs/eden";
|
||||
import type { ApiApp } from "../index";
|
||||
|
||||
const baseUrl =
|
||||
import.meta.env.VITE_PUBLIC_URL ||
|
||||
(typeof window !== "undefined"
|
||||
? window.location.origin
|
||||
: "http://localhost:3000");
|
||||
|
||||
export const apiClient = edenTreaty<ApiApp>(baseUrl);
|
||||
7
src/utils/auth-client.ts
Normal file
7
src/utils/auth-client.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: import.meta.env.VITE_PUBLIC_URL || "http://localhost:3000",
|
||||
});
|
||||
|
||||
export const { useSession, signIn, signOut, signUp, getSession } = authClient;
|
||||
52
src/utils/auth.ts
Normal file
52
src/utils/auth.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { prismaAdapter } from "better-auth/adapters/prisma";
|
||||
import { PrismaClient } from "../../generated/prisma";
|
||||
import logger from "./logger";
|
||||
|
||||
const baseUrl = process.env.VITE_PUBLIC_URL;
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
if (!baseUrl) {
|
||||
logger.error("VITE_PUBLIC_URL is not defined");
|
||||
throw new Error("VITE_PUBLIC_URL is not defined");
|
||||
}
|
||||
|
||||
// logger.info('Initializing Better Auth with Prisma adapter');
|
||||
export const auth = betterAuth({
|
||||
baseURL: baseUrl,
|
||||
basePath: "/api/auth",
|
||||
database: prismaAdapter(prisma, {
|
||||
provider: "postgresql",
|
||||
}),
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID || "CLIENT_ID_MISSING",
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET || "CLIENT_SECRET_MISSING",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
additionalFields: {
|
||||
role: {
|
||||
type: "string",
|
||||
required: false,
|
||||
defaultValue: "user",
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: process.env.BETTER_AUTH_SECRET,
|
||||
trustedOrigins: ["http://localhost:5173", "http://localhost:3000"],
|
||||
session: {
|
||||
cookieCache: {
|
||||
enabled: true,
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
},
|
||||
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
||||
},
|
||||
advanced: {
|
||||
cookiePrefix: "bun-react",
|
||||
},
|
||||
});
|
||||
13
src/utils/db.ts
Normal file
13
src/utils/db.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PrismaClient } from "generated/prisma";
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined;
|
||||
};
|
||||
|
||||
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
globalForPrisma.prisma = prisma;
|
||||
}
|
||||
|
||||
// logger.info('Prisma client initialized');
|
||||
18
src/utils/logger.ts
Normal file
18
src/utils/logger.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import pino from "pino";
|
||||
|
||||
const logger = pino({
|
||||
level: process.env.LOG_LEVEL || "info",
|
||||
transport:
|
||||
process.env.NODE_ENV !== "production"
|
||||
? {
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: "SYS:standard",
|
||||
ignore: "pid,hostname",
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
export default logger;
|
||||
114
src/utils/open-in-editor.ts
Normal file
114
src/utils/open-in-editor.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
// open-in-editor.ts
|
||||
// DEV utility: open source file in local editor
|
||||
|
||||
import { spawn } from "child_process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
/* -------------------------------------------------------
|
||||
* Types
|
||||
* ----------------------------------------------------- */
|
||||
|
||||
export interface EditorOptions {
|
||||
line?: number;
|
||||
column?: number;
|
||||
editor?: "vscode" | "cursor" | "windsurf" | "antigravity" | "subl";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------
|
||||
* Editor commands
|
||||
* ----------------------------------------------------- */
|
||||
|
||||
const EDITORS = {
|
||||
vscode: "code",
|
||||
cursor: "cursor",
|
||||
windsurf: "windsurf",
|
||||
antigravity: "antigravity",
|
||||
subl: "subl",
|
||||
} as const;
|
||||
|
||||
const buildCommand = (
|
||||
editor: keyof typeof EDITORS,
|
||||
file: string,
|
||||
line = 1,
|
||||
column = 1,
|
||||
): [string, ...string[]] => {
|
||||
const cmd = EDITORS[editor];
|
||||
const location = `${file}:${line}:${column}`;
|
||||
|
||||
return editor === "subl" ? [cmd, location] : [cmd, "--goto", location];
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------
|
||||
* Main function
|
||||
* ----------------------------------------------------- */
|
||||
|
||||
export function openInEditor(
|
||||
filePath: string,
|
||||
options: EditorOptions = {},
|
||||
): void {
|
||||
// Resolve path
|
||||
const absolutePath = path.isAbsolute(filePath)
|
||||
? filePath
|
||||
: path.join(process.cwd(), filePath);
|
||||
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
console.error("[openInEditor] File not found:", absolutePath);
|
||||
return;
|
||||
}
|
||||
|
||||
const { line, column, editor } = options;
|
||||
|
||||
// Launch helper
|
||||
const launch = (editorKey: keyof typeof EDITORS) => {
|
||||
const [cmd, ...args] = buildCommand(editorKey, absolutePath, line, column);
|
||||
spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
|
||||
};
|
||||
|
||||
// 1. Explicit editor
|
||||
if (editor) {
|
||||
launch(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. ENV detection
|
||||
const envEditor = (
|
||||
process.env.VISUAL ||
|
||||
process.env.EDITOR ||
|
||||
""
|
||||
).toLowerCase();
|
||||
const detectedEditor = Object.keys(EDITORS).find((key) =>
|
||||
envEditor.includes(key),
|
||||
) as keyof typeof EDITORS | undefined;
|
||||
|
||||
if (detectedEditor) {
|
||||
launch(detectedEditor);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Fallback priority
|
||||
const fallbackOrder: (keyof typeof EDITORS)[] = [
|
||||
"cursor",
|
||||
"windsurf",
|
||||
"vscode",
|
||||
"antigravity",
|
||||
"subl",
|
||||
];
|
||||
|
||||
for (const editorKey of fallbackOrder) {
|
||||
try {
|
||||
launch(editorKey);
|
||||
return;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
console.error("[openInEditor] No supported editor detected");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------
|
||||
* Usage
|
||||
* ----------------------------------------------------- */
|
||||
/*
|
||||
openInEditor("src/pages/dashboard/index.tsx", { line: 31, column: 5 })
|
||||
openInEditor("src/app.tsx", { editor: "cursor" })
|
||||
*/
|
||||
Reference in New Issue
Block a user