mirror of
https://github.com/ershisan99/mlb-overlay-server.git
synced 2025-12-18 05:09:31 +00:00
initial commit
This commit is contained in:
4
apps/server/.env.example
Normal file
4
apps/server/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
CORS_ORIGIN=
|
||||
BETTER_AUTH_SECRET=
|
||||
BETTER_AUTH_URL=
|
||||
DATABASE_URL=
|
||||
52
apps/server/.gitignore
vendored
Normal file
52
apps/server/.gitignore
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# prod
|
||||
dist/
|
||||
/build
|
||||
/out/
|
||||
|
||||
# dev
|
||||
.yarn/
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
.vscode/*
|
||||
!.vscode/launch.json
|
||||
!.vscode/*.code-snippets
|
||||
.idea/workspace.xml
|
||||
.idea/usage.statistics.xml
|
||||
.idea/shelf
|
||||
.wrangler
|
||||
/.next/
|
||||
.vercel
|
||||
|
||||
# deps
|
||||
node_modules/
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
|
||||
# env
|
||||
.env*
|
||||
.env.production
|
||||
!.env.example
|
||||
.dev.vars
|
||||
|
||||
# logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# local db
|
||||
*.db*
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
10
apps/server/drizzle.config.ts
Normal file
10
apps/server/drizzle.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "./src/db/schema",
|
||||
out: "./src/db/migrations",
|
||||
dialect: "postgresql",
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL || "",
|
||||
},
|
||||
});
|
||||
34
apps/server/package.json
Normal file
34
apps/server/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "server",
|
||||
"main": "src/index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"check-types": "tsc -b",
|
||||
"compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server",
|
||||
"dev": "bun run --hot src/index.ts",
|
||||
"start": "bun run dist/index.js",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.7",
|
||||
"zod": "^4.0.2",
|
||||
"@trpc/server": "^11.4.2",
|
||||
"@trpc/client": "^11.4.2",
|
||||
"@hono/trpc-server": "^0.4.0",
|
||||
"hono": "^4.8.2",
|
||||
"drizzle-orm": "^0.44.2",
|
||||
"pg": "^8.14.1",
|
||||
"better-auth": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsdown": "^0.12.9",
|
||||
"typescript": "^5.8.2",
|
||||
"@types/bun": "^1.2.6",
|
||||
"drizzle-kit": "^0.31.2",
|
||||
"@types/pg": "^8.11.11"
|
||||
}
|
||||
}
|
||||
4
apps/server/src/db/index.ts
Normal file
4
apps/server/src/db/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
|
||||
export const db = drizzle(process.env.DATABASE_URL || "");
|
||||
|
||||
47
apps/server/src/db/schema/auth.ts
Normal file
47
apps/server/src/db/schema/auth.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { pgTable, text, timestamp, boolean, serial } from "drizzle-orm/pg-core";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
email: text('email').notNull().unique(),
|
||||
emailVerified: boolean('email_verified').notNull(),
|
||||
image: text('image'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const session = pgTable("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
token: text('token').notNull().unique(),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull(),
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' })
|
||||
});
|
||||
|
||||
export const account = pgTable("account", {
|
||||
id: text("id").primaryKey(),
|
||||
accountId: text('account_id').notNull(),
|
||||
providerId: text('provider_id').notNull(),
|
||||
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }),
|
||||
accessToken: text('access_token'),
|
||||
refreshToken: text('refresh_token'),
|
||||
idToken: text('id_token'),
|
||||
accessTokenExpiresAt: timestamp('access_token_expires_at'),
|
||||
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
|
||||
scope: text('scope'),
|
||||
password: text('password'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const verification = pgTable("verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text('identifier').notNull(),
|
||||
value: text('value').notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
createdAt: timestamp('created_at'),
|
||||
updatedAt: timestamp('updated_at')
|
||||
});
|
||||
36
apps/server/src/index.ts
Normal file
36
apps/server/src/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import "dotenv/config";
|
||||
import { trpcServer } from "@hono/trpc-server";
|
||||
import { createContext } from "./lib/context";
|
||||
import { appRouter } from "./routers/index";
|
||||
import { auth } from "./lib/auth";
|
||||
import { Hono } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
import { logger } from "hono/logger";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
app.use(logger());
|
||||
app.use("/*", cors({
|
||||
origin: process.env.CORS_ORIGIN || "",
|
||||
allowMethods: ["GET", "POST", "OPTIONS"],
|
||||
allowHeaders: ["Content-Type", "Authorization"],
|
||||
credentials: true,
|
||||
}));
|
||||
|
||||
app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
|
||||
|
||||
|
||||
app.use("/trpc/*", trpcServer({
|
||||
router: appRouter,
|
||||
createContext: (_opts, context) => {
|
||||
return createContext({ context });
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
|
||||
app.get("/", (c) => {
|
||||
return c.text("OK");
|
||||
});
|
||||
|
||||
export default app;
|
||||
25
apps/server/src/lib/auth.ts
Normal file
25
apps/server/src/lib/auth.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema/auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
|
||||
|
||||
schema: schema,
|
||||
}),
|
||||
trustedOrigins: [
|
||||
process.env.CORS_ORIGIN || "",
|
||||
],
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
secret: process.env.BETTER_AUTH_SECRET,
|
||||
baseURL: process.env.BETTER_AUTH_URL,
|
||||
});
|
||||
|
||||
|
||||
|
||||
18
apps/server/src/lib/context.ts
Normal file
18
apps/server/src/lib/context.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
import { auth } from "./auth";
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.req.raw.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
24
apps/server/src/lib/trpc.ts
Normal file
24
apps/server/src/lib/trpc.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
});
|
||||
17
apps/server/src/routers/index.ts
Normal file
17
apps/server/src/routers/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
protectedProcedure, publicProcedure,
|
||||
router,
|
||||
} from "../lib/trpc";
|
||||
|
||||
export const appRouter = router({
|
||||
healthCheck: publicProcedure.query(() => {
|
||||
return "OK";
|
||||
}),
|
||||
privateData: protectedProcedure.query(({ ctx }) => {
|
||||
return {
|
||||
message: "This is private",
|
||||
user: ctx.session.user,
|
||||
};
|
||||
}),
|
||||
});
|
||||
export type AppRouter = typeof appRouter;
|
||||
21
apps/server/tsconfig.json
Normal file
21
apps/server/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"verbatimModuleSyntax": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"outDir": "./dist",
|
||||
"types": [
|
||||
"bun"
|
||||
],
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user