Release v1.0.56: Credential & Browser Profile Manager
All checks were successful
技能自动化发布 / release (push) Successful in 7s
All checks were successful
技能自动化发布 / release (push) Successful in 7s
This commit is contained in:
@@ -1,21 +1,124 @@
|
||||
"""SQLite connection and schema bootstrap."""
|
||||
import sqlite3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
SQLite connection with schema migration (v1 -> v2).
|
||||
|
||||
from db.schema import ACCOUNTS_TABLE_SQL
|
||||
Migration strategy:
|
||||
- Check PRAGMA user_version.
|
||||
- If version < 2 and old 'accounts' table exists, back it up as
|
||||
'accounts_legacy_backup_<timestamp>' before creating new tables.
|
||||
- New tables are created with CREATE TABLE IF NOT EXISTS.
|
||||
- Logs all migration steps.
|
||||
"""
|
||||
import os
|
||||
import sqlite3
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from db.schema import ALL_DDL, SCHEMA_VERSION
|
||||
from util.constants import SKILL_SLUG
|
||||
from util.logging_config import get_skill_logger
|
||||
from util.runtime_paths import get_db_path
|
||||
|
||||
|
||||
def get_conn():
|
||||
return sqlite3.connect(get_db_path())
|
||||
def get_conn() -> sqlite3.Connection:
|
||||
"""Open a connection to the skill database (autocommit off)."""
|
||||
conn = sqlite3.connect(get_db_path())
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.execute("PRAGMA foreign_keys=OFF")
|
||||
return conn
|
||||
|
||||
|
||||
def init_db():
|
||||
def _backup_old_accounts(conn: sqlite3.Connection) -> bool:
|
||||
"""Rename old 'accounts' table to 'accounts_legacy_backup_<ts>' if it exists with old schema.
|
||||
Returns True if backup was performed."""
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'"
|
||||
)
|
||||
if not cur.fetchone():
|
||||
return False
|
||||
|
||||
# Check if it's the old schema (has 'phone' column but no 'platform_key')
|
||||
# We just check the column count — old schema had fewer columns.
|
||||
cur.execute("PRAGMA table_info(accounts)")
|
||||
columns = [row[1] for row in cur.fetchall()]
|
||||
# Old schema: id, name, platform, phone, profile_dir, url, extra_json, created_at, updated_at = 9
|
||||
# New schema: many more. If it looks like old, back it up.
|
||||
has_legacy_structure = "phone" in columns and "platform_key" not in columns
|
||||
if not has_legacy_structure:
|
||||
return False
|
||||
|
||||
ts = int(time.time())
|
||||
backup_name = f"accounts_legacy_backup_{ts}"
|
||||
cur.execute(f"ALTER TABLE accounts RENAME TO {backup_name}")
|
||||
conn.commit()
|
||||
logger = get_skill_logger()
|
||||
logger.info(
|
||||
"schema_migration_backup old_table=accounts backup_table=%s",
|
||||
backup_name,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def _check_schema_version(conn: sqlite3.Connection) -> int:
|
||||
cur = conn.cursor()
|
||||
cur.execute("PRAGMA user_version")
|
||||
row = cur.fetchone()
|
||||
return int(row[0]) if row else 0
|
||||
|
||||
|
||||
def _create_new_tables(conn: sqlite3.Connection) -> None:
|
||||
"""Execute ALL_DDL (all CREATE TABLE/INDEX IF NOT EXISTS)."""
|
||||
cur = conn.cursor()
|
||||
cur.executescript(ALL_DDL)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
"""Idempotent: migrate if needed, create tables, set schema version."""
|
||||
log = get_skill_logger()
|
||||
log.info("schema_init_start")
|
||||
conn = get_conn()
|
||||
try:
|
||||
old_version = _check_schema_version(conn)
|
||||
log.info("schema_current_version=%s", old_version)
|
||||
|
||||
if old_version < SCHEMA_VERSION:
|
||||
if old_version < 2:
|
||||
backed_up = _backup_old_accounts(conn)
|
||||
if backed_up:
|
||||
log.info("schema_init_backup_done old_version=%s", old_version)
|
||||
_create_new_tables(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute(f"PRAGMA user_version = {SCHEMA_VERSION}")
|
||||
conn.commit()
|
||||
log.info(
|
||||
"schema_init_done version=%s migrated_from=%s",
|
||||
SCHEMA_VERSION,
|
||||
old_version,
|
||||
)
|
||||
else:
|
||||
log.info("schema_init_uptodate version=%s", SCHEMA_VERSION)
|
||||
|
||||
# Ensure all tables exist even if schema version matches
|
||||
_create_new_tables(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute(f"PRAGMA user_version = {SCHEMA_VERSION}")
|
||||
conn.commit()
|
||||
except Exception:
|
||||
log.exception("schema_init_failure")
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_db_version() -> int:
|
||||
"""Return current PRAGMA user_version without initializing."""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'")
|
||||
if not cur.fetchone():
|
||||
cur.executescript(ACCOUNTS_TABLE_SQL)
|
||||
conn.commit()
|
||||
cur.execute("PRAGMA user_version")
|
||||
row = cur.fetchone()
|
||||
return int(row[0]) if row else 0
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user