Add PostgreSQL production backend to eliminate SQLite lock contention.

Support DATABASE_URL with connection pooling, pg_dump backups, SQLite migration script, and deploy_postgres.sh with docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-01 08:11:42 +08:00
parent 39eac983ff
commit 52aca456e9
23 changed files with 1208 additions and 150 deletions
+9 -7
View File
@@ -7,9 +7,8 @@
from __future__ import annotations
import json
import sqlite3
from datetime import datetime
from typing import Any, Callable, Optional
from typing import Any, Optional
from zoneinfo import ZoneInfo
TZ = ZoneInfo("Asia/Shanghai")
@@ -26,7 +25,7 @@ CREATE TABLE IF NOT EXISTS ai_messages (
"""
def ensure_ai_messages_table(conn: sqlite3.Connection) -> None:
def ensure_ai_messages_table(conn) -> None:
conn.execute(CREATE_SQL)
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_ai_messages_created ON ai_messages(created_at DESC)"
@@ -34,7 +33,7 @@ def ensure_ai_messages_table(conn: sqlite3.Connection) -> None:
def insert_ai_message(
conn: sqlite3.Connection,
conn,
*,
kind: str,
title: str,
@@ -45,13 +44,16 @@ def insert_ai_message(
now = datetime.now(TZ).strftime("%Y-%m-%d %H:%M:%S")
cur = conn.execute(
"""INSERT INTO ai_messages (kind, title, content, meta_json, created_at)
VALUES (?,?,?,?,?)""",
VALUES (?,?,?,?,?) RETURNING id""",
(kind, title, content, json.dumps(meta or {}, ensure_ascii=False), now),
)
return int(cur.lastrowid)
row = cur.fetchone()
if row is not None:
return int(row["id"] if isinstance(row, dict) else row[0])
return int(cur.lastrowid or 0)
def list_ai_messages(conn: sqlite3.Connection, *, limit: int = 100) -> list[dict]:
def list_ai_messages(conn, *, limit: int = 100) -> list[dict]:
ensure_ai_messages_table(conn)
rows = conn.execute(
"SELECT * FROM ai_messages ORDER BY id DESC LIMIT ?",