This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
"""Nancode Portal 后端 — SQLite 持久化链接 + SSO Cookie 认证"""
import os, sqlite3
from contextlib import contextmanager
from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from jose import jwt, JWTError
# ── 配置 ─────────────────────────────────
DB_PATH = os.path.join(os.path.dirname(__file__), "portal.db")
SSO_SECRET_KEY = "your-secret-key-change-in-production" # 与 SSO 中心一致
ALGORITHM = "HS256"
# ── 数据库 ───────────────────────────────
@contextmanager
def get_db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()
def init_db():
with get_db() as conn:
conn.execute("""CREATE TABLE IF NOT EXISTS links (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
desc TEXT DEFAULT '',
icon TEXT DEFAULT '',
sort_order INTEGER DEFAULT 0
)""")
# 首次启动:如果表为空则写入默认数据
count = conn.execute("SELECT COUNT(*) FROM links").fetchone()[0]
if count == 0:
defaults = [
('1','个人博客','http://129.211.173.87:8888','Next.js技术博客,个人技术文章与生活记录','book-open',1),
('2','相册查看器','http://129.211.173.87:8892','图片相册浏览与管理工具','image',2),
('3','快乐游戏','http://129.211.173.87:8895','Flask小游戏集合,休闲益智','gamepad-2',3),
('4','网游引擎','http://129.211.173.87:8900','网络游戏引擎服务平台','server',4),
('5','有声书播放器','http://129.211.173.87:8899','在线有声书播放与TTS服务','headphones',5),
('6','WanAndroid','http://129.211.173.87:8893','Material Design风格的Web应用','smartphone',6),
('7','FumaMX CRM','http://129.211.173.87:10000','客户关系管理系统','briefcase',7),
('8','AI聊天服务','http://129.211.173.87:8890','AI对话机器人服务','bot',8),
('9','新媒体平台','http://129.211.173.87:18888','CMS内容管理系统与网站后台','layout-dashboard',9),
('10','微信小程序桥接','http://129.211.173.87:5000','微信-Nanobot消息桥接服务','message-circle',10),
('11','音频录制工具','http://129.211.173.87:8901','在线音频录制与处理工具','mic',11),
('12','粒子实验室','http://129.211.173.87:8902','粒子特效与图形渲染实验','sparkles',12),
('13','WebGPU演示','http://129.211.173.87:8902','WebGPU与PlayCanvas 3D演示','globe',13),
('14','Hermes网关','http://129.211.173.87:8894','AI网关与代理服务','hexagon',14),
]
conn.executemany("INSERT INTO links (id,name,url,desc,icon,sort_order) VALUES (?,?,?,?,?,?)", defaults)
conn.commit()
init_db()
# ── SSO 认证 ─────────────────────────────
def require_sso(request: Request) -> str:
"""从 Cookie 验签,返回 username;失败抛 401"""
token = request.cookies.get("access_token")
if not token:
raise HTTPException(401, "未登录")
try:
payload = jwt.decode(token, SSO_SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if not username:
raise HTTPException(401, "无效令牌")
return username
except JWTError:
raise HTTPException(401, "令牌已过期或无效")
# ── FastAPI ──────────────────────────────
app = FastAPI(title="Nancode Portal API")
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://129.211.173.87",
"http://129.211.173.87:8887",
"http://129.211.173.87:8888",
"http://129.211.173.87:9000",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class LinkCreate(BaseModel):
name: str
url: str
desc: str = ""
icon: str = ""
@app.get("/api/links")
def list_links():
with get_db() as conn:
rows = conn.execute("SELECT id,name,url,desc,icon FROM links ORDER BY sort_order, id").fetchall()
return [dict(r) for r in rows]
@app.post("/api/links")
def add_link(link: LinkCreate, username: str = Depends(require_sso)):
import time, random
lid = f"{int(time.time()):x}{random.randint(0,0xffffff):06x}"
with get_db() as conn:
max_sort = conn.execute("SELECT COALESCE(MAX(sort_order),0) FROM links").fetchone()[0]
conn.execute("INSERT INTO links (id,name,url,desc,icon,sort_order) VALUES (?,?,?,?,?,?)",
(lid, link.name, link.url, link.desc, link.icon, max_sort + 1))
conn.commit()
return {"id": lid, "name": link.name, "url": link.url, "desc": link.desc, "icon": link.icon}
@app.delete("/api/links/{link_id}")
def delete_link(link_id: str, username: str = Depends(require_sso)):
with get_db() as conn:
cur = conn.execute("DELETE FROM links WHERE id=?", (link_id,))
conn.commit()
if cur.rowcount == 0:
raise HTTPException(404, "链接不存在")
return {"success": True}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8887)