107 lines
2.5 KiB
PL/PgSQL
107 lines
2.5 KiB
PL/PgSQL
-- member_center: API 呼叫方白名單表
|
||
-- 位置: public schema
|
||
|
||
BEGIN;
|
||
|
||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'client_status') THEN
|
||
CREATE TYPE client_status AS ENUM ('active', 'inactive');
|
||
END IF;
|
||
END $$;
|
||
|
||
CREATE TABLE IF NOT EXISTS api_clients (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
client_key TEXT NOT NULL UNIQUE,
|
||
name TEXT NOT NULL,
|
||
status client_status NOT NULL DEFAULT 'active',
|
||
|
||
-- 只存 hash,不存明文 key
|
||
api_key_hash TEXT NOT NULL,
|
||
|
||
-- 可先留空,之後再嚴格化
|
||
allowed_origins JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||
allowed_ips JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||
allowed_paths JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||
|
||
rate_limit_per_min INTEGER,
|
||
expires_at TIMESTAMPTZ,
|
||
last_used_at TIMESTAMPTZ,
|
||
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_api_clients_status ON api_clients(status);
|
||
CREATE INDEX IF NOT EXISTS idx_api_clients_expires_at ON api_clients(expires_at);
|
||
|
||
CREATE OR REPLACE FUNCTION set_updated_at_api_clients()
|
||
RETURNS TRIGGER AS $$
|
||
BEGIN
|
||
NEW.updated_at = NOW();
|
||
RETURN NEW;
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM pg_trigger WHERE tgname = 'trg_api_clients_set_updated_at'
|
||
) THEN
|
||
CREATE TRIGGER trg_api_clients_set_updated_at
|
||
BEFORE UPDATE ON api_clients
|
||
FOR EACH ROW EXECUTE FUNCTION set_updated_at_api_clients();
|
||
END IF;
|
||
END $$;
|
||
|
||
-- 建議初始化 2~3 個 client(api_key_hash 先放占位,後續再更新)
|
||
INSERT INTO api_clients (
|
||
client_key,
|
||
name,
|
||
status,
|
||
api_key_hash,
|
||
allowed_origins,
|
||
allowed_ips,
|
||
allowed_paths,
|
||
rate_limit_per_min
|
||
)
|
||
VALUES
|
||
(
|
||
'mkt-backend',
|
||
'MKT Backend Service',
|
||
'active',
|
||
'REPLACE_WITH_BCRYPT_OR_ARGON2_HASH',
|
||
'[]'::jsonb,
|
||
'[]'::jsonb,
|
||
'["/internal/users/upsert-by-sub", "/internal/permissions"]'::jsonb,
|
||
600
|
||
),
|
||
(
|
||
'admin-frontend',
|
||
'Admin Frontend',
|
||
'active',
|
||
'REPLACE_WITH_BCRYPT_OR_ARGON2_HASH',
|
||
'["https://admin.ose.tw", "https://member.ose.tw"]'::jsonb,
|
||
'[]'::jsonb,
|
||
'["/admin"]'::jsonb,
|
||
300
|
||
),
|
||
(
|
||
'ops-local',
|
||
'Ops Local Tooling',
|
||
'inactive',
|
||
'REPLACE_WITH_BCRYPT_OR_ARGON2_HASH',
|
||
'[]'::jsonb,
|
||
'["127.0.0.1"]'::jsonb,
|
||
'["/internal", "/admin"]'::jsonb,
|
||
120
|
||
)
|
||
ON CONFLICT (client_key) DO NOTHING;
|
||
|
||
COMMIT;
|
||
|
||
-- 快速檢查
|
||
-- SELECT client_key, status, expires_at, created_at FROM api_clients ORDER BY client_key;
|