diff --git a/migrations/.gitkeep b/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/migrations/0001_init.down.sql b/migrations/0001_init.down.sql new file mode 100644 index 0000000..4d70584 --- /dev/null +++ b/migrations/0001_init.down.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS audit_logs; +DROP TABLE IF EXISTS notifications; +DROP TABLE IF EXISTS user_sessions; +DROP TABLE IF EXISTS users; diff --git a/migrations/0001_init.up.sql b/migrations/0001_init.up.sql new file mode 100644 index 0000000..c99a039 --- /dev/null +++ b/migrations/0001_init.up.sql @@ -0,0 +1,54 @@ +-- ============ 用户与会话 ============ +CREATE TABLE users ( + id UUID PRIMARY KEY, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + display_name TEXT NOT NULL, + is_admin BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE user_sessions ( + id UUID PRIMARY KEY, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + token_hash BYTEA NOT NULL UNIQUE, + expires_at TIMESTAMPTZ NOT NULL, + last_seen_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX idx_user_sessions_user_id ON user_sessions(user_id); +CREATE INDEX idx_user_sessions_expires_at ON user_sessions(expires_at); + +-- ============ 站内信 ============ +CREATE TABLE notifications ( + id UUID PRIMARY KEY, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + topic TEXT NOT NULL, + severity TEXT NOT NULL, + title TEXT NOT NULL, + body TEXT NOT NULL, + link TEXT, + metadata JSONB, + read_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX idx_notifications_user_created ON notifications(user_id, created_at DESC); +CREATE INDEX idx_notifications_user_unread ON notifications(user_id) WHERE read_at IS NULL; + +-- ============ 审计日志 ============ +CREATE TABLE audit_logs ( + id BIGSERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id) ON DELETE SET NULL, + action TEXT NOT NULL, + target_type TEXT, + target_id TEXT, + ip INET, + metadata JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX idx_audit_user_created ON audit_logs(user_id, created_at DESC); +CREATE INDEX idx_audit_action_created ON audit_logs(action, created_at DESC);