refactor(server): 切换 SQLite 并重建设备/任务表结构
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,6 +9,11 @@
|
||||
# Bun build
|
||||
*.bun-build
|
||||
|
||||
# SQLite database files
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Turborepo
|
||||
.turbo/
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
|
||||
DATABASE_PATH=data.db
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
},
|
||||
}))
|
||||
|
||||
11
apps/server/src/server/db/schema/device.ts
Normal file
11
apps/server/src/server/db/schema/device.ts
Normal 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'),
|
||||
})
|
||||
@@ -1 +1,2 @@
|
||||
export * from './todo'
|
||||
export * from './device'
|
||||
export * from './task'
|
||||
|
||||
16
apps/server/src/server/db/schema/task.ts
Normal file
16
apps/server/src/server/db/schema/task.ts
Normal 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'),
|
||||
})
|
||||
@@ -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),
|
||||
})
|
||||
Reference in New Issue
Block a user