feat: 定义数据库 Schema
- 用户表 (users) - 内容表 (content) - 版本历史表 (content_versions) - 站点配置表 (site_config) - 操作日志表 (audit_logs)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import type { Config } from 'drizzle-kit';
|
||||
|
||||
export default {
|
||||
schema: './src/db/schema.ts',
|
||||
out: './drizzle',
|
||||
driver: 'libsql',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL || 'file:./data.db',
|
||||
},
|
||||
} satisfies Config;
|
||||
+6
-1
@@ -17,7 +17,12 @@
|
||||
"audit:accessibility": "node scripts/accessibility-test.js",
|
||||
"audit:forms": "node scripts/form-validation.js",
|
||||
"audit:all": "./scripts/run-all-tests.sh",
|
||||
"report:generate": "node scripts/generate-test-report.js"
|
||||
"report:generate": "node scripts/generate-test-report.js",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"db:seed": "tsx src/db/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g2": "^5.4.8",
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
export const users = sqliteTable('users', {
|
||||
id: text('id').primaryKey(),
|
||||
email: text('email').notNull().unique(),
|
||||
passwordHash: text('password_hash'),
|
||||
name: text('name').notNull(),
|
||||
role: text('role', { enum: ['admin', 'editor', 'viewer'] }).notNull().default('editor'),
|
||||
avatar: text('avatar'),
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
||||
});
|
||||
|
||||
export const content = sqliteTable('content', {
|
||||
id: text('id').primaryKey(),
|
||||
type: text('type', { enum: ['news', 'product', 'service', 'case'] }).notNull(),
|
||||
title: text('title').notNull(),
|
||||
slug: text('slug').notNull().unique(),
|
||||
excerpt: text('excerpt'),
|
||||
content: text('content').notNull(),
|
||||
coverImage: text('cover_image'),
|
||||
category: text('category'),
|
||||
tags: text('tags', { mode: 'json' }).$type<string[]>(),
|
||||
status: text('status', { enum: ['draft', 'published', 'archived'] }).notNull().default('draft'),
|
||||
publishedAt: integer('published_at', { mode: 'timestamp' }),
|
||||
authorId: text('author_id').notNull().references(() => users.id),
|
||||
sortOrder: integer('sort_order').default(0),
|
||||
metadata: text('metadata', { mode: 'json' }).$type<Record<string, any>>(),
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
||||
});
|
||||
|
||||
export const contentVersions = sqliteTable('content_versions', {
|
||||
id: text('id').primaryKey(),
|
||||
contentId: text('content_id').notNull().references(() => content.id),
|
||||
version: integer('version').notNull(),
|
||||
title: text('title').notNull(),
|
||||
content: text('content').notNull(),
|
||||
changes: text('changes', { mode: 'json' }).$type<Record<string, any>>(),
|
||||
changedBy: text('changed_by').notNull().references(() => users.id),
|
||||
changedAt: integer('changed_at', { mode: 'timestamp' }).notNull(),
|
||||
});
|
||||
|
||||
export const siteConfig = sqliteTable('site_config', {
|
||||
id: text('id').primaryKey(),
|
||||
key: text('key').notNull().unique(),
|
||||
value: text('value', { mode: 'json' }).notNull(),
|
||||
category: text('category', { enum: ['feature', 'style', 'seo', 'general'] }).notNull(),
|
||||
description: text('description'),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
||||
updatedBy: text('updated_by').references(() => users.id),
|
||||
});
|
||||
|
||||
export const auditLogs = sqliteTable('audit_logs', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: text('user_id').references(() => users.id),
|
||||
action: text('action', { enum: ['create', 'update', 'delete', 'publish', 'login'] }).notNull(),
|
||||
resourceType: text('resource_type').notNull(),
|
||||
resourceId: text('resource_id'),
|
||||
details: text('details', { mode: 'json' }).$type<Record<string, any>>(),
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
timestamp: integer('timestamp', { mode: 'timestamp' }).notNull(),
|
||||
});
|
||||
|
||||
export const usersRelations = relations(users, ({ many }) => ({
|
||||
content: many(content),
|
||||
versions: many(contentVersions),
|
||||
logs: many(auditLogs),
|
||||
}));
|
||||
|
||||
export const contentRelations = relations(content, ({ one, many }) => ({
|
||||
author: one(users, {
|
||||
fields: [content.authorId],
|
||||
references: [users.id],
|
||||
}),
|
||||
versions: many(contentVersions),
|
||||
}));
|
||||
|
||||
export type User = typeof users.$inferSelect;
|
||||
export type NewUser = typeof users.$inferInsert;
|
||||
export type Content = typeof content.$inferSelect;
|
||||
export type NewContent = typeof content.$inferInsert;
|
||||
export type SiteConfig = typeof siteConfig.$inferSelect;
|
||||
export type NewSiteConfig = typeof siteConfig.$inferInsert;
|
||||
Reference in New Issue
Block a user