feat: add admin social login bindings
This commit is contained in:
101
db/admin.py
101
db/admin.py
@@ -245,6 +245,107 @@ def update_admin_username(old_username: str, new_username: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
# ==================== 管理员聚合登录绑定 ====================
|
||||
|
||||
|
||||
def find_admin_social_login_binding_by_identity(provider: str, social_uid: str):
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM admin_social_login_bindings WHERE provider = ? AND social_uid = ?",
|
||||
(provider, social_uid),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def find_admin_social_login_binding(admin_id: int, provider: str):
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM admin_social_login_bindings WHERE admin_id = ? AND provider = ?",
|
||||
(int(admin_id), provider),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def upsert_admin_social_login_binding(*, admin_id: int, provider: str, social_uid: str, nickname: str = "", avatar_url: str = ""):
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
now = get_cst_now_str()
|
||||
try:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO admin_social_login_bindings (
|
||||
admin_id, provider, social_uid, nickname, avatar_url, created_at, updated_at, last_login_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(admin_id, provider) DO UPDATE SET
|
||||
social_uid = excluded.social_uid,
|
||||
nickname = excluded.nickname,
|
||||
avatar_url = excluded.avatar_url,
|
||||
updated_at = excluded.updated_at,
|
||||
last_login_at = excluded.last_login_at
|
||||
""",
|
||||
(
|
||||
int(admin_id),
|
||||
provider,
|
||||
social_uid,
|
||||
str(nickname or "")[:128],
|
||||
str(avatar_url or "")[:512],
|
||||
now,
|
||||
now,
|
||||
now,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
except sqlite3.IntegrityError:
|
||||
conn.rollback()
|
||||
return None
|
||||
return find_admin_social_login_binding(admin_id, provider)
|
||||
|
||||
|
||||
def update_admin_social_login_binding_profile(binding_id: int, *, nickname: str = "", avatar_url: str = "") -> bool:
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
UPDATE admin_social_login_bindings
|
||||
SET nickname = ?, avatar_url = ?, updated_at = ?, last_login_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(str(nickname or "")[:128], str(avatar_url or "")[:512], get_cst_now_str(), get_cst_now_str(), int(binding_id)),
|
||||
)
|
||||
conn.commit()
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def list_admin_social_login_bindings(admin_id: int) -> list[dict]:
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT * FROM admin_social_login_bindings
|
||||
WHERE admin_id = ?
|
||||
ORDER BY created_at ASC
|
||||
""",
|
||||
(int(admin_id),),
|
||||
)
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
|
||||
def delete_admin_social_login_binding(admin_id: int, provider: str) -> bool:
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"DELETE FROM admin_social_login_bindings WHERE admin_id = ? AND provider = ?",
|
||||
(int(admin_id), provider),
|
||||
)
|
||||
conn.commit()
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def get_system_stats() -> dict:
|
||||
"""获取系统统计信息"""
|
||||
with db_pool.get_db() as conn:
|
||||
|
||||
@@ -77,6 +77,7 @@ def _get_migration_steps():
|
||||
(20, _migrate_to_v20),
|
||||
(21, _migrate_to_v21),
|
||||
(22, _migrate_to_v22),
|
||||
(23, _migrate_to_v23),
|
||||
]
|
||||
|
||||
|
||||
@@ -1002,3 +1003,33 @@ def _migrate_to_v22(conn):
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_pending_binds_expires ON social_pending_binds(expires_at)")
|
||||
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _migrate_to_v23(conn):
|
||||
"""迁移到版本23 - 管理员聚合登录绑定。"""
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS admin_social_login_bindings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
admin_id INTEGER NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
social_uid TEXT NOT NULL,
|
||||
nickname TEXT DEFAULT '',
|
||||
avatar_url TEXT DEFAULT '',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login_at TIMESTAMP,
|
||||
UNIQUE (provider, social_uid),
|
||||
UNIQUE (admin_id, provider),
|
||||
FOREIGN KEY (admin_id) REFERENCES admins (id) ON DELETE CASCADE
|
||||
)
|
||||
"""
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_admin_social_login_bindings_admin ON admin_social_login_bindings(admin_id)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_admin_social_login_bindings_provider_uid ON admin_social_login_bindings(provider, social_uid)"
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
|
||||
24
db/schema.py
24
db/schema.py
@@ -276,6 +276,26 @@ def ensure_schema(conn) -> None:
|
||||
"""
|
||||
)
|
||||
|
||||
# 管理员聚合登录绑定表
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS admin_social_login_bindings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
admin_id INTEGER NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
social_uid TEXT NOT NULL,
|
||||
nickname TEXT DEFAULT '',
|
||||
avatar_url TEXT DEFAULT '',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login_at TIMESTAMP,
|
||||
UNIQUE (provider, social_uid),
|
||||
UNIQUE (admin_id, provider),
|
||||
FOREIGN KEY (admin_id) REFERENCES admins (id) ON DELETE CASCADE
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
# 聚合登录短期待绑定凭证表
|
||||
cursor.execute(
|
||||
"""
|
||||
@@ -432,6 +452,10 @@ def ensure_schema(conn) -> None:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_passkeys_owner_last_used ON passkeys(owner_type, owner_id, last_used_at)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_login_bindings_user ON social_login_bindings(user_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_login_bindings_provider_uid ON social_login_bindings(provider, social_uid)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_admin_social_login_bindings_admin ON admin_social_login_bindings(admin_id)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_admin_social_login_bindings_provider_uid ON admin_social_login_bindings(provider, social_uid)"
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_pending_binds_token ON social_pending_binds(token)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_pending_binds_provider_uid ON social_pending_binds(provider, social_uid)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_social_pending_binds_expires ON social_pending_binds(expires_at)")
|
||||
|
||||
Reference in New Issue
Block a user