feat: add Space aggregate login

This commit is contained in:
237899745
2026-05-27 20:39:46 +08:00
parent e725db79a9
commit 056948612a
136 changed files with 2405 additions and 322 deletions

View File

@@ -297,6 +297,156 @@ def get_user_by_username(username):
return _get_user_by_field("username", username)
# ==================== 聚合登录绑定 ====================
def cleanup_expired_social_pending_binds() -> None:
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM social_pending_binds WHERE expires_at < ?", (get_cst_now_str(),))
conn.commit()
def create_social_pending_bind(*, token: str, provider: str, social_uid: str, nickname: str = "", avatar_url: str = "", expires_at: str) -> dict:
cleanup_expired_social_pending_binds()
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"DELETE FROM social_pending_binds WHERE provider = ? AND social_uid = ?",
(provider, social_uid),
)
cursor.execute(
"""
INSERT INTO social_pending_binds (token, provider, social_uid, nickname, avatar_url, created_at, expires_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
token,
provider,
social_uid,
str(nickname or "")[:128],
str(avatar_url or "")[:512],
get_cst_now_str(),
expires_at,
),
)
conn.commit()
cursor.execute("SELECT * FROM social_pending_binds WHERE token = ?", (token,))
return _row_to_dict(cursor.fetchone())
def get_social_pending_bind(token: str):
cleanup_expired_social_pending_binds()
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM social_pending_binds WHERE token = ?", ((token or "").strip(),))
return _row_to_dict(cursor.fetchone())
def delete_social_pending_bind(token: str) -> bool:
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM social_pending_binds WHERE token = ?", ((token or "").strip(),))
conn.commit()
return cursor.rowcount > 0
def find_social_login_binding(provider: str, social_uid: str):
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM social_login_bindings WHERE provider = ? AND social_uid = ?",
(provider, social_uid),
)
return _row_to_dict(cursor.fetchone())
def find_user_social_login_binding(user_id: int, provider: str):
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM social_login_bindings WHERE user_id = ? AND provider = ?",
(int(user_id), provider),
)
return _row_to_dict(cursor.fetchone())
def upsert_social_login_binding(*, user_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 social_login_bindings (
user_id, provider, social_uid, nickname, avatar_url, created_at, updated_at, last_login_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(user_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(user_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_user_social_login_binding(user_id, provider)
def update_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 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_social_login_bindings(user_id: int) -> list[dict]:
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"""
SELECT * FROM social_login_bindings
WHERE user_id = ?
ORDER BY created_at ASC
""",
(int(user_id),),
)
return [dict(row) for row in cursor.fetchall()]
def delete_social_login_binding(user_id: int, provider: str) -> bool:
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"DELETE FROM social_login_bindings WHERE user_id = ? AND provider = ?",
(int(user_id), provider),
)
conn.commit()
return cursor.rowcount > 0
def _normalize_limit_offset(limit, offset, *, max_limit: int = 500):
normalized_limit = None
if limit is not None: