mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 23:56:37 +08:00
refactor(repo): consolidate openflare-server to root and move subprojects to internal/apps
- Merge all files inside openflare-server to the repository root directory. - Relocate agent, relay, and flared subprojects from internal/ to internal/apps/. - Combine docker-compose files and update build context paths to root. - Update GitHub workflows and Dockerfiles to refer to new directories and package names. - Rewrite Go package imports across all files. - Resolve database renew test race condition and clean up docs.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// Copyright 2025 linux.do
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/Rain-kl/Wavelet/internal/config"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
clickhouseMigrationDir = "goose/clickhouse"
|
||||
clickhouseGooseVersionTable = "goose_clickhouse_version"
|
||||
clickhouseMaxExecTime = 60
|
||||
clickhouseReadTimeoutFactor = 2
|
||||
)
|
||||
|
||||
// clickhouseMigrationFS contains SQL migrations under goose/clickhouse.
|
||||
//
|
||||
//go:embed goose/clickhouse/*.sql
|
||||
var clickhouseMigrationFS embed.FS
|
||||
|
||||
// MigrateClickHouse runs goose migrations against ClickHouse when enabled.
|
||||
func MigrateClickHouse() {
|
||||
if !config.Config.ClickHouse.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Config.ClickHouse
|
||||
sqlDB := clickhouse.OpenDB(&clickhouse.Options{
|
||||
Addr: cfg.Hosts,
|
||||
Auth: clickhouse.Auth{
|
||||
Database: cfg.Database,
|
||||
Username: cfg.Username,
|
||||
Password: cfg.Password,
|
||||
},
|
||||
Settings: clickhouse.Settings{
|
||||
"max_execution_time": clickhouseMaxExecTime,
|
||||
},
|
||||
Compression: &clickhouse.Compression{
|
||||
Method: clickhouse.CompressionLZ4,
|
||||
},
|
||||
DialTimeout: time.Duration(cfg.DialTimeout) * time.Second,
|
||||
MaxOpenConns: cfg.MaxOpenConn,
|
||||
MaxIdleConns: cfg.MaxIdleConn,
|
||||
ConnMaxLifetime: time.Duration(cfg.ConnMaxLifetime) * time.Second,
|
||||
ReadTimeout: time.Duration(cfg.DialTimeout*clickhouseReadTimeoutFactor) * time.Second,
|
||||
BlockBufferSize: cfg.BlockBufferSize,
|
||||
})
|
||||
|
||||
goose.SetBaseFS(clickhouseMigrationFS)
|
||||
if err := goose.SetDialect("clickhouse"); err != nil {
|
||||
closeClickHouseDB(sqlDB)
|
||||
log.Fatalf("[ClickHouse] set goose dialect failed: %v\n", err)
|
||||
}
|
||||
goose.SetTableName(clickhouseGooseVersionTable)
|
||||
if err := goose.Up(sqlDB, clickhouseMigrationDir); err != nil {
|
||||
closeClickHouseDB(sqlDB)
|
||||
log.Fatalf("[ClickHouse] goose migrate failed: %v\n", err)
|
||||
}
|
||||
closeClickHouseDB(sqlDB)
|
||||
|
||||
log.Println("[ClickHouse] goose migrate success")
|
||||
}
|
||||
|
||||
func closeClickHouseDB(sqlDB *sql.DB) {
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
log.Printf("[ClickHouse] close sql db failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/config"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
func TestClickHouseMigrationFilesEmbedded(t *testing.T) {
|
||||
entries, err := clickhouseMigrationFS.ReadDir(clickhouseMigrationDir)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadDir(%q) error = %v", clickhouseMigrationDir, err)
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
t.Fatal("expected embedded ClickHouse migrations, got none")
|
||||
}
|
||||
|
||||
expected := map[string]bool{
|
||||
"202606190001_create_user_access_logs.sql": false,
|
||||
"202606200001_create_node_access_logs.sql": false,
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
if _, ok := expected[entry.Name()]; ok {
|
||||
expected[entry.Name()] = true
|
||||
}
|
||||
}
|
||||
for name, found := range expected {
|
||||
if !found {
|
||||
t.Fatalf("expected %s in embedded migrations", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClickHouseGooseDialect(t *testing.T) {
|
||||
if err := goose.SetDialect("clickhouse"); err != nil {
|
||||
t.Fatalf("SetDialect(clickhouse) error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateClickHouseSkipsWhenDisabled(t *testing.T) {
|
||||
previousEnabled := config.Config.ClickHouse.Enabled
|
||||
config.Config.ClickHouse.Enabled = false
|
||||
t.Cleanup(func() {
|
||||
config.Config.ClickHouse.Enabled = previousEnabled
|
||||
})
|
||||
|
||||
MigrateClickHouse()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS w_user_access_logs
|
||||
(
|
||||
id UInt64,
|
||||
user_id UInt64,
|
||||
path String,
|
||||
method String,
|
||||
ip String,
|
||||
user_agent String,
|
||||
headers String,
|
||||
status Int32,
|
||||
latency Int64,
|
||||
created_at DateTime
|
||||
)
|
||||
ENGINE = MergeTree()
|
||||
PARTITION BY toYYYYMM(created_at)
|
||||
ORDER BY (created_at, ip, user_id)
|
||||
SETTINGS index_granularity = 8192;
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS w_user_access_logs;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_node_access_logs
|
||||
(
|
||||
id UInt64,
|
||||
node_id String,
|
||||
logged_at DateTime64(3, 'UTC'),
|
||||
remote_addr String,
|
||||
region String,
|
||||
host String,
|
||||
path String,
|
||||
status_code Int32,
|
||||
created_at DateTime64(3, 'UTC')
|
||||
)
|
||||
ENGINE = MergeTree()
|
||||
PARTITION BY toYYYYMM(logged_at)
|
||||
ORDER BY (node_id, logged_at, remote_addr, host, path, status_code)
|
||||
SETTINGS index_granularity = 8192;
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS of_node_access_logs;
|
||||
@@ -0,0 +1,184 @@
|
||||
-- +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', 'true', '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;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +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
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +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
|
||||
@@ -0,0 +1,66 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,10 @@
|
||||
-- +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');
|
||||
@@ -0,0 +1,8 @@
|
||||
-- +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';
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- +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);
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,34 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +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
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,67 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +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);
|
||||
@@ -0,0 +1,67 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,43 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,44 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,60 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,61 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,53 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,136 @@
|
||||
-- +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;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +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);
|
||||
@@ -0,0 +1,14 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,31 @@
|
||||
-- +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');
|
||||
@@ -0,0 +1,25 @@
|
||||
-- +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';
|
||||
|
||||
UPDATE w_system_configs
|
||||
SET value = 'true', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE key = 'cap_login_enabled' AND value = 'false';
|
||||
|
||||
-- +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';
|
||||
|
||||
UPDATE w_system_configs
|
||||
SET value = 'false', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE key = 'cap_login_enabled' AND value = 'true';
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,28 @@
|
||||
-- +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);
|
||||
@@ -0,0 +1,184 @@
|
||||
-- +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', 'true', '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;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE access_tokens DROP COLUMN last_used_at;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE access_tokens ADD COLUMN last_used_at DATETIME;
|
||||
-- +goose StatementEnd
|
||||
@@ -0,0 +1,32 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
-- 1. Rename existing schedules table
|
||||
ALTER TABLE schedules RENAME TO schedules_old;
|
||||
|
||||
-- 2. Create new schedules table with AUTOINCREMENT
|
||||
CREATE TABLE schedules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
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
|
||||
);
|
||||
|
||||
-- 3. Copy existing data
|
||||
INSERT INTO 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 schedules_old;
|
||||
|
||||
-- 4. Drop the old table
|
||||
DROP TABLE schedules_old;
|
||||
|
||||
-- 5. Recreate index
|
||||
CREATE INDEX IF NOT EXISTS idx_schedules_is_active ON schedules (is_active);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
-- Re-creating table with AUTOINCREMENT cannot be undone simply without recreating table again.
|
||||
-- +goose StatementEnd
|
||||
@@ -0,0 +1,123 @@
|
||||
-- +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;
|
||||
|
||||
-- Drop and Recreate SQLite indexes
|
||||
DROP INDEX IF EXISTS idx_users_email;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_users_email ON w_users (email);
|
||||
DROP INDEX IF EXISTS idx_users_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_users_is_active ON w_users (is_active);
|
||||
DROP INDEX IF EXISTS idx_users_last_login_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_users_last_login_at ON w_users (last_login_at);
|
||||
DROP INDEX IF EXISTS idx_users_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_users_created_at ON w_users (created_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_auth_sources_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_auth_sources_is_active ON w_auth_sources (is_active);
|
||||
|
||||
DROP INDEX IF EXISTS idx_external_accounts_auth_source_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_external_accounts_auth_source_id ON w_external_accounts (auth_source_id);
|
||||
DROP INDEX IF EXISTS idx_external_accounts_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_external_accounts_user_id ON w_external_accounts (user_id);
|
||||
DROP INDEX IF EXISTS idx_external_accounts_source_external;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_w_external_accounts_source_external ON w_external_accounts (auth_source_id, external_id);
|
||||
|
||||
DROP INDEX IF EXISTS idx_uploads_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_uploads_user_id ON w_uploads (user_id);
|
||||
DROP INDEX IF EXISTS idx_uploads_file_path;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_uploads_file_path ON w_uploads (file_path);
|
||||
DROP INDEX IF EXISTS idx_uploads_hash;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_uploads_hash ON w_uploads (hash);
|
||||
DROP INDEX IF EXISTS idx_uploads_type;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_uploads_type ON w_uploads (type);
|
||||
|
||||
DROP INDEX IF EXISTS idx_access_tokens_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_access_tokens_user_id ON w_access_tokens (user_id);
|
||||
|
||||
DROP INDEX IF EXISTS idx_task_executions_task_type;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_task_executions_task_type ON w_task_executions (task_type);
|
||||
DROP INDEX IF EXISTS idx_task_executions_status;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_task_executions_status ON w_task_executions (status);
|
||||
DROP INDEX IF EXISTS idx_task_executions_started_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_task_executions_started_at ON w_task_executions (started_at);
|
||||
DROP INDEX IF EXISTS idx_task_executions_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_task_executions_created_at ON w_task_executions (created_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_templates_is_system;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_templates_is_system ON w_templates (is_system);
|
||||
DROP INDEX IF EXISTS idx_templates_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_templates_created_at ON w_templates (created_at);
|
||||
DROP INDEX IF EXISTS idx_templates_updated_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_templates_updated_at ON w_templates (updated_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_schedules_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_w_schedules_is_active ON w_schedules (is_active);
|
||||
|
||||
-- +goose Down
|
||||
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;
|
||||
|
||||
-- Revert indexes
|
||||
DROP INDEX IF EXISTS idx_w_users_email;
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
|
||||
DROP INDEX IF EXISTS idx_w_users_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users (is_active);
|
||||
DROP INDEX IF EXISTS idx_w_users_last_login_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_users_last_login_at ON users (last_login_at);
|
||||
DROP INDEX IF EXISTS idx_w_users_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users (created_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_auth_sources_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_sources_is_active ON auth_sources (is_active);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_external_accounts_auth_source_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_external_accounts_auth_source_id ON external_accounts (auth_source_id);
|
||||
DROP INDEX IF EXISTS idx_w_external_accounts_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_external_accounts_user_id ON external_accounts (user_id);
|
||||
DROP INDEX IF EXISTS idx_w_external_accounts_source_external;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_external_accounts_source_external ON external_accounts (auth_source_id, external_id);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_uploads_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_uploads_user_id ON uploads (user_id);
|
||||
DROP INDEX IF EXISTS idx_w_uploads_file_path;
|
||||
CREATE INDEX IF NOT EXISTS idx_uploads_file_path ON uploads (file_path);
|
||||
DROP INDEX IF EXISTS idx_w_uploads_hash;
|
||||
CREATE INDEX IF NOT EXISTS idx_uploads_hash ON uploads (hash);
|
||||
DROP INDEX IF EXISTS idx_w_uploads_type;
|
||||
CREATE INDEX IF NOT EXISTS idx_uploads_type ON uploads (type);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_access_tokens_user_id;
|
||||
CREATE INDEX IF NOT EXISTS idx_access_tokens_user_id ON access_tokens (user_id);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_task_executions_task_type;
|
||||
CREATE INDEX IF NOT EXISTS idx_task_executions_task_type ON task_executions (task_type);
|
||||
DROP INDEX IF EXISTS idx_w_task_executions_status;
|
||||
CREATE INDEX IF NOT EXISTS idx_task_executions_status ON task_executions (status);
|
||||
DROP INDEX IF EXISTS idx_w_task_executions_started_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_task_executions_started_at ON task_executions (started_at);
|
||||
DROP INDEX IF EXISTS idx_w_task_executions_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_task_executions_created_at ON task_executions (created_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_templates_is_system;
|
||||
CREATE INDEX IF NOT EXISTS idx_templates_is_system ON templates (is_system);
|
||||
DROP INDEX IF EXISTS idx_w_templates_created_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_templates_created_at ON templates (created_at);
|
||||
DROP INDEX IF EXISTS idx_w_templates_updated_at;
|
||||
CREATE INDEX IF NOT EXISTS idx_templates_updated_at ON templates (updated_at);
|
||||
|
||||
DROP INDEX IF EXISTS idx_w_schedules_is_active;
|
||||
CREATE INDEX IF NOT EXISTS idx_schedules_is_active ON schedules (is_active);
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,10 @@
|
||||
-- +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');
|
||||
@@ -0,0 +1,8 @@
|
||||
-- +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';
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +goose Up
|
||||
-- SQLite stores VARCHAR and TEXT with the same TEXT affinity.
|
||||
SELECT 1;
|
||||
|
||||
-- +goose Down
|
||||
SELECT 1;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +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';
|
||||
@@ -0,0 +1,34 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE w_push_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
channels TEXT NOT NULL,
|
||||
targets TEXT NOT NULL,
|
||||
template TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_w_push_events_enabled ON w_push_events(enabled);
|
||||
|
||||
CREATE TABLE w_push_histories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_key TEXT NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
level TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
error_msg TEXT,
|
||||
created_at DATETIME 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;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +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;
|
||||
|
||||
-- +goose Down
|
||||
DELETE FROM w_users WHERE username = 'system';
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE w_push_channels (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL DEFAULT 'custom',
|
||||
token TEXT,
|
||||
url TEXT NOT NULL,
|
||||
other TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME 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;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +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;
|
||||
-- SQLite does not support DROP COLUMN in older versions easily, but standard ALTER TABLE DROP COLUMN works in SQLite 3.35.0+.
|
||||
-- We can write standard DROP COLUMN.
|
||||
ALTER TABLE w_push_events DROP COLUMN task_type;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +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
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- +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 DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (dimension, stat_key)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS w_upload_stats;
|
||||
@@ -0,0 +1,67 @@
|
||||
-- +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',
|
||||
STRFTIME('%Y-%m-%d', created_at),
|
||||
COUNT(*),
|
||||
COALESCE(SUM(file_size), 0)
|
||||
FROM w_uploads
|
||||
WHERE status != 'deleted'
|
||||
GROUP BY STRFTIME('%Y-%m-%d', created_at)
|
||||
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;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- +goose Up
|
||||
DROP INDEX IF EXISTS idx_w_uploads_storage_driver_status;
|
||||
-- SQLite lacks DROP COLUMN IF EXISTS; goose runs this only after initial schema created the column.
|
||||
ALTER TABLE w_uploads DROP COLUMN storage_driver;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE w_uploads ADD COLUMN 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);
|
||||
@@ -0,0 +1,66 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_options (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
INSERT OR IGNORE 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');
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS of_options;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_origins (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
remark TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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;
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE of_apply_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
result TEXT NOT NULL,
|
||||
message TEXT,
|
||||
checksum TEXT NOT NULL DEFAULT '',
|
||||
main_config_checksum TEXT NOT NULL DEFAULT '',
|
||||
route_config_checksum TEXT NOT NULL DEFAULT '',
|
||||
support_file_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME 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;
|
||||
@@ -0,0 +1,43 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_proxy_routes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
site_name TEXT NOT NULL DEFAULT '',
|
||||
domain TEXT NOT NULL,
|
||||
domains TEXT NOT NULL DEFAULT '[]',
|
||||
origin_id INTEGER,
|
||||
origin_url TEXT NOT NULL,
|
||||
origin_host TEXT NOT NULL DEFAULT '',
|
||||
upstreams TEXT NOT NULL DEFAULT '[]',
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
enable_https INTEGER NOT NULL DEFAULT 0,
|
||||
cert_id INTEGER,
|
||||
cert_ids TEXT NOT NULL DEFAULT '[]',
|
||||
domain_cert_ids TEXT NOT NULL DEFAULT '[]',
|
||||
redirect_http INTEGER NOT NULL DEFAULT 0,
|
||||
limit_conn_per_server INTEGER NOT NULL DEFAULT 0,
|
||||
limit_conn_per_ip INTEGER NOT NULL DEFAULT 0,
|
||||
limit_rate TEXT NOT NULL DEFAULT '',
|
||||
cache_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
cache_policy TEXT NOT NULL DEFAULT '',
|
||||
cache_rules TEXT NOT NULL DEFAULT '[]',
|
||||
custom_headers TEXT NOT NULL DEFAULT '[]',
|
||||
basic_auth_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
basic_auth_username TEXT NOT NULL DEFAULT '',
|
||||
basic_auth_password TEXT NOT NULL DEFAULT '',
|
||||
remark TEXT NOT NULL DEFAULT '',
|
||||
upstream_type TEXT NOT NULL DEFAULT 'direct',
|
||||
tunnel_node_id INTEGER,
|
||||
tunnel_target_addr TEXT NOT NULL DEFAULT '',
|
||||
tunnel_target_protocol TEXT NOT NULL DEFAULT '',
|
||||
pages_project_id INTEGER,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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;
|
||||
@@ -0,0 +1,44 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_nodes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
ip TEXT NOT NULL DEFAULT '',
|
||||
ip_manual_override INTEGER NOT NULL DEFAULT 0,
|
||||
geo_name TEXT NOT NULL DEFAULT '',
|
||||
geo_latitude REAL,
|
||||
geo_longitude REAL,
|
||||
geo_manual_override INTEGER NOT NULL DEFAULT 0,
|
||||
access_token TEXT NOT NULL DEFAULT '',
|
||||
auto_update_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
update_requested INTEGER NOT NULL DEFAULT 0,
|
||||
update_channel TEXT NOT NULL DEFAULT 'stable',
|
||||
update_tag TEXT NOT NULL DEFAULT '',
|
||||
restart_openresty_requested INTEGER NOT NULL DEFAULT 0,
|
||||
version TEXT NOT NULL DEFAULT '',
|
||||
ext_version TEXT NOT NULL DEFAULT '',
|
||||
openresty_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
openresty_message TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'offline',
|
||||
current_version TEXT NOT NULL DEFAULT '',
|
||||
last_seen_at DATETIME,
|
||||
last_error TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
node_type TEXT 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 TEXT NOT NULL DEFAULT '',
|
||||
relay_agent_access_addr TEXT NOT NULL DEFAULT '',
|
||||
relay_client_access_addr TEXT NOT NULL DEFAULT '',
|
||||
relay_client_proxy_url TEXT NOT NULL DEFAULT '',
|
||||
capabilities_json TEXT NOT NULL DEFAULT '[]',
|
||||
relay_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
relay_web_server_enabled INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,60 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_waf_rule_groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT 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 TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT 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 TEXT NOT NULL DEFAULT '',
|
||||
subscription_format TEXT NOT NULL DEFAULT 'text',
|
||||
subscription_mapping_rule TEXT NOT NULL DEFAULT '',
|
||||
sync_interval_minutes INTEGER NOT NULL DEFAULT 1440,
|
||||
last_synced_at DATETIME,
|
||||
next_sync_at DATETIME,
|
||||
last_sync_status TEXT NOT NULL DEFAULT '',
|
||||
last_sync_message TEXT NOT NULL DEFAULT '',
|
||||
remark TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
rule_group_id INTEGER NOT NULL,
|
||||
proxy_route_id INTEGER NOT NULL,
|
||||
created_at DATETIME 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;
|
||||
@@ -0,0 +1,61 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_tls_certificates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
cert_pem TEXT NOT NULL,
|
||||
key_pem TEXT NOT NULL,
|
||||
not_before DATETIME,
|
||||
not_after DATETIME,
|
||||
remark TEXT NOT NULL DEFAULT '',
|
||||
provider TEXT NOT NULL DEFAULT 'upload',
|
||||
acme_account_id INTEGER NOT NULL DEFAULT 0,
|
||||
dns_account_id INTEGER NOT NULL DEFAULT 0,
|
||||
key_algorithm TEXT NOT NULL DEFAULT '',
|
||||
auto_renew INTEGER NOT NULL DEFAULT 0,
|
||||
primary_domain TEXT NOT NULL DEFAULT '',
|
||||
other_domains TEXT NOT NULL DEFAULT '',
|
||||
disable_cname INTEGER NOT NULL DEFAULT 0,
|
||||
skip_dns INTEGER NOT NULL DEFAULT 0,
|
||||
dns1 TEXT NOT NULL DEFAULT '',
|
||||
dns2 TEXT NOT NULL DEFAULT '',
|
||||
apply_status TEXT NOT NULL DEFAULT 'ready',
|
||||
apply_message TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
domain TEXT NOT NULL,
|
||||
cert_id INTEGER,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
remark TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
authorization TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_acme_accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
email TEXT NOT NULL DEFAULT '',
|
||||
url TEXT NOT NULL DEFAULT '',
|
||||
private_key TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_config_versions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
version TEXT 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 TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL DEFAULT 0,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at DATETIME 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;
|
||||
@@ -0,0 +1,53 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_pages_projects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
spa_fallback_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
spa_fallback_path TEXT NOT NULL DEFAULT '/index.html',
|
||||
api_proxy_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
api_proxy_path TEXT NOT NULL DEFAULT '',
|
||||
api_proxy_pass TEXT NOT NULL DEFAULT '',
|
||||
api_proxy_rewrite TEXT NOT NULL DEFAULT '',
|
||||
active_deployment_id INTEGER,
|
||||
root_dir TEXT NOT NULL DEFAULT '',
|
||||
entry_file TEXT NOT NULL DEFAULT 'index.html',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME 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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
deployment_number INTEGER NOT NULL,
|
||||
checksum TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'uploaded',
|
||||
artifact_path TEXT NOT NULL,
|
||||
file_count INTEGER NOT NULL DEFAULT 0,
|
||||
total_size INTEGER NOT NULL DEFAULT 0,
|
||||
created_by TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
activated_at DATETIME
|
||||
);
|
||||
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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
deployment_id INTEGER NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
size INTEGER NOT NULL DEFAULT 0,
|
||||
checksum TEXT NOT NULL,
|
||||
created_at DATETIME 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;
|
||||
@@ -0,0 +1,136 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_node_system_profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
hostname TEXT NOT NULL DEFAULT '',
|
||||
os_name TEXT NOT NULL DEFAULT '',
|
||||
os_version TEXT NOT NULL DEFAULT '',
|
||||
kernel_version TEXT NOT NULL DEFAULT '',
|
||||
architecture TEXT NOT NULL DEFAULT '',
|
||||
cpu_model TEXT NOT NULL DEFAULT '',
|
||||
cpu_cores INTEGER NOT NULL DEFAULT 0,
|
||||
total_memory_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
total_disk_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
uptime_seconds INTEGER NOT NULL DEFAULT 0,
|
||||
reported_at DATETIME NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_of_node_system_profiles_node_id ON of_node_system_profiles (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_system_profiles_reported_at ON of_node_system_profiles (reported_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_metric_snapshots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
captured_at DATETIME NOT NULL,
|
||||
cpu_usage_percent REAL NOT NULL DEFAULT 0,
|
||||
memory_used_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
memory_total_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
storage_used_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
storage_total_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
disk_read_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
disk_write_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
network_rx_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
network_tx_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_metric_snapshots_node_id ON of_node_metric_snapshots (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_metric_snapshots_captured_at ON of_node_metric_snapshots (captured_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_request_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
window_started_at DATETIME NOT NULL,
|
||||
window_ended_at DATETIME NOT NULL,
|
||||
request_count INTEGER NOT NULL DEFAULT 0,
|
||||
error_count INTEGER NOT NULL DEFAULT 0,
|
||||
unique_visitor_count INTEGER NOT NULL DEFAULT 0,
|
||||
status_codes_json TEXT,
|
||||
top_domains_json TEXT,
|
||||
source_countries_json TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_request_reports_node_id ON of_node_request_reports (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_request_reports_window_started_at ON of_node_request_reports (window_started_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_request_reports_window_ended_at ON of_node_request_reports (window_ended_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_health_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
severity TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
message TEXT,
|
||||
first_triggered_at DATETIME NOT NULL,
|
||||
last_triggered_at DATETIME NOT NULL,
|
||||
reported_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME,
|
||||
metadata_json TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_node_id ON of_node_health_events (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_event_type ON of_node_health_events (event_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_status ON of_node_health_events (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_first_triggered_at ON of_node_health_events (first_triggered_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_last_triggered_at ON of_node_health_events (last_triggered_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_reported_at ON of_node_health_events (reported_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_health_events_resolved_at ON of_node_health_events (resolved_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_obs_openresty (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
captured_at DATETIME NOT NULL,
|
||||
openresty_rx_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
openresty_tx_bytes INTEGER NOT NULL DEFAULT 0,
|
||||
openresty_connections INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_openresty_node_id ON of_node_obs_openresty (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_openresty_captured_at ON of_node_obs_openresty (captured_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_obs_frps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
captured_at DATETIME 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 DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frps_node_id ON of_node_obs_frps (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frps_captured_at ON of_node_obs_frps (captured_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS of_node_access_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
logged_at DATETIME NOT NULL,
|
||||
remote_addr TEXT NOT NULL DEFAULT '',
|
||||
region TEXT NOT NULL DEFAULT '',
|
||||
host TEXT NOT NULL DEFAULT '',
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
status_code INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_node_id ON of_node_access_logs (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_logged_at ON of_node_access_logs (logged_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_remote_addr ON of_node_access_logs (remote_addr);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_access_logs_host ON of_node_access_logs (host);
|
||||
CREATE INDEX IF NOT EXISTS 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;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- +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;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS of_node_obs_frpc (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
captured_at DATETIME NOT NULL,
|
||||
tunnel_status TEXT NOT NULL DEFAULT '',
|
||||
connected_relays_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frpc_node_id ON of_node_obs_frpc (node_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_of_node_obs_frpc_captured_at ON of_node_obs_frpc (captured_at);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS of_node_obs_frpc;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +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 * * *', '{}', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
(102, 'OpenFlare 可观测数据自动清理', 'of_database_auto_cleanup', '0 3 * * *', '{}', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
(103, 'OpenFlare WAF IP 组同步', 'of_waf_ip_group_sync', '*/5 * * * *', '{}', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
(104, 'OpenFlare Uptime Kuma 同步', 'of_uptime_kuma_sync', '* * * * *', '{}', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- +goose Down
|
||||
DELETE FROM w_schedules WHERE id IN (101, 102, 103, 104);
|
||||
@@ -0,0 +1,10 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE of_pages_deployments
|
||||
ADD COLUMN upload_id INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_of_pages_deployments_upload_id ON of_pages_deployments (upload_id);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX IF EXISTS idx_of_pages_deployments_upload_id;
|
||||
|
||||
-- SQLite cannot drop columns without table rebuild; keep upload_id on rollback.
|
||||
@@ -0,0 +1,31 @@
|
||||
-- +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');
|
||||
@@ -0,0 +1,25 @@
|
||||
-- +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';
|
||||
|
||||
UPDATE w_system_configs
|
||||
SET value = 'true', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE key = 'cap_login_enabled' AND value = 'false';
|
||||
|
||||
-- +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';
|
||||
|
||||
UPDATE w_system_configs
|
||||
SET value = 'false', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE key = 'cap_login_enabled' AND value = 'true';
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
DELETE FROM of_options
|
||||
WHERE key IN (
|
||||
'GlobalApiRateLimitNum',
|
||||
'GlobalApiRateLimitDuration',
|
||||
'GlobalWebRateLimitNum',
|
||||
'GlobalWebRateLimitDuration',
|
||||
'CriticalRateLimitNum',
|
||||
'CriticalRateLimitDuration'
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
INSERT OR IGNORE INTO of_options (key, value) VALUES
|
||||
('GlobalApiRateLimitNum', '300'),
|
||||
('GlobalApiRateLimitDuration', '180'),
|
||||
('GlobalWebRateLimitNum', '300'),
|
||||
('GlobalWebRateLimitDuration', '180'),
|
||||
('CriticalRateLimitNum', '100'),
|
||||
('CriticalRateLimitDuration', '1200');
|
||||
@@ -0,0 +1,28 @@
|
||||
-- +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 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id TEXT NOT NULL,
|
||||
logged_at DATETIME NOT NULL,
|
||||
remote_addr TEXT NOT NULL DEFAULT '',
|
||||
region TEXT NOT NULL DEFAULT '',
|
||||
host TEXT NOT NULL DEFAULT '',
|
||||
path TEXT NOT NULL DEFAULT '',
|
||||
status_code INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME 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);
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2025 linux.do
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package migrator 提供数据库迁移功能
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"log"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/config"
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
// migrationFS contains SQL migrations under goose/<dialect>.
|
||||
//
|
||||
//go:embed goose/postgres/*.sql goose/sqlite/*.sql
|
||||
var migrationFS embed.FS
|
||||
|
||||
// dbType 返回当前数据库类型名称(用于日志输出)
|
||||
func dbType() string {
|
||||
if !config.Config.Database.Enabled {
|
||||
return "SQLite"
|
||||
}
|
||||
return "PostgreSQL"
|
||||
}
|
||||
|
||||
func gooseDialect() string {
|
||||
if !config.Config.Database.Enabled {
|
||||
return "sqlite3"
|
||||
}
|
||||
return "postgres"
|
||||
}
|
||||
|
||||
func migrationDir() string {
|
||||
if !config.Config.Database.Enabled {
|
||||
return "goose/sqlite"
|
||||
}
|
||||
return "goose/postgres"
|
||||
}
|
||||
|
||||
// Migrate 执行数据库迁移
|
||||
func Migrate() {
|
||||
gormDB := db.DB(context.Background())
|
||||
if gormDB == nil {
|
||||
log.Fatalf("[%s] database not initialized\n", dbType())
|
||||
}
|
||||
|
||||
sqlDB, err := gormDB.DB()
|
||||
if err != nil {
|
||||
log.Fatalf("[%s] load sql db failed: %v\n", dbType(), err)
|
||||
}
|
||||
|
||||
goose.SetBaseFS(migrationFS)
|
||||
if err := goose.SetDialect(gooseDialect()); err != nil {
|
||||
log.Fatalf("[%s] set goose dialect failed: %v\n", dbType(), err)
|
||||
}
|
||||
if err := goose.Up(sqlDB, migrationDir()); err != nil {
|
||||
log.Fatalf("[%s] goose migrate failed: %v\n", dbType(), err)
|
||||
}
|
||||
|
||||
clearSystemConfigCache()
|
||||
|
||||
log.Printf("[%s] goose migrate success\n", dbType())
|
||||
}
|
||||
|
||||
func clearSystemConfigCache() {
|
||||
if err := repository.InvalidateAllSystemConfigCaches(context.Background()); err != nil {
|
||||
log.Printf("[%s] clear system config cache failed: %v\n", dbType(), err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/config"
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/redis/go-redis/v9/maintnotifications"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const expectedMigratedSystemConfigCount = 30
|
||||
|
||||
func TestMigrateInitializesSQLiteDatabase(t *testing.T) {
|
||||
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("gorm.Open(sqlite) error = %v", err)
|
||||
}
|
||||
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
t.Fatalf("miniredis.Run() error = %v", err)
|
||||
}
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: mr.Addr(),
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeDisabled,
|
||||
},
|
||||
})
|
||||
|
||||
previousDBEnabled := config.Config.Database.Enabled
|
||||
config.Config.Database.Enabled = false
|
||||
db.SetDB(sqliteDB)
|
||||
t.Cleanup(func() {
|
||||
config.Config.Database.Enabled = previousDBEnabled
|
||||
db.SetDB(nil)
|
||||
_ = redisClient.Close()
|
||||
mr.Close()
|
||||
})
|
||||
|
||||
Migrate()
|
||||
|
||||
var systemConfigCount int64
|
||||
if err := sqliteDB.Table("w_system_configs").Count(&systemConfigCount).Error; err != nil {
|
||||
t.Fatalf("Migrate() count w_system_configs error = %v", err)
|
||||
}
|
||||
if systemConfigCount != expectedMigratedSystemConfigCount {
|
||||
t.Errorf("Migrate() w_system_configs count = %d, want %d", systemConfigCount, expectedMigratedSystemConfigCount)
|
||||
}
|
||||
|
||||
var adminCount int64
|
||||
if err := sqliteDB.Table("w_users").Where("username = ?", "admin").Count(&adminCount).Error; err != nil {
|
||||
t.Fatalf("Migrate() count admin user error = %v", err)
|
||||
}
|
||||
if adminCount != 1 {
|
||||
t.Errorf("Migrate() admin user count = %d, want %d", adminCount, 1)
|
||||
}
|
||||
|
||||
var templateCount int64
|
||||
if err := sqliteDB.Table("w_templates").Count(&templateCount).Error; err != nil {
|
||||
t.Fatalf("Migrate() count templates error = %v", err)
|
||||
}
|
||||
if templateCount != 2 {
|
||||
t.Errorf("Migrate() templates count = %d, want %d", templateCount, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateClearsStaleSystemConfigCache(t *testing.T) {
|
||||
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("gorm.Open(sqlite) error = %v", err)
|
||||
}
|
||||
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
t.Fatalf("miniredis.Run() error = %v", err)
|
||||
}
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
||||
|
||||
previousDBEnabled := config.Config.Database.Enabled
|
||||
previousRedis := db.Redis
|
||||
config.Config.Database.Enabled = false
|
||||
db.SetDB(sqliteDB)
|
||||
db.Redis = redisClient
|
||||
t.Cleanup(func() {
|
||||
config.Config.Database.Enabled = previousDBEnabled
|
||||
db.SetDB(nil)
|
||||
db.Redis = previousRedis
|
||||
_ = redisClient.Close()
|
||||
mr.Close()
|
||||
})
|
||||
|
||||
staleConfig := model.SystemConfig{
|
||||
Key: model.ConfigKeyEmailLoginVerificationEnabled,
|
||||
Value: "true",
|
||||
Type: "system",
|
||||
}
|
||||
if err := db.HSetJSON(context.Background(), repository.SystemConfigRedisHashKey, model.ConfigKeyEmailLoginVerificationEnabled, &staleConfig); err != nil {
|
||||
t.Fatalf("HSetJSON() error = %v", err)
|
||||
}
|
||||
|
||||
Migrate()
|
||||
|
||||
exists, err := db.Redis.Exists(context.Background(), db.PrefixedKey(repository.SystemConfigRedisHashKey)).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("Redis.Exists() error = %v", err)
|
||||
}
|
||||
if exists != 0 {
|
||||
t.Fatalf("system config cache exists = %d, want 0", exists)
|
||||
}
|
||||
|
||||
enabled, err := repository.GetBoolByKey(context.Background(), model.ConfigKeyEmailLoginVerificationEnabled)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBoolByKey(%s) error = %v", model.ConfigKeyEmailLoginVerificationEnabled, err)
|
||||
}
|
||||
if enabled {
|
||||
t.Fatalf("GetBoolByKey(%s) = true, want false", model.ConfigKeyEmailLoginVerificationEnabled)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user