dns-worker 1.0.2-3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +662 -0
- package/README.md +294 -0
- package/dist/serverfull.mjs +11777 -0
- package/migrations/0000_init.sql +80 -0
- package/migrations/0001_move_lists_to_d1.sql +12 -0
- package/migrations/0002_activity_tracking.sql +7 -0
- package/migrations/0003_fix_profiles_cascade.sql +21 -0
- package/migrations/0004_system_settings.sql +12 -0
- package/migrations/0005_add_bloom_storage.sql +3 -0
- package/migrations/0006_cleanup_profiles_table.sql +18 -0
- package/migrations/0007_user_activity_and_totp.sql +37 -0
- package/migrations/0008_add_profile_key.sql +8 -0
- package/migrations/0009_add_profile_blooms.sql +5 -0
- package/migrations/0010_session_device_info.sql +3 -0
- package/migrations/0011_session_created_at.sql +2 -0
- package/migrations/0012_profile_bloom_chunks.sql +9 -0
- package/migrations/0013_add_session_revoked_to_activity_log.sql +29 -0
- package/migrations/0014_add_session_geo.sql +3 -0
- package/migrations/0015_add_signup_to_activity_log.sql +30 -0
- package/migrations/0016_add_session_rotation_counter.sql +2 -0
- package/migrations/0017_add_access_points.sql +31 -0
- package/migrations/0018_add_list_updated_at.sql +2 -0
- package/migrations/0019_add_list_sync_error.sql +2 -0
- package/migrations/0020_add_session_last_active.sql +5 -0
- package/migrations/0021_add_user_timezone.sql +2 -0
- package/migrations/0022_add_user_locale.sql +2 -0
- package/migrations/0023_add_user_password_version.sql +2 -0
- package/migrations/0024_session_pin_and_pause.sql +37 -0
- package/migrations/0025_add_secret_table.sql +10 -0
- package/migrations/0026_session_lock_config.sql +2 -0
- package/migrations/0027_add_session_id_to_activity_log.sql +2 -0
- package/migrations/0028_rename_session_id_to_session_id_hash.sql +2 -0
- package/migrations/0029_profile_blooms_staging.sql +12 -0
- package/migrations/0030_add_list_blooms.sql +7 -0
- package/migrations/0031_list_blooms_chunks.sql +13 -0
- package/migrations/0032_profile_blooms_cascade.sql +40 -0
- package/migrations/0033_remove_system_secrets.sql +2 -0
- package/migrations/0034_remove_list_entries.sql +2 -0
- package/migrations/0035_add_envelope_encryption_columns.sql +5 -0
- package/migrations/0036_add_cf_proxy_cidrs_setting.sql +7 -0
- package/migrations/0037_optimize_logs_composite_indexes.sql +25 -0
- package/migrations/0038_optimize_logs_table_and_indexes.sql +49 -0
- package/migrations/0039_add_lists_profile_id_index.sql +5 -0
- package/migrations/0040_reduce_logs_indexes_write_cost.sql +9 -0
- package/migrations/0041_create_log_hourly_rollups.sql +21 -0
- package/migrations/0042_add_rule_created_at.sql +3 -0
- package/migrations/0043_create_client_hourly_rollups.sql +26 -0
- package/migrations/0044_create_destination_hourly_rollups.sql +29 -0
- package/migrations/0045_transform_logs_without_rowid.sql +38 -0
- package/migrations/0046_add_passkeys.sql +19 -0
- package/migrations/0047_add_passkey_actions_to_activity_log.sql +35 -0
- package/migrations/0048_add_recovery_key_rotated_to_activity_log.sql +35 -0
- package/migrations/0049_optimize_rollups_and_add_domain_rollups.sql +26 -0
- package/migrations/0050_add_dest_columns_to_logs.sql +6 -0
- package/migrations/0051_add_registration_enabled_setting.sql +2 -0
- package/package.json +71 -0
- package/static/assets/AccountView-BvRvODjN.js +4 -0
- package/static/assets/AnalyticsView-DQ7hDChh.js +2 -0
- package/static/assets/AuthView-Bwv6hbdi.js +2 -0
- package/static/assets/DestinationMap-BEQNbcb-.js +1 -0
- package/static/assets/FilteringView-BWD3e-DS.js +1 -0
- package/static/assets/LogsView-kQy7qTGd.js +5 -0
- package/static/assets/QRCodeCanvas-X07w5x_G.js +2 -0
- package/static/assets/RulesView-3d5hga0L.js +1 -0
- package/static/assets/SettingsView-B9D0kPUF.js +1 -0
- package/static/assets/SetupView-D7d-Gsdr.js +1 -0
- package/static/assets/SwipeableDrawer-B-MfxTWM.js +1 -0
- package/static/assets/TrendChart-B20w7SzD.js +53 -0
- package/static/assets/allPaths-BEI3QvIM.js +1 -0
- package/static/assets/allPathsLoader-DM2JAMeC.js +1 -0
- package/static/assets/browser-icErLjuM.js +1 -0
- package/static/assets/date-C4b8kvr-.js +1 -0
- package/static/assets/de-DE-CXKS_pep.js +10 -0
- package/static/assets/es-ES-B2e3ea35.js +10 -0
- package/static/assets/formatCountryName-CSBD8pP8.js +1 -0
- package/static/assets/fr-FR-1qQHEpoW.js +10 -0
- package/static/assets/getFlagEmoji-_Wxhq0lT.js +1 -0
- package/static/assets/index-7FKlsF79.js +16 -0
- package/static/assets/index-DdQuGqR5.css +2 -0
- package/static/assets/ja-JP-CCVeJXwX.js +10 -0
- package/static/assets/ko-KR-bQ1okpC2.js +10 -0
- package/static/assets/obex_cat_eye_logo-256-CgYNvUIC.webp +0 -0
- package/static/assets/paths-CfqdBnl8.js +1 -0
- package/static/assets/paths-cc-gUzxh.js +1 -0
- package/static/assets/rolldown-runtime-C4Xdq4VV.js +1 -0
- package/static/assets/ru-RU-DeRsms_o.js +10 -0
- package/static/assets/services-fIMx5oSx.js +1 -0
- package/static/assets/splitPathsBySizeLoader-BgBwSA38.js +1 -0
- package/static/assets/vendor-i18next-cO7FDf_e.js +1 -0
- package/static/assets/vendor-icons-16-BgwRBCeU.js +1 -0
- package/static/assets/vendor-icons-20-X5NPN60g.js +1 -0
- package/static/assets/vendor-icons-other-Bme3ywH9.js +2 -0
- package/static/assets/vendor-react-Kt98jRfM.js +11 -0
- package/static/assets/vendor-ui-core-BpucEp_q.js +1 -0
- package/static/assets/vendor-utils-CfYGGC23.js +9 -0
- package/static/assets/vi-VN-BcXfqmzH.js +10 -0
- package/static/assets/zh-CN-D8YK7j27.js +12 -0
- package/static/assets/zh-TW-C2_-uaZk.js +12 -0
- package/static/favicon.webp +0 -0
- package/static/index.html +27 -0
- package/static/manifest.webmanifest +1 -0
- package/static/registerSW.js +1 -0
- package/static/sw.js +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
-- Profiles table
|
|
2
|
+
CREATE TABLE IF NOT EXISTS profiles (
|
|
3
|
+
id TEXT PRIMARY KEY, -- 6-char alphanumeric ID
|
|
4
|
+
name TEXT NOT NULL,
|
|
5
|
+
settings TEXT NOT NULL, -- JSON object: { upstream, ecs, log_retention, default_policy }
|
|
6
|
+
owner_id TEXT REFERENCES users(id),
|
|
7
|
+
created_at INTEGER NOT NULL,
|
|
8
|
+
updated_at INTEGER NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
-- Users table
|
|
12
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
13
|
+
id TEXT PRIMARY KEY,
|
|
14
|
+
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
|
|
15
|
+
hashed_password TEXT NOT NULL,
|
|
16
|
+
role TEXT DEFAULT 'user' CHECK(role IN ('admin', 'user')),
|
|
17
|
+
created_at INTEGER NOT NULL
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
-- Sessions table (Lucia v3 requirement)
|
|
21
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
user_id TEXT NOT NULL,
|
|
24
|
+
expires_at INTEGER NOT NULL,
|
|
25
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- Custom Rules table
|
|
29
|
+
CREATE TABLE IF NOT EXISTS rules (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
profile_id TEXT NOT NULL,
|
|
32
|
+
type TEXT CHECK(type IN ('ALLOW', 'BLOCK', 'REDIRECT')) NOT NULL,
|
|
33
|
+
pattern TEXT NOT NULL, -- Domain or wildcard
|
|
34
|
+
value TEXT, -- legacy redirect value (optional)
|
|
35
|
+
record_type TEXT DEFAULT 'A',
|
|
36
|
+
v_a TEXT,
|
|
37
|
+
v_aaaa TEXT,
|
|
38
|
+
v_txt TEXT,
|
|
39
|
+
v_cname TEXT,
|
|
40
|
+
priority INTEGER DEFAULT 0,
|
|
41
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
-- External Blocklists subscriptions
|
|
45
|
+
CREATE TABLE IF NOT EXISTS lists (
|
|
46
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
|
+
profile_id TEXT NOT NULL,
|
|
48
|
+
url TEXT NOT NULL,
|
|
49
|
+
enabled INTEGER DEFAULT 1,
|
|
50
|
+
last_synced_at INTEGER,
|
|
51
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- Resolution Logs table
|
|
55
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
56
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
57
|
+
profile_id TEXT NOT NULL,
|
|
58
|
+
timestamp INTEGER NOT NULL, -- Unix timestamp
|
|
59
|
+
client_ip TEXT,
|
|
60
|
+
geo_country TEXT, -- ISO 3166-1 alpha-2
|
|
61
|
+
domain TEXT NOT NULL,
|
|
62
|
+
record_type TEXT NOT NULL,
|
|
63
|
+
action TEXT CHECK(action IN ('PASS', 'BLOCK', 'REDIRECT', 'FAIL')) NOT NULL,
|
|
64
|
+
reason TEXT, -- Rule ID or List Name
|
|
65
|
+
answer TEXT,
|
|
66
|
+
dest_geoip TEXT,
|
|
67
|
+
ecs TEXT,
|
|
68
|
+
upstream TEXT,
|
|
69
|
+
latency INTEGER,
|
|
70
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
-- Indexes
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_rules_profile ON rules(profile_id);
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_lists_profile ON lists(profile_id);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_time ON logs(profile_id, timestamp);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_logs_domain ON logs(domain);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username COLLATE NOCASE);
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_owner ON profiles(owner_id);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Migration to move external list data from KV to D1
|
|
2
|
+
CREATE TABLE IF NOT EXISTS list_entries (
|
|
3
|
+
profile_id TEXT NOT NULL,
|
|
4
|
+
domain TEXT NOT NULL,
|
|
5
|
+
PRIMARY KEY (profile_id, domain)
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
CREATE INDEX IF NOT EXISTS idx_list_entries_profile ON list_entries(profile_id);
|
|
9
|
+
|
|
10
|
+
-- We also need a way to store the Bloom Filter and sync metadata in D1
|
|
11
|
+
ALTER TABLE profiles ADD COLUMN list_bloom BLOB;
|
|
12
|
+
ALTER TABLE profiles ADD COLUMN list_updated_at INTEGER;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- Add activity tracking
|
|
2
|
+
ALTER TABLE users ADD COLUMN last_active_at INTEGER;
|
|
3
|
+
ALTER TABLE profiles ADD COLUMN last_active_at INTEGER;
|
|
4
|
+
|
|
5
|
+
-- Initialize existing records
|
|
6
|
+
UPDATE users SET last_active_at = created_at WHERE last_active_at IS NULL;
|
|
7
|
+
UPDATE profiles SET last_active_at = created_at WHERE last_active_at IS NULL;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Fix profiles foreign key to support ON DELETE CASCADE
|
|
2
|
+
-- 1. Create a temporary table with the correct constraint
|
|
3
|
+
CREATE TABLE profiles_new (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
name TEXT NOT NULL,
|
|
6
|
+
settings TEXT NOT NULL,
|
|
7
|
+
owner_id TEXT REFERENCES users(id) ON DELETE CASCADE,
|
|
8
|
+
created_at INTEGER NOT NULL,
|
|
9
|
+
updated_at INTEGER NOT NULL,
|
|
10
|
+
last_active_at INTEGER
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- 2. Copy data from old table
|
|
14
|
+
INSERT INTO profiles_new SELECT id, name, settings, owner_id, created_at, updated_at, last_active_at FROM profiles;
|
|
15
|
+
|
|
16
|
+
-- 3. Drop old table and rename new one
|
|
17
|
+
DROP TABLE profiles;
|
|
18
|
+
ALTER TABLE profiles_new RENAME TO profiles;
|
|
19
|
+
|
|
20
|
+
-- 4. Re-create indexes for the new table
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_owner ON profiles(owner_id);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- 系统全局配置表
|
|
2
|
+
CREATE TABLE IF NOT EXISTS system_settings (
|
|
3
|
+
key TEXT PRIMARY KEY,
|
|
4
|
+
value TEXT NOT NULL,
|
|
5
|
+
updated_at INTEGER NOT NULL
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
-- 预设默认值
|
|
9
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES ('turnstile_site_key', '', 0);
|
|
10
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES ('turnstile_secret_key', '', 0);
|
|
11
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES ('turnstile_enabled_signup', 'false', 0);
|
|
12
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES ('turnstile_enabled_login', 'false', 0);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Remove Bloom filter columns from profiles table (deprecating migration 0005 approach)
|
|
2
|
+
-- SQLite doesn't support DROP COLUMN, so we recreate the table
|
|
3
|
+
CREATE TABLE profiles_temp (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
owner_id TEXT REFERENCES users(id) ON DELETE CASCADE,
|
|
6
|
+
name TEXT NOT NULL,
|
|
7
|
+
settings TEXT NOT NULL,
|
|
8
|
+
created_at INTEGER NOT NULL,
|
|
9
|
+
updated_at INTEGER NOT NULL,
|
|
10
|
+
last_active_at INTEGER
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
INSERT INTO profiles_temp SELECT id, owner_id, name, settings, created_at, updated_at, last_active_at FROM profiles;
|
|
14
|
+
|
|
15
|
+
DROP TABLE profiles;
|
|
16
|
+
ALTER TABLE profiles_temp RENAME TO profiles;
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_owner ON profiles(owner_id);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- 用户安全活动日志
|
|
2
|
+
CREATE TABLE IF NOT EXISTS user_activity_log (
|
|
3
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
|
+
user_id TEXT NOT NULL,
|
|
5
|
+
action TEXT NOT NULL CHECK(action IN (
|
|
6
|
+
'login_success', 'login_fail', 'logout',
|
|
7
|
+
'password_change_success', 'password_change_fail',
|
|
8
|
+
'totp_verify_success', 'totp_verify_fail',
|
|
9
|
+
'totp_setup', 'totp_removed',
|
|
10
|
+
'recovery_key_used'
|
|
11
|
+
)),
|
|
12
|
+
ip_address TEXT,
|
|
13
|
+
user_agent TEXT,
|
|
14
|
+
timestamp INTEGER NOT NULL,
|
|
15
|
+
extra TEXT, -- JSON 附加信息 (如失败原因)
|
|
16
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- 待验证的 TOTP 临时会话 (密码验证通过,等待 TOTP 验证)
|
|
20
|
+
CREATE TABLE IF NOT EXISTS pending_totp_sessions (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
user_id TEXT NOT NULL,
|
|
23
|
+
expires_at INTEGER NOT NULL,
|
|
24
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
-- TOTP 相关字段
|
|
28
|
+
ALTER TABLE users ADD COLUMN totp_secret TEXT;
|
|
29
|
+
ALTER TABLE users ADD COLUMN totp_enabled INTEGER DEFAULT 0;
|
|
30
|
+
-- 跳过密码模式:启用后登录只需 TOTP,不需要密码
|
|
31
|
+
ALTER TABLE users ADD COLUMN totp_skip_password INTEGER DEFAULT 0;
|
|
32
|
+
-- 恢复密钥:JSON 数组,存储 SHA-256 哈希后的 8 个一次性密钥
|
|
33
|
+
ALTER TABLE users ADD COLUMN totp_recovery_keys TEXT;
|
|
34
|
+
|
|
35
|
+
-- 索引
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_activity_log_user ON user_activity_log(user_id, timestamp DESC);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_pending_totp_expires ON pending_totp_sessions(expires_at);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Add profile_key column to profiles table
|
|
2
|
+
ALTER TABLE profiles ADD COLUMN profile_key TEXT;
|
|
3
|
+
|
|
4
|
+
-- Backfill profile_key with existing id for backward compatibility
|
|
5
|
+
UPDATE profiles SET profile_key = LOWER(id) WHERE profile_key IS NULL;
|
|
6
|
+
|
|
7
|
+
-- Create unique index
|
|
8
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_profiles_key ON profiles(profile_key);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
PRAGMA foreign_keys=off;
|
|
2
|
+
|
|
3
|
+
CREATE TABLE user_activity_log_new (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
user_id TEXT NOT NULL,
|
|
6
|
+
action TEXT NOT NULL CHECK(action IN (
|
|
7
|
+
'login_success', 'login_fail', 'logout',
|
|
8
|
+
'password_change_success', 'password_change_fail',
|
|
9
|
+
'totp_verify_success', 'totp_verify_fail',
|
|
10
|
+
'totp_setup', 'totp_removed',
|
|
11
|
+
'recovery_key_used',
|
|
12
|
+
'session_revoked'
|
|
13
|
+
)),
|
|
14
|
+
ip_address TEXT,
|
|
15
|
+
user_agent TEXT,
|
|
16
|
+
timestamp INTEGER NOT NULL,
|
|
17
|
+
extra TEXT,
|
|
18
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
INSERT INTO user_activity_log_new SELECT * FROM user_activity_log;
|
|
22
|
+
|
|
23
|
+
DROP TABLE user_activity_log;
|
|
24
|
+
|
|
25
|
+
ALTER TABLE user_activity_log_new RENAME TO user_activity_log;
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_activity_log_user ON user_activity_log(user_id, timestamp DESC);
|
|
28
|
+
|
|
29
|
+
PRAGMA foreign_keys=on;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
PRAGMA foreign_keys=off;
|
|
2
|
+
|
|
3
|
+
CREATE TABLE user_activity_log_new (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
user_id TEXT NOT NULL,
|
|
6
|
+
action TEXT NOT NULL CHECK(action IN (
|
|
7
|
+
'signup',
|
|
8
|
+
'login_success', 'login_fail', 'logout',
|
|
9
|
+
'password_change_success', 'password_change_fail',
|
|
10
|
+
'totp_verify_success', 'totp_verify_fail',
|
|
11
|
+
'totp_setup', 'totp_removed',
|
|
12
|
+
'recovery_key_used',
|
|
13
|
+
'session_revoked'
|
|
14
|
+
)),
|
|
15
|
+
ip_address TEXT,
|
|
16
|
+
user_agent TEXT,
|
|
17
|
+
timestamp INTEGER NOT NULL,
|
|
18
|
+
extra TEXT,
|
|
19
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
INSERT INTO user_activity_log_new SELECT * FROM user_activity_log;
|
|
23
|
+
|
|
24
|
+
DROP TABLE user_activity_log;
|
|
25
|
+
|
|
26
|
+
ALTER TABLE user_activity_log_new RENAME TO user_activity_log;
|
|
27
|
+
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_activity_log_user ON user_activity_log(user_id, timestamp DESC);
|
|
29
|
+
|
|
30
|
+
PRAGMA foreign_keys=on;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- Create access_points table
|
|
2
|
+
CREATE TABLE IF NOT EXISTS access_points (
|
|
3
|
+
id TEXT PRIMARY KEY,
|
|
4
|
+
profile_id TEXT NOT NULL,
|
|
5
|
+
name TEXT NOT NULL,
|
|
6
|
+
token TEXT NOT NULL UNIQUE,
|
|
7
|
+
created_at INTEGER NOT NULL,
|
|
8
|
+
updated_at INTEGER NOT NULL,
|
|
9
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_access_points_profile ON access_points(profile_id);
|
|
13
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_access_points_token ON access_points(token);
|
|
14
|
+
|
|
15
|
+
-- Backfill access points for existing profiles
|
|
16
|
+
-- We use lower(hex(randomblob(6))) to generate a 12-character hex ID for each existing profile's default access point
|
|
17
|
+
INSERT INTO access_points (id, profile_id, name, token, created_at, updated_at)
|
|
18
|
+
SELECT
|
|
19
|
+
lower(hex(randomblob(6))),
|
|
20
|
+
id,
|
|
21
|
+
'Device-1',
|
|
22
|
+
COALESCE(profile_key, lower(id)),
|
|
23
|
+
created_at,
|
|
24
|
+
updated_at
|
|
25
|
+
FROM profiles;
|
|
26
|
+
|
|
27
|
+
-- Alter logs table to add access_point_id
|
|
28
|
+
ALTER TABLE logs ADD COLUMN access_point_id TEXT REFERENCES access_points(id) ON DELETE SET NULL;
|
|
29
|
+
|
|
30
|
+
-- Create an index to speed up log filtering by access point
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_logs_access_point ON logs(access_point_id);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Add last_active_at to sessions table for idle timeout tracking
|
|
2
|
+
ALTER TABLE sessions ADD COLUMN last_active_at INTEGER;
|
|
3
|
+
|
|
4
|
+
-- Initialize last_active_at for existing sessions
|
|
5
|
+
UPDATE sessions SET last_active_at = COALESCE(created_at, expires_at - 86400) WHERE last_active_at IS NULL;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Add pin_hash to users
|
|
2
|
+
ALTER TABLE users ADD COLUMN pin_hash TEXT;
|
|
3
|
+
|
|
4
|
+
-- Add is_paused to sessions
|
|
5
|
+
ALTER TABLE sessions ADD COLUMN is_paused INTEGER DEFAULT 0;
|
|
6
|
+
|
|
7
|
+
-- Recreate user_activity_log to support pin_verify_success and pin_verify_fail check constraints
|
|
8
|
+
CREATE TABLE IF NOT EXISTS user_activity_log_new (
|
|
9
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10
|
+
user_id TEXT NOT NULL,
|
|
11
|
+
action TEXT NOT NULL CHECK(action IN (
|
|
12
|
+
'signup', 'login_success', 'login_fail', 'logout',
|
|
13
|
+
'password_change_success', 'password_change_fail',
|
|
14
|
+
'totp_verify_success', 'totp_verify_fail',
|
|
15
|
+
'totp_setup', 'totp_removed',
|
|
16
|
+
'recovery_key_used', 'session_revoked',
|
|
17
|
+
'pin_verify_success', 'pin_verify_fail'
|
|
18
|
+
)),
|
|
19
|
+
ip_address TEXT,
|
|
20
|
+
user_agent TEXT,
|
|
21
|
+
timestamp INTEGER NOT NULL,
|
|
22
|
+
extra TEXT,
|
|
23
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- Copy data
|
|
27
|
+
INSERT INTO user_activity_log_new (id, user_id, action, ip_address, user_agent, timestamp, extra)
|
|
28
|
+
SELECT id, user_id, action, ip_address, user_agent, timestamp, extra FROM user_activity_log;
|
|
29
|
+
|
|
30
|
+
-- Drop old table
|
|
31
|
+
DROP TABLE user_activity_log;
|
|
32
|
+
|
|
33
|
+
-- Rename new table
|
|
34
|
+
ALTER TABLE user_activity_log_new RENAME TO user_activity_log;
|
|
35
|
+
|
|
36
|
+
-- Recreate index
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_activity_log_user ON user_activity_log(user_id, timestamp DESC);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Create system_secrets table
|
|
2
|
+
CREATE TABLE IF NOT EXISTS system_secrets (
|
|
3
|
+
key TEXT PRIMARY KEY,
|
|
4
|
+
value TEXT NOT NULL,
|
|
5
|
+
updated_at INTEGER NOT NULL
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
-- Migrate existing jwt_secret from system_settings if it exists
|
|
9
|
+
INSERT OR IGNORE INTO system_secrets (key, value, updated_at)
|
|
10
|
+
SELECT key, value, updated_at FROM system_settings WHERE key = 'jwt_secret';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- A/B Bloom Filter upgrade: staging table holds the bloom currently being built.
|
|
2
|
+
-- Active DNS queries always read from profile_blooms (the fully-completed bloom).
|
|
3
|
+
-- Once all lists in a sync cycle are done, the staging bloom is atomically
|
|
4
|
+
-- promoted to profile_blooms, eliminating partial/inconsistent states during sync.
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS profile_blooms_staging (
|
|
7
|
+
profile_id TEXT NOT NULL,
|
|
8
|
+
chunk_index INTEGER NOT NULL,
|
|
9
|
+
bloom_filter_chunk BLOB NOT NULL,
|
|
10
|
+
updated_at INTEGER NOT NULL,
|
|
11
|
+
PRIMARY KEY (profile_id, chunk_index)
|
|
12
|
+
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Migration to recreate list_blooms with chunking support
|
|
2
|
+
DROP TABLE IF EXISTS list_blooms;
|
|
3
|
+
|
|
4
|
+
CREATE TABLE list_blooms (
|
|
5
|
+
list_id INTEGER NOT NULL,
|
|
6
|
+
chunk_index INTEGER NOT NULL,
|
|
7
|
+
bloom_filter_chunk BLOB NOT NULL,
|
|
8
|
+
updated_at INTEGER NOT NULL,
|
|
9
|
+
PRIMARY KEY (list_id, chunk_index),
|
|
10
|
+
FOREIGN KEY (list_id) REFERENCES lists(id) ON DELETE CASCADE
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
CREATE INDEX IF NOT EXISTS idx_list_blooms_list ON list_blooms(list_id);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- Recreate profile_blooms with foreign key cascade constraint
|
|
2
|
+
CREATE TABLE IF NOT EXISTS profile_blooms_new (
|
|
3
|
+
profile_id TEXT NOT NULL,
|
|
4
|
+
chunk_index INTEGER NOT NULL,
|
|
5
|
+
bloom_filter_chunk BLOB NOT NULL,
|
|
6
|
+
updated_at INTEGER NOT NULL,
|
|
7
|
+
PRIMARY KEY (profile_id, chunk_index),
|
|
8
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
-- Copy existing data, automatically filter out orphan records
|
|
12
|
+
INSERT INTO profile_blooms_new (profile_id, chunk_index, bloom_filter_chunk, updated_at)
|
|
13
|
+
SELECT pb.profile_id, pb.chunk_index, pb.bloom_filter_chunk, pb.updated_at
|
|
14
|
+
FROM profile_blooms pb
|
|
15
|
+
INNER JOIN profiles p ON pb.profile_id = p.id;
|
|
16
|
+
|
|
17
|
+
DROP TABLE profile_blooms;
|
|
18
|
+
ALTER TABLE profile_blooms_new RENAME TO profile_blooms;
|
|
19
|
+
|
|
20
|
+
-- Recreate profile_blooms_staging with foreign key cascade constraint
|
|
21
|
+
CREATE TABLE IF NOT EXISTS profile_blooms_staging_new (
|
|
22
|
+
profile_id TEXT NOT NULL,
|
|
23
|
+
chunk_index INTEGER NOT NULL,
|
|
24
|
+
bloom_filter_chunk BLOB NOT NULL,
|
|
25
|
+
updated_at INTEGER NOT NULL,
|
|
26
|
+
PRIMARY KEY (profile_id, chunk_index),
|
|
27
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
-- Copy existing data, automatically filter out orphan records
|
|
31
|
+
INSERT INTO profile_blooms_staging_new (profile_id, chunk_index, bloom_filter_chunk, updated_at)
|
|
32
|
+
SELECT pbs.profile_id, pbs.chunk_index, pbs.bloom_filter_chunk, pbs.updated_at
|
|
33
|
+
FROM profile_blooms_staging pbs
|
|
34
|
+
INNER JOIN profiles p ON pbs.profile_id = p.id;
|
|
35
|
+
|
|
36
|
+
DROP TABLE profile_blooms_staging;
|
|
37
|
+
ALTER TABLE profile_blooms_staging_new RENAME TO profile_blooms_staging;
|
|
38
|
+
|
|
39
|
+
-- Clean up legacy jwt_secret from system_settings table
|
|
40
|
+
DELETE FROM system_settings WHERE key = 'jwt_secret';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Migration to add Envelope Encryption columns for user TOTP secrets
|
|
2
|
+
ALTER TABLE users ADD COLUMN totp_secret_encrypted TEXT;
|
|
3
|
+
ALTER TABLE users ADD COLUMN totp_secret_dek TEXT;
|
|
4
|
+
ALTER TABLE users ADD COLUMN totp_recovery_keys_encrypted TEXT;
|
|
5
|
+
ALTER TABLE users ADD COLUMN totp_recovery_keys_dek TEXT;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- 初始化 Cloudflare Proxy CIDR 列表至系统配置表
|
|
2
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES (
|
|
3
|
+
'cf_proxy_cidrs',
|
|
4
|
+
'["173.245.48.0/20","103.21.244.0/22","103.22.200.0/22","103.31.4.0/22","141.101.64.0/18","108.162.192.0/18","190.93.240.0/20","188.114.96.0/20","197.234.240.0/22","198.41.128.0/17","162.158.0.0/15","104.16.0.0/13","104.24.0.0/14","172.64.0.0/13","131.0.72.0/22","2400:cb00::/32","2606:4700::/32","2803:f800::/32","2405:b500::/32","2405:8100::/32","2a06:98c0::/29","2c0f:f248::/32"]',
|
|
5
|
+
0
|
|
6
|
+
);
|
|
7
|
+
INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES ('cf_proxy_cidrs_updated_at', '0', 0);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- 1. Composite indexes for high-frequency log filtering with time ordering
|
|
2
|
+
-- Access point filtering
|
|
3
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_ap_time ON logs(profile_id, access_point_id, timestamp);
|
|
4
|
+
|
|
5
|
+
-- Action / status filtering (PASS, BLOCK, etc.)
|
|
6
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_action_time ON logs(profile_id, action, timestamp);
|
|
7
|
+
|
|
8
|
+
-- Client geo-country filtering
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_geo_time ON logs(profile_id, geo_country, timestamp);
|
|
10
|
+
|
|
11
|
+
-- Block / allow reason filtering
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_reason_time ON logs(profile_id, reason, timestamp);
|
|
13
|
+
|
|
14
|
+
-- Exact domain filtering per profile
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_domain_time ON logs(profile_id, domain, timestamp);
|
|
16
|
+
|
|
17
|
+
-- DNS record type filtering (A, AAAA, HTTPS, etc.)
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_record_type_time ON logs(profile_id, record_type, timestamp);
|
|
19
|
+
|
|
20
|
+
-- 2. System indexes to prevent full-table scans during scheduled jobs
|
|
21
|
+
-- Profiles list sync ordering index (eliminates full table scan on cron list sync)
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_list_updated ON profiles(list_updated_at);
|
|
23
|
+
|
|
24
|
+
-- Sessions expiration range index (eliminates full table scan on expired sessions purge)
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
-- 1. Create the new optimized logs table
|
|
2
|
+
-- Improvements:
|
|
3
|
+
-- - Removed AUTOINCREMENT: uses SQLite native 64-bit rowid, eliminating 1 read and 1 write to sqlite_sequence per insert
|
|
4
|
+
-- - Removed FOREIGN KEY constraints: eliminates 2 index-seek reads to profiles and access_points per insert
|
|
5
|
+
CREATE TABLE IF NOT EXISTS logs_new (
|
|
6
|
+
id INTEGER PRIMARY KEY,
|
|
7
|
+
profile_id TEXT NOT NULL,
|
|
8
|
+
timestamp INTEGER NOT NULL,
|
|
9
|
+
client_ip TEXT,
|
|
10
|
+
geo_country TEXT,
|
|
11
|
+
domain TEXT NOT NULL,
|
|
12
|
+
record_type TEXT NOT NULL,
|
|
13
|
+
action TEXT CHECK(action IN ('PASS', 'BLOCK', 'REDIRECT', 'FAIL')) NOT NULL,
|
|
14
|
+
reason TEXT,
|
|
15
|
+
answer TEXT,
|
|
16
|
+
dest_geoip TEXT,
|
|
17
|
+
ecs TEXT,
|
|
18
|
+
upstream TEXT,
|
|
19
|
+
latency INTEGER,
|
|
20
|
+
access_point_id TEXT
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
-- 2. Migrate unexpired logs within 90-day retention window
|
|
24
|
+
-- (Automatically purges expired records, reducing database disk size)
|
|
25
|
+
INSERT INTO logs_new (id, profile_id, timestamp, client_ip, geo_country, domain, record_type, action, reason, answer, dest_geoip, ecs, upstream, latency, access_point_id)
|
|
26
|
+
SELECT id, profile_id, timestamp, client_ip, geo_country, domain, record_type, action, reason, answer, dest_geoip, ecs, upstream, latency, access_point_id
|
|
27
|
+
FROM logs
|
|
28
|
+
WHERE timestamp >= (strftime('%s', 'now') - 7776000);
|
|
29
|
+
|
|
30
|
+
-- 3. Replace logs table
|
|
31
|
+
DROP TABLE logs;
|
|
32
|
+
ALTER TABLE logs_new RENAME TO logs;
|
|
33
|
+
|
|
34
|
+
-- 4. Clean up sqlite_sequence entry if present
|
|
35
|
+
DELETE FROM sqlite_sequence WHERE name = 'logs';
|
|
36
|
+
|
|
37
|
+
-- 5. Create strictly curated composite indexes (3 core indexes)
|
|
38
|
+
-- Baseline time index for default profile queries and range cleanup
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_time ON logs(profile_id, timestamp);
|
|
40
|
+
|
|
41
|
+
-- Device-level time index for access point filtering
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_ap_time ON logs(profile_id, access_point_id, timestamp);
|
|
43
|
+
|
|
44
|
+
-- Action-level time index for ALLOW / BLOCK filtering and top stats
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_action_time ON logs(profile_id, action, timestamp);
|
|
46
|
+
|
|
47
|
+
-- 6. System indexes to prevent full-table scans in background jobs
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_list_updated ON profiles(list_updated_at);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Migration 0039: Add indexes to optimize list sync target queries
|
|
2
|
+
-- Accelerates list sync target lookup in profiles and existence check on lists
|
|
3
|
+
|
|
4
|
+
CREATE INDEX IF NOT EXISTS idx_profiles_list_updated_at ON profiles(list_updated_at);
|
|
5
|
+
CREATE INDEX IF NOT EXISTS idx_lists_profile_id ON lists(profile_id);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- Migration 0040: Reduce logs index write amplification from 4 to 2 rows per insert
|
|
2
|
+
-- Drops secondary action and access point indexes; retains single primary (profile_id, timestamp) index.
|
|
3
|
+
-- This reduces D1 write rows per DNS log insert/delete by 50% and lowers database storage usage.
|
|
4
|
+
|
|
5
|
+
DROP INDEX IF EXISTS idx_logs_profile_ap_time;
|
|
6
|
+
DROP INDEX IF EXISTS idx_logs_profile_action_time;
|
|
7
|
+
|
|
8
|
+
-- Ensure baseline profile time index is present for time-range filtering, pagination, and cleanup
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_logs_profile_time ON logs(profile_id, timestamp);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Migration 0041: Create log_hourly_rollups table for high-performance analytics aggregation
|
|
2
|
+
-- Reduces D1 read row scans on analytics queries (getSummary, getTrend) by >98%
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS log_hourly_rollups (
|
|
5
|
+
profile_id TEXT NOT NULL,
|
|
6
|
+
hour_timestamp INTEGER NOT NULL,
|
|
7
|
+
action TEXT NOT NULL,
|
|
8
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
PRIMARY KEY (profile_id, hour_timestamp, action)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
-- Backfill completed historical hours older than current hour
|
|
13
|
+
INSERT OR REPLACE INTO log_hourly_rollups (profile_id, hour_timestamp, action, count)
|
|
14
|
+
SELECT
|
|
15
|
+
profile_id,
|
|
16
|
+
(timestamp / 3600) * 3600 AS hour_timestamp,
|
|
17
|
+
action,
|
|
18
|
+
COUNT(*) AS count
|
|
19
|
+
FROM logs
|
|
20
|
+
WHERE timestamp < (CAST(strftime('%s', 'now') AS INTEGER) / 3600) * 3600
|
|
21
|
+
GROUP BY profile_id, (timestamp / 3600) * 3600, action;
|