feat(admin): add authenticated restart endpoint + fix admin username
- POST /api/v1/admin/restart: admin-only restart (forks new process) - Fix: MemUserStore now uses cfg.AdminUsername instead of hardcoded 'admin' - RestartFunc injected by main.go for clean process fork + exit
This commit is contained in:
1 parent
dee48b6766
commit
628d416dcd
4 files changed
+115
-6
No files matched your search
@@ -0,0 +1,74 @@
|
||||
# ============================================
|
||||
# Cyrene v2 配置文件 (.env)
|
||||
# 复制为 .env 后修改
|
||||
# ============================================
|
||||
|
||||
# ---- 运行环境 ----
|
||||
ENV=development
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# ---- 服务器 ----
|
||||
PORT=8080
|
||||
HOST=0.0.0.0
|
||||
|
||||
# ---- 数据库 (PostgreSQL + pgvector) ----
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_USER=cyrene
|
||||
POSTGRES_PASSWORD=cyrene_pass
|
||||
POSTGRES_DB=cyrene_ai
|
||||
|
||||
# ---- LLM API ----
|
||||
LLM_API_URL=https://api.deepseek.com/v1
|
||||
LLM_API_KEY=sk-6086952ab537454ab3bc0cf9b0c92343
|
||||
LLM_MODEL=deepseek-v4-flash
|
||||
LLM_FALLBACK_MODEL=deepseek-v4-flash
|
||||
|
||||
# ---- 记忆嵌入 (可选) ----
|
||||
# 不配置时使用本地 n-gram 文本相似度
|
||||
# EMBEDDING_API_URL=http://localhost:11434/v1
|
||||
# EMBEDDING_MODEL=bge-m3
|
||||
|
||||
# ---- 认证 ----
|
||||
JWT_SECRET=change-me-to-a-random-string-at-least-32-characters-long
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=admin12345
|
||||
REGISTRATION_ENABLED=true
|
||||
|
||||
# ---- IoT 设备提供者 ----
|
||||
# mock = 8 个模拟智能家居设备 (开发测试用)
|
||||
# none = 禁用 IoT
|
||||
# 未来: homeassistant / mqtt
|
||||
IOT_PROVIDER=mock
|
||||
|
||||
# ---- 语音服务 ----
|
||||
# DASHSCOPE_API_KEY=sk-xxxxx
|
||||
TTS_PROVIDER=edge-tts
|
||||
ASR_PROVIDER=dashscope
|
||||
|
||||
# ---- 后台思考 ----
|
||||
ENABLE_BACKGROUND_THINKING=true
|
||||
THINK_INTERVAL_SEC=300
|
||||
THINK_SILENCE_TIMEOUT_SEC=120
|
||||
|
||||
# ---- 平台桥接 ----
|
||||
# 桥接配置不在 .env 中管理。
|
||||
# 通过 Admin 面板 → 平台桥接 页面进行配置。
|
||||
# 配置将保存在 configs/bridge.json,支持热重载。
|
||||
|
||||
# ---- 搜索 (SearXNG) ----
|
||||
# SEARXNG_URL=http://localhost:8088
|
||||
|
||||
# ---- 数据目录 ----
|
||||
DATA_DIR=./data
|
||||
PERSONA_DIR=./internal/persona
|
||||
|
||||
# ---- 跨域 ----
|
||||
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
|
||||
|
||||
# ---- WebSocket ----
|
||||
WS_MAX_CONNECTIONS=1000
|
||||
SESSION_IDLE_TIMEOUT_MIN=30
|
||||
|
||||
# ---- 时区 ----
|
||||
TZ=Asia/Shanghai
|
||||
@@ -212,7 +212,7 @@ func main() {
|
||||
log.Printf("[bridge] config store ready (path=%s, %d adapters)", bridgeCfgPath, bridgeStore.Count())
|
||||
|
||||
// === 12. Gateway ===
|
||||
userStore := api.NewMemUserStore(cfg.AdminPassword)
|
||||
userStore := api.NewMemUserStore(cfg.AdminUsername, cfg.AdminPassword)
|
||||
authMgr := api.NewRealAuthManager(cfg.JWTSecret, cfg.JWTExpiryHours, userStore)
|
||||
|
||||
// Build dependencies for gateway handlers
|
||||
@@ -254,6 +254,24 @@ func main() {
|
||||
srv := &http.Server{Addr: addr, Handler: mainMux}
|
||||
|
||||
// === 13. Start ===
|
||||
// Register restart handler — forks a new process and exits
|
||||
api.RestartFunc = func() {
|
||||
log.Printf("[gateway] restarting...")
|
||||
reg.Shutdown()
|
||||
srv.Close()
|
||||
// Re-exec self with same args and env
|
||||
exe, _ := os.Executable()
|
||||
proc, err := os.StartProcess(exe, os.Args, &os.ProcAttr{
|
||||
Env: os.Environ(),
|
||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("[gateway] restart failed: %v", err)
|
||||
}
|
||||
log.Printf("[gateway] new process PID=%d, exiting...", proc.Pid)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Printf("[gateway] listening on %s", addr)
|
||||
log.Printf("[gateway] plugins=%d tools=%d", reg.Count(), len(reg.GetAllToolDefinitions()))
|
||||
|
||||
@@ -28,14 +28,16 @@ type MemUserStore struct {
|
||||
users map[string]*UserRecord
|
||||
}
|
||||
|
||||
// NewMemUserStore creates an in-memory user store with a default admin.
|
||||
func NewMemUserStore(adminPassword string) *MemUserStore {
|
||||
// NewMemUserStore creates an in-memory user store with the configured admin account.
|
||||
func NewMemUserStore(adminUsername, adminPassword string) *MemUserStore {
|
||||
s := &MemUserStore{users: make(map[string]*UserRecord)}
|
||||
|
||||
hash, _ := auth.HashPassword(adminPassword)
|
||||
s.users["admin"] = &UserRecord{
|
||||
UserID: "admin", Username: "admin",
|
||||
PasswordHash: hash, Role: "admin",
|
||||
s.users[adminUsername] = &UserRecord{
|
||||
UserID: adminUsername,
|
||||
Username: adminUsername,
|
||||
PasswordHash: hash,
|
||||
Role: "admin",
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ func (r *Router) setupRoutes() {
|
||||
|
||||
// Admin
|
||||
r.mux.HandleFunc("/api/v1/admin/dashboard", r.authMW(r.adminMW(r.adminDashboardHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/restart", r.authMW(r.adminMW(r.adminRestartHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/llm-calls", r.authMW(r.adminMW(r.llmCallsHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/system", r.authMW(r.adminMW(r.systemInfoHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/plugins", r.authMW(r.adminMW(r.pluginListHandler)))
|
||||
@@ -417,6 +418,19 @@ func (r *Router) adminDashboardHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// RestartFunc is injected by main.go — called to restart the process.
|
||||
var RestartFunc func()
|
||||
|
||||
func (r *Router) adminRestartHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
writeJSON(w, 200, M{"restart": "ok", "message": "Cyrene is restarting..."})
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond) // Let the response flush
|
||||
if RestartFunc != nil {
|
||||
RestartFunc()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// --- Admin extra handlers ---
|
||||
|
||||
func (r *Router) llmCallsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -638,3 +652,4 @@ func (r *Router) userID(req *http.Request) string { return req.Header.Get("X-Use
|
||||
|
||||
// Ensure unused imports don't cause issues
|
||||
|
||||
// Debug: this will be overwritten
|
||||
Reference in new issue
Block a user