refactor(server): merge migrate package and drop unused admin/migrator trees

Collapse stamp, of_* SQL and ClickHouse into server/migrate, hoist updater out of admin, and delete the unused 76-file historical chain.
This commit is contained in:
ryan
2026-08-30 16:54:08 +08:00
parent 3e8a777817
commit d531d71778
187 changed files with 22 additions and 5028 deletions
@@ -2,8 +2,7 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package chmigrate applies ClickHouse of_node_* goose migrations.
package chmigrate
package migrate
import (
"context"
@@ -20,17 +19,17 @@ import (
)
const (
clickhouseMigrationDir = "goose/clickhouse"
clickhouseMigrationDir = "clickhouse"
clickhouseGooseVersionTable = "goose_clickhouse_version"
clickhouseMaxExecTime = 60
clickhouseReadTimeoutFactor = 2
)
//go:embed goose/clickhouse/*.sql
//go:embed clickhouse/*.sql
var clickhouseMigrationFS embed.FS
// Up runs of_node_* goose migrations against ClickHouse when enabled.
func Up() error {
// UpClickHouse runs of_node_* goose migrations against ClickHouse when enabled.
func UpClickHouse() error {
if !runtimeconfig.ClickHouseEnabled() {
return nil
}
@@ -1,7 +1,7 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package chmigrate
package migrate
import (
"strings"
@@ -46,7 +46,7 @@ func TestClickHouseGooseDialect(t *testing.T) {
func TestUpSkipsWhenDisabled(t *testing.T) {
t.Cleanup(runtimeconfig.Override(runtimeconfig.DatabaseEnabled(), false))
if err := Up(); err != nil {
t.Fatalf("Up() error = %v, want nil when ClickHouse disabled", err)
if err := UpClickHouse(); err != nil {
t.Fatalf("UpClickHouse() error = %v, want nil when ClickHouse disabled", err)
}
}
@@ -1,7 +1,7 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package migrations
package migrate
import (
"os"
@@ -1,8 +1,8 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package stamp records pre-Cordis schema versions into w_schema_versions.
package stamp
// Package migrate records pre-Cordis schema versions and applies of_* SQL.
package migrate
import (
"context"
@@ -1,7 +1,7 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package stamp
package migrate
import (
"context"
@@ -1,7 +0,0 @@
-- +goose Up
-- Historical Wavelet→OpenFlare table rename bridge (formerly Go migration).
-- Fresh installs and current of_* schemas need no action.
SELECT 1;
-- +goose Down
SELECT 1;
@@ -1,184 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY,
username VARCHAR(64) UNIQUE,
password VARCHAR(255),
nickname VARCHAR(255),
email VARCHAR(255),
avatar_url VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
is_admin BOOLEAN DEFAULT FALSE,
bio VARCHAR(500),
phone VARCHAR(32),
gender VARCHAR(16),
website VARCHAR(255),
location VARCHAR(255),
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users (is_active);
CREATE INDEX IF NOT EXISTS idx_users_last_login_at ON users (last_login_at);
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users (created_at);
CREATE TABLE IF NOT EXISTS auth_sources (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(80) NOT NULL UNIQUE,
type VARCHAR(20) NOT NULL,
display_name VARCHAR(100),
is_active BOOLEAN NOT NULL DEFAULT FALSE,
client_id VARCHAR(255),
client_secret VARCHAR(1024),
openid_discovery_url VARCHAR(1024),
scopes VARCHAR(255),
icon_url VARCHAR(1024),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_auth_sources_is_active ON auth_sources (is_active);
CREATE TABLE IF NOT EXISTS external_accounts (
id BIGSERIAL PRIMARY KEY,
auth_source_id BIGINT,
user_id BIGINT NOT NULL,
external_id VARCHAR(255) NOT NULL,
external_username VARCHAR(255),
email VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_external_accounts_auth_source_id ON external_accounts (auth_source_id);
CREATE INDEX IF NOT EXISTS idx_external_accounts_user_id ON external_accounts (user_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_external_accounts_source_external ON external_accounts (auth_source_id, external_id);
CREATE TABLE IF NOT EXISTS system_configs (
key VARCHAR(64) PRIMARY KEY,
value TEXT NOT NULL,
type VARCHAR(32) NOT NULL DEFAULT 'system',
visibility INTEGER NOT NULL DEFAULT 0,
description VARCHAR(255),
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS uploads (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
extension VARCHAR(50) NOT NULL,
hash VARCHAR(64),
storage_driver VARCHAR(50) NOT NULL,
type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_uploads_user_id ON uploads (user_id);
CREATE INDEX IF NOT EXISTS idx_uploads_file_path ON uploads (file_path);
CREATE INDEX IF NOT EXISTS idx_uploads_hash ON uploads (hash);
CREATE INDEX IF NOT EXISTS idx_uploads_type ON uploads (type);
CREATE TABLE IF NOT EXISTS access_tokens (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
name VARCHAR(128) NOT NULL,
token_hash VARCHAR(64) NOT NULL UNIQUE,
masked_token VARCHAR(64) NOT NULL,
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_access_tokens_user_id ON access_tokens (user_id);
CREATE TABLE IF NOT EXISTS task_executions (
id BIGINT PRIMARY KEY,
task_id VARCHAR(128) NOT NULL UNIQUE,
task_type VARCHAR(64) NOT NULL,
task_name VARCHAR(128),
status VARCHAR(32) NOT NULL,
retryable BOOLEAN NOT NULL DEFAULT FALSE,
max_retry INTEGER NOT NULL DEFAULT 0,
retry_count INTEGER NOT NULL DEFAULT 0,
log TEXT,
error_message TEXT,
result TEXT,
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
duration BIGINT,
payload TEXT,
triggered_by VARCHAR(32) NOT NULL DEFAULT 'system',
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_task_executions_task_type ON task_executions (task_type);
CREATE INDEX IF NOT EXISTS idx_task_executions_status ON task_executions (status);
CREATE INDEX IF NOT EXISTS idx_task_executions_started_at ON task_executions (started_at);
CREATE INDEX IF NOT EXISTS idx_task_executions_created_at ON task_executions (created_at);
CREATE TABLE IF NOT EXISTS templates (
id BIGSERIAL PRIMARY KEY,
key VARCHAR(80) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
type VARCHAR(20) NOT NULL DEFAULT 'email',
subject VARCHAR(255),
content TEXT NOT NULL,
description VARCHAR(255),
is_system BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_templates_is_system ON templates (is_system);
CREATE INDEX IF NOT EXISTS idx_templates_created_at ON templates (created_at);
CREATE INDEX IF NOT EXISTS idx_templates_updated_at ON templates (updated_at);
INSERT INTO system_configs (key, value, type, visibility, description, created_at, updated_at) VALUES
('cap_login_enabled', 'false', 'system', 1, '是否启用登录人机验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_auto_solve', 'true', 'system', 1, '打开页面后是否自动开始计算,关闭则需用户手动点击触发', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_count', '1', 'system', 0, '客户端需求解的 PoW 难题总数,默认 1,推荐 1~5', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_size', '32', 'system', 0, '人机验证盐值长度', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_difficulty', '4', 'system', 0, '人机验证 PoW 难度(目标前缀长度)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_ttl_seconds', '600', 'system', 0, '人机验证难题有效时间(秒)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_token_ttl_seconds', '1200', 'system', 0, '人机验证兑换凭证有效时间(秒)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('server_address', '', 'system', 0, '服务器地址(用于跨域源控制,不设定则允许任意源)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_host', '', 'system', 0, 'SMTP 服务器地址(例如 smtp.example.com)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_port', '587', 'system', 0, 'SMTP 端口(例如 587 或 465)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_username', '', 'system', 0, 'SMTP 账户(如 sender@example.com)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_password', '', 'system', 0, 'SMTP 访问凭证(授权码/密码)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('upload_allowed_extensions', 'jpg,png,webp', 'system', 1, '允许上传的图片扩展名(逗号分隔)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('site_name', 'OpenFlare', 'system', 1, '系统平台的展示名称', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('password_login_enabled', 'true', 'system', 1, '是否允许使用账号密码登录', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('registration_enabled', 'false', 'system', 1, '控制普通用户是否可以自主注册(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('password_register_enabled', 'false', 'system', 1, '是否允许通过密码创建本地账号', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('oidc_login_enabled', 'true', 'system', 1, '是否允许使用第三方 OIDC 认证源登录', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('max_api_keys_per_user', '5', 'business', 1, '限制每个普通用户可以创建的 API Key 最大数量', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('email_login_verification_enabled', 'false', 'system', 1, '是否开启邮箱登录验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('email_register_verification_enabled', 'false', 'system', 1, '是否开启邮箱注册验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('menu_display_config', '{}', 'system', 1, '目录显示配置(JSON 字符串,格式为 {url: enabled})', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('search_engine_indexing_enabled', 'false', 'system', 1, '是否允许搜索引擎爬取/检索该站点(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('update_upstream_repository', 'Rain-kl/OpenFlare', 'system', 0, 'GitHub Actions Release 上游仓库(owner/repo 或 GitHub 仓库地址)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('storage_config', '{"driver":"local","local":{"root":"."},"s3":{"region":"us-east-1"},"r2":{"region":"auto"},"minio":{"region":"us-east-1","path_style":true},"oss":{},"webdav":{}}', 'system', 0, '文件存储驱动及连接配置(JSON)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
INSERT INTO users (id, username, password, nickname, avatar_url, is_active, is_admin, last_login_at, created_at, updated_at)
VALUES (1, 'admin', '12345678', 'Administrator', '', TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (username) DO NOTHING;
INSERT INTO templates (key, name, type, subject, content, description, is_system, created_at, updated_at) VALUES
('login_email', '登录验证码邮件', 'email', 'OpenFlare 登录验证码', '<h3>OpenFlare 登录验证</h3><p>您的登录验证码为:<strong>{{.Code}}</strong>,5分钟内有效,请勿将验证码泄露给他人。</p>', '用户密码登录时发送的验证码邮件模板,支持变量:{{.Code}}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('register_email', '注册验证码邮件', 'email', 'OpenFlare 注册验证码', '<h3>OpenFlare 注册验证</h3><p>您的注册验证码为:<strong>{{.Code}}</strong>,5分钟内有效,请勿泄露给他人。</p>', '用户注册时发送的验证码邮件模板,支持变量:{{.Code}}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DROP TABLE IF EXISTS templates;
DROP TABLE IF EXISTS task_executions;
DROP TABLE IF EXISTS access_tokens;
DROP TABLE IF EXISTS uploads;
DROP TABLE IF EXISTS system_configs;
DROP TABLE IF EXISTS external_accounts;
DROP TABLE IF EXISTS auth_sources;
DROP TABLE IF EXISTS users;
@@ -1,20 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS schedules (
id BIGINT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
task_type VARCHAR(64) NOT NULL,
cron VARCHAR(64) NOT NULL,
payload TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_schedules_is_active ON schedules (is_active);
-- Seed initial cleanup task
INSERT INTO schedules (id, name, task_type, cron, payload, is_active, created_at, updated_at)
VALUES (1, '清理未使用上传', 'cleanup_unused_uploads', '0 */2 * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
-- +goose Down
DROP TABLE IF EXISTS schedules;
@@ -1,5 +0,0 @@
-- +goose Up
ALTER TABLE access_tokens ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT FALSE;
-- +goose Down
ALTER TABLE access_tokens DROP COLUMN IF EXISTS is_admin;
@@ -1,9 +0,0 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE access_tokens DROP COLUMN IF EXISTS last_used_at;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE access_tokens ADD COLUMN last_used_at TIMESTAMPTZ;
-- +goose StatementEnd
@@ -1,9 +0,0 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE schedules ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH 100);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE schedules ALTER COLUMN id DROP IDENTITY IF EXISTS;
-- +goose StatementEnd
@@ -1,66 +0,0 @@
-- +goose Up
ALTER TABLE users RENAME TO w_users;
ALTER TABLE auth_sources RENAME TO w_auth_sources;
ALTER TABLE external_accounts RENAME TO w_external_accounts;
ALTER TABLE system_configs RENAME TO w_system_configs;
ALTER TABLE uploads RENAME TO w_uploads;
ALTER TABLE access_tokens RENAME TO w_access_tokens;
ALTER TABLE task_executions RENAME TO w_task_executions;
ALTER TABLE templates RENAME TO w_templates;
ALTER TABLE schedules RENAME TO w_schedules;
-- Rename indexes for consistency
ALTER INDEX IF EXISTS idx_users_email RENAME TO idx_w_users_email;
ALTER INDEX IF EXISTS idx_users_is_active RENAME TO idx_w_users_is_active;
ALTER INDEX IF EXISTS idx_users_last_login_at RENAME TO idx_w_users_last_login_at;
ALTER INDEX IF EXISTS idx_users_created_at RENAME TO idx_w_users_created_at;
ALTER INDEX IF EXISTS idx_auth_sources_is_active RENAME TO idx_w_auth_sources_is_active;
ALTER INDEX IF EXISTS idx_external_accounts_auth_source_id RENAME TO idx_w_external_accounts_auth_source_id;
ALTER INDEX IF EXISTS idx_external_accounts_user_id RENAME TO idx_w_external_accounts_user_id;
ALTER INDEX IF EXISTS idx_external_accounts_source_external RENAME TO idx_w_external_accounts_source_external;
ALTER INDEX IF EXISTS idx_uploads_user_id RENAME TO idx_w_uploads_user_id;
ALTER INDEX IF EXISTS idx_uploads_file_path RENAME TO idx_w_uploads_file_path;
ALTER INDEX IF EXISTS idx_uploads_hash RENAME TO idx_w_uploads_hash;
ALTER INDEX IF EXISTS idx_uploads_type RENAME TO idx_w_uploads_type;
ALTER INDEX IF EXISTS idx_access_tokens_user_id RENAME TO idx_w_access_tokens_user_id;
ALTER INDEX IF EXISTS idx_task_executions_task_type RENAME TO idx_w_task_executions_task_type;
ALTER INDEX IF EXISTS idx_task_executions_status RENAME TO idx_w_task_executions_status;
ALTER INDEX IF EXISTS idx_task_executions_started_at RENAME TO idx_w_task_executions_started_at;
ALTER INDEX IF EXISTS idx_task_executions_created_at RENAME TO idx_w_task_executions_created_at;
ALTER INDEX IF EXISTS idx_templates_is_system RENAME TO idx_w_templates_is_system;
ALTER INDEX IF EXISTS idx_templates_created_at RENAME TO idx_w_templates_created_at;
ALTER INDEX IF EXISTS idx_templates_updated_at RENAME TO idx_w_templates_updated_at;
ALTER INDEX IF EXISTS idx_schedules_is_active RENAME TO idx_w_schedules_is_active;
-- +goose Down
ALTER INDEX IF EXISTS idx_w_schedules_is_active RENAME TO idx_schedules_is_active;
ALTER INDEX IF EXISTS idx_w_templates_updated_at RENAME TO idx_templates_updated_at;
ALTER INDEX IF EXISTS idx_w_templates_created_at RENAME TO idx_templates_created_at;
ALTER INDEX IF EXISTS idx_w_templates_is_system RENAME TO idx_templates_is_system;
ALTER INDEX IF EXISTS idx_w_task_executions_created_at RENAME TO idx_task_executions_created_at;
ALTER INDEX IF EXISTS idx_w_task_executions_started_at RENAME TO idx_task_executions_started_at;
ALTER INDEX IF EXISTS idx_w_task_executions_status RENAME TO idx_task_executions_status;
ALTER INDEX IF EXISTS idx_w_task_executions_task_type RENAME TO idx_task_executions_task_type;
ALTER INDEX IF EXISTS idx_w_access_tokens_user_id RENAME TO idx_access_tokens_user_id;
ALTER INDEX IF EXISTS idx_w_uploads_type RENAME TO idx_uploads_type;
ALTER INDEX IF EXISTS idx_w_uploads_hash RENAME TO idx_uploads_hash;
ALTER INDEX IF EXISTS idx_w_uploads_file_path RENAME TO idx_uploads_file_path;
ALTER INDEX IF EXISTS idx_w_uploads_user_id RENAME TO idx_uploads_user_id;
ALTER INDEX IF EXISTS idx_w_external_accounts_source_external RENAME TO idx_external_accounts_source_external;
ALTER INDEX IF EXISTS idx_w_external_accounts_user_id RENAME TO idx_external_accounts_user_id;
ALTER INDEX IF EXISTS idx_w_external_accounts_auth_source_id RENAME TO idx_external_accounts_auth_source_id;
ALTER INDEX IF EXISTS idx_w_auth_sources_is_active RENAME TO idx_auth_sources_is_active;
ALTER INDEX IF EXISTS idx_w_users_created_at RENAME TO idx_users_created_at;
ALTER INDEX IF EXISTS idx_w_users_last_login_at RENAME TO idx_users_last_login_at;
ALTER INDEX IF EXISTS idx_w_users_is_active RENAME TO idx_users_is_active;
ALTER INDEX IF EXISTS idx_w_users_email RENAME TO idx_users_email;
ALTER TABLE w_schedules RENAME TO schedules;
ALTER TABLE w_templates RENAME TO templates;
ALTER TABLE w_task_executions RENAME TO task_executions;
ALTER TABLE w_access_tokens RENAME TO access_tokens;
ALTER TABLE w_uploads RENAME TO uploads;
ALTER TABLE w_system_configs RENAME TO system_configs;
ALTER TABLE w_external_accounts RENAME TO external_accounts;
ALTER TABLE w_auth_sources RENAME TO auth_sources;
ALTER TABLE w_users RENAME TO users;
@@ -1,7 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES ('file_access_whitelist', '["avatar"]', 'system', 1, '免登录访问的文件业务类型白名单 (JSON 数组格式)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'file_access_whitelist';
@@ -1,10 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('disk_cache_max_size_mb', '100', 'system', 0, '磁盘缓存最大空间大小 (MB)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('disk_cache_ttl_minutes', '60', 'system', 0, '磁盘缓存默认有效期 (分钟)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('disk_cache_lru_enabled', 'true', 'system', 0, '是否启用 LRU 淘汰机制', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN ('disk_cache_max_size_mb', 'disk_cache_ttl_minutes', 'disk_cache_lru_enabled');
@@ -1,8 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('login_session_ttl_hours', '0', 'system', 0, '登录会话过期时间 (小时,0表示浏览器关闭后自动退出,-1表示永不过期)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'login_session_ttl_hours';
@@ -1,7 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES ('update_upstream_repository', 'Rain-kl/OpenFlare', 'system', 0, 'GitHub Actions Release 上游仓库(owner/repo 或 GitHub 仓库地址)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'update_upstream_repository';
@@ -1,6 +0,0 @@
-- +goose Up
ALTER TABLE w_uploads ADD COLUMN access_mode INTEGER NOT NULL DEFAULT 0;
UPDATE w_uploads SET access_mode = 1 WHERE type = 'avatar';
-- +goose Down
ALTER TABLE w_uploads DROP COLUMN access_mode;
@@ -1,5 +0,0 @@
-- +goose Up
ALTER TABLE w_system_configs ALTER COLUMN value TYPE TEXT;
-- +goose Down
ALTER TABLE w_system_configs ALTER COLUMN value TYPE VARCHAR(255);
@@ -1,15 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES (
'storage_config',
'{"driver":"local","local":{"root":"."},"s3":{"region":"us-east-1"},"r2":{"region":"auto"},"minio":{"region":"us-east-1","path_style":true},"oss":{},"webdav":{}}',
'system',
0,
'文件存储驱动及连接配置(JSON)',
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'storage_config';
@@ -1,34 +0,0 @@
-- +goose Up
CREATE TABLE w_push_events (
id BIGSERIAL PRIMARY KEY,
event_key VARCHAR(80) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
channels TEXT NOT NULL,
targets TEXT NOT NULL,
template TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX idx_w_push_events_enabled ON w_push_events(enabled);
CREATE TABLE w_push_histories (
id BIGSERIAL PRIMARY KEY,
event_key VARCHAR(80) NOT NULL,
channel VARCHAR(50) NOT NULL,
target VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
level VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL,
error_msg TEXT,
created_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX idx_w_push_histories_event ON w_push_histories(event_key);
CREATE INDEX idx_w_push_histories_created ON w_push_histories(created_at);
-- +goose Down
DROP TABLE IF EXISTS w_push_histories;
DROP TABLE IF EXISTS w_push_events;
@@ -1,8 +0,0 @@
-- +goose Up
INSERT INTO w_users (id, username, password, nickname, avatar_url, is_active, is_admin, last_login_at, created_at, updated_at)
VALUES (999, 'system', '*', '系统', '', TRUE, FALSE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (username) DO NOTHING;
SELECT setval(pg_get_serial_sequence('w_users', 'id'), COALESCE((SELECT MAX(id) FROM w_users), 1));
-- +goose Down
DELETE FROM w_users WHERE username = 'system';
@@ -1,19 +0,0 @@
-- +goose Up
CREATE TABLE w_push_channels (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(80) NOT NULL UNIQUE,
description VARCHAR(255),
type VARCHAR(50) NOT NULL DEFAULT 'custom',
token VARCHAR(100),
url TEXT NOT NULL,
other TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX idx_w_push_channels_name ON w_push_channels(name);
CREATE INDEX idx_w_push_channels_enabled ON w_push_channels(enabled);
-- +goose Down
DROP TABLE IF EXISTS w_push_channels;
@@ -1,11 +0,0 @@
-- +goose Up
UPDATE w_schedules
SET name = '系统定期垃圾清理',
task_type = 'system_cleanup'
WHERE id = 1;
-- +goose Down
UPDATE w_schedules
SET name = '清理未使用上传',
task_type = 'cleanup_unused_uploads'
WHERE id = 1;
@@ -1,7 +0,0 @@
-- +goose Up
ALTER TABLE w_push_events ADD COLUMN task_type VARCHAR(100) NOT NULL DEFAULT '';
CREATE INDEX idx_w_push_events_task_type ON w_push_events(task_type);
-- +goose Down
DROP INDEX IF EXISTS idx_w_push_events_task_type;
ALTER TABLE w_push_events DROP COLUMN task_type;
@@ -1,6 +0,0 @@
-- +goose Up
DELETE FROM w_system_configs WHERE key = 'push_config';
DELETE FROM w_push_events WHERE event_key = 'admin_login';
DELETE FROM w_system_configs WHERE key = 'push_global_token';
-- +goose Down
@@ -1,9 +0,0 @@
-- +goose Up
CREATE INDEX IF NOT EXISTS idx_w_uploads_status_created_at ON w_uploads (status, created_at);
CREATE INDEX IF NOT EXISTS idx_w_uploads_storage_driver_status ON w_uploads (storage_driver, status);
CREATE INDEX IF NOT EXISTS idx_w_uploads_hash_file_size_status ON w_uploads (hash, file_size, status);
-- +goose Down
DROP INDEX IF EXISTS idx_w_uploads_hash_file_size_status;
DROP INDEX IF EXISTS idx_w_uploads_storage_driver_status;
DROP INDEX IF EXISTS idx_w_uploads_status_created_at;
@@ -1,12 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS w_upload_stats (
dimension VARCHAR(32) NOT NULL,
stat_key VARCHAR(64) NOT NULL DEFAULT '',
file_count BIGINT NOT NULL DEFAULT 0,
file_size BIGINT NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (dimension, stat_key)
);
-- +goose Down
DROP TABLE IF EXISTS w_upload_stats;
@@ -1,67 +0,0 @@
-- +goose Up
INSERT INTO w_upload_stats (dimension, stat_key, file_count, file_size)
SELECT 'total', '', COUNT(*), COALESCE(SUM(file_size), 0)
FROM w_uploads
WHERE status != 'deleted'
ON CONFLICT (dimension, stat_key) DO UPDATE SET
file_count = EXCLUDED.file_count,
file_size = EXCLUDED.file_size,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO w_upload_stats (dimension, stat_key, file_count, file_size)
SELECT
'type',
COALESCE(NULLIF(type, ''), 'generic'),
COUNT(*),
COALESCE(SUM(file_size), 0)
FROM w_uploads
WHERE status != 'deleted'
GROUP BY COALESCE(NULLIF(type, ''), 'generic')
ON CONFLICT (dimension, stat_key) DO UPDATE SET
file_count = EXCLUDED.file_count,
file_size = EXCLUDED.file_size,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO w_upload_stats (dimension, stat_key, file_count, file_size)
SELECT
'category',
CASE
WHEN LOWER(mime_type) LIKE 'image/%'
OR LOWER(extension) IN ('jpg', 'jpeg', 'png', 'webp', 'gif') THEN '图片'
WHEN LOWER(mime_type) LIKE 'video/%' THEN '视频'
WHEN LOWER(mime_type) LIKE 'audio/%' THEN '音频'
WHEN LOWER(extension) IN ('zip', 'rar', '7z', 'tar', 'gz', 'tgz', 'bz2', 'xz')
OR LOWER(mime_type) LIKE '%zip%'
OR LOWER(mime_type) LIKE '%tar%'
OR LOWER(mime_type) LIKE '%gzip%' THEN '压缩包'
WHEN LOWER(extension) IN ('pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'csv', 'json', 'yaml', 'yml', 'xml')
OR LOWER(mime_type) LIKE 'text/%'
OR LOWER(mime_type) = 'application/pdf' THEN '文档'
ELSE '其他'
END,
COUNT(*),
COALESCE(SUM(file_size), 0)
FROM w_uploads
WHERE status != 'deleted'
GROUP BY 2
ON CONFLICT (dimension, stat_key) DO UPDATE SET
file_count = EXCLUDED.file_count,
file_size = EXCLUDED.file_size,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO w_upload_stats (dimension, stat_key, file_count, file_size)
SELECT
'trend',
TO_CHAR(created_at, 'YYYY-MM-DD'),
COUNT(*),
COALESCE(SUM(file_size), 0)
FROM w_uploads
WHERE status != 'deleted'
GROUP BY TO_CHAR(created_at, 'YYYY-MM-DD')
ON CONFLICT (dimension, stat_key) DO UPDATE SET
file_count = EXCLUDED.file_count,
file_size = EXCLUDED.file_size,
updated_at = CURRENT_TIMESTAMP;
-- +goose Down
DELETE FROM w_upload_stats;
@@ -1,7 +0,0 @@
-- +goose Up
DROP INDEX IF EXISTS idx_w_uploads_storage_driver_status;
ALTER TABLE w_uploads DROP COLUMN IF EXISTS storage_driver;
-- +goose Down
ALTER TABLE w_uploads ADD COLUMN IF NOT EXISTS storage_driver VARCHAR(50) NOT NULL DEFAULT 'local';
CREATE INDEX IF NOT EXISTS idx_w_uploads_storage_driver_status ON w_uploads (storage_driver, status);
@@ -1,67 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_options (
key VARCHAR(128) PRIMARY KEY,
value TEXT NOT NULL DEFAULT ''
);
INSERT INTO of_options (key, value) VALUES
('PasswordLoginEnabled', 'true'),
('CapLoginEnabled', 'true'),
('PasswordRegisterEnabled', 'false'),
('EmailVerificationEnabled', 'false'),
('GitHubOAuthEnabled', 'false'),
('WeChatAuthEnabled', 'false'),
('SMTPPort', '587'),
('SystemName', 'OpenFlare'),
('AgentHeartbeatInterval', '10000'),
('AgentWebsocketUpgradeEnabled', 'true'),
('NodeOfflineThreshold', '120000'),
('AgentUpdateRepo', 'Rain-kl/OpenFlare'),
('GeoIPProvider', 'ipinfo'),
('DatabaseAutoCleanupEnabled', 'false'),
('DatabaseAutoCleanupRetentionDays', '30'),
('UptimeKumaEnabled', 'false'),
('UptimeKumaMonitorScope', 'all'),
('UptimeKumaSyncInterval', '5'),
('UptimeKumaInterval', '60'),
('UptimeKumaRetry', '0'),
('UptimeKumaRetryInterval', '60'),
('UptimeKumaTimeout', '48'),
('OpenRestyDefaultServerReturnStatus', '421'),
('OpenRestyWorkerProcesses', 'auto'),
('OpenRestyWorkerConnections', '4096'),
('OpenRestyWorkerRlimitNofile', '65535'),
('OpenRestyEventsUse', 'epoll'),
('OpenRestyEventsMultiAcceptEnabled', 'true'),
('OpenRestyKeepaliveTimeout', '20'),
('OpenRestyKeepaliveRequests', '1000'),
('OpenRestyClientHeaderTimeout', '15'),
('OpenRestyClientBodyTimeout', '15'),
('OpenRestyClientMaxBodySize', '64m'),
('OpenRestyLargeClientHeaderBuffers', '4 16k'),
('OpenRestySendTimeout', '30'),
('OpenRestyProxyConnectTimeout', '3'),
('OpenRestyProxySendTimeout', '60'),
('OpenRestyProxyReadTimeout', '60'),
('OpenRestyWebsocketEnabled', 'true'),
('OpenRestyHTTP3Enabled', 'true'),
('OpenRestyProxyRequestBufferingEnabled', 'false'),
('OpenRestyProxyBufferingEnabled', 'true'),
('OpenRestyProxyBuffers', '16 16k'),
('OpenRestyProxyBufferSize', '8k'),
('OpenRestyProxyBusyBuffersSize', '64k'),
('OpenRestyGzipEnabled', 'true'),
('OpenRestyGzipMinLength', '1024'),
('OpenRestyGzipCompLevel', '5'),
('OpenRestyCacheEnabled', 'false'),
('OpenRestyCacheLevels', '1:2'),
('OpenRestyCacheInactive', '30m'),
('OpenRestyCacheMaxSize', '1g'),
('OpenRestyCacheKeyTemplate', '$scheme$host$request_uri'),
('OpenRestyCacheLockEnabled', 'true'),
('OpenRestyCacheLockTimeout', '5s'),
('OpenRestyCacheUseStale', 'error timeout updating http_500 http_502 http_503 http_504')
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DROP TABLE IF EXISTS of_options;
@@ -1,13 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_origins (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
address VARCHAR(255) NOT NULL,
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_origins_address ON of_origins (address);
-- +goose Down
DROP TABLE IF EXISTS of_origins;
@@ -1,19 +0,0 @@
-- +goose Up
CREATE TABLE of_apply_logs (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
version VARCHAR(32) NOT NULL,
result VARCHAR(32) NOT NULL,
message TEXT,
checksum VARCHAR(64) NOT NULL DEFAULT '',
main_config_checksum VARCHAR(64) NOT NULL DEFAULT '',
route_config_checksum VARCHAR(64) NOT NULL DEFAULT '',
support_file_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX idx_of_apply_logs_node_id ON of_apply_logs(node_id);
CREATE INDEX idx_of_apply_logs_created_at ON of_apply_logs(created_at);
-- +goose Down
DROP TABLE IF EXISTS of_apply_logs;
@@ -1,43 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_proxy_routes (
id BIGSERIAL PRIMARY KEY,
site_name VARCHAR(255) NOT NULL DEFAULT '',
domain VARCHAR(255) NOT NULL,
domains TEXT NOT NULL DEFAULT '[]',
origin_id BIGINT,
origin_url VARCHAR(2048) NOT NULL,
origin_host VARCHAR(255) NOT NULL DEFAULT '',
upstreams TEXT NOT NULL DEFAULT '[]',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
enable_https BOOLEAN NOT NULL DEFAULT FALSE,
cert_id BIGINT,
cert_ids TEXT NOT NULL DEFAULT '[]',
domain_cert_ids TEXT NOT NULL DEFAULT '[]',
redirect_http BOOLEAN NOT NULL DEFAULT FALSE,
limit_conn_per_server INTEGER NOT NULL DEFAULT 0,
limit_conn_per_ip INTEGER NOT NULL DEFAULT 0,
limit_rate VARCHAR(32) NOT NULL DEFAULT '',
cache_enabled BOOLEAN NOT NULL DEFAULT FALSE,
cache_policy VARCHAR(32) NOT NULL DEFAULT '',
cache_rules TEXT NOT NULL DEFAULT '[]',
custom_headers TEXT NOT NULL DEFAULT '[]',
basic_auth_enabled BOOLEAN NOT NULL DEFAULT FALSE,
basic_auth_username VARCHAR(255) NOT NULL DEFAULT '',
basic_auth_password VARCHAR(255) NOT NULL DEFAULT '',
remark VARCHAR(255) NOT NULL DEFAULT '',
upstream_type VARCHAR(32) NOT NULL DEFAULT 'direct',
tunnel_node_id BIGINT,
tunnel_target_addr VARCHAR(512) NOT NULL DEFAULT '',
tunnel_target_protocol VARCHAR(16) NOT NULL DEFAULT '',
pages_project_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_proxy_routes_domain ON of_proxy_routes (domain);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_proxy_routes_site_name ON of_proxy_routes (site_name);
CREATE INDEX IF NOT EXISTS idx_of_proxy_routes_origin_id ON of_proxy_routes (origin_id);
CREATE INDEX IF NOT EXISTS idx_of_proxy_routes_tunnel_node_id ON of_proxy_routes (tunnel_node_id);
CREATE INDEX IF NOT EXISTS idx_of_proxy_routes_pages_project_id ON of_proxy_routes (pages_project_id);
-- +goose Down
DROP TABLE IF EXISTS of_proxy_routes;
@@ -1,44 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_nodes (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
ip VARCHAR(64) NOT NULL DEFAULT '',
ip_manual_override BOOLEAN NOT NULL DEFAULT FALSE,
geo_name VARCHAR(128) NOT NULL DEFAULT '',
geo_latitude DOUBLE PRECISION,
geo_longitude DOUBLE PRECISION,
geo_manual_override BOOLEAN NOT NULL DEFAULT FALSE,
access_token VARCHAR(128) NOT NULL DEFAULT '',
auto_update_enabled BOOLEAN NOT NULL DEFAULT FALSE,
update_requested BOOLEAN NOT NULL DEFAULT FALSE,
update_channel VARCHAR(16) NOT NULL DEFAULT 'stable',
update_tag VARCHAR(64) NOT NULL DEFAULT '',
restart_openresty_requested BOOLEAN NOT NULL DEFAULT FALSE,
version VARCHAR(64) NOT NULL DEFAULT '',
ext_version VARCHAR(64) NOT NULL DEFAULT '',
openresty_status VARCHAR(16) NOT NULL DEFAULT 'unknown',
openresty_message TEXT,
status VARCHAR(16) NOT NULL DEFAULT 'offline',
current_version VARCHAR(32) NOT NULL DEFAULT '',
last_seen_at TIMESTAMPTZ,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
node_type VARCHAR(32) NOT NULL DEFAULT 'edge_node',
relay_bind_port INTEGER NOT NULL DEFAULT 0,
relay_vhost_http_port INTEGER NOT NULL DEFAULT 0,
relay_auth_token VARCHAR(128) NOT NULL DEFAULT '',
relay_agent_access_addr VARCHAR(255) NOT NULL DEFAULT '',
relay_client_access_addr VARCHAR(255) NOT NULL DEFAULT '',
relay_client_proxy_url VARCHAR(512) NOT NULL DEFAULT '',
capabilities_json TEXT NOT NULL DEFAULT '[]',
relay_status VARCHAR(16) NOT NULL DEFAULT 'unknown',
relay_web_server_enabled BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_nodes_node_id ON of_nodes (node_id);
CREATE INDEX IF NOT EXISTS idx_of_nodes_access_token ON of_nodes (access_token);
-- +goose Down
DROP TABLE IF EXISTS of_nodes;
@@ -1,60 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_waf_rule_groups (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
is_global BOOLEAN NOT NULL DEFAULT FALSE,
block_status_code INTEGER NOT NULL DEFAULT 418,
block_response_body TEXT NOT NULL DEFAULT '',
ip_whitelist TEXT NOT NULL DEFAULT '[]',
ip_blacklist TEXT NOT NULL DEFAULT '[]',
ip_whitelist_groups TEXT NOT NULL DEFAULT '[]',
ip_blacklist_groups TEXT NOT NULL DEFAULT '[]',
country_whitelist TEXT NOT NULL DEFAULT '[]',
country_blacklist TEXT NOT NULL DEFAULT '[]',
region_whitelist TEXT NOT NULL DEFAULT '[]',
region_blacklist TEXT NOT NULL DEFAULT '[]',
pow_enabled BOOLEAN NOT NULL DEFAULT FALSE,
pow_config TEXT NOT NULL DEFAULT '{}',
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_waf_rule_groups_is_global ON of_waf_rule_groups (is_global);
CREATE TABLE IF NOT EXISTS of_waf_ip_groups (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
ip_list TEXT NOT NULL DEFAULT '[]',
auto_config TEXT NOT NULL DEFAULT '{}',
ext_ips TEXT NOT NULL DEFAULT '[]',
subscription_url VARCHAR(2048) NOT NULL DEFAULT '',
subscription_format VARCHAR(32) NOT NULL DEFAULT 'text',
subscription_mapping_rule VARCHAR(255) NOT NULL DEFAULT '',
sync_interval_minutes INTEGER NOT NULL DEFAULT 1440,
last_synced_at TIMESTAMPTZ,
next_sync_at TIMESTAMPTZ,
last_sync_status VARCHAR(32) NOT NULL DEFAULT '',
last_sync_message TEXT NOT NULL DEFAULT '',
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_waf_ip_groups_type ON of_waf_ip_groups (type);
CREATE INDEX IF NOT EXISTS idx_of_waf_ip_groups_next_sync_at ON of_waf_ip_groups (next_sync_at);
CREATE TABLE IF NOT EXISTS of_waf_rule_group_bindings (
id BIGSERIAL PRIMARY KEY,
rule_group_id BIGINT NOT NULL,
proxy_route_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_waf_group_route ON of_waf_rule_group_bindings (rule_group_id, proxy_route_id);
CREATE INDEX IF NOT EXISTS idx_of_waf_rule_group_bindings_proxy_route_id ON of_waf_rule_group_bindings (proxy_route_id);
-- +goose Down
DROP TABLE IF EXISTS of_waf_rule_group_bindings;
DROP TABLE IF EXISTS of_waf_ip_groups;
DROP TABLE IF EXISTS of_waf_rule_groups;
@@ -1,61 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_tls_certificates (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
cert_pem TEXT NOT NULL,
key_pem TEXT NOT NULL,
not_before TIMESTAMPTZ,
not_after TIMESTAMPTZ,
remark VARCHAR(255) NOT NULL DEFAULT '',
provider VARCHAR(64) NOT NULL DEFAULT 'upload',
acme_account_id BIGINT NOT NULL DEFAULT 0,
dns_account_id BIGINT NOT NULL DEFAULT 0,
key_algorithm VARCHAR(32) NOT NULL DEFAULT '',
auto_renew BOOLEAN NOT NULL DEFAULT FALSE,
primary_domain VARCHAR(255) NOT NULL DEFAULT '',
other_domains TEXT NOT NULL DEFAULT '',
disable_cname BOOLEAN NOT NULL DEFAULT FALSE,
skip_dns BOOLEAN NOT NULL DEFAULT FALSE,
dns1 VARCHAR(128) NOT NULL DEFAULT '',
dns2 VARCHAR(128) NOT NULL DEFAULT '',
apply_status VARCHAR(64) NOT NULL DEFAULT 'ready',
apply_message TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_tls_certificates_name ON of_tls_certificates (name);
CREATE TABLE IF NOT EXISTS of_managed_domains (
id BIGSERIAL PRIMARY KEY,
domain VARCHAR(255) NOT NULL,
cert_id BIGINT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_managed_domains_domain ON of_managed_domains (domain);
CREATE TABLE IF NOT EXISTS of_dns_accounts (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(64) NOT NULL,
"authorization" TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS of_acme_accounts (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL DEFAULT '',
url VARCHAR(255) NOT NULL DEFAULT '',
private_key TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- +goose Down
DROP TABLE IF EXISTS of_managed_domains;
DROP TABLE IF EXISTS of_tls_certificates;
DROP TABLE IF EXISTS of_dns_accounts;
DROP TABLE IF EXISTS of_acme_accounts;
@@ -1,18 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_config_versions (
id BIGSERIAL PRIMARY KEY,
version VARCHAR(32) NOT NULL,
snapshot_json TEXT NOT NULL,
main_config TEXT NOT NULL DEFAULT '',
rendered_config TEXT NOT NULL,
support_files_json TEXT NOT NULL DEFAULT '[]',
checksum VARCHAR(64) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT FALSE,
created_by VARCHAR(64) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_config_versions_version ON of_config_versions (version);
CREATE INDEX IF NOT EXISTS idx_of_config_versions_is_active ON of_config_versions (is_active);
-- +goose Down
DROP TABLE IF EXISTS of_config_versions;
@@ -1,53 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_pages_projects (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
slug VARCHAR(128) NOT NULL,
description TEXT NOT NULL DEFAULT '',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
spa_fallback_enabled BOOLEAN NOT NULL DEFAULT FALSE,
spa_fallback_path VARCHAR(512) NOT NULL DEFAULT '/index.html',
api_proxy_enabled BOOLEAN NOT NULL DEFAULT FALSE,
api_proxy_path VARCHAR(255) NOT NULL DEFAULT '',
api_proxy_pass VARCHAR(2048) NOT NULL DEFAULT '',
api_proxy_rewrite VARCHAR(255) NOT NULL DEFAULT '',
active_deployment_id BIGINT,
root_dir VARCHAR(512) NOT NULL DEFAULT '',
entry_file VARCHAR(512) NOT NULL DEFAULT 'index.html',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_pages_projects_slug ON of_pages_projects (slug);
CREATE INDEX IF NOT EXISTS idx_of_pages_projects_active_deployment_id ON of_pages_projects (active_deployment_id);
CREATE TABLE IF NOT EXISTS of_pages_deployments (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
deployment_number INTEGER NOT NULL,
checksum VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'uploaded',
artifact_path VARCHAR(2048) NOT NULL,
file_count INTEGER NOT NULL DEFAULT 0,
total_size BIGINT NOT NULL DEFAULT 0,
created_by VARCHAR(64) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
activated_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_of_pages_deployments_project_id ON of_pages_deployments (project_id);
CREATE INDEX IF NOT EXISTS idx_of_pages_deployments_checksum ON of_pages_deployments (checksum);
CREATE INDEX IF NOT EXISTS idx_of_pages_deployments_status ON of_pages_deployments (status);
CREATE TABLE IF NOT EXISTS of_pages_deployment_files (
id BIGSERIAL PRIMARY KEY,
deployment_id BIGINT NOT NULL,
path VARCHAR(2048) NOT NULL,
size BIGINT NOT NULL DEFAULT 0,
checksum VARCHAR(64) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_pages_deployment_files_deployment_id ON of_pages_deployment_files (deployment_id);
-- +goose Down
DROP TABLE IF EXISTS of_pages_deployment_files;
DROP TABLE IF EXISTS of_pages_deployments;
DROP TABLE IF EXISTS of_pages_projects;
@@ -1,136 +0,0 @@
-- +goose Up
CREATE TABLE of_node_system_profiles (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
hostname VARCHAR(255) NOT NULL DEFAULT '',
os_name VARCHAR(128) NOT NULL DEFAULT '',
os_version VARCHAR(128) NOT NULL DEFAULT '',
kernel_version VARCHAR(128) NOT NULL DEFAULT '',
architecture VARCHAR(64) NOT NULL DEFAULT '',
cpu_model VARCHAR(255) NOT NULL DEFAULT '',
cpu_cores INTEGER NOT NULL DEFAULT 0,
total_memory_bytes BIGINT NOT NULL DEFAULT 0,
total_disk_bytes BIGINT NOT NULL DEFAULT 0,
uptime_seconds BIGINT NOT NULL DEFAULT 0,
reported_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX idx_of_node_system_profiles_node_id ON of_node_system_profiles (node_id);
CREATE INDEX idx_of_node_system_profiles_reported_at ON of_node_system_profiles (reported_at);
CREATE TABLE of_node_metric_snapshots (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
cpu_usage_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
memory_used_bytes BIGINT NOT NULL DEFAULT 0,
memory_total_bytes BIGINT NOT NULL DEFAULT 0,
storage_used_bytes BIGINT NOT NULL DEFAULT 0,
storage_total_bytes BIGINT NOT NULL DEFAULT 0,
disk_read_bytes BIGINT NOT NULL DEFAULT 0,
disk_write_bytes BIGINT NOT NULL DEFAULT 0,
network_rx_bytes BIGINT NOT NULL DEFAULT 0,
network_tx_bytes BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_metric_snapshots_node_id ON of_node_metric_snapshots (node_id);
CREATE INDEX idx_of_node_metric_snapshots_captured_at ON of_node_metric_snapshots (captured_at);
CREATE TABLE of_node_request_reports (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
window_started_at TIMESTAMPTZ NOT NULL,
window_ended_at TIMESTAMPTZ NOT NULL,
request_count BIGINT NOT NULL DEFAULT 0,
error_count BIGINT NOT NULL DEFAULT 0,
unique_visitor_count BIGINT NOT NULL DEFAULT 0,
status_codes_json TEXT,
top_domains_json TEXT,
source_countries_json TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_request_reports_node_id ON of_node_request_reports (node_id);
CREATE INDEX idx_of_node_request_reports_window_started_at ON of_node_request_reports (window_started_at);
CREATE INDEX idx_of_node_request_reports_window_ended_at ON of_node_request_reports (window_ended_at);
CREATE TABLE of_node_health_events (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
severity VARCHAR(16) NOT NULL,
status VARCHAR(16) NOT NULL,
message TEXT,
first_triggered_at TIMESTAMPTZ NOT NULL,
last_triggered_at TIMESTAMPTZ NOT NULL,
reported_at TIMESTAMPTZ NOT NULL,
resolved_at TIMESTAMPTZ,
metadata_json TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_health_events_node_id ON of_node_health_events (node_id);
CREATE INDEX idx_of_node_health_events_event_type ON of_node_health_events (event_type);
CREATE INDEX idx_of_node_health_events_status ON of_node_health_events (status);
CREATE INDEX idx_of_node_health_events_first_triggered_at ON of_node_health_events (first_triggered_at);
CREATE INDEX idx_of_node_health_events_last_triggered_at ON of_node_health_events (last_triggered_at);
CREATE INDEX idx_of_node_health_events_reported_at ON of_node_health_events (reported_at);
CREATE INDEX idx_of_node_health_events_resolved_at ON of_node_health_events (resolved_at);
CREATE TABLE of_node_obs_openresty (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
openresty_rx_bytes BIGINT NOT NULL DEFAULT 0,
openresty_tx_bytes BIGINT NOT NULL DEFAULT 0,
openresty_connections BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_openresty_node_id ON of_node_obs_openresty (node_id);
CREATE INDEX idx_of_node_obs_openresty_captured_at ON of_node_obs_openresty (captured_at);
CREATE TABLE of_node_obs_frps (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
frps_connections INTEGER NOT NULL DEFAULT 0,
frps_proxy_count INTEGER NOT NULL DEFAULT 0,
frps_client_count INTEGER NOT NULL DEFAULT 0,
frps_proxies TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_frps_node_id ON of_node_obs_frps (node_id);
CREATE INDEX idx_of_node_obs_frps_captured_at ON of_node_obs_frps (captured_at);
CREATE TABLE of_node_access_logs (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
logged_at TIMESTAMPTZ NOT NULL,
remote_addr VARCHAR(128) NOT NULL DEFAULT '',
region VARCHAR(128) NOT NULL DEFAULT '',
host VARCHAR(255) NOT NULL DEFAULT '',
path VARCHAR(2048) NOT NULL DEFAULT '',
status_code INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_access_logs_node_id ON of_node_access_logs (node_id);
CREATE INDEX idx_of_node_access_logs_logged_at ON of_node_access_logs (logged_at);
CREATE INDEX idx_of_node_access_logs_remote_addr ON of_node_access_logs (remote_addr);
CREATE INDEX idx_of_node_access_logs_host ON of_node_access_logs (host);
CREATE INDEX idx_of_node_access_logs_status_code ON of_node_access_logs (status_code);
-- +goose Down
DROP TABLE IF EXISTS of_node_access_logs;
DROP TABLE IF EXISTS of_node_obs_frps;
DROP TABLE IF EXISTS of_node_obs_openresty;
DROP TABLE IF EXISTS of_node_health_events;
DROP TABLE IF EXISTS of_node_request_reports;
DROP TABLE IF EXISTS of_node_metric_snapshots;
DROP TABLE IF EXISTS of_node_system_profiles;
@@ -1,5 +0,0 @@
-- +goose Up
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_node_id_logged_at ON of_node_access_logs (node_id, logged_at);
-- +goose Down
DROP INDEX IF EXISTS idx_of_node_access_logs_node_id_logged_at;
@@ -1,15 +0,0 @@
-- +goose Up
CREATE TABLE of_node_obs_frpc (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
tunnel_status VARCHAR(16) NOT NULL DEFAULT '',
connected_relays_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_frpc_node_id ON of_node_obs_frpc (node_id);
CREATE INDEX idx_of_node_obs_frpc_captured_at ON of_node_obs_frpc (captured_at);
-- +goose Down
DROP TABLE IF EXISTS of_node_obs_frpc;
@@ -1,11 +0,0 @@
-- +goose Up
INSERT INTO w_schedules (id, name, task_type, cron, payload, is_active, created_at, updated_at)
VALUES
(101, 'OpenFlare SSL 自动续期', 'of_ssl_renew', '0 0 * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
(102, 'OpenFlare 可观测数据自动清理', 'of_database_auto_cleanup', '0 3 * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
(103, 'OpenFlare WAF IP 组同步', 'of_waf_ip_group_sync', '*/5 * * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
(104, 'OpenFlare Uptime Kuma 同步', 'of_uptime_kuma_sync', '* * * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
-- +goose Down
DELETE FROM w_schedules WHERE id IN (101, 102, 103, 104);
@@ -1,14 +0,0 @@
-- +goose Up
ALTER TABLE of_pages_deployments
ADD COLUMN IF NOT EXISTS upload_id BIGINT NOT NULL DEFAULT 0;
CREATE INDEX IF NOT EXISTS idx_of_pages_deployments_upload_id ON of_pages_deployments (upload_id);
ALTER TABLE of_pages_deployments
ALTER COLUMN artifact_path SET DEFAULT '';
-- +goose Down
DROP INDEX IF EXISTS idx_of_pages_deployments_upload_id;
ALTER TABLE of_pages_deployments
DROP COLUMN IF EXISTS upload_id;
@@ -1,31 +0,0 @@
-- +goose Up
UPDATE w_system_configs
SET value = 'OpenFlare', updated_at = CURRENT_TIMESTAMP
WHERE key = 'site_name' AND value = 'Wavelet';
UPDATE w_system_configs
SET value = 'Rain-kl/OpenFlare', updated_at = CURRENT_TIMESTAMP
WHERE key = 'update_upstream_repository' AND value = 'Rain-kl/Wavelet';
UPDATE w_templates
SET
subject = REPLACE(subject, 'Wavelet', 'OpenFlare'),
content = REPLACE(content, 'Wavelet', 'OpenFlare'),
updated_at = CURRENT_TIMESTAMP
WHERE key IN ('login_email', 'register_email');
-- +goose Down
UPDATE w_system_configs
SET value = 'Wavelet', updated_at = CURRENT_TIMESTAMP
WHERE key = 'site_name' AND value = 'OpenFlare';
UPDATE w_system_configs
SET value = 'Rain-kl/Wavelet', updated_at = CURRENT_TIMESTAMP
WHERE key = 'update_upstream_repository' AND value = 'Rain-kl/OpenFlare';
UPDATE w_templates
SET
subject = REPLACE(subject, 'OpenFlare', 'Wavelet'),
content = REPLACE(content, 'OpenFlare', 'Wavelet'),
updated_at = CURRENT_TIMESTAMP
WHERE key IN ('login_email', 'register_email');
@@ -1,17 +0,0 @@
-- +goose Up
UPDATE w_system_configs
SET value = 'false', updated_at = CURRENT_TIMESTAMP
WHERE key = 'registration_enabled' AND value = 'true';
UPDATE w_system_configs
SET value = 'false', updated_at = CURRENT_TIMESTAMP
WHERE key = 'password_register_enabled' AND value = 'true';
-- +goose Down
UPDATE w_system_configs
SET value = 'true', updated_at = CURRENT_TIMESTAMP
WHERE key = 'registration_enabled' AND value = 'false';
UPDATE w_system_configs
SET value = 'true', updated_at = CURRENT_TIMESTAMP
WHERE key = 'password_register_enabled' AND value = 'false';
@@ -1,20 +0,0 @@
-- +goose Up
DELETE FROM of_options
WHERE key IN (
'GlobalApiRateLimitNum',
'GlobalApiRateLimitDuration',
'GlobalWebRateLimitNum',
'GlobalWebRateLimitDuration',
'CriticalRateLimitNum',
'CriticalRateLimitDuration'
);
-- +goose Down
INSERT INTO of_options (key, value) VALUES
('GlobalApiRateLimitNum', '300'),
('GlobalApiRateLimitDuration', '180'),
('GlobalWebRateLimitNum', '300'),
('GlobalWebRateLimitDuration', '180'),
('CriticalRateLimitNum', '100'),
('CriticalRateLimitDuration', '1200')
ON CONFLICT (key) DO NOTHING;
@@ -1,28 +0,0 @@
-- +goose Up
DROP INDEX IF EXISTS idx_of_node_access_logs_node_id_logged_at;
DROP INDEX IF EXISTS idx_of_node_access_logs_status_code;
DROP INDEX IF EXISTS idx_of_node_access_logs_host;
DROP INDEX IF EXISTS idx_of_node_access_logs_remote_addr;
DROP INDEX IF EXISTS idx_of_node_access_logs_logged_at;
DROP INDEX IF EXISTS idx_of_node_access_logs_node_id;
DROP TABLE IF EXISTS of_node_access_logs;
-- +goose Down
CREATE TABLE of_node_access_logs (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
logged_at TIMESTAMPTZ NOT NULL,
remote_addr VARCHAR(128) NOT NULL DEFAULT '',
region VARCHAR(128) NOT NULL DEFAULT '',
host VARCHAR(255) NOT NULL DEFAULT '',
path VARCHAR(2048) NOT NULL DEFAULT '',
status_code INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_access_logs_node_id ON of_node_access_logs (node_id);
CREATE INDEX idx_of_node_access_logs_logged_at ON of_node_access_logs (logged_at);
CREATE INDEX idx_of_node_access_logs_remote_addr ON of_node_access_logs (remote_addr);
CREATE INDEX idx_of_node_access_logs_host ON of_node_access_logs (host);
CREATE INDEX idx_of_node_access_logs_status_code ON of_node_access_logs (status_code);
CREATE INDEX idx_of_node_access_logs_node_id_logged_at ON of_node_access_logs (node_id, logged_at);
@@ -1,98 +0,0 @@
-- +goose Up
DROP INDEX IF EXISTS idx_of_node_obs_frpc_captured_at;
DROP INDEX IF EXISTS idx_of_node_obs_frpc_node_id;
DROP TABLE IF EXISTS of_node_obs_frpc;
DROP INDEX IF EXISTS idx_of_node_obs_frps_captured_at;
DROP INDEX IF EXISTS idx_of_node_obs_frps_node_id;
DROP TABLE IF EXISTS of_node_obs_frps;
DROP INDEX IF EXISTS idx_of_node_obs_openresty_captured_at;
DROP INDEX IF EXISTS idx_of_node_obs_openresty_node_id;
DROP TABLE IF EXISTS of_node_obs_openresty;
DROP INDEX IF EXISTS idx_of_node_request_reports_window_ended_at;
DROP INDEX IF EXISTS idx_of_node_request_reports_window_started_at;
DROP INDEX IF EXISTS idx_of_node_request_reports_node_id;
DROP TABLE IF EXISTS of_node_request_reports;
DROP INDEX IF EXISTS idx_of_node_metric_snapshots_captured_at;
DROP INDEX IF EXISTS idx_of_node_metric_snapshots_node_id;
DROP TABLE IF EXISTS of_node_metric_snapshots;
-- +goose Down
CREATE TABLE of_node_metric_snapshots (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
cpu_usage_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
memory_used_bytes BIGINT NOT NULL DEFAULT 0,
memory_total_bytes BIGINT NOT NULL DEFAULT 0,
storage_used_bytes BIGINT NOT NULL DEFAULT 0,
storage_total_bytes BIGINT NOT NULL DEFAULT 0,
disk_read_bytes BIGINT NOT NULL DEFAULT 0,
disk_write_bytes BIGINT NOT NULL DEFAULT 0,
network_rx_bytes BIGINT NOT NULL DEFAULT 0,
network_tx_bytes BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_metric_snapshots_node_id ON of_node_metric_snapshots (node_id);
CREATE INDEX idx_of_node_metric_snapshots_captured_at ON of_node_metric_snapshots (captured_at);
CREATE TABLE of_node_request_reports (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
window_started_at TIMESTAMPTZ NOT NULL,
window_ended_at TIMESTAMPTZ NOT NULL,
request_count BIGINT NOT NULL DEFAULT 0,
error_count BIGINT NOT NULL DEFAULT 0,
unique_visitor_count BIGINT NOT NULL DEFAULT 0,
status_codes_json TEXT,
top_domains_json TEXT,
source_countries_json TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_request_reports_node_id ON of_node_request_reports (node_id);
CREATE INDEX idx_of_node_request_reports_window_started_at ON of_node_request_reports (window_started_at);
CREATE INDEX idx_of_node_request_reports_window_ended_at ON of_node_request_reports (window_ended_at);
CREATE TABLE of_node_obs_openresty (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
openresty_rx_bytes BIGINT NOT NULL DEFAULT 0,
openresty_tx_bytes BIGINT NOT NULL DEFAULT 0,
openresty_connections BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_openresty_node_id ON of_node_obs_openresty (node_id);
CREATE INDEX idx_of_node_obs_openresty_captured_at ON of_node_obs_openresty (captured_at);
CREATE TABLE of_node_obs_frps (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
frps_connections INTEGER NOT NULL DEFAULT 0,
frps_proxy_count INTEGER NOT NULL DEFAULT 0,
frps_client_count INTEGER NOT NULL DEFAULT 0,
frps_proxies TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_frps_node_id ON of_node_obs_frps (node_id);
CREATE INDEX idx_of_node_obs_frps_captured_at ON of_node_obs_frps (captured_at);
CREATE TABLE of_node_obs_frpc (
id BIGSERIAL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
tunnel_status VARCHAR(16) NOT NULL DEFAULT '',
connected_relays_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_of_node_obs_frpc_node_id ON of_node_obs_frpc (node_id);
CREATE INDEX idx_of_node_obs_frpc_captured_at ON of_node_obs_frpc (captured_at);
@@ -1,7 +0,0 @@
-- +goose Up
-- Historical copy from legacy_* tables into of_* / w_* (formerly Go migration).
-- Already-applied environments keep their data; new installs have no legacy_* sources.
SELECT 1;
-- +goose Down
SELECT 1;
@@ -1,9 +0,0 @@
-- +goose Up
SELECT setval(
pg_get_serial_sequence('of_waf_rule_group_bindings', 'id'),
GREATEST(COALESCE((SELECT MAX(id) FROM of_waf_rule_group_bindings), 0), 1),
COALESCE((SELECT MAX(id) FROM of_waf_rule_group_bindings), 0) > 0
);
-- +goose Down
SELECT 1;
@@ -1,22 +0,0 @@
-- +goose Up
DELETE FROM of_options
WHERE key IN (
'GitHubOAuthEnabled',
'GitHubClientId',
'GitHubClientSecret',
'WeChatAuthEnabled',
'WeChatServerAddress',
'WeChatServerToken',
'WeChatAccountQRCodeImageURL'
);
-- +goose Down
INSERT INTO of_options (key, value) VALUES
('GitHubOAuthEnabled', 'false'),
('GitHubClientId', ''),
('GitHubClientSecret', ''),
('WeChatAuthEnabled', 'false'),
('WeChatServerAddress', ''),
('WeChatServerToken', ''),
('WeChatAccountQRCodeImageURL', '')
ON CONFLICT (key) DO NOTHING;
@@ -1,5 +0,0 @@
-- +goose Up
DELETE FROM of_options WHERE key = 'Notice';
-- +goose Down
INSERT OR IGNORE INTO of_options (key, value) VALUES ('Notice', '');
@@ -1,9 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('relay_frps_web_ui_enabled', 'false', 'business', 0, '是否启用 FRPS 内置 Web 管理界面', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('relay_frps_web_ui_port', '17500', 'business', 0, 'FRPS 内置 Web 管理界面端口', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN ('relay_frps_web_ui_enabled', 'relay_frps_web_ui_port');
@@ -1,347 +0,0 @@
-- +goose Up
-- 将 of_options 配置迁移到 w_system_configs(仅迁移新配置,已存在的不重复)
-- Agent 相关配置 (business, visibility=0)
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'agent_discovery_token', COALESCE(value, ''), 'business', 0, 'Agent 发现令牌'
FROM of_options WHERE key = 'AgentDiscoveryToken'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'agent_heartbeat_interval', value, 'business', 0, 'Agent 心跳间隔(毫秒)'
FROM of_options WHERE key = 'AgentHeartbeatInterval'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'agent_websocket_upgrade_enabled', value, 'business', 0, 'Agent WebSocket 升级开关'
FROM of_options WHERE key = 'AgentWebsocketUpgradeEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'node_offline_threshold', value, 'business', 0, '节点离线阈值(毫秒)'
FROM of_options WHERE key = 'NodeOfflineThreshold'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'agent_update_repo', value, 'business', 0, 'Agent 更新仓库'
FROM of_options WHERE key = 'AgentUpdateRepo'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
-- 系统功能配置 (business, visibility=0)
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'geoip_provider', value, 'business', 0, 'GeoIP 服务商'
FROM of_options WHERE key = 'GeoIPProvider'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'database_auto_cleanup_enabled', value, 'business', 0, '数据库自动清理开关'
FROM of_options WHERE key = 'DatabaseAutoCleanupEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'database_auto_cleanup_retention_days', value, 'business', 0, '数据库保留天数'
FROM of_options WHERE key = 'DatabaseAutoCleanupRetentionDays'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
-- UptimeKuma 集成配置 (business, visibility=0)
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_enabled', value, 'business', 0, 'UptimeKuma 集成开关'
FROM of_options WHERE key = 'UptimeKumaEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_url', COALESCE(value, ''), 'business', 0, 'UptimeKuma URL'
FROM of_options WHERE key = 'UptimeKumaUrl'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_username', COALESCE(value, ''), 'business', 0, 'UptimeKuma 用户名'
FROM of_options WHERE key = 'UptimeKumaUsername'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_password', COALESCE(value, ''), 'business', 0, 'UptimeKuma 密码'
FROM of_options WHERE key = 'UptimeKumaPassword'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_monitor_scope', value, 'business', 0, 'UptimeKuma 监控范围'
FROM of_options WHERE key = 'UptimeKumaMonitorScope'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_selected_sites', COALESCE(value, ''), 'business', 0, 'UptimeKuma 选定站点'
FROM of_options WHERE key = 'UptimeKumaSelectedSites'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_sync_interval', value, 'business', 0, 'UptimeKuma 同步间隔(分钟)'
FROM of_options WHERE key = 'UptimeKumaSyncInterval'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_interval', value, 'business', 0, 'UptimeKuma 监控间隔(秒)'
FROM of_options WHERE key = 'UptimeKumaInterval'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_retry', value, 'business', 0, 'UptimeKuma 重试次数'
FROM of_options WHERE key = 'UptimeKumaRetry'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_retry_interval', value, 'business', 0, 'UptimeKuma 重试间隔(秒)'
FROM of_options WHERE key = 'UptimeKumaRetryInterval'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'uptime_kuma_timeout', value, 'business', 0, 'UptimeKuma 超时(秒)'
FROM of_options WHERE key = 'UptimeKumaTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
-- OpenResty 配置(全部 business 类型,visibility=0)
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_default_server_return_status', value, 'business', 0, '默认服务器返回状态码'
FROM of_options WHERE key = 'OpenRestyDefaultServerReturnStatus'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_worker_processes', value, 'business', 0, 'Worker 进程数'
FROM of_options WHERE key = 'OpenRestyWorkerProcesses'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_worker_connections', value, 'business', 0, 'Worker 连接数'
FROM of_options WHERE key = 'OpenRestyWorkerConnections'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_worker_rlimit_nofile', value, 'business', 0, 'Worker 文件描述符限制'
FROM of_options WHERE key = 'OpenRestyWorkerRlimitNofile'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_events_use', value, 'business', 0, '事件模型'
FROM of_options WHERE key = 'OpenRestyEventsUse'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_events_multi_accept_enabled', value, 'business', 0, '多路接受开关'
FROM of_options WHERE key = 'OpenRestyEventsMultiAcceptEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_keepalive_timeout', value, 'business', 0, 'Keepalive 超时(秒)'
FROM of_options WHERE key = 'OpenRestyKeepaliveTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_keepalive_requests', value, 'business', 0, 'Keepalive 请求数'
FROM of_options WHERE key = 'OpenRestyKeepaliveRequests'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_client_header_timeout', value, 'business', 0, '客户端头超时(秒)'
FROM of_options WHERE key = 'OpenRestyClientHeaderTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_client_body_timeout', value, 'business', 0, '客户端体超时(秒)'
FROM of_options WHERE key = 'OpenRestyClientBodyTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_client_max_body_size', value, 'business', 0, '客户端最大体大小'
FROM of_options WHERE key = 'OpenRestyClientMaxBodySize'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_large_client_header_buffers', value, 'business', 0, '大客户端头缓冲区'
FROM of_options WHERE key = 'OpenRestyLargeClientHeaderBuffers'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_send_timeout', value, 'business', 0, '发送超时(秒)'
FROM of_options WHERE key = 'OpenRestySendTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_resolvers', COALESCE(value, ''), 'business', 0, 'DNS 解析器'
FROM of_options WHERE key = 'OpenRestyResolvers'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_connect_timeout', value, 'business', 0, '代理连接超时(秒)'
FROM of_options WHERE key = 'OpenRestyProxyConnectTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_send_timeout', value, 'business', 0, '代理发送超时(秒)'
FROM of_options WHERE key = 'OpenRestyProxySendTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_read_timeout', value, 'business', 0, '代理读取超时(秒)'
FROM of_options WHERE key = 'OpenRestyProxyReadTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_websocket_enabled', value, 'business', 0, 'WebSocket 支持开关'
FROM of_options WHERE key = 'OpenRestyWebsocketEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_http3_enabled', value, 'business', 0, 'HTTP/3 支持开关'
FROM of_options WHERE key = 'OpenRestyHTTP3Enabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_request_buffering_enabled', value, 'business', 0, '代理请求缓冲开关'
FROM of_options WHERE key = 'OpenRestyProxyRequestBufferingEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_buffering_enabled', value, 'business', 0, '代理响应缓冲开关'
FROM of_options WHERE key = 'OpenRestyProxyBufferingEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_buffers', value, 'business', 0, '代理缓冲区'
FROM of_options WHERE key = 'OpenRestyProxyBuffers'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_buffer_size', value, 'business', 0, '代理缓冲区大小'
FROM of_options WHERE key = 'OpenRestyProxyBufferSize'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_proxy_busy_buffers_size', value, 'business', 0, '代理繁忙缓冲区大小'
FROM of_options WHERE key = 'OpenRestyProxyBusyBuffersSize'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_gzip_enabled', value, 'business', 0, 'Gzip 压缩开关'
FROM of_options WHERE key = 'OpenRestyGzipEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_gzip_min_length', value, 'business', 0, 'Gzip 最小长度'
FROM of_options WHERE key = 'OpenRestyGzipMinLength'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_gzip_comp_level', value, 'business', 0, 'Gzip 压缩级别'
FROM of_options WHERE key = 'OpenRestyGzipCompLevel'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_enabled', value, 'business', 0, '缓存开关'
FROM of_options WHERE key = 'OpenRestyCacheEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_path', COALESCE(value, ''), 'business', 0, '缓存路径'
FROM of_options WHERE key = 'OpenRestyCachePath'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_levels', value, 'business', 0, '缓存层级'
FROM of_options WHERE key = 'OpenRestyCacheLevels'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_inactive', value, 'business', 0, '缓存不活跃时间'
FROM of_options WHERE key = 'OpenRestyCacheInactive'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_max_size', value, 'business', 0, '缓存最大大小'
FROM of_options WHERE key = 'OpenRestyCacheMaxSize'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_key_template', value, 'business', 0, '缓存键模板'
FROM of_options WHERE key = 'OpenRestyCacheKeyTemplate'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_lock_enabled', value, 'business', 0, '缓存锁开关'
FROM of_options WHERE key = 'OpenRestyCacheLockEnabled'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_lock_timeout', value, 'business', 0, '缓存锁超时'
FROM of_options WHERE key = 'OpenRestyCacheLockTimeout'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_cache_use_stale', value, 'business', 0, '缓存失效策略'
FROM of_options WHERE key = 'OpenRestyCacheUseStale'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
INSERT INTO w_system_configs (key, value, type, visibility, description)
SELECT 'openresty_main_config_template', value, 'business', 0, '主配置模板'
FROM of_options WHERE key = 'OpenRestyMainConfigTemplate'
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
-- +goose Down
-- 回滚时删除所有迁移的配置项
DELETE FROM w_system_configs WHERE key IN (
'agent_discovery_token',
'agent_heartbeat_interval',
'agent_websocket_upgrade_enabled',
'node_offline_threshold',
'agent_update_repo',
'geoip_provider',
'database_auto_cleanup_enabled',
'database_auto_cleanup_retention_days',
'uptime_kuma_enabled',
'uptime_kuma_url',
'uptime_kuma_username',
'uptime_kuma_password',
'uptime_kuma_monitor_scope',
'uptime_kuma_selected_sites',
'uptime_kuma_sync_interval',
'uptime_kuma_interval',
'uptime_kuma_retry',
'uptime_kuma_retry_interval',
'uptime_kuma_timeout',
'openresty_default_server_return_status',
'openresty_worker_processes',
'openresty_worker_connections',
'openresty_worker_rlimit_nofile',
'openresty_events_use',
'openresty_events_multi_accept_enabled',
'openresty_keepalive_timeout',
'openresty_keepalive_requests',
'openresty_client_header_timeout',
'openresty_client_body_timeout',
'openresty_client_max_body_size',
'openresty_large_client_header_buffers',
'openresty_send_timeout',
'openresty_resolvers',
'openresty_proxy_connect_timeout',
'openresty_proxy_send_timeout',
'openresty_proxy_read_timeout',
'openresty_websocket_enabled',
'openresty_http3_enabled',
'openresty_proxy_request_buffering_enabled',
'openresty_proxy_buffering_enabled',
'openresty_proxy_buffers',
'openresty_proxy_buffer_size',
'openresty_proxy_busy_buffers_size',
'openresty_gzip_enabled',
'openresty_gzip_min_length',
'openresty_gzip_comp_level',
'openresty_cache_enabled',
'openresty_cache_path',
'openresty_cache_levels',
'openresty_cache_inactive',
'openresty_cache_max_size',
'openresty_cache_key_template',
'openresty_cache_lock_enabled',
'openresty_cache_lock_timeout',
'openresty_cache_use_stale',
'openresty_main_config_template'
);
@@ -1,10 +0,0 @@
-- +goose Up
-- of_options 配置已于 202606220004 迁移至 w_system_configs,删除遗留表。
DROP TABLE IF EXISTS of_options;
-- +goose Down
-- 回滚时重建空表结构(数据无法恢复,迁移来源已为 w_system_configs)。
CREATE TABLE IF NOT EXISTS of_options (
key VARCHAR(128) PRIMARY KEY,
value TEXT NOT NULL DEFAULT ''
);
@@ -1,19 +0,0 @@
-- +goose Up
-- Remove old PRIMARY KEY on id, set version as PRIMARY KEY, drop id column
-- 1. 移除旧主键约束
ALTER TABLE of_config_versions DROP CONSTRAINT IF EXISTS of_config_versions_pkey;
-- 2. 移除原先在 version 字段的唯一索引(因为 version 变成主键,主键隐含唯一性)
DROP INDEX IF EXISTS idx_of_config_versions_version;
-- 3. 将 version 设为新主键
ALTER TABLE of_config_versions ADD PRIMARY KEY (version);
-- 4. 彻底删除 id 列
ALTER TABLE of_config_versions DROP COLUMN IF EXISTS id;
-- +goose Down
ALTER TABLE of_config_versions DROP CONSTRAINT IF EXISTS of_config_versions_pkey;
ALTER TABLE of_config_versions ADD COLUMN IF NOT EXISTS id BIGSERIAL PRIMARY KEY;
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_config_versions_version ON of_config_versions (version);
@@ -1,20 +0,0 @@
-- +goose Up
SELECT setval(pg_get_serial_sequence('w_users', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM w_users), 0), 1), COALESCE((SELECT MAX(id) FROM w_users), 0) > 0);
SELECT setval(pg_get_serial_sequence('w_auth_sources', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM w_auth_sources), 0), 1), COALESCE((SELECT MAX(id) FROM w_auth_sources), 0) > 0);
SELECT setval(pg_get_serial_sequence('w_external_accounts', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM w_external_accounts), 0), 1), COALESCE((SELECT MAX(id) FROM w_external_accounts), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_origins', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_origins), 0), 1), COALESCE((SELECT MAX(id) FROM of_origins), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_apply_logs', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_apply_logs), 0), 1), COALESCE((SELECT MAX(id) FROM of_apply_logs), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_proxy_routes', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_proxy_routes), 0), 1), COALESCE((SELECT MAX(id) FROM of_proxy_routes), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_nodes', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_nodes), 0), 1), COALESCE((SELECT MAX(id) FROM of_nodes), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_waf_rule_groups', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_waf_rule_groups), 0), 1), COALESCE((SELECT MAX(id) FROM of_waf_rule_groups), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_waf_ip_groups', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_waf_ip_groups), 0), 1), COALESCE((SELECT MAX(id) FROM of_waf_ip_groups), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_tls_certificates', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_tls_certificates), 0), 1), COALESCE((SELECT MAX(id) FROM of_tls_certificates), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_managed_domains', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_managed_domains), 0), 1), COALESCE((SELECT MAX(id) FROM of_managed_domains), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_dns_accounts', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_dns_accounts), 0), 1), COALESCE((SELECT MAX(id) FROM of_dns_accounts), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_acme_accounts', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_acme_accounts), 0), 1), COALESCE((SELECT MAX(id) FROM of_acme_accounts), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_pages_projects', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_pages_projects), 0), 1), COALESCE((SELECT MAX(id) FROM of_pages_projects), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_pages_deployments', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_pages_deployments), 0), 1), COALESCE((SELECT MAX(id) FROM of_pages_deployments), 0) > 0);
SELECT setval(pg_get_serial_sequence('of_pages_deployment_files', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM of_pages_deployment_files), 0), 1), COALESCE((SELECT MAX(id) FROM of_pages_deployment_files), 0) > 0);
-- +goose Down
SELECT 1;
@@ -1,28 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_zones (
id BIGSERIAL PRIMARY KEY,
domain VARCHAR(255) NOT NULL,
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_zones_domain ON of_zones (domain);
CREATE TABLE IF NOT EXISTS of_zone_domains (
id BIGSERIAL PRIMARY KEY,
zone_id BIGINT NOT NULL,
proxy_route_id BIGINT,
domain VARCHAR(255) NOT NULL,
cert_id BIGINT,
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_zone_domains_domain ON of_zone_domains (domain);
CREATE INDEX IF NOT EXISTS idx_of_zone_domains_zone_id ON of_zone_domains (zone_id);
CREATE INDEX IF NOT EXISTS idx_of_zone_domains_proxy_route_id ON of_zone_domains (proxy_route_id);
CREATE INDEX IF NOT EXISTS idx_of_zone_domains_cert_id ON of_zone_domains (cert_id);
-- +goose Down
DROP TABLE IF EXISTS of_zone_domains;
DROP TABLE IF EXISTS of_zones;
@@ -1,8 +0,0 @@
-- +goose Up
-- Marker: Zone 域名从旧路由列 / of_managed_domains 的导入由 migrator.Migrate()
-- 在全部 goose SQL 应用后自动执行(publicsuffix 根域解析无法用纯 SQL 正确完成)。
-- 本文件仅占位版本号,保证升级链路有序:建表 → 导入 → 删旧列。
SELECT 1;
-- +goose Down
SELECT 1;
@@ -1,35 +0,0 @@
-- +goose Up
-- 第二阶段:删除反代路由冗余域名/证书列与托管域名表。
-- 执行前必须完成 migrate-zones 且配置预览验收通过。
DROP INDEX IF EXISTS idx_of_proxy_routes_domain;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS domain;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS domains;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS cert_id;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS cert_ids;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS domain_cert_ids;
DROP TABLE IF EXISTS of_managed_domains;
-- +goose Down
-- 仅恢复开发库结构,不回填历史域名/证书数据。
CREATE TABLE IF NOT EXISTS of_managed_domains (
id BIGSERIAL PRIMARY KEY,
domain VARCHAR(255) NOT NULL,
cert_id BIGINT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
remark VARCHAR(255) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_managed_domains_domain ON of_managed_domains (domain);
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS domain VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS domains TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS cert_id BIGINT;
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS cert_ids TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS domain_cert_ids TEXT NOT NULL DEFAULT '[]';
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_proxy_routes_domain ON of_proxy_routes (domain);
@@ -1,16 +0,0 @@
-- +goose Up
-- 移除业务实体上未再使用的备注列(证书与源站备注保留)。
ALTER TABLE of_zones DROP COLUMN IF EXISTS remark;
ALTER TABLE of_zone_domains DROP COLUMN IF EXISTS remark;
ALTER TABLE of_proxy_routes DROP COLUMN IF EXISTS remark;
ALTER TABLE of_waf_rule_groups DROP COLUMN IF EXISTS remark;
ALTER TABLE of_waf_ip_groups DROP COLUMN IF EXISTS remark;
-- +goose Down
ALTER TABLE of_zones ADD COLUMN IF NOT EXISTS remark VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE of_zone_domains ADD COLUMN IF NOT EXISTS remark VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE of_proxy_routes ADD COLUMN IF NOT EXISTS remark VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE of_waf_rule_groups ADD COLUMN IF NOT EXISTS remark VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE of_waf_ip_groups ADD COLUMN IF NOT EXISTS remark VARCHAR(255) NOT NULL DEFAULT '';
@@ -1,11 +0,0 @@
-- +goose Up
ALTER TABLE of_waf_rule_groups
ADD COLUMN graph TEXT NOT NULL DEFAULT '',
ADD COLUMN revision BIGINT NOT NULL DEFAULT 1;
ALTER TABLE of_waf_rule_group_bindings
ADD COLUMN sequence INTEGER NOT NULL DEFAULT 0;
-- +goose Down
ALTER TABLE of_waf_rule_group_bindings DROP COLUMN sequence;
ALTER TABLE of_waf_rule_groups DROP COLUMN revision, DROP COLUMN graph;
@@ -1,17 +0,0 @@
-- +goose Up
UPDATE of_waf_rule_groups
SET graph = '{"schema_version":1,"nodes":[{"id":"start","type":"start","position":{"x":0,"y":0},"config":{}},{"id":"allow","type":"allow","position":{"x":320,"y":0},"config":{}}],"edges":[{"id":"start-allow","source":"start","source_handle":"next","target":"allow"}]}',
revision = 1;
WITH ordered AS (
SELECT id, ROW_NUMBER() OVER (PARTITION BY proxy_route_id ORDER BY id) - 1 AS new_sequence
FROM of_waf_rule_group_bindings
)
UPDATE of_waf_rule_group_bindings AS binding
SET sequence = ordered.new_sequence
FROM ordered
WHERE binding.id = ordered.id;
-- +goose Down
UPDATE of_waf_rule_group_bindings SET sequence = 0;
UPDATE of_waf_rule_groups SET revision = 1;
@@ -1,27 +0,0 @@
-- +goose Up
ALTER TABLE of_waf_rule_groups DROP COLUMN block_status_code;
ALTER TABLE of_waf_rule_groups DROP COLUMN block_response_body;
ALTER TABLE of_waf_rule_groups DROP COLUMN ip_whitelist;
ALTER TABLE of_waf_rule_groups DROP COLUMN ip_blacklist;
ALTER TABLE of_waf_rule_groups DROP COLUMN ip_whitelist_groups;
ALTER TABLE of_waf_rule_groups DROP COLUMN ip_blacklist_groups;
ALTER TABLE of_waf_rule_groups DROP COLUMN country_whitelist;
ALTER TABLE of_waf_rule_groups DROP COLUMN country_blacklist;
ALTER TABLE of_waf_rule_groups DROP COLUMN region_whitelist;
ALTER TABLE of_waf_rule_groups DROP COLUMN region_blacklist;
ALTER TABLE of_waf_rule_groups DROP COLUMN pow_enabled;
ALTER TABLE of_waf_rule_groups DROP COLUMN pow_config;
-- +goose Down
ALTER TABLE of_waf_rule_groups ADD COLUMN block_status_code INTEGER NOT NULL DEFAULT 418;
ALTER TABLE of_waf_rule_groups ADD COLUMN block_response_body TEXT NOT NULL DEFAULT '';
ALTER TABLE of_waf_rule_groups ADD COLUMN ip_whitelist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN ip_blacklist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN ip_whitelist_groups TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN ip_blacklist_groups TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN country_whitelist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN country_blacklist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN region_whitelist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN region_blacklist TEXT NOT NULL DEFAULT '[]';
ALTER TABLE of_waf_rule_groups ADD COLUMN pow_enabled BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE of_waf_rule_groups ADD COLUMN pow_config TEXT NOT NULL DEFAULT '{}';
@@ -1,9 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('pages_max_package_size_mb', '100', 'business', 0, 'Pages 部署包上传大小上限(MiB)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('pages_max_history_count', '20', 'business', 0, 'Pages 每个项目最大历史部署保留数(0 表示不限制)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN ('pages_max_package_size_mb', 'pages_max_history_count');
@@ -1,27 +0,0 @@
-- +goose Up
-- 将仍为历史默认值的心跳/离线配置升级为新默认:心跳 3s、离线 60s。
-- 已由管理员改成其他值的配置不受影响。
UPDATE w_system_configs
SET value = '3000',
updated_at = CURRENT_TIMESTAMP
WHERE key = 'agent_heartbeat_interval'
AND value = '10000';
UPDATE w_system_configs
SET value = '60000',
updated_at = CURRENT_TIMESTAMP
WHERE key = 'node_offline_threshold'
AND value = '120000';
-- +goose Down
UPDATE w_system_configs
SET value = '10000',
updated_at = CURRENT_TIMESTAMP
WHERE key = 'agent_heartbeat_interval'
AND value = '3000';
UPDATE w_system_configs
SET value = '120000',
updated_at = CURRENT_TIMESTAMP
WHERE key = 'node_offline_threshold'
AND value = '60000';
@@ -1,14 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('openresty_default_limit_conn_per_server', '0', 'business', 0, '默认站点并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('openresty_default_limit_conn_per_ip', '0', 'business', 0, '默认单 IP 并发连接上限(0 关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('openresty_default_limit_rate', '', 'business', 0, '默认单请求带宽限速(空关闭)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN (
'openresty_default_limit_conn_per_server',
'openresty_default_limit_conn_per_ip',
'openresty_default_limit_rate'
);
@@ -1,82 +0,0 @@
-- +goose Up
ALTER TABLE of_pages_projects
ADD COLUMN IF NOT EXISTS content_config_version INTEGER NOT NULL DEFAULT 0;
ALTER TABLE of_pages_deployments
ADD COLUMN IF NOT EXISTS source_type VARCHAR(32) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS source_identity CHAR(64),
ADD COLUMN IF NOT EXISTS source_revision CHAR(64),
ADD COLUMN IF NOT EXISTS source_label VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS source_meta TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS trigger_type VARCHAR(32) NOT NULL DEFAULT '';
UPDATE of_pages_deployments
SET source_type = 'manual_upload',
trigger_type = 'manual_upload';
CREATE TABLE IF NOT EXISTS of_pages_project_sources (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
source_type VARCHAR(32) NOT NULL DEFAULT '',
remote_url TEXT NOT NULL DEFAULT '',
allow_insecure BOOLEAN NOT NULL DEFAULT FALSE,
github_repository VARCHAR(255) NOT NULL DEFAULT '',
release_selector VARCHAR(16) NOT NULL DEFAULT '',
release_tag VARCHAR(255) NOT NULL DEFAULT '',
asset_name VARCHAR(255) NOT NULL DEFAULT '',
auto_update_enabled BOOLEAN NOT NULL DEFAULT FALSE,
check_interval_minutes INTEGER NOT NULL DEFAULT 0,
config_version INTEGER NOT NULL DEFAULT 0,
source_identity CHAR(64) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_pages_project_sources_project_id
ON of_pages_project_sources (project_id);
CREATE TABLE IF NOT EXISTS of_pages_project_source_runtime (
source_id BIGINT PRIMARY KEY,
etag VARCHAR(512) NOT NULL DEFAULT '',
last_seen_revision CHAR(64) NOT NULL DEFAULT '',
last_seen_detail TEXT NOT NULL DEFAULT '',
last_applied_revision CHAR(64) NOT NULL DEFAULT '',
last_applied_detail TEXT NOT NULL DEFAULT '',
sync_status VARCHAR(32) NOT NULL DEFAULT '',
last_error TEXT NOT NULL DEFAULT '',
last_checked_at TIMESTAMPTZ,
last_synced_at TIMESTAMPTZ,
next_check_at TIMESTAMPTZ,
lease_expires_at TIMESTAMPTZ,
lease_token VARCHAR(64) NOT NULL DEFAULT '',
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_pages_project_source_runtime_next_check_at
ON of_pages_project_source_runtime (next_check_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_pages_deployments_project_number
ON of_pages_deployments (project_id, deployment_number);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_pages_deployments_source_revision
ON of_pages_deployments (project_id, source_identity, source_revision)
WHERE source_identity IS NOT NULL AND source_revision IS NOT NULL;
-- +goose Down
DROP INDEX IF EXISTS idx_of_pages_deployments_source_revision;
DROP INDEX IF EXISTS idx_of_pages_deployments_project_number;
DROP INDEX IF EXISTS idx_of_pages_project_source_runtime_next_check_at;
DROP TABLE IF EXISTS of_pages_project_source_runtime;
DROP INDEX IF EXISTS idx_of_pages_project_sources_project_id;
DROP TABLE IF EXISTS of_pages_project_sources;
ALTER TABLE of_pages_deployments
DROP COLUMN IF EXISTS trigger_type,
DROP COLUMN IF EXISTS source_meta,
DROP COLUMN IF EXISTS source_label,
DROP COLUMN IF EXISTS source_revision,
DROP COLUMN IF EXISTS source_identity,
DROP COLUMN IF EXISTS source_type;
ALTER TABLE of_pages_projects
DROP COLUMN IF EXISTS content_config_version;
@@ -1,38 +0,0 @@
-- +goose Up
-- Earlier built-in schedules used explicit IDs, so advance the identity only
-- when it trails either existing rows or an already-higher sequence value.
SELECT setval(
pg_get_serial_sequence('w_schedules', 'id'),
GREATEST(
1,
COALESCE((SELECT MAX(id) FROM w_schedules), 0),
COALESCE((
SELECT sequences.last_value
FROM pg_sequences AS sequences
WHERE format('%I.%I', sequences.schemaname, sequences.sequencename)::regclass =
pg_get_serial_sequence('w_schedules', 'id')::regclass
), 0)
),
TRUE
);
INSERT INTO w_schedules (name, task_type, cron, payload, is_active, created_at, updated_at)
SELECT
'OpenFlare Pages 部署源扫描',
'of_pages_source_scan',
'0 0 * * *',
'{}',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
WHERE NOT EXISTS (
SELECT 1 FROM w_schedules WHERE task_type = 'of_pages_source_scan'
);
-- +goose Down
DELETE FROM w_schedules
WHERE task_type = 'of_pages_source_scan'
AND name = 'OpenFlare Pages 部署源扫描'
AND cron = '0 0 * * *'
AND payload = '{}'
AND is_active = TRUE;
@@ -1,12 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('openresty_default_limit_req_per_ip', '', 'business', 0, '默认单 IP 请求频率限制(空关闭,例如 10r/s、100r/m)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
ALTER TABLE of_proxy_routes ADD COLUMN limit_req_per_ip VARCHAR(32) NOT NULL DEFAULT '';
-- +goose Down
ALTER TABLE of_proxy_routes DROP COLUMN limit_req_per_ip;
DELETE FROM w_system_configs WHERE key = 'openresty_default_limit_req_per_ip';
@@ -1,49 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS of_cf_connections (
id BIGSERIAL PRIMARY KEY,
source VARCHAR(32) NOT NULL DEFAULT '',
dns_account_id BIGINT,
"authorization" TEXT NOT NULL DEFAULT '',
status VARCHAR(16) NOT NULL DEFAULT '',
verified_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_cf_connections_dns_account_id ON of_cf_connections (dns_account_id);
CREATE TABLE IF NOT EXISTS of_cf_pointing_groups (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
primary_node_id BIGINT NOT NULL,
backup_node_id BIGINT,
active_node_id BIGINT NOT NULL,
default_proxied BOOLEAN NOT NULL DEFAULT FALSE,
enabled BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_cf_pointing_groups_primary_node_id ON of_cf_pointing_groups (primary_node_id);
CREATE INDEX IF NOT EXISTS idx_of_cf_pointing_groups_backup_node_id ON of_cf_pointing_groups (backup_node_id);
CREATE INDEX IF NOT EXISTS idx_of_cf_pointing_groups_active_node_id ON of_cf_pointing_groups (active_node_id);
CREATE TABLE IF NOT EXISTS of_cf_pointing_members (
id BIGSERIAL PRIMARY KEY,
group_id BIGINT NOT NULL,
zone_domain_id BIGINT NOT NULL,
proxied BOOLEAN NOT NULL DEFAULT FALSE,
cf_zone_id VARCHAR(64) NOT NULL DEFAULT '',
cf_record_id VARCHAR(64) NOT NULL DEFAULT '',
desired_ip VARCHAR(64) NOT NULL DEFAULT '',
sync_status VARCHAR(16) NOT NULL DEFAULT 'pending',
last_error TEXT NOT NULL DEFAULT '',
synced_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_cf_pointing_members_group_id ON of_cf_pointing_members (group_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_cf_pointing_members_zone_domain_id ON of_cf_pointing_members (zone_domain_id);
-- +goose Down
DROP TABLE IF EXISTS of_cf_pointing_members;
DROP TABLE IF EXISTS of_cf_pointing_groups;
DROP TABLE IF EXISTS of_cf_connections;
@@ -1,14 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('origin_error_page_enabled', 'true', 'business', 0, '是否启用源站错误页', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('origin_error_page_status_codes', '["500-599"]', 'business', 0, '源站错误页触发状态码标签 JSON 数组', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('origin_error_page_html', '', 'business', 0, '源站错误页自定义 HTML,空则使用内置默认', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN (
'origin_error_page_enabled',
'origin_error_page_status_codes',
'origin_error_page_html'
);
@@ -1,8 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('origin_error_page_get_only', 'false', 'business', 0, '源站错误页是否仅对 GET 请求生效(其它方法透传)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'origin_error_page_get_only';
@@ -1,12 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('sw_offline_enabled', 'false', 'business', 0, '是否启用 Service Worker 离线兜底', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('sw_offline_html', '', 'business', 0, '离线联系页自定义 HTML,空则使用内置默认', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key IN (
'sw_offline_enabled',
'sw_offline_html'
);
@@ -1,7 +0,0 @@
-- +goose Up
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES ('sw_offline_domains', '[]', 'business', 0, 'SW 离线兜底生效域名列表(JSON 数组,空则仅总开关无效)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'sw_offline_domains';
@@ -1,115 +0,0 @@
-- +goose Up
-- 节点访问日志:按月 RANGE 分区,复合主键 (id, logged_at) 满足分区键进唯一索引要求。
CREATE TABLE IF NOT EXISTS of_node_access_logs (
id BIGINT NOT NULL,
node_id VARCHAR(64) NOT NULL DEFAULT '',
logged_at TIMESTAMPTZ NOT NULL,
remote_addr VARCHAR(128) NOT NULL DEFAULT '',
region VARCHAR(128) NOT NULL DEFAULT '',
host VARCHAR(255) NOT NULL DEFAULT '',
path VARCHAR(2048) NOT NULL DEFAULT '',
user_agent TEXT NOT NULL DEFAULT '',
cache_status VARCHAR(64) NOT NULL DEFAULT '',
status_code INTEGER NOT NULL DEFAULT 0,
bytes_sent BIGINT NOT NULL DEFAULT 0,
request_length BIGINT NOT NULL DEFAULT 0,
request_time_ms INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, logged_at)
) PARTITION BY RANGE (logged_at);
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_node_id ON of_node_access_logs (node_id, logged_at DESC);
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_host ON of_node_access_logs (host, logged_at DESC);
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_remote_addr ON of_node_access_logs (remote_addr, logged_at DESC);
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_status_code ON of_node_access_logs (status_code, logged_at DESC);
-- 用户访问日志:按月分区。
CREATE TABLE IF NOT EXISTS w_user_access_logs (
id BIGINT NOT NULL,
user_id BIGINT NOT NULL DEFAULT 0,
path VARCHAR(2048) NOT NULL DEFAULT '',
method VARCHAR(16) NOT NULL DEFAULT '',
ip VARCHAR(128) NOT NULL DEFAULT '',
user_agent TEXT NOT NULL DEFAULT '',
headers TEXT NOT NULL DEFAULT '',
status INTEGER NOT NULL DEFAULT 0,
latency BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
CREATE INDEX IF NOT EXISTS idx_w_user_access_logs_user_id ON w_user_access_logs (user_id, created_at DESC);
-- 可观测 4 表:普通表 + (node_id, captured_at DESC) 索引。
CREATE TABLE IF NOT EXISTS of_node_metric_snapshots (
id BIGINT NOT NULL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL DEFAULT '',
captured_at TIMESTAMPTZ NOT NULL,
cpu_usage_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
memory_used_bytes BIGINT NOT NULL DEFAULT 0,
memory_total_bytes BIGINT NOT NULL DEFAULT 0,
storage_used_bytes BIGINT NOT NULL DEFAULT 0,
storage_total_bytes BIGINT NOT NULL DEFAULT 0,
disk_read_bytes BIGINT NOT NULL DEFAULT 0,
disk_write_bytes BIGINT NOT NULL DEFAULT 0,
network_rx_bytes BIGINT NOT NULL DEFAULT 0,
network_tx_bytes BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_node_metric_snapshots_node ON of_node_metric_snapshots (node_id, captured_at DESC);
CREATE TABLE IF NOT EXISTS of_node_edge_health (
id BIGINT NOT NULL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL DEFAULT '',
captured_at TIMESTAMPTZ NOT NULL,
status VARCHAR(64) NOT NULL DEFAULT '',
connections BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_node_edge_health_node ON of_node_edge_health (node_id, captured_at DESC);
CREATE TABLE IF NOT EXISTS of_node_obs_frps (
id BIGINT NOT NULL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL DEFAULT '',
captured_at TIMESTAMPTZ NOT NULL,
frps_connections INTEGER NOT NULL DEFAULT 0,
frps_proxy_count INTEGER NOT NULL DEFAULT 0,
frps_client_count INTEGER NOT NULL DEFAULT 0,
frps_proxies TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frps_node ON of_node_obs_frps (node_id, captured_at DESC);
CREATE TABLE IF NOT EXISTS of_node_obs_frpc (
id BIGINT NOT NULL PRIMARY KEY,
node_id VARCHAR(64) NOT NULL DEFAULT '',
captured_at TIMESTAMPTZ NOT NULL,
tunnel_status VARCHAR(16) NOT NULL DEFAULT '',
connected_relays_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frpc_node ON of_node_obs_frpc (node_id, captured_at DESC);
-- 分区预建:创建当月及未来 2 个月分区(共 3 个月)。
-- +goose StatementBegin
DO $$
DECLARE
d date;
BEGIN
FOR d IN SELECT generate_series(date_trunc('month', now())::date, (date_trunc('month', now()) + interval '2 months')::date, interval '1 month')::date
LOOP
EXECUTE format('CREATE TABLE IF NOT EXISTS of_node_access_logs_%s PARTITION OF of_node_access_logs FOR VALUES FROM (%L) TO (%L)',
to_char(d, 'YYYYMM'), d, d + interval '1 month');
EXECUTE format('CREATE TABLE IF NOT EXISTS w_user_access_logs_%s PARTITION OF w_user_access_logs FOR VALUES FROM (%L) TO (%L)',
to_char(d, 'YYYYMM'), d, d + interval '1 month');
END LOOP;
END $$;
-- +goose StatementEnd
-- +goose Down
DROP TABLE IF EXISTS w_user_access_logs;
DROP TABLE IF EXISTS of_node_access_logs;
DROP TABLE IF EXISTS of_node_metric_snapshots;
DROP TABLE IF EXISTS of_node_edge_health;
DROP TABLE IF EXISTS of_node_obs_frps;
DROP TABLE IF EXISTS of_node_obs_frpc;
@@ -1,23 +0,0 @@
-- +goose Up
-- 日志保留天数配置(business),替换旧的 database_auto_cleanup_* 键。
-- 默认统一 30 天:不继承旧键值,避免旧配置被静默带入导致日志被过度清理。
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
SELECT k, '30', 'business', 0, descr, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
FROM (VALUES
('log_retention_days_postgres', 'PostgreSQL 日志保留天数(访问日志与可观测统一)'),
('log_retention_days_sqlite', 'SQLite 日志保留天数'),
('log_retention_days_clickhouse', 'ClickHouse 日志保留天数')
) AS v(k, descr)
ON CONFLICT (key) DO NOTHING;
DELETE FROM w_system_configs WHERE key IN ('database_auto_cleanup_enabled', 'database_auto_cleanup_retention_days');
-- +goose Down
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES
('database_auto_cleanup_enabled', 'true', 'business', 0, '数据库自动清理开关', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('database_auto_cleanup_retention_days', COALESCE((SELECT value FROM w_system_configs WHERE key = 'log_retention_days_postgres'), '30'), 'business', 0, '数据库保留天数', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
DELETE FROM w_system_configs WHERE key IN ('log_retention_days_postgres', 'log_retention_days_sqlite', 'log_retention_days_clickhouse');
@@ -1,19 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS w_schedules_backup_of_database_auto_cleanup AS
SELECT * FROM w_schedules WHERE task_type = 'of_database_auto_cleanup';
DELETE FROM w_schedules WHERE task_type = 'of_database_auto_cleanup';
-- +goose Down
INSERT INTO w_schedules (id, name, task_type, cron, payload, is_active, created_at, updated_at)
SELECT id, name, task_type, cron, payload, is_active, created_at, updated_at
FROM w_schedules_backup_of_database_auto_cleanup
ON CONFLICT (id) DO NOTHING;
INSERT INTO w_schedules (id, name, task_type, cron, payload, is_active, created_at, updated_at)
SELECT 102, 'OpenFlare 可观测数据自动清理', 'of_database_auto_cleanup', '0 3 * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
WHERE NOT EXISTS (SELECT 1 FROM w_schedules WHERE task_type = 'of_database_auto_cleanup')
ON CONFLICT (id) DO NOTHING;
DROP TABLE IF EXISTS w_schedules_backup_of_database_auto_cleanup;
@@ -1,9 +0,0 @@
-- +goose Up
-- 性能指标(CPU/内存/磁盘/网络)保留天数:三库共用、独立短留存(默认 3 天),
-- 不随 log_retention_days_*(访问日志保留配置)变化。
INSERT INTO w_system_configs (key, value, type, visibility, description, created_at, updated_at)
VALUES ('metric_retention_days', '3', 'business', 0, '性能指标(CPU/内存/磁盘/网络)保留天数(三库共用)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DELETE FROM w_system_configs WHERE key = 'metric_retention_days';
@@ -1,13 +0,0 @@
-- +goose Up
-- 系统定期垃圾清理改为每日执行一次(凌晨 3 点,Asia/Shanghai)。
-- 此前为每 2 小时(0 */2 * * *)高频扫描,日常清理收益有限,降频减少非必要扫描。
UPDATE w_schedules
SET cron = '0 3 * * *',
updated_at = CURRENT_TIMESTAMP
WHERE task_type = 'system_cleanup';
-- +goose Down
UPDATE w_schedules
SET cron = '0 */2 * * *',
updated_at = CURRENT_TIMESTAMP
WHERE task_type = 'system_cleanup';
@@ -1,9 +0,0 @@
-- +goose Up
-- 日志列表默认排序与 retention 清理的时间范围条件:补 logged_at 前导索引。
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_logged_at ON of_node_access_logs (logged_at DESC, id DESC);
-- hosts 过滤为 lower(trim(host)) IN:补表达式索引,普通 (host,...) 索引无法命中函数表达式。
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_host_lower ON of_node_access_logs (lower(trim(host)));
-- +goose Down
DROP INDEX IF EXISTS idx_of_node_access_logs_host_lower;
DROP INDEX IF EXISTS idx_of_node_access_logs_logged_at;
@@ -1,7 +0,0 @@
-- +goose Up
-- Historical Wavelet→OpenFlare table rename bridge (formerly Go migration).
-- Fresh installs and current of_* schemas need no action.
SELECT 1;
-- +goose Down
SELECT 1;
@@ -1,184 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY,
username VARCHAR(64) UNIQUE,
password VARCHAR(255),
nickname VARCHAR(255),
email VARCHAR(255),
avatar_url VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
is_admin BOOLEAN DEFAULT FALSE,
bio VARCHAR(500),
phone VARCHAR(32),
gender VARCHAR(16),
website VARCHAR(255),
location VARCHAR(255),
last_login_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users (is_active);
CREATE INDEX IF NOT EXISTS idx_users_last_login_at ON users (last_login_at);
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users (created_at);
CREATE TABLE IF NOT EXISTS auth_sources (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(80) NOT NULL UNIQUE,
type VARCHAR(20) NOT NULL,
display_name VARCHAR(100),
is_active BOOLEAN NOT NULL DEFAULT FALSE,
client_id VARCHAR(255),
client_secret VARCHAR(1024),
openid_discovery_url VARCHAR(1024),
scopes VARCHAR(255),
icon_url VARCHAR(1024),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_auth_sources_is_active ON auth_sources (is_active);
CREATE TABLE IF NOT EXISTS external_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
auth_source_id BIGINT,
user_id BIGINT NOT NULL,
external_id VARCHAR(255) NOT NULL,
external_username VARCHAR(255),
email VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_external_accounts_auth_source_id ON external_accounts (auth_source_id);
CREATE INDEX IF NOT EXISTS idx_external_accounts_user_id ON external_accounts (user_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_external_accounts_source_external ON external_accounts (auth_source_id, external_id);
CREATE TABLE IF NOT EXISTS system_configs (
key VARCHAR(64) PRIMARY KEY,
value TEXT NOT NULL,
type VARCHAR(32) NOT NULL DEFAULT 'system',
visibility INTEGER NOT NULL DEFAULT 0,
description VARCHAR(255),
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS uploads (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
extension VARCHAR(50) NOT NULL,
hash VARCHAR(64),
storage_driver VARCHAR(50) NOT NULL,
type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL,
metadata JSON,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_uploads_user_id ON uploads (user_id);
CREATE INDEX IF NOT EXISTS idx_uploads_file_path ON uploads (file_path);
CREATE INDEX IF NOT EXISTS idx_uploads_hash ON uploads (hash);
CREATE INDEX IF NOT EXISTS idx_uploads_type ON uploads (type);
CREATE TABLE IF NOT EXISTS access_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id BIGINT NOT NULL,
name VARCHAR(128) NOT NULL,
token_hash VARCHAR(64) NOT NULL UNIQUE,
masked_token VARCHAR(64) NOT NULL,
last_used_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_access_tokens_user_id ON access_tokens (user_id);
CREATE TABLE IF NOT EXISTS task_executions (
id BIGINT PRIMARY KEY,
task_id VARCHAR(128) NOT NULL UNIQUE,
task_type VARCHAR(64) NOT NULL,
task_name VARCHAR(128),
status VARCHAR(32) NOT NULL,
retryable BOOLEAN NOT NULL DEFAULT FALSE,
max_retry INTEGER NOT NULL DEFAULT 0,
retry_count INTEGER NOT NULL DEFAULT 0,
log TEXT,
error_message TEXT,
result TEXT,
started_at DATETIME,
finished_at DATETIME,
duration BIGINT,
payload TEXT,
triggered_by VARCHAR(32) NOT NULL DEFAULT 'system',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_task_executions_task_type ON task_executions (task_type);
CREATE INDEX IF NOT EXISTS idx_task_executions_status ON task_executions (status);
CREATE INDEX IF NOT EXISTS idx_task_executions_started_at ON task_executions (started_at);
CREATE INDEX IF NOT EXISTS idx_task_executions_created_at ON task_executions (created_at);
CREATE TABLE IF NOT EXISTS templates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key VARCHAR(80) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
type VARCHAR(20) NOT NULL DEFAULT 'email',
subject VARCHAR(255),
content TEXT NOT NULL,
description VARCHAR(255),
is_system BOOLEAN NOT NULL DEFAULT FALSE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_templates_is_system ON templates (is_system);
CREATE INDEX IF NOT EXISTS idx_templates_created_at ON templates (created_at);
CREATE INDEX IF NOT EXISTS idx_templates_updated_at ON templates (updated_at);
INSERT INTO system_configs (key, value, type, visibility, description, created_at, updated_at) VALUES
('cap_login_enabled', 'false', 'system', 1, '是否启用登录人机验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_auto_solve', 'true', 'system', 1, '打开页面后是否自动开始计算,关闭则需用户手动点击触发', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_count', '1', 'system', 0, '客户端需求解的 PoW 难题总数,默认 1,推荐 1~5', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_size', '32', 'system', 0, '人机验证盐值长度', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_difficulty', '4', 'system', 0, '人机验证 PoW 难度(目标前缀长度)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_challenge_ttl_seconds', '600', 'system', 0, '人机验证难题有效时间(秒)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('cap_token_ttl_seconds', '1200', 'system', 0, '人机验证兑换凭证有效时间(秒)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('server_address', '', 'system', 0, '服务器地址(用于跨域源控制,不设定则允许任意源)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_host', '', 'system', 0, 'SMTP 服务器地址(例如 smtp.example.com)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_port', '587', 'system', 0, 'SMTP 端口(例如 587 或 465)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_username', '', 'system', 0, 'SMTP 账户(如 sender@example.com)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('smtp_password', '', 'system', 0, 'SMTP 访问凭证(授权码/密码)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('upload_allowed_extensions', 'jpg,png,webp', 'system', 1, '允许上传的图片扩展名(逗号分隔)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('site_name', 'OpenFlare', 'system', 1, '系统平台的展示名称', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('password_login_enabled', 'true', 'system', 1, '是否允许使用账号密码登录', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('registration_enabled', 'false', 'system', 1, '控制普通用户是否可以自主注册(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('password_register_enabled', 'false', 'system', 1, '是否允许通过密码创建本地账号', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('oidc_login_enabled', 'true', 'system', 1, '是否允许使用第三方 OIDC 认证源登录', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('max_api_keys_per_user', '5', 'business', 1, '限制每个普通用户可以创建的 API Key 最大数量', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('email_login_verification_enabled', 'false', 'system', 1, '是否开启邮箱登录验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('email_register_verification_enabled', 'false', 'system', 1, '是否开启邮箱注册验证(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('menu_display_config', '{}', 'system', 1, '目录显示配置(JSON 字符串,格式为 {url: enabled})', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('search_engine_indexing_enabled', 'false', 'system', 1, '是否允许搜索引擎爬取/检索该站点(true/false)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('update_upstream_repository', 'Rain-kl/OpenFlare', 'system', 0, 'GitHub Actions Release 上游仓库(owner/repo 或 GitHub 仓库地址)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('storage_config', '{"driver":"local","local":{"root":"."},"s3":{"region":"us-east-1"},"r2":{"region":"auto"},"minio":{"region":"us-east-1","path_style":true},"oss":{},"webdav":{}}', 'system', 0, '文件存储驱动及连接配置(JSON)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
INSERT INTO users (id, username, password, nickname, avatar_url, is_active, is_admin, last_login_at, created_at, updated_at)
VALUES (1, 'admin', '12345678', 'Administrator', '', TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (username) DO NOTHING;
INSERT INTO templates (key, name, type, subject, content, description, is_system, created_at, updated_at) VALUES
('login_email', '登录验证码邮件', 'email', 'OpenFlare 登录验证码', '<h3>OpenFlare 登录验证</h3><p>您的登录验证码为:<strong>{{.Code}}</strong>,5分钟内有效,请勿将验证码泄露给他人。</p>', '用户密码登录时发送的验证码邮件模板,支持变量:{{.Code}}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('register_email', '注册验证码邮件', 'email', 'OpenFlare 注册验证码', '<h3>OpenFlare 注册验证</h3><p>您的注册验证码为:<strong>{{.Code}}</strong>,5分钟内有效,请勿泄露给他人。</p>', '用户注册时发送的验证码邮件模板,支持变量:{{.Code}}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (key) DO NOTHING;
-- +goose Down
DROP TABLE IF EXISTS templates;
DROP TABLE IF EXISTS task_executions;
DROP TABLE IF EXISTS access_tokens;
DROP TABLE IF EXISTS uploads;
DROP TABLE IF EXISTS system_configs;
DROP TABLE IF EXISTS external_accounts;
DROP TABLE IF EXISTS auth_sources;
DROP TABLE IF EXISTS users;
@@ -1,20 +0,0 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS schedules (
id BIGINT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
task_type VARCHAR(64) NOT NULL,
cron VARCHAR(64) NOT NULL,
payload TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_schedules_is_active ON schedules (is_active);
-- Seed initial cleanup task
INSERT INTO schedules (id, name, task_type, cron, payload, is_active, created_at, updated_at)
VALUES (1, '清理未使用上传', 'cleanup_unused_uploads', '0 */2 * * *', '{}', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
-- +goose Down
DROP TABLE IF EXISTS schedules;
@@ -1,5 +0,0 @@
-- +goose Up
ALTER TABLE access_tokens ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT 0;
-- +goose Down
ALTER TABLE access_tokens DROP COLUMN is_admin;

Some files were not shown because too many files have changed in this diff Show More