chore: 初始提交 Fonu 1.0.2

- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听)
- 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP)
- 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点)
- 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
This commit is contained in:
2026-09-23 09:40:59 +08:00
commit 278f8267b7
333 changed files with 53562 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
CREATE TABLE IF NOT EXISTS schema_migrations (
version INTEGER PRIMARY KEY,
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS admins (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
admin_id INTEGER NOT NULL REFERENCES admins(id) ON DELETE CASCADE,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
CREATE TABLE IF NOT EXISTS proxy_rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE COLLATE NOCASE,
upstream TEXT NOT NULL,
https_enabled INTEGER NOT NULL DEFAULT 1 CHECK (https_enabled IN (0, 1)),
http_redirect INTEGER NOT NULL DEFAULT 1 CHECK (http_redirect IN (0, 1)),
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
+31
View File
@@ -0,0 +1,31 @@
CREATE TABLE IF NOT EXISTS ddns_configs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider TEXT NOT NULL DEFAULT 'cloudflare',
root_domain TEXT NOT NULL,
record_name TEXT NOT NULL,
ipv4_enabled INTEGER NOT NULL DEFAULT 1 CHECK (ipv4_enabled IN (0, 1)),
ipv6_enabled INTEGER NOT NULL DEFAULT 0 CHECK (ipv6_enabled IN (0, 1)),
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
api_token_enc TEXT,
last_ipv4 TEXT,
last_ipv6 TEXT,
last_status TEXT NOT NULL DEFAULT 'unknown',
last_error TEXT,
last_updated_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS certificates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE,
wildcard INTEGER NOT NULL DEFAULT 0 CHECK (wildcard IN (0, 1)),
cert_path TEXT,
key_path TEXT,
expires_at TEXT,
last_renew_at TEXT,
status TEXT NOT NULL DEFAULT 'pending',
last_error TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
+2
View File
@@ -0,0 +1,2 @@
CREATE UNIQUE INDEX IF NOT EXISTS idx_ddns_configs_domain_record
ON ddns_configs(root_domain, record_name);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE certificates ADD COLUMN acme_ca TEXT NOT NULL DEFAULT 'letsencrypt';
+1
View File
@@ -0,0 +1 @@
ALTER TABLE certificates ADD COLUMN domains TEXT NOT NULL DEFAULT '';
+50
View File
@@ -0,0 +1,50 @@
CREATE TEMP TABLE proxy_domain_backup AS
SELECT id, domain FROM proxy_rules;
CREATE TABLE proxy_rules_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
upstream TEXT NOT NULL,
listen_port INTEGER NOT NULL DEFAULT 80 CHECK (listen_port > 0 AND listen_port <= 65535),
listen_ipv4 INTEGER NOT NULL DEFAULT 1 CHECK (listen_ipv4 IN (0, 1)),
listen_ipv6 INTEGER NOT NULL DEFAULT 0 CHECK (listen_ipv6 IN (0, 1)),
https_enabled INTEGER NOT NULL DEFAULT 1 CHECK (https_enabled IN (0, 1)),
http_redirect INTEGER NOT NULL DEFAULT 1 CHECK (http_redirect IN (0, 1)),
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT INTO proxy_rules_new (id, upstream, listen_port, listen_ipv4, listen_ipv6, https_enabled, http_redirect, enabled, created_at, updated_at)
SELECT
id,
upstream,
CASE WHEN https_enabled = 1 THEN 443 ELSE 80 END,
1,
0,
https_enabled,
http_redirect,
enabled,
created_at,
updated_at
FROM proxy_rules;
DROP TABLE proxy_rules;
ALTER TABLE proxy_rules_new RENAME TO proxy_rules;
CREATE TABLE IF NOT EXISTS proxy_hosts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rule_id INTEGER NOT NULL REFERENCES proxy_rules(id) ON DELETE CASCADE,
hostname TEXT NOT NULL COLLATE NOCASE,
listen_port INTEGER CHECK (listen_port IS NULL OR (listen_port > 0 AND listen_port <= 65535)),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(hostname)
);
CREATE INDEX IF NOT EXISTS idx_proxy_hosts_rule_id ON proxy_hosts(rule_id);
INSERT INTO proxy_hosts (rule_id, hostname, listen_port)
SELECT id, domain, NULL
FROM proxy_domain_backup
WHERE domain IS NOT NULL AND TRIM(domain) != '';
DROP TABLE proxy_domain_backup;
+5
View File
@@ -0,0 +1,5 @@
ALTER TABLE ddns_configs ADD COLUMN record_names TEXT NOT NULL DEFAULT '[]';
UPDATE ddns_configs
SET record_names = json_array(record_name)
WHERE record_names = '[]' OR record_names IS NULL OR record_names = '';
+1
View File
@@ -0,0 +1 @@
ALTER TABLE ddns_configs ADD COLUMN domain_records TEXT NOT NULL DEFAULT '[]';
+6
View File
@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS proxy_traffic (
host TEXT PRIMARY KEY,
upload_bytes INTEGER NOT NULL DEFAULT 0,
download_bytes INTEGER NOT NULL DEFAULT 0,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
+22
View File
@@ -0,0 +1,22 @@
UPDATE proxy_hosts
SET listen_port = (
SELECT listen_port FROM proxy_rules WHERE proxy_rules.id = proxy_hosts.rule_id
)
WHERE listen_port IS NULL;
CREATE TABLE proxy_hosts_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rule_id INTEGER NOT NULL REFERENCES proxy_rules(id) ON DELETE CASCADE,
hostname TEXT NOT NULL COLLATE NOCASE,
listen_port INTEGER NOT NULL CHECK (listen_port > 0 AND listen_port <= 65535),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(hostname, listen_port)
);
INSERT INTO proxy_hosts_new (id, rule_id, hostname, listen_port, created_at)
SELECT id, rule_id, hostname, listen_port, created_at FROM proxy_hosts;
DROP TABLE proxy_hosts;
ALTER TABLE proxy_hosts_new RENAME TO proxy_hosts;
CREATE INDEX IF NOT EXISTS idx_proxy_hosts_rule_id ON proxy_hosts(rule_id);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE proxy_rules ADD COLUMN remark TEXT NOT NULL DEFAULT '';
+7
View File
@@ -0,0 +1,7 @@
ALTER TABLE proxy_rules RENAME COLUMN remark TO name;
ALTER TABLE proxy_rules ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;
UPDATE proxy_rules SET sort_order = id;
CREATE INDEX IF NOT EXISTS idx_proxy_rules_sort_order ON proxy_rules(sort_order);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE proxy_rules ADD COLUMN security_json TEXT NOT NULL DEFAULT '{}';
+1
View File
@@ -0,0 +1 @@
ALTER TABLE proxy_rules ADD COLUMN nginx_mode TEXT NOT NULL DEFAULT 'auto';
+44
View File
@@ -0,0 +1,44 @@
CREATE TABLE IF NOT EXISTS frp_servers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE COLLATE NOCASE,
server_addr TEXT NOT NULL,
server_port INTEGER NOT NULL CHECK (server_port > 0 AND server_port <= 65535),
auth_token_enc TEXT,
tls_enabled INTEGER NOT NULL DEFAULT 1 CHECK (tls_enabled IN (0, 1)),
tls_server_name TEXT NOT NULL DEFAULT '',
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS frp_proxies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL REFERENCES frp_servers(id) ON DELETE CASCADE,
name TEXT NOT NULL COLLATE NOCASE,
type TEXT NOT NULL CHECK (type IN ('tcp', 'http', 'https')),
local_ip TEXT NOT NULL DEFAULT '127.0.0.1',
local_port INTEGER NOT NULL CHECK (local_port > 0 AND local_port <= 65535),
remote_port INTEGER CHECK (remote_port IS NULL OR (remote_port > 0 AND remote_port <= 65535)),
custom_domains_json TEXT NOT NULL DEFAULT '[]',
host_header_rewrite TEXT NOT NULL DEFAULT '',
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
remark TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(server_id, name)
);
CREATE TABLE IF NOT EXISTS frp_runtime_status (
server_id INTEGER PRIMARY KEY REFERENCES frp_servers(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'stopped',
pid INTEGER,
frpc_version TEXT NOT NULL DEFAULT '',
last_started_at TEXT,
last_reloaded_at TEXT,
last_error TEXT NOT NULL DEFAULT '',
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_frp_proxies_tcp_remote_port
ON frp_proxies(server_id, remote_port)
WHERE type = 'tcp' AND remote_port IS NOT NULL;
+2
View File
@@ -0,0 +1,2 @@
ALTER TABLE frp_servers ADD COLUMN config_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE frp_servers ADD COLUMN oidc_client_secret_enc TEXT NOT NULL DEFAULT '';
+11
View File
@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS frp_server_diagnostics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL REFERENCES frp_servers(id) ON DELETE CASCADE,
checked_at TEXT NOT NULL DEFAULT (datetime('now')),
available INTEGER NOT NULL DEFAULT 0 CHECK (available IN (0, 1)),
latency_ms INTEGER,
error TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_frp_server_diagnostics_server_time
ON frp_server_diagnostics(server_id, checked_at);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE frp_proxies ADD COLUMN config_json TEXT NOT NULL DEFAULT '{}';
+18
View File
@@ -0,0 +1,18 @@
ALTER TABLE frp_runtime_status ADD COLUMN desired_state TEXT NOT NULL DEFAULT 'stopped';
ALTER TABLE frp_runtime_status ADD COLUMN process_state TEXT NOT NULL DEFAULT 'stopped';
ALTER TABLE frp_runtime_status ADD COLUMN connection_state TEXT NOT NULL DEFAULT 'unknown';
ALTER TABLE frp_runtime_status ADD COLUMN state_source TEXT NOT NULL DEFAULT 'legacy';
ALTER TABLE frp_runtime_status ADD COLUMN process_pid INTEGER;
ALTER TABLE frp_runtime_status ADD COLUMN exit_code INTEGER;
ALTER TABLE frp_runtime_status ADD COLUMN last_connected_at TEXT;
ALTER TABLE frp_runtime_status ADD COLUMN last_disconnected_at TEXT;
UPDATE frp_runtime_status
SET desired_state = CASE WHEN status IN ('running', 'starting') THEN 'running' ELSE 'stopped' END,
process_state = CASE WHEN status IN ('running', 'starting') THEN 'unknown' ELSE 'stopped' END,
connection_state = CASE WHEN status = 'running' THEN 'unknown' ELSE 'unknown' END,
state_source = 'migration'
WHERE desired_state = 'stopped' AND status IN ('running', 'starting');
CREATE INDEX IF NOT EXISTS idx_frp_runtime_process_state
ON frp_runtime_status(process_state, connection_state);
+1
View File
@@ -0,0 +1 @@
ALTER TABLE frp_servers ADD COLUMN dashboard_password_enc TEXT;
+42
View File
@@ -0,0 +1,42 @@
-- FRP 规则类型与端口约束修正(文档 FRP_CROSS_MODULE_IMPLEMENTATION_PLAN.md 23.2)
-- 原 014_frp.sql 的 type CHECK 只有 tcp/http/https,但校验器与前端已支持
-- udp/tcpmux/stcp/sudp/xtcp:这些类型会在校验通过后因 SQLite CHECK 约束插入失败。
-- SQLite 无法 ALTER CHECK 约束,这里重建 frp_proxies 表并统一约束。
CREATE TABLE IF NOT EXISTS frp_proxies_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL REFERENCES frp_servers(id) ON DELETE CASCADE,
name TEXT NOT NULL COLLATE NOCASE,
type TEXT NOT NULL CHECK (type IN ('tcp', 'udp', 'http', 'https', 'tcpmux', 'stcp', 'sudp', 'xtcp')),
local_ip TEXT NOT NULL DEFAULT '127.0.0.1',
local_port INTEGER NOT NULL CHECK (local_port > 0 AND local_port <= 65535),
remote_port INTEGER CHECK (
remote_port IS NULL
OR (remote_port > 0 AND remote_port <= 65535 AND type IN ('tcp', 'udp'))
),
custom_domains_json TEXT NOT NULL DEFAULT '[]',
host_header_rewrite TEXT NOT NULL DEFAULT '',
config_json TEXT NOT NULL DEFAULT '{}',
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
remark TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(server_id, name)
);
INSERT INTO frp_proxies_new
(id, server_id, name, type, local_ip, local_port, remote_port, custom_domains_json, host_header_rewrite, config_json, enabled, remark, created_at, updated_at)
SELECT
id, server_id, name, type, local_ip, local_port, remote_port, custom_domains_json, host_header_rewrite, COALESCE(config_json, '{}'), enabled, remark, created_at, updated_at
FROM frp_proxies;
DROP TABLE frp_proxies;
ALTER TABLE frp_proxies_new RENAME TO frp_proxies;
-- tcp 与 udp 的远程端口在同一 server_id 内分别查重(原索引混用两种协议)
DROP INDEX IF EXISTS idx_frp_proxies_tcp_remote_port;
CREATE UNIQUE INDEX IF NOT EXISTS idx_frp_proxies_tcp_remote_port
ON frp_proxies(server_id, remote_port)
WHERE type = 'tcp' AND remote_port IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_frp_proxies_udp_remote_port
ON frp_proxies(server_id, remote_port)
WHERE type = 'udp' AND remote_port IS NOT NULL;
+3
View File
@@ -0,0 +1,3 @@
-- DDNS 支持自定义 IP:填写后更新记录时直接使用该 IP(如公网服务器 IP),不填则检测本机公网 IP。
ALTER TABLE ddns_configs ADD COLUMN custom_ipv4 TEXT NOT NULL DEFAULT '';
ALTER TABLE ddns_configs ADD COLUMN custom_ipv6 TEXT NOT NULL DEFAULT '';
+9
View File
@@ -0,0 +1,9 @@
-- 公网 fonu-agent 远程管理配置
ALTER TABLE frp_servers ADD COLUMN agent_url TEXT NOT NULL DEFAULT '';
ALTER TABLE frp_servers ADD COLUMN agent_token_enc TEXT NOT NULL DEFAULT '';
ALTER TABLE frp_servers ADD COLUMN ssh_host TEXT NOT NULL DEFAULT '';
ALTER TABLE frp_servers ADD COLUMN ssh_port INTEGER NOT NULL DEFAULT 22;
ALTER TABLE frp_servers ADD COLUMN ssh_user TEXT NOT NULL DEFAULT '';
ALTER TABLE frp_servers ADD COLUMN ssh_auth TEXT NOT NULL DEFAULT 'key';
ALTER TABLE frp_servers ADD COLUMN ssh_secret_enc TEXT NOT NULL DEFAULT '';
ALTER TABLE frp_servers ADD COLUMN mgmt_enabled INTEGER NOT NULL DEFAULT 0 CHECK (mgmt_enabled IN (0, 1));