refactor(server): 切换 SQLite 并重建设备/任务表结构

This commit is contained in:
2026-03-05 16:23:30 +08:00
parent d2eb98d612
commit 8261409d7d
11 changed files with 68 additions and 50 deletions

View File

@@ -1 +1 @@
DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
DATABASE_PATH=data.db

View File

@@ -4,8 +4,8 @@ import { env } from '@/env'
export default defineConfig({
out: './drizzle',
schema: './src/server/db/schema/index.ts',
dialect: 'postgresql',
dialect: 'sqlite',
dbCredentials: {
url: env.DATABASE_URL,
url: env.DATABASE_PATH,
},
})

View File

@@ -3,7 +3,7 @@ import { z } from 'zod'
export const env = createEnv({
server: {
DATABASE_URL: z.url(),
DATABASE_PATH: z.string().min(1).default('data.db'),
},
clientPrefix: 'VITE_',
client: {

View File

@@ -1,47 +1,28 @@
import { sql } from 'drizzle-orm'
import { timestamp, uuid } from 'drizzle-orm/pg-core'
import { integer, text } from 'drizzle-orm/sqlite-core'
import { v7 as uuidv7 } from 'uuid'
// id
export const pk = (name = 'id') =>
text(name)
.primaryKey()
.$defaultFn(() => uuidv7())
const id = (name: string) => uuid(name)
export const pk = (name: string, strategy?: 'native' | 'extension') => {
switch (strategy) {
// PG 18+
case 'native':
return id(name).primaryKey().default(sql`uuidv7()`)
// PG 13+ with extension
case 'extension':
return id(name).primaryKey().default(sql`uuid_generate_v7()`)
// Any PG version
default:
return id(name)
.primaryKey()
.$defaultFn(() => uuidv7())
}
}
// timestamp
export const createdAt = (name = 'created_at') => timestamp(name, { withTimezone: true }).notNull().defaultNow()
export const createdAt = (name = 'created_at') =>
integer(name, { mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date())
export const updatedAt = (name = 'updated_at') =>
timestamp(name, { withTimezone: true })
integer(name, { mode: 'timestamp_ms' })
.notNull()
.defaultNow()
.$defaultFn(() => new Date())
.$onUpdateFn(() => new Date())
// generated fields
export const generatedFields = {
id: pk('id'),
createdAt: createdAt('created_at'),
updatedAt: updatedAt('updated_at'),
}
// Helper to create omit keys from generatedFields
const createGeneratedFieldKeys = <T extends Record<string, unknown>>(fields: T): Record<keyof T, true> => {
return Object.keys(fields).reduce(
(acc, key) => {

View File

@@ -1,12 +1,14 @@
import { drizzle } from 'drizzle-orm/postgres-js'
import { Database } from 'bun:sqlite'
import { drizzle } from 'drizzle-orm/bun-sqlite'
import { env } from '@/env'
import { relations } from '@/server/db/relations'
export const createDB = () =>
drizzle({
connection: env.DATABASE_URL,
relations,
})
export const createDB = () => {
const sqlite = new Database(env.DATABASE_PATH)
sqlite.exec('PRAGMA journal_mode = WAL')
sqlite.exec('PRAGMA foreign_keys = ON')
return drizzle({ client: sqlite, relations })
}
export type DB = ReturnType<typeof createDB>

View File

@@ -1,4 +1,14 @@
import { defineRelations } from 'drizzle-orm'
import * as schema from './schema'
export const relations = defineRelations(schema, (_r) => ({}))
export const relations = defineRelations(schema, (r) => ({
deviceTable: {
tasks: r.many.taskTable(),
},
taskTable: {
device: r.one.deviceTable({
from: r.taskTable.deviceId,
to: r.deviceTable.id,
}),
},
}))

View File

@@ -0,0 +1,11 @@
import { sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { generatedFields } from '../fields'
export const deviceTable = sqliteTable('device', {
...generatedFields,
licence: text('licence').notNull().unique(),
fingerprint: text('fingerprint').notNull(),
platformPublicKey: text('platform_public_key').notNull(),
pgpPrivateKey: text('pgp_private_key'),
pgpPublicKey: text('pgp_public_key'),
})

View File

@@ -1 +1,2 @@
export * from './todo'
export * from './device'
export * from './task'

View File

@@ -0,0 +1,16 @@
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { generatedFields } from '../fields'
export const taskTable = sqliteTable('task', {
...generatedFields,
deviceId: text('device_id').notNull(),
taskId: text('task_id').notNull(),
enterpriseId: text('enterprise_id'),
orgName: text('org_name'),
inspectionId: text('inspection_id'),
inspectionPerson: text('inspection_person'),
issuedAt: integer('issued_at', { mode: 'timestamp_ms' }),
status: text('status', { enum: ['pending', 'in_progress', 'done'] })
.notNull()
.default('pending'),
})

View File

@@ -1,8 +0,0 @@
import { boolean, pgTable, text } from 'drizzle-orm/pg-core'
import { generatedFields } from '../fields'
export const todoTable = pgTable('todo', {
...generatedFields,
title: text('title').notNull(),
completed: boolean('completed').notNull().default(false),
})