refactor: 统一表命名规范,简化 DB 单例

- 去掉所有 Drizzle 表变量的 Table 后缀(userTable→user 等)
- 修复 Better Auth adapter 找不到 schema model 的问题
- DB 实例从 IIFE 闭包工厂简化为模块级导出
- db middleware 重命名为 dbMiddleware 避免与 db 实例冲突
- 添加 babel-plugin-react-compiler 依赖
This commit is contained in:
2026-03-30 21:53:30 +08:00
parent 8c3425359d
commit 430c0b0c64
10 changed files with 76 additions and 86 deletions
+1
View File
@@ -55,6 +55,7 @@
"@tanstack/react-router-devtools": "catalog:",
"@types/bun": "catalog:",
"@vitejs/plugin-react": "catalog:",
"babel-plugin-react-compiler": "^1.0.0",
"drizzle-kit": "catalog:",
"nitro": "catalog:",
"tailwindcss": "catalog:",
@@ -1,16 +1,16 @@
import { oc } from '@orpc/contract'
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/zod'
import { z } from 'zod'
import { bookmarkTable, categoryTable } from '@/modules/bookmarks/schema'
import * as schema from '@/modules/bookmarks/schema'
import { generatedFieldKeys } from '@/server/db/fields'
const categorySelect = createSelectSchema(categoryTable)
const categoryInsert = createInsertSchema(categoryTable).omit(generatedFieldKeys).omit({ userId: true })
const categoryUpdate = createUpdateSchema(categoryTable).omit(generatedFieldKeys).omit({ userId: true })
const categorySelect = createSelectSchema(schema.category)
const categoryInsert = createInsertSchema(schema.category).omit(generatedFieldKeys).omit({ userId: true })
const categoryUpdate = createUpdateSchema(schema.category).omit(generatedFieldKeys).omit({ userId: true })
const bookmarkSelect = createSelectSchema(bookmarkTable)
const bookmarkInsert = createInsertSchema(bookmarkTable).omit(generatedFieldKeys).omit({ userId: true })
const bookmarkUpdate = createUpdateSchema(bookmarkTable).omit(generatedFieldKeys).omit({ userId: true })
const bookmarkSelect = createSelectSchema(schema.bookmark)
const bookmarkInsert = createInsertSchema(schema.bookmark).omit(generatedFieldKeys).omit({ userId: true })
const bookmarkUpdate = createUpdateSchema(schema.bookmark).omit(generatedFieldKeys).omit({ userId: true })
export const category = {
list: oc.input(z.void()).output(z.array(categorySelect.extend({ bookmarks: z.array(bookmarkSelect) }))),
+28 -28
View File
@@ -1,15 +1,15 @@
import { ORPCError } from '@orpc/server'
import { and, eq } from 'drizzle-orm'
import { bookmarkTable, categoryTable } from '@/modules/bookmarks/schema'
import { authMiddleware, db } from '@/server/api/middlewares'
import * as schema from '@/modules/bookmarks/schema'
import { authMiddleware, dbMiddleware } from '@/server/api/middlewares'
import { os } from '@/server/api/server'
export const category = {
list: os.bookmarks.category.list
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context }) => {
return await context.db.query.categoryTable.findMany({
return await context.db.query.category.findMany({
where: { userId: context.user.id },
orderBy: { orderId: 'asc' },
with: {
@@ -21,11 +21,11 @@ export const category = {
}),
create: os.bookmarks.category.create
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [created] = await context.db
.insert(categoryTable)
.insert(schema.category)
.values({ ...input, userId: context.user.id })
.returning()
if (!created) throw new ORPCError('INTERNAL_SERVER_ERROR', { message: 'Failed to create category' })
@@ -33,39 +33,39 @@ export const category = {
}),
update: os.bookmarks.category.update
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [updated] = await context.db
.update(categoryTable)
.update(schema.category)
.set(input.data)
.where(and(eq(categoryTable.id, input.id), eq(categoryTable.userId, context.user.id)))
.where(and(eq(schema.category.id, input.id), eq(schema.category.userId, context.user.id)))
.returning()
if (!updated) throw new ORPCError('NOT_FOUND')
return updated
}),
remove: os.bookmarks.category.remove
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [deleted] = await context.db
.delete(categoryTable)
.where(and(eq(categoryTable.id, input.id), eq(categoryTable.userId, context.user.id)))
.returning({ id: categoryTable.id })
.delete(schema.category)
.where(and(eq(schema.category.id, input.id), eq(schema.category.userId, context.user.id)))
.returning({ id: schema.category.id })
if (!deleted) throw new ORPCError('NOT_FOUND')
}),
reorder: os.bookmarks.category.reorder
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
await context.db.transaction(async (tx) => {
for (const item of input) {
await tx
.update(categoryTable)
.update(schema.category)
.set({ orderId: item.orderId })
.where(and(eq(categoryTable.id, item.id), eq(categoryTable.userId, context.user.id)))
.where(and(eq(schema.category.id, item.id), eq(schema.category.userId, context.user.id)))
}
})
}),
@@ -73,11 +73,11 @@ export const category = {
export const bookmark = {
create: os.bookmarks.bookmark.create
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [created] = await context.db
.insert(bookmarkTable)
.insert(schema.bookmark)
.values({ ...input, userId: context.user.id })
.returning()
if (!created) throw new ORPCError('INTERNAL_SERVER_ERROR', { message: 'Failed to create bookmark' })
@@ -85,39 +85,39 @@ export const bookmark = {
}),
update: os.bookmarks.bookmark.update
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [updated] = await context.db
.update(bookmarkTable)
.update(schema.bookmark)
.set(input.data)
.where(and(eq(bookmarkTable.id, input.id), eq(bookmarkTable.userId, context.user.id)))
.where(and(eq(schema.bookmark.id, input.id), eq(schema.bookmark.userId, context.user.id)))
.returning()
if (!updated) throw new ORPCError('NOT_FOUND')
return updated
}),
remove: os.bookmarks.bookmark.remove
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
const [deleted] = await context.db
.delete(bookmarkTable)
.where(and(eq(bookmarkTable.id, input.id), eq(bookmarkTable.userId, context.user.id)))
.returning({ id: bookmarkTable.id })
.delete(schema.bookmark)
.where(and(eq(schema.bookmark.id, input.id), eq(schema.bookmark.userId, context.user.id)))
.returning({ id: schema.bookmark.id })
if (!deleted) throw new ORPCError('NOT_FOUND')
}),
reorder: os.bookmarks.bookmark.reorder
.use(db)
.use(dbMiddleware)
.use(authMiddleware)
.handler(async ({ context, input }) => {
await context.db.transaction(async (tx) => {
for (const item of input) {
await tx
.update(bookmarkTable)
.update(schema.bookmark)
.set({ orderId: item.orderId })
.where(and(eq(bookmarkTable.id, item.id), eq(bookmarkTable.userId, context.user.id)))
.where(and(eq(schema.bookmark.id, item.id), eq(schema.bookmark.userId, context.user.id)))
}
})
}),
+6 -6
View File
@@ -1,8 +1,8 @@
import { boolean, integer, pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { userTable } from '../../server/auth/schema'
import { user } from '../../server/auth/schema'
import { generatedFields } from '../../server/db/fields'
export const categoryTable = pgTable('category', {
export const category = pgTable('category', {
...generatedFields,
name: text('name').notNull(),
isPinned: boolean('is_pinned').notNull().default(false),
@@ -10,20 +10,20 @@ export const categoryTable = pgTable('category', {
orderId: integer('order_id').notNull().default(0),
userId: text('user_id')
.notNull()
.references(() => userTable.id, { onDelete: 'cascade' }),
.references(() => user.id, { onDelete: 'cascade' }),
})
export const bookmarkTable = pgTable('bookmark', {
export const bookmark = pgTable('bookmark', {
...generatedFields,
name: text('name').notNull(),
url: text('url').notNull(),
icon: text('icon'),
categoryId: uuid('category_id')
.notNull()
.references(() => categoryTable.id, { onDelete: 'cascade' }),
.references(() => category.id, { onDelete: 'cascade' }),
isPublic: boolean('is_public').notNull().default(true),
orderId: integer('order_id').notNull().default(0),
userId: text('user_id')
.notNull()
.references(() => userTable.id, { onDelete: 'cascade' }),
.references(() => user.id, { onDelete: 'cascade' }),
})
@@ -1,11 +1,11 @@
import { os } from '@/server/api/server'
import { getDB } from '@/server/db'
import { db } from '@/server/db'
export const db = os.middleware(async ({ context, next }) => {
export const dbMiddleware = os.middleware(async ({ context, next }) => {
return next({
context: {
...context,
db: getDB(),
db,
},
})
})
+2 -2
View File
@@ -3,12 +3,12 @@ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { tanstackStartCookies } from 'better-auth/tanstack-start'
import { env } from '@/env'
import * as authSchema from '@/server/auth/schema'
import { getDB } from '@/server/db'
import { db } from '@/server/db'
export const auth = betterAuth({
baseURL: env.BETTER_AUTH_URL,
secret: env.BETTER_AUTH_SECRET,
database: drizzleAdapter(getDB(), {
database: drizzleAdapter(db, {
provider: 'pg',
schema: authSchema,
}),
+6 -6
View File
@@ -7,7 +7,7 @@ import { boolean, pgTable, text, timestamp } from 'drizzle-orm/pg-core'
* 不使用项目的 generatedFieldsUUID v7)。
*/
export const userTable = pgTable('user', {
export const user = pgTable('user', {
id: text('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
@@ -20,7 +20,7 @@ export const userTable = pgTable('user', {
.$onUpdateFn(() => new Date()),
})
export const sessionTable = pgTable('session', {
export const session = pgTable('session', {
id: text('id').primaryKey(),
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
token: text('token').notNull().unique(),
@@ -33,16 +33,16 @@ export const sessionTable = pgTable('session', {
userAgent: text('user_agent'),
userId: text('user_id')
.notNull()
.references(() => userTable.id, { onDelete: 'cascade' }),
.references(() => user.id, { onDelete: 'cascade' }),
})
export const accountTable = pgTable('account', {
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(() => userTable.id, { onDelete: 'cascade' }),
.references(() => user.id, { onDelete: 'cascade' }),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
idToken: text('id_token'),
@@ -57,7 +57,7 @@ export const accountTable = pgTable('account', {
.$onUpdateFn(() => new Date()),
})
export const verificationTable = pgTable('verification', {
export const verification = pgTable('verification', {
id: text('id').primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
+5 -19
View File
@@ -2,23 +2,9 @@ import { drizzle } from 'drizzle-orm/postgres-js'
import { env } from '@/env'
import { relations } from '@/server/db/relations'
export const createDB = () =>
drizzle({
connection: env.DATABASE_URL,
relations,
})
export const db = drizzle({
connection: env.DATABASE_URL,
relations,
})
export type DB = ReturnType<typeof createDB>
export const getDB = (() => {
let db: DB | null = null
return (singleton = true): DB => {
if (!singleton) {
return createDB()
}
db ??= createDB()
return db
}
})()
export type DB = typeof db
+15 -15
View File
@@ -2,25 +2,25 @@ import { defineRelations } from 'drizzle-orm'
import * as schema from './schema'
export const relations = defineRelations(schema, (r) => ({
userTable: {
categories: r.many.categoryTable(),
bookmarks: r.many.bookmarkTable(),
user: {
categories: r.many.category(),
bookmarks: r.many.bookmark(),
},
categoryTable: {
user: r.one.userTable({
from: r.categoryTable.userId,
to: r.userTable.id,
category: {
user: r.one.user({
from: r.category.userId,
to: r.user.id,
}),
bookmarks: r.many.bookmarkTable(),
bookmarks: r.many.bookmark(),
},
bookmarkTable: {
user: r.one.userTable({
from: r.bookmarkTable.userId,
to: r.userTable.id,
bookmark: {
user: r.one.user({
from: r.bookmark.userId,
to: r.user.id,
}),
category: r.one.categoryTable({
from: r.bookmarkTable.categoryId,
to: r.categoryTable.id,
category: r.one.category({
from: r.bookmark.categoryId,
to: r.category.id,
}),
},
}))