build(security): add migration and CI security gates

This commit is contained in:
2026-07-28 16:41:36 +08:00
parent 6b43b77c3b
commit 4bfe2900fb
15 changed files with 186 additions and 97 deletions
+1 -66
View File
@@ -3,7 +3,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import select
from app.config import settings, validate_security_settings
from app.database import engine, Base, async_session
from app.database import engine, async_session
from app.api import router as api_router
from app.api import auth, users, customers, visits, work_plans, mini_business, key_visits
from app.api import dashboard, upload, export, import_data, wecom, daily_notes, ai_summary, system_config, leaves
@@ -15,71 +15,6 @@ from app.services.scheduler_manager import start_scheduler, shutdown_scheduler
@asynccontextmanager
async def lifespan(app: FastAPI):
validate_security_settings()
# Startup: create tables if not exists (for dev convenience)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Add columns that may be missing from older tables
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE customers ADD COLUMN IF NOT EXISTS remarks TEXT DEFAULT ''"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS visitor_name VARCHAR(50) DEFAULT ''"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS visitor_phone VARCHAR(20) DEFAULT ''"
))
# edit_log columns for change tracking
for tbl in ["visits", "daily_notes", "work_plans", "mini_business", "key_visits"]:
await conn.run_sync(lambda c, t=tbl: c.exec_driver_sql(
f"ALTER TABLE {t} ADD COLUMN IF NOT EXISTS edit_log JSONB DEFAULT '[]'"
))
# New columns for v0.3
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE users ADD COLUMN IF NOT EXISTS require_report BOOLEAN DEFAULT TRUE"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE customers ADD COLUMN IF NOT EXISTS last_visit_date DATE"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE customers ADD COLUMN IF NOT EXISTS last_visit_manager_id UUID"
))
# New columns for v0.4
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS companion_names TEXT[] DEFAULT '{}'"
))
# v0.6 — manager color tag
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE users ADD COLUMN IF NOT EXISTS color VARCHAR(7)"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE UNIQUE INDEX IF NOT EXISTS uq_users_wecom_userid "
"ON users (wecom_userid) WHERE wecom_userid IS NOT NULL"
))
# system_config table for v0.5
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE TABLE IF NOT EXISTS system_config (key VARCHAR(64) PRIMARY KEY, value TEXT DEFAULT '')"
))
# leaves table for v0.7
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE TABLE IF NOT EXISTS leaves ("
" id UUID PRIMARY KEY DEFAULT gen_random_uuid(),"
" manager_id UUID NOT NULL REFERENCES users(id),"
" leave_type VARCHAR(20) NOT NULL DEFAULT '事假',"
" start_date DATE NOT NULL,"
" end_date DATE NOT NULL,"
" reason VARCHAR(500) DEFAULT '',"
" submitted_by UUID NOT NULL REFERENCES users(id),"
" created_at TIMESTAMPTZ DEFAULT now(),"
" updated_at TIMESTAMPTZ DEFAULT now()"
")"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE INDEX IF NOT EXISTS idx_leaves_manager_id ON leaves(manager_id)"
))
await conn.run_sync(lambda c: c.exec_driver_sql(
"CREATE INDEX IF NOT EXISTS idx_leaves_dates ON leaves(start_date, end_date)"
))
# Read notification_time from DB (or use default 17:30)
notification_hour, notification_minute = 17, 30
async with async_session() as db: