style: apply latest black formatting for ci
This commit is contained in:
1 parent
48783387ac
commit
8ee73e6572
9 files changed
+123
-244
No files matched your search
@@ -1803,8 +1803,7 @@ def api_get_failed_refresh_logs() -> Any:
|
|||||||
db = get_db()
|
db = get_db()
|
||||||
|
|
||||||
# 获取每个账号最近一次失败的刷新记录
|
# 获取每个账号最近一次失败的刷新记录
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT l.*, a.email as account_email, a.status as account_status
|
SELECT l.*, a.email as account_email, a.status as account_status
|
||||||
FROM account_refresh_logs l
|
FROM account_refresh_logs l
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
@@ -1815,8 +1814,7 @@ def api_get_failed_refresh_logs() -> Any:
|
|||||||
LEFT JOIN accounts a ON l.account_id = a.id
|
LEFT JOIN accounts a ON l.account_id = a.id
|
||||||
WHERE l.status = 'failed'
|
WHERE l.status = 'failed'
|
||||||
ORDER BY l.created_at DESC
|
ORDER BY l.created_at DESC
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
logs = []
|
logs = []
|
||||||
for row in cursor.fetchall():
|
for row in cursor.fetchall():
|
||||||
@@ -1841,27 +1839,22 @@ def api_get_refresh_stats() -> Any:
|
|||||||
"""获取刷新统计信息(统计当前失败状态的邮箱数量)"""
|
"""获取刷新统计信息(统计当前失败状态的邮箱数量)"""
|
||||||
db = get_db()
|
db = get_db()
|
||||||
|
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT MAX(created_at) as last_refresh_time
|
SELECT MAX(created_at) as last_refresh_time
|
||||||
FROM account_refresh_logs
|
FROM account_refresh_logs
|
||||||
WHERE refresh_type IN ('manual', 'manual_all', 'scheduled', 'retry')
|
WHERE refresh_type IN ('manual', 'manual_all', 'scheduled', 'retry')
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
last_refresh_time = row["last_refresh_time"] if row else None
|
last_refresh_time = row["last_refresh_time"] if row else None
|
||||||
|
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT COUNT(*) as total_accounts
|
SELECT COUNT(*) as total_accounts
|
||||||
FROM accounts
|
FROM accounts
|
||||||
WHERE status = 'active'
|
WHERE status = 'active'
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
total_accounts = cursor.fetchone()["total_accounts"]
|
total_accounts = cursor.fetchone()["total_accounts"]
|
||||||
|
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT COUNT(DISTINCT l.account_id) as failed_count
|
SELECT COUNT(DISTINCT l.account_id) as failed_count
|
||||||
FROM account_refresh_logs l
|
FROM account_refresh_logs l
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
@@ -1871,8 +1864,7 @@ def api_get_refresh_stats() -> Any:
|
|||||||
) latest ON l.account_id = latest.account_id AND l.created_at = latest.last_refresh
|
) latest ON l.account_id = latest.account_id AND l.created_at = latest.last_refresh
|
||||||
INNER JOIN accounts a ON l.account_id = a.id
|
INNER JOIN accounts a ON l.account_id = a.id
|
||||||
WHERE l.status = 'failed' AND a.status = 'active'
|
WHERE l.status = 'failed' AND a.status = 'active'
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
failed_count = cursor.fetchone()["failed_count"]
|
failed_count = cursor.fetchone()["failed_count"]
|
||||||
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
|
|||||||
@@ -35,13 +35,11 @@ def api_get_scheduler_status() -> Any:
|
|||||||
refresh_cron = settings_repo.get_setting("refresh_cron", "0 2 * * *")
|
refresh_cron = settings_repo.get_setting("refresh_cron", "0 2 * * *")
|
||||||
|
|
||||||
# 心跳
|
# 心跳
|
||||||
heartbeat_row = conn.execute(
|
heartbeat_row = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT value, updated_at
|
SELECT value, updated_at
|
||||||
FROM settings
|
FROM settings
|
||||||
WHERE key = 'scheduler_heartbeat'
|
WHERE key = 'scheduler_heartbeat'
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
heartbeat = None
|
heartbeat = None
|
||||||
heartbeat_age_seconds = None
|
heartbeat_age_seconds = None
|
||||||
@@ -78,28 +76,24 @@ def api_get_scheduler_status() -> Any:
|
|||||||
lock_info = {"locked": False}
|
lock_info = {"locked": False}
|
||||||
|
|
||||||
# 最近一次定时刷新(含手动触发 scheduled_manual)
|
# 最近一次定时刷新(含手动触发 scheduled_manual)
|
||||||
last_scheduled_run = conn.execute(
|
last_scheduled_run = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, trigger_source, status, started_at, finished_at,
|
SELECT id, trigger_source, status, started_at, finished_at,
|
||||||
total, success_count, failed_count, message, trace_id, requested_by_ip
|
total, success_count, failed_count, message, trace_id, requested_by_ip
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
last_scheduled = dict(last_scheduled_run) if last_scheduled_run else None
|
last_scheduled = dict(last_scheduled_run) if last_scheduled_run else None
|
||||||
|
|
||||||
running_run = conn.execute(
|
running_run = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, trigger_source, status, started_at, total, success_count, failed_count, trace_id
|
SELECT id, trigger_source, status, started_at, total, success_count, failed_count, trace_id
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE status = 'running'
|
WHERE status = 'running'
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
running = dict(running_run) if running_run else None
|
running = dict(running_run) if running_run else None
|
||||||
|
|
||||||
@@ -119,8 +113,7 @@ def api_get_scheduler_status() -> Any:
|
|||||||
future_runs = []
|
future_runs = []
|
||||||
else:
|
else:
|
||||||
# 按天数策略:基于最近一次已完成的 scheduled/scheduled_manual 计算 next_due
|
# 按天数策略:基于最近一次已完成的 scheduled/scheduled_manual 计算 next_due
|
||||||
row = conn.execute(
|
row = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT finished_at
|
SELECT finished_at
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
||||||
@@ -128,8 +121,7 @@ def api_get_scheduler_status() -> Any:
|
|||||||
AND finished_at IS NOT NULL
|
AND finished_at IS NOT NULL
|
||||||
ORDER BY finished_at DESC
|
ORDER BY finished_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
last_finished_at = row["finished_at"] if row else None
|
last_finished_at = row["finished_at"] if row else None
|
||||||
try:
|
try:
|
||||||
last_time = datetime.fromisoformat(last_finished_at) if last_finished_at else None
|
last_time = datetime.fromisoformat(last_finished_at) if last_finished_at else None
|
||||||
|
|||||||
@@ -47,13 +47,11 @@ def api_system_health() -> Any:
|
|||||||
db_ok = False
|
db_ok = False
|
||||||
|
|
||||||
# Scheduler 心跳
|
# Scheduler 心跳
|
||||||
heartbeat_row = conn.execute(
|
heartbeat_row = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT updated_at
|
SELECT updated_at
|
||||||
FROM settings
|
FROM settings
|
||||||
WHERE key = 'scheduler_heartbeat'
|
WHERE key = 'scheduler_heartbeat'
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
heartbeat_age_seconds = None
|
heartbeat_age_seconds = None
|
||||||
if heartbeat_row and heartbeat_row["updated_at"]:
|
if heartbeat_row and heartbeat_row["updated_at"]:
|
||||||
@@ -78,15 +76,13 @@ def api_system_health() -> Any:
|
|||||||
).fetchone()
|
).fetchone()
|
||||||
locked = bool(lock_row and lock_row["expires_at"] and lock_row["expires_at"] > time.time())
|
locked = bool(lock_row and lock_row["expires_at"] and lock_row["expires_at"] > time.time())
|
||||||
|
|
||||||
running_run = conn.execute(
|
running_run = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, trigger_source, started_at, trace_id
|
SELECT id, trigger_source, started_at, trace_id
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE status = 'running'
|
WHERE status = 'running'
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{
|
{
|
||||||
@@ -137,32 +133,26 @@ def api_system_diagnostics() -> Any:
|
|||||||
(now_ts,),
|
(now_ts,),
|
||||||
).fetchone()["c"]
|
).fetchone()["c"]
|
||||||
|
|
||||||
running_runs = conn.execute(
|
running_runs = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, trigger_source, started_at, trace_id
|
SELECT id, trigger_source, started_at, trace_id
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE status = 'running'
|
WHERE status = 'running'
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 5
|
LIMIT 5
|
||||||
"""
|
""").fetchall()
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
last_runs = conn.execute(
|
last_runs = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, trigger_source, status, started_at, finished_at, total, success_count, failed_count, trace_id
|
SELECT id, trigger_source, status, started_at, finished_at, total, success_count, failed_count, trace_id
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 10
|
LIMIT 10
|
||||||
"""
|
""").fetchall()
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
locks = conn.execute(
|
locks = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT name, owner_id, acquired_at, expires_at
|
SELECT name, owner_id, acquired_at, expires_at
|
||||||
FROM distributed_locks
|
FROM distributed_locks
|
||||||
ORDER BY name ASC
|
ORDER BY name ASC
|
||||||
"""
|
""").fetchall()
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
# 数据库升级状态(可验证)
|
# 数据库升级状态(可验证)
|
||||||
schema_version_row = conn.execute(
|
schema_version_row = conn.execute(
|
||||||
@@ -172,14 +162,12 @@ def api_system_diagnostics() -> Any:
|
|||||||
|
|
||||||
last_migration = None
|
last_migration = None
|
||||||
try:
|
try:
|
||||||
mig = conn.execute(
|
mig = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, from_version, to_version, status, started_at, finished_at, error, trace_id
|
SELECT id, from_version, to_version, status, started_at, finished_at, error, trace_id
|
||||||
FROM schema_migrations
|
FROM schema_migrations
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
last_migration = dict(mig) if mig else None
|
last_migration = dict(mig) if mig else None
|
||||||
except Exception:
|
except Exception:
|
||||||
last_migration = None
|
last_migration = None
|
||||||
@@ -225,14 +213,12 @@ def api_system_upgrade_status() -> Any:
|
|||||||
|
|
||||||
last_migration = None
|
last_migration = None
|
||||||
try:
|
try:
|
||||||
mig = conn.execute(
|
mig = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, from_version, to_version, status, started_at, finished_at, error, trace_id
|
SELECT id, from_version, to_version, status, started_at, finished_at, error, trace_id
|
||||||
FROM schema_migrations
|
FROM schema_migrations
|
||||||
ORDER BY started_at DESC
|
ORDER BY started_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
last_migration = dict(mig) if mig else None
|
last_migration = dict(mig) if mig else None
|
||||||
except Exception:
|
except Exception:
|
||||||
last_migration = None
|
last_migration = None
|
||||||
|
|||||||
+78
-156
@@ -92,19 +92,16 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
cursor.execute("BEGIN IMMEDIATE")
|
cursor.execute("BEGIN IMMEDIATE")
|
||||||
|
|
||||||
# 创建设置表
|
# 创建设置表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS settings (
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
value TEXT,
|
value TEXT,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 数据库迁移记录(用于升级可验证/可诊断)
|
# 数据库迁移记录(用于升级可验证/可诊断)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
from_version INTEGER NOT NULL,
|
from_version INTEGER NOT NULL,
|
||||||
@@ -115,14 +112,11 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
error TEXT,
|
error TEXT,
|
||||||
trace_id TEXT
|
trace_id TEXT
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_schema_migrations_started_at
|
CREATE INDEX IF NOT EXISTS idx_schema_migrations_started_at
|
||||||
ON schema_migrations(started_at)
|
ON schema_migrations(started_at)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 在锁内读取当前 schema 版本(保证一致性)
|
# 在锁内读取当前 schema 版本(保证一致性)
|
||||||
row = cursor.execute("SELECT value FROM settings WHERE key = ?", (DB_SCHEMA_VERSION_KEY,)).fetchone()
|
row = cursor.execute("SELECT value FROM settings WHERE key = ?", (DB_SCHEMA_VERSION_KEY,)).fetchone()
|
||||||
@@ -155,8 +149,7 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
# -------------------- Schema 创建/迁移(幂等) --------------------
|
# -------------------- Schema 创建/迁移(幂等) --------------------
|
||||||
|
|
||||||
# 分组表
|
# 分组表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS groups (
|
CREATE TABLE IF NOT EXISTS groups (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT UNIQUE NOT NULL,
|
name TEXT UNIQUE NOT NULL,
|
||||||
@@ -166,12 +159,10 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
is_system INTEGER DEFAULT 0,
|
is_system INTEGER DEFAULT 0,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 邮箱账号表
|
# 邮箱账号表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS accounts (
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
email TEXT UNIQUE NOT NULL,
|
email TEXT UNIQUE NOT NULL,
|
||||||
@@ -191,12 +182,10 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (group_id) REFERENCES groups (id)
|
FOREIGN KEY (group_id) REFERENCES groups (id)
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 临时邮箱表
|
# 临时邮箱表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS temp_emails (
|
CREATE TABLE IF NOT EXISTS temp_emails (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
email TEXT UNIQUE NOT NULL,
|
email TEXT UNIQUE NOT NULL,
|
||||||
@@ -204,12 +193,10 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 临时邮件表(存储从 GPTMail 获取的邮件)
|
# 临时邮件表(存储从 GPTMail 获取的邮件)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS temp_email_messages (
|
CREATE TABLE IF NOT EXISTS temp_email_messages (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
message_id TEXT UNIQUE NOT NULL,
|
message_id TEXT UNIQUE NOT NULL,
|
||||||
@@ -224,12 +211,10 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (email_address) REFERENCES temp_emails (email)
|
FOREIGN KEY (email_address) REFERENCES temp_emails (email)
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 刷新记录表(账号级)
|
# 刷新记录表(账号级)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS account_refresh_logs (
|
CREATE TABLE IF NOT EXISTS account_refresh_logs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
account_id INTEGER NOT NULL,
|
account_id INTEGER NOT NULL,
|
||||||
@@ -241,18 +226,14 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE
|
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_run_id
|
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_run_id
|
||||||
ON account_refresh_logs(run_id)
|
ON account_refresh_logs(run_id)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 审计日志表
|
# 审计日志表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
action TEXT NOT NULL,
|
action TEXT NOT NULL,
|
||||||
@@ -263,36 +244,28 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
trace_id TEXT,
|
trace_id TEXT,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_trace_id
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_trace_id
|
||||||
ON audit_logs(trace_id)
|
ON audit_logs(trace_id)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at
|
||||||
ON audit_logs(created_at)
|
ON audit_logs(created_at)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 标签表
|
# 标签表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS tags (
|
CREATE TABLE IF NOT EXISTS tags (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT UNIQUE NOT NULL,
|
name TEXT UNIQUE NOT NULL,
|
||||||
color TEXT NOT NULL,
|
color TEXT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 账号标签关联表
|
# 账号标签关联表
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS account_tags (
|
CREATE TABLE IF NOT EXISTS account_tags (
|
||||||
account_id INTEGER NOT NULL,
|
account_id INTEGER NOT NULL,
|
||||||
tag_id INTEGER NOT NULL,
|
tag_id INTEGER NOT NULL,
|
||||||
@@ -301,24 +274,20 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE
|
FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 分布式锁(用于刷新冲突控制/多进程一致性)
|
# 分布式锁(用于刷新冲突控制/多进程一致性)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS distributed_locks (
|
CREATE TABLE IF NOT EXISTS distributed_locks (
|
||||||
name TEXT PRIMARY KEY,
|
name TEXT PRIMARY KEY,
|
||||||
owner_id TEXT NOT NULL,
|
owner_id TEXT NOT NULL,
|
||||||
acquired_at REAL NOT NULL,
|
acquired_at REAL NOT NULL,
|
||||||
expires_at REAL NOT NULL
|
expires_at REAL NOT NULL
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 导出二次验证 Token(持久化,支持重启/多进程)
|
# 导出二次验证 Token(持久化,支持重启/多进程)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS export_verify_tokens (
|
CREATE TABLE IF NOT EXISTS export_verify_tokens (
|
||||||
token TEXT PRIMARY KEY,
|
token TEXT PRIMARY KEY,
|
||||||
ip TEXT,
|
ip TEXT,
|
||||||
@@ -326,24 +295,20 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
expires_at REAL NOT NULL,
|
expires_at REAL NOT NULL,
|
||||||
created_at REAL NOT NULL
|
created_at REAL NOT NULL
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 登录速率限制(持久化,支持重启/多进程)
|
# 登录速率限制(持久化,支持重启/多进程)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS login_attempts (
|
CREATE TABLE IF NOT EXISTS login_attempts (
|
||||||
ip TEXT PRIMARY KEY,
|
ip TEXT PRIMARY KEY,
|
||||||
count INTEGER NOT NULL,
|
count INTEGER NOT NULL,
|
||||||
last_attempt_at REAL NOT NULL,
|
last_attempt_at REAL NOT NULL,
|
||||||
locked_until_at REAL
|
locked_until_at REAL
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 刷新运行记录(用于“最近触发/来源/统计/运行中状态”的可验证性)
|
# 刷新运行记录(用于“最近触发/来源/统计/运行中状态”的可验证性)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS refresh_runs (
|
CREATE TABLE IF NOT EXISTS refresh_runs (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
trigger_source TEXT NOT NULL,
|
trigger_source TEXT NOT NULL,
|
||||||
@@ -358,20 +323,15 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
message TEXT,
|
message TEXT,
|
||||||
trace_id TEXT
|
trace_id TEXT
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_refresh_runs_started_at
|
CREATE INDEX IF NOT EXISTS idx_refresh_runs_started_at
|
||||||
ON refresh_runs(started_at)
|
ON refresh_runs(started_at)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_refresh_runs_trigger_source
|
CREATE INDEX IF NOT EXISTS idx_refresh_runs_trigger_source
|
||||||
ON refresh_runs(trigger_source)
|
ON refresh_runs(trigger_source)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 兼容旧 schema:补齐缺失列
|
# 兼容旧 schema:补齐缺失列
|
||||||
cursor.execute("PRAGMA table_info(accounts)")
|
cursor.execute("PRAGMA table_info(accounts)")
|
||||||
@@ -420,20 +380,16 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
cursor.execute("ALTER TABLE audit_logs ADD COLUMN trace_id TEXT")
|
cursor.execute("ALTER TABLE audit_logs ADD COLUMN trace_id TEXT")
|
||||||
|
|
||||||
# 默认分组
|
# 默认分组
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO groups (name, description, color)
|
INSERT OR IGNORE INTO groups (name, description, color)
|
||||||
VALUES ('默认分组', '未分组的邮箱', '#666666')
|
VALUES ('默认分组', '未分组的邮箱', '#666666')
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 临时邮箱分组(系统分组)
|
# 临时邮箱分组(系统分组)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO groups (name, description, color, is_system)
|
INSERT OR IGNORE INTO groups (name, description, color, is_system)
|
||||||
VALUES ('临时邮箱', 'GPTMail 临时邮箱服务', '#00bcf2', 1)
|
VALUES ('临时邮箱', 'GPTMail 临时邮箱服务', '#00bcf2', 1)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 初始化默认设置:登录密码(自动迁移明文 -> 哈希)
|
# 初始化默认设置:登录密码(自动迁移明文 -> 哈希)
|
||||||
cursor.execute("SELECT value FROM settings WHERE key = 'login_password'")
|
cursor.execute("SELECT value FROM settings WHERE key = 'login_password'")
|
||||||
@@ -467,98 +423,69 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 初始化刷新配置
|
# 初始化刷新配置
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('refresh_interval_days', '30')
|
VALUES ('refresh_interval_days', '30')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('refresh_delay_seconds', '5')
|
VALUES ('refresh_delay_seconds', '5')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('refresh_cron', '0 2 * * *')
|
VALUES ('refresh_cron', '0 2 * * *')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('use_cron_schedule', 'false')
|
VALUES ('use_cron_schedule', 'false')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('enable_scheduled_refresh', 'true')
|
VALUES ('enable_scheduled_refresh', 'true')
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 初始化轮询配置
|
# 初始化轮询配置
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('enable_auto_polling', 'false')
|
VALUES ('enable_auto_polling', 'false')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('polling_interval', '10')
|
VALUES ('polling_interval', '10')
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
INSERT OR IGNORE INTO settings (key, value)
|
INSERT OR IGNORE INTO settings (key, value)
|
||||||
VALUES ('polling_count', '5')
|
VALUES ('polling_count', '5')
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 索引(性能基线)
|
# 索引(性能基线)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_accounts_last_refresh_at
|
CREATE INDEX IF NOT EXISTS idx_accounts_last_refresh_at
|
||||||
ON accounts(last_refresh_at)
|
ON accounts(last_refresh_at)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_accounts_status
|
CREATE INDEX IF NOT EXISTS idx_accounts_status
|
||||||
ON accounts(status)
|
ON accounts(status)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_accounts_group_id
|
CREATE INDEX IF NOT EXISTS idx_accounts_group_id
|
||||||
ON accounts(group_id)
|
ON accounts(group_id)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_account_id
|
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_account_id
|
||||||
ON account_refresh_logs(account_id)
|
ON account_refresh_logs(account_id)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_account_id_id
|
CREATE INDEX IF NOT EXISTS idx_account_refresh_logs_account_id_id
|
||||||
ON account_refresh_logs(account_id, id)
|
ON account_refresh_logs(account_id, id)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_account_tags_tag_id
|
CREATE INDEX IF NOT EXISTS idx_account_tags_tag_id
|
||||||
ON account_tags(tag_id)
|
ON account_tags(tag_id)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# v5: Telegram 推送去重日志(BUG-00011 P2)
|
# v5: Telegram 推送去重日志(BUG-00011 P2)
|
||||||
cursor.execute(
|
cursor.execute("""
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS telegram_push_log (
|
CREATE TABLE IF NOT EXISTS telegram_push_log (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
account_id INTEGER NOT NULL,
|
account_id INTEGER NOT NULL,
|
||||||
@@ -566,20 +493,15 @@ def init_db(database_path: Optional[str] = None):
|
|||||||
pushed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%S', 'now')),
|
pushed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%S', 'now')),
|
||||||
UNIQUE(account_id, message_id)
|
UNIQUE(account_id, message_id)
|
||||||
)
|
)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_telegram_push_log_account_id
|
CREATE INDEX IF NOT EXISTS idx_telegram_push_log_account_id
|
||||||
ON telegram_push_log(account_id)
|
ON telegram_push_log(account_id)
|
||||||
"""
|
""")
|
||||||
)
|
cursor.execute("""
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_telegram_push_log_pushed_at
|
CREATE INDEX IF NOT EXISTS idx_telegram_push_log_pushed_at
|
||||||
ON telegram_push_log(pushed_at)
|
ON telegram_push_log(pushed_at)
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
# 迁移现有明文数据为加密数据
|
# 迁移现有明文数据为加密数据
|
||||||
migrate_sensitive_data(conn)
|
migrate_sensitive_data(conn)
|
||||||
|
|||||||
@@ -22,14 +22,12 @@ def load_accounts(group_id: int = None) -> List[Dict]:
|
|||||||
(group_id,),
|
(group_id,),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT a.*, g.name as group_name, g.color as group_color
|
SELECT a.*, g.name as group_name, g.color as group_color
|
||||||
FROM accounts a
|
FROM accounts a
|
||||||
LEFT JOIN groups g ON a.group_id = g.id
|
LEFT JOIN groups g ON a.group_id = g.id
|
||||||
ORDER BY a.created_at DESC
|
ORDER BY a.created_at DESC
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
tags_by_account: Dict[int, List[Dict[str, Any]]] = {}
|
tags_by_account: Dict[int, List[Dict[str, Any]]] = {}
|
||||||
@@ -417,13 +415,11 @@ def update_telegram_cursor(account_id: int, checked_at: str) -> None:
|
|||||||
def get_telegram_push_accounts() -> List[Dict]:
|
def get_telegram_push_accounts() -> List[Dict]:
|
||||||
"""返回所有 telegram_push_enabled=1 且非 disabled 状态的账号。"""
|
"""返回所有 telegram_push_enabled=1 且非 disabled 状态的账号。"""
|
||||||
db = get_db()
|
db = get_db()
|
||||||
rows = db.execute(
|
rows = db.execute("""SELECT a.id, a.email, a.provider, a.client_id, a.refresh_token,
|
||||||
"""SELECT a.id, a.email, a.provider, a.client_id, a.refresh_token,
|
|
||||||
a.imap_host, a.imap_port, a.imap_password,
|
a.imap_host, a.imap_port, a.imap_password,
|
||||||
a.telegram_last_checked_at, a.group_id,
|
a.telegram_last_checked_at, a.group_id,
|
||||||
g.proxy_url
|
g.proxy_url
|
||||||
FROM accounts a
|
FROM accounts a
|
||||||
LEFT JOIN groups g ON a.group_id = g.id
|
LEFT JOIN groups g ON a.group_id = g.id
|
||||||
WHERE a.telegram_push_enabled = 1 AND a.status != 'disabled'"""
|
WHERE a.telegram_push_enabled = 1 AND a.status != 'disabled'""").fetchall()
|
||||||
).fetchall()
|
|
||||||
return [dict(r) for r in rows]
|
return [dict(r) for r in rows]
|
||||||
@@ -9,14 +9,12 @@ from outlook_web.db import get_db
|
|||||||
def load_groups() -> List[Dict]:
|
def load_groups() -> List[Dict]:
|
||||||
"""加载所有分组(临时邮箱分组排在最前面)"""
|
"""加载所有分组(临时邮箱分组排在最前面)"""
|
||||||
db = get_db()
|
db = get_db()
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT * FROM groups
|
SELECT * FROM groups
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE WHEN name = '临时邮箱' THEN 0 ELSE 1 END,
|
CASE WHEN name = '临时邮箱' THEN 0 ELSE 1 END,
|
||||||
id
|
id
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
return [dict(row) for row in rows]
|
return [dict(row) for row in rows]
|
||||||
|
|
||||||
|
|||||||
@@ -316,8 +316,7 @@ def stream_trigger_scheduled_refresh(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (not force) and (not use_cron):
|
if (not force) and (not use_cron):
|
||||||
row = conn.execute(
|
row = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT finished_at
|
SELECT finished_at
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
WHERE trigger_source IN ('scheduled', 'scheduled_manual')
|
||||||
@@ -325,8 +324,7 @@ def stream_trigger_scheduled_refresh(
|
|||||||
AND finished_at IS NOT NULL
|
AND finished_at IS NOT NULL
|
||||||
ORDER BY finished_at DESC
|
ORDER BY finished_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
if row and row["finished_at"]:
|
if row and row["finished_at"]:
|
||||||
try:
|
try:
|
||||||
@@ -564,8 +562,7 @@ def refresh_failed_accounts(
|
|||||||
"""重试所有失败的账号(非流式)"""
|
"""重试所有失败的账号(非流式)"""
|
||||||
lock_owner_id = uuid.uuid4().hex
|
lock_owner_id = uuid.uuid4().hex
|
||||||
|
|
||||||
cursor = db.execute(
|
cursor = db.execute("""
|
||||||
"""
|
|
||||||
SELECT DISTINCT a.id, a.email, a.client_id, a.refresh_token, a.group_id
|
SELECT DISTINCT a.id, a.email, a.client_id, a.refresh_token, a.group_id
|
||||||
FROM accounts a
|
FROM accounts a
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
@@ -575,8 +572,7 @@ def refresh_failed_accounts(
|
|||||||
) latest ON a.id = latest.account_id
|
) latest ON a.id = latest.account_id
|
||||||
INNER JOIN account_refresh_logs l ON a.id = l.account_id AND l.created_at = latest.last_refresh
|
INNER JOIN account_refresh_logs l ON a.id = l.account_id AND l.created_at = latest.last_refresh
|
||||||
WHERE l.status = 'failed' AND a.status = 'active'
|
WHERE l.status = 'failed' AND a.status = 'active'
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
accounts = cursor.fetchall()
|
accounts = cursor.fetchall()
|
||||||
|
|
||||||
total = len(accounts)
|
total = len(accounts)
|
||||||
|
|||||||
@@ -233,8 +233,7 @@ def scheduled_refresh_task(app, test_refresh_token):
|
|||||||
|
|
||||||
# 按天数策略:未到周期则跳过(不产生账号级刷新日志)
|
# 按天数策略:未到周期则跳过(不产生账号级刷新日志)
|
||||||
if not use_cron:
|
if not use_cron:
|
||||||
row = conn.execute(
|
row = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT finished_at
|
SELECT finished_at
|
||||||
FROM refresh_runs
|
FROM refresh_runs
|
||||||
WHERE trigger_source = 'scheduled'
|
WHERE trigger_source = 'scheduled'
|
||||||
@@ -242,8 +241,7 @@ def scheduled_refresh_task(app, test_refresh_token):
|
|||||||
AND finished_at IS NOT NULL
|
AND finished_at IS NOT NULL
|
||||||
ORDER BY finished_at DESC
|
ORDER BY finished_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""").fetchone()
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
if row and row["finished_at"]:
|
if row and row["finished_at"]:
|
||||||
try:
|
try:
|
||||||
@@ -278,14 +276,12 @@ def scheduled_refresh_task(app, test_refresh_token):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# PRD-00005 / TDD-00005:定时刷新只处理 Outlook 账号(IMAP 账号无 OAuth token 刷新语义)
|
# PRD-00005 / TDD-00005:定时刷新只处理 Outlook 账号(IMAP 账号无 OAuth token 刷新语义)
|
||||||
accounts = conn.execute(
|
accounts = conn.execute("""
|
||||||
"""
|
|
||||||
SELECT id, email, client_id, refresh_token, group_id
|
SELECT id, email, client_id, refresh_token, group_id
|
||||||
FROM accounts
|
FROM accounts
|
||||||
WHERE status = 'active'
|
WHERE status = 'active'
|
||||||
AND (account_type = 'outlook' OR account_type IS NULL)
|
AND (account_type = 'outlook' OR account_type IS NULL)
|
||||||
"""
|
""").fetchall()
|
||||||
).fetchall()
|
|
||||||
total = len(accounts)
|
total = len(accounts)
|
||||||
|
|
||||||
# 更新 run_id 的 total
|
# 更新 run_id 的 total
|
||||||
|
|||||||
Reference in new issue
Block a user