update
Some checks failed
Deploy / deploy (push) Failing after 4m2s

This commit is contained in:
Никита Круглицкий 2025-10-09 22:29:42 +06:00
parent 6ada2c3fbd
commit bb48e52a99
14 changed files with 1234 additions and 15 deletions

View File

@ -35,6 +35,7 @@ jobs:
docker run -d \
--name chad-server \
--network traefik \
--volume /home/koptilnya/services/chad/database.db:/app/prisma/database.db
-p 40000-40100:40000-40100/udp \
--label "traefik.enable=true" \
--label "traefik.http.routers.chad-server.rule=Host(\`api.koptilnya.xyz\`) && PathPrefix(\`/chad\`)" \

View File

@ -17,7 +17,6 @@ declare module 'vue' {
PrimeFloatLabel: typeof import('primevue/floatlabel')['default']
PrimeInputText: typeof import('primevue/inputtext')['default']
PrimeMenu: typeof import('primevue/menu')['default']
PrimeMessage: typeof import('primevue/message')['default']
PrimeSlider: typeof import('primevue/slider')['default']
PrimeToast: typeof import('primevue/toast')['default']
RouterLink: typeof import('vue-router')['RouterLink']

3
server/.gitignore vendored
View File

@ -15,3 +15,6 @@ node_modules
#!.yarn/cache
.pnp.*
.env*
*.db

View File

@ -11,6 +11,8 @@ ENV PORT=80
ENV CORS_ORIGIN=chad.koptilnya.xyz
ENV ANNOUNCED_ADDRESS=91.144.171.182
RUN yarn db:deploy
EXPOSE 80
CMD ["yarn", "start"]

20
server/auth/lucia.ts Normal file
View File

@ -0,0 +1,20 @@
import { PrismaAdapter } from '@lucia-auth/adapter-prisma'
import { Lucia } from 'lucia'
import prisma from '../prisma/client'
export const auth = new Lucia<object, { username: string, displayName: string }>(new PrismaAdapter(prisma.session, prisma.user))
// export const auth = new Lucia({
// adapter: new PrismaAdapter(prisma.session, prisma.user),
// env: process.env.NODE_ENV === 'production' ? 'PROD' : 'DEV',
// middleware: req => ({
// headers: req.headers,
// }),
// transformUserData: user => ({
// id: user.id,
// username: user.username,
// displayName: user.username,
// }),
// })
export type Auth = typeof auth

View File

@ -1,19 +1,33 @@
{
"name": "server",
"scripts": {
"start": "ts-node --transpile-only index.ts"
"start": "ts-node --transpile-only server.ts",
"db:deploy": "npx prisma migrate deploy && npx prisma generate"
},
"packageManager": "yarn@4.10.3",
"dependencies": {
"@lucia-auth/adapter-prisma": "^4.0.1",
"@prisma/client": "^6.17.0",
"@trpc/server": "^11.6.0",
"bcrypt": "^6.0.0",
"consola": "^3.4.2",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"lucia": "^3.2.2",
"mediasoup": "^3.19.3",
"socket.io": "^4.8.1"
"prisma": "^6.17.0",
"socket.io": "^4.8.1",
"ws": "^8.18.3",
"zod": "^4.1.12"
},
"devDependencies": {
"@antfu/eslint-config": "^5.4.1",
"@types/bcrypt": "^6",
"@types/cookie-parser": "^1",
"@types/express": "^5.0.3",
"@types/ws": "^8",
"eslint": "^9.36.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"

7
server/prisma/client.ts Normal file
View File

@ -0,0 +1,7 @@
import { PrismaClient } from '@prisma/client'
const instance = new PrismaClient({
log: ['query', 'error', 'warn'],
})
export default instance

View File

@ -0,0 +1,29 @@
datasource db {
provider = "sqlite"
url = "file:./database.db"
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
username String @unique
password String
displayName String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Session Session[]
}
model Session {
id String @id
userId String
expiresAt DateTime
user User @relation(references: [id], fields: [userId], onDelete: Cascade)
@@index([userId])
}

21
server/server.ts Normal file
View File

@ -0,0 +1,21 @@
import { createExpressMiddleware } from '@trpc/server/adapters/express'
import cookieParser from 'cookie-parser'
import express from 'express'
import { createContext } from './trpc/context'
import { appRouter } from './trpc/routers'
const app = express()
app.use(cookieParser())
app.use(express.json())
app.use(
'/trpc',
createExpressMiddleware({
router: appRouter,
createContext,
}),
)
app.listen(process.env.PORT || 4000, () => {
console.log('✅ Server running')
})

23
server/trpc/context.ts Normal file
View File

@ -0,0 +1,23 @@
import type { CreateExpressContextOptions } from '@trpc/server/adapters/express'
import { auth } from '../auth/lucia'
export async function createContext({ res, req }: CreateExpressContextOptions) {
const sessionId = auth.readSessionCookie(req.headers.cookie ?? '')
if (!sessionId)
return { res, req }
const { session, user } = await auth.validateSession(sessionId)
if (session && session.fresh) {
res.appendHeader('Set-Cookie', auth.createSessionCookie(session.id).serialize())
}
if (!session) {
res.appendHeader('Set-Cookie', auth.createBlankSessionCookie().serialize())
}
return { res, req, session, user }
}
export type Context = Awaited<ReturnType<typeof createContext>>

15
server/trpc/router.ts Normal file
View File

@ -0,0 +1,15 @@
import type { Context } from './context'
import { initTRPC } from '@trpc/server'
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?.fresh)
throw new Error('UNAUTHORIZED')
return next({ ctx: { ...ctx } })
})

View File

@ -0,0 +1,74 @@
import { TRPCError } from '@trpc/server'
import bcrypt from 'bcrypt'
import { z } from 'zod'
import { auth } from '../../auth/lucia'
import client from '../../prisma/client'
import { protectedProcedure, publicProcedure, router } from '../router'
export const authRouter = router({
register: publicProcedure
.input(z.object({ username: z.string().min(1), password: z.string().min(6) }))
.mutation(async ({ input, ctx }) => {
const hashed = await bcrypt.hash(input.password, 10)
const user = await client.user.create({
data: {
username: input.username,
password: hashed,
displayName: input.username,
},
})
const session = await auth.createSession(user.id, {})
const cookie = auth.createSessionCookie(session.id)
ctx.res.setHeader('Set-Cookie', cookie.serialize())
return { user }
}),
login: publicProcedure
.input(z.object({ username: z.string().min(1), password: z.string() }))
.mutation(async ({ input, ctx }) => {
const user = await client.user.findFirst({
where: {
username: input.username,
},
})
if (!user) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Incorrect username or password',
})
}
const validPassword = await bcrypt.compare(input.password, user.password)
if (!validPassword) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Incorrect username or password',
})
}
const session = await auth.createSession(user.id, {})
const cookie = auth.createSessionCookie(session.id)
ctx.res.setHeader('Set-Cookie', cookie.serialize())
return { user }
}),
me: protectedProcedure.query(({ ctx }) => {
return ctx.user
}),
logout: publicProcedure.mutation(async ({ ctx }) => {
if (ctx.session)
await auth.invalidateSession(ctx.session.id)
ctx.res.setHeader('Set-Cookie', auth.createBlankSessionCookie().serialize())
return true
}),
})

View File

@ -0,0 +1,8 @@
import { router } from '../router'
import { authRouter } from './auth'
export const appRouter = router({
auth: authRouter,
})
export type AppRouter = typeof appRouter

File diff suppressed because it is too large Load Diff