refactor: flatten monorepo into standalone project
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import type { DB } from '@/server/db'
|
||||
|
||||
/**
|
||||
* 基础 Context - 所有请求都包含的上下文
|
||||
*/
|
||||
export interface BaseContext {
|
||||
headers: Headers
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库 Context - 通过 db middleware 扩展
|
||||
*/
|
||||
export interface DBContext extends BaseContext {
|
||||
db: DB
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证 Context - 通过 auth middleware 扩展(未来使用)
|
||||
*
|
||||
* @example
|
||||
* export interface AuthContext extends DBContext {
|
||||
* userId: string
|
||||
* user: User
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,7 @@
|
||||
import * as todo from './todo.contract'
|
||||
|
||||
export const contract = {
|
||||
todo,
|
||||
}
|
||||
|
||||
export type Contract = typeof contract
|
||||
@@ -0,0 +1,32 @@
|
||||
import { oc } from '@orpc/contract'
|
||||
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/zod'
|
||||
import { z } from 'zod'
|
||||
import { generatedFieldKeys } from '@/server/db/fields'
|
||||
import { todoTable } from '@/server/db/schema'
|
||||
|
||||
const selectSchema = createSelectSchema(todoTable)
|
||||
|
||||
const insertSchema = createInsertSchema(todoTable).omit(generatedFieldKeys)
|
||||
|
||||
const updateSchema = createUpdateSchema(todoTable).omit(generatedFieldKeys)
|
||||
|
||||
export const list = oc.input(z.void()).output(z.array(selectSchema))
|
||||
|
||||
export const create = oc.input(insertSchema).output(selectSchema)
|
||||
|
||||
export const update = oc
|
||||
.input(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
data: updateSchema,
|
||||
}),
|
||||
)
|
||||
.output(selectSchema)
|
||||
|
||||
export const remove = oc
|
||||
.input(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
}),
|
||||
)
|
||||
.output(z.void())
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ORPCError, ValidationError } from '@orpc/server'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const logError = (error: unknown) => {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
export const handleValidationError = (error: unknown) => {
|
||||
if (error instanceof ORPCError && error.code === 'BAD_REQUEST' && error.cause instanceof ValidationError) {
|
||||
// If you only use Zod you can safely cast to ZodIssue[] (per ORPC official docs)
|
||||
const zodError = new z.ZodError(error.cause.issues as z.core.$ZodIssue[])
|
||||
|
||||
throw new ORPCError('INPUT_VALIDATION_FAILED', {
|
||||
status: 422,
|
||||
message: z.prettifyError(zodError),
|
||||
data: z.flattenError(zodError),
|
||||
cause: error.cause,
|
||||
})
|
||||
}
|
||||
|
||||
if (error instanceof ORPCError && error.code === 'INTERNAL_SERVER_ERROR' && error.cause instanceof ValidationError) {
|
||||
throw new ORPCError('OUTPUT_VALIDATION_FAILED', {
|
||||
cause: error.cause,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { os } from '@/server/api/server'
|
||||
import { getDB } from '@/server/db'
|
||||
|
||||
export const db = os.middleware(async ({ context, next }) => {
|
||||
return next({
|
||||
context: {
|
||||
...context,
|
||||
db: getDB(),
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
export * from './db.middleware'
|
||||
@@ -0,0 +1,6 @@
|
||||
import { os } from '../server'
|
||||
import * as todo from './todo.router'
|
||||
|
||||
export const router = os.router({
|
||||
todo,
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ORPCError } from '@orpc/server'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { todoTable } from '@/server/db/schema'
|
||||
import { db } from '../middlewares'
|
||||
import { os } from '../server'
|
||||
|
||||
export const list = os.todo.list.use(db).handler(async ({ context }) => {
|
||||
const todos = await context.db.query.todoTable.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
return todos
|
||||
})
|
||||
|
||||
export const create = os.todo.create.use(db).handler(async ({ context, input }) => {
|
||||
const [newTodo] = await context.db.insert(todoTable).values(input).returning()
|
||||
|
||||
if (!newTodo) {
|
||||
throw new ORPCError('INTERNAL_SERVER_ERROR', { message: 'Failed to create todo' })
|
||||
}
|
||||
|
||||
return newTodo
|
||||
})
|
||||
|
||||
export const update = os.todo.update.use(db).handler(async ({ context, input }) => {
|
||||
const [updatedTodo] = await context.db.update(todoTable).set(input.data).where(eq(todoTable.id, input.id)).returning()
|
||||
|
||||
if (!updatedTodo) {
|
||||
throw new ORPCError('NOT_FOUND')
|
||||
}
|
||||
|
||||
return updatedTodo
|
||||
})
|
||||
|
||||
export const remove = os.todo.remove.use(db).handler(async ({ context, input }) => {
|
||||
const [deleted] = await context.db.delete(todoTable).where(eq(todoTable.id, input.id)).returning({ id: todoTable.id })
|
||||
|
||||
if (!deleted) {
|
||||
throw new ORPCError('NOT_FOUND')
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import { implement } from '@orpc/server'
|
||||
import type { BaseContext } from './context'
|
||||
import { contract } from './contracts'
|
||||
|
||||
export const os = implement(contract).$context<BaseContext>()
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { ContractRouterClient, InferContractRouterInputs, InferContractRouterOutputs } from '@orpc/contract'
|
||||
import type { Contract } from './contracts'
|
||||
|
||||
export type RouterClient = ContractRouterClient<Contract>
|
||||
export type RouterInputs = InferContractRouterInputs<Contract>
|
||||
export type RouterOutputs = InferContractRouterOutputs<Contract>
|
||||
Reference in New Issue
Block a user