116 lines
3.9 KiB
PL/PgSQL
116 lines
3.9 KiB
PL/PgSQL
-- =========================================
|
|
-- PostgreSQL schema initialization script
|
|
-- Safe to re-run (幂等)
|
|
-- =========================================
|
|
|
|
SET statement_timeout = 0;
|
|
SET lock_timeout = 0;
|
|
SET client_encoding = 'UTF8';
|
|
SET standard_conforming_strings = on;
|
|
SET row_security = off;
|
|
SET search_path = public;
|
|
|
|
-- 必要扩展
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;
|
|
|
|
-- =========================================
|
|
-- Function: set_updated_at()
|
|
-- =========================================
|
|
DROP FUNCTION IF EXISTS set_updated_at() CASCADE;
|
|
|
|
CREATE FUNCTION set_updated_at() RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
NEW.updated_at := now();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- =========================================
|
|
-- Tables
|
|
-- =========================================
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
username TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
email TEXT,
|
|
level INTEGER DEFAULT 20 NOT NULL,
|
|
role TEXT DEFAULT 'user' NOT NULL,
|
|
groups JSONB DEFAULT '[]'::jsonb NOT NULL,
|
|
permissions JSONB DEFAULT '[]'::jsonb NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
uuid UUID DEFAULT gen_random_uuid() NOT NULL,
|
|
mfa_totp_secret TEXT,
|
|
mfa_enabled BOOLEAN DEFAULT false NOT NULL,
|
|
mfa_secret_issued_at TIMESTAMPTZ,
|
|
mfa_confirmed_at TIMESTAMPTZ,
|
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
|
email_verified_at TIMESTAMPTZ,
|
|
email_verified BOOLEAN GENERATED ALWAYS AS ((email_verified_at IS NOT NULL)) STORED,
|
|
CONSTRAINT users_pkey PRIMARY KEY (uuid),
|
|
CONSTRAINT users_username_uk UNIQUE (username),
|
|
CONSTRAINT users_email_uk UNIQUE (email)
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS users_username_lower_uk ON users (lower(username));
|
|
CREATE UNIQUE INDEX IF NOT EXISTS users_email_lower_uk ON users (lower(email)) WHERE email IS NOT NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS identities (
|
|
provider TEXT NOT NULL,
|
|
external_id TEXT NOT NULL,
|
|
uuid UUID DEFAULT gen_random_uuid() NOT NULL,
|
|
user_uuid UUID NOT NULL,
|
|
CONSTRAINT identities_pkey PRIMARY KEY (uuid),
|
|
CONSTRAINT identities_provider_external_id_uk UNIQUE (provider, external_id),
|
|
CONSTRAINT identities_user_fk FOREIGN KEY (user_uuid)
|
|
REFERENCES users(uuid) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
token TEXT NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
uuid UUID DEFAULT gen_random_uuid() NOT NULL,
|
|
user_uuid UUID NOT NULL,
|
|
CONSTRAINT sessions_pkey PRIMARY KEY (uuid),
|
|
CONSTRAINT sessions_user_fk FOREIGN KEY (user_uuid)
|
|
REFERENCES users(uuid) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS admin_settings (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
module_key TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
|
version BIGINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
CONSTRAINT admin_settings_module_role_uk UNIQUE (module_key, role)
|
|
);
|
|
|
|
-- =========================================
|
|
-- Indexes
|
|
-- =========================================
|
|
CREATE INDEX IF NOT EXISTS idx_identities_provider ON identities (provider);
|
|
CREATE INDEX IF NOT EXISTS idx_identities_user_uuid ON identities (user_uuid);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user_uuid ON sessions (user_uuid);
|
|
CREATE INDEX IF NOT EXISTS idx_admin_settings_version ON admin_settings (version);
|
|
|
|
-- =========================================
|
|
-- Trigger
|
|
-- =========================================
|
|
DROP TRIGGER IF EXISTS trg_users_set_updated_at ON users;
|
|
CREATE TRIGGER trg_users_set_updated_at
|
|
BEFORE UPDATE ON users
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
|
|
DROP TRIGGER IF EXISTS trg_admin_settings_set_updated_at ON admin_settings;
|
|
CREATE TRIGGER trg_admin_settings_set_updated_at
|
|
BEFORE UPDATE ON admin_settings
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
|
|
-- =========================================
|
|
-- End of schema.sql
|
|
-- =========================================
|