后端接入 AI 模型并支持测试账号托管回复
按协议而不是按厂商做适配,与现有短信、对象存储的做法一致:openai 兼容格式 一个适配器即可覆盖 DeepSeek、通义、智谱、火山方舟、Ollama 等,新增厂商通常 只需在后台加一条模型记录;anthropic、gemini、webhook、debug 各一个适配器。 密钥沿用 integration.go 的 AES-GCM 加密存储。 测试账号托管的回复走 persistMessage 同一条落库和推送路径,因此未读数、 WebSocket 推送和会话排序全部复用现有逻辑,客户端无需改动。为此把 persistMessage 抽出 persistMessageContext,因为 worker 没有 *http.Request。 任务队列用数据库表而非内存:重启不丢回复,多实例不重复消费。领取用 UPDATE 打 claim_token 再回读,没有用 SELECT ... FOR UPDATE SKIP LOCKED——生产存在 MySQL 5.7 环境,那里该语法无法解析。同一会话同时只允许一个待处理任务 (pending_key 生成列 + 唯一键),所以用户连发多条消息只会得到一条回复。 闸门:仅 is_test=1 且在允许批次内的账号生效,托管账号之间不互相触发,三层 配额,调用失败默认静默。会话内是否标注 AI 身份由 ai.disclose_in_chat 控制 并默认开启,关闭前需确认所在地区的监管要求。 app.go、integration.go、im.go 三个文件同时包含本次改动之前工作区里就已存在 的未提交修改,一并带入。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
334890171e
commit
a024d59827
+118
-65
@@ -97,6 +97,23 @@ func New(config Config) (*App, error) {
|
||||
func (a *App) Close() { _ = a.db.Close() }
|
||||
|
||||
func (a *App) Run() {
|
||||
workerContext, stopWorkers := context.WithCancel(context.Background())
|
||||
defer stopWorkers()
|
||||
go func() {
|
||||
a.processDueAccountClosures(workerContext)
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-workerContext.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.processDueAccountClosures(workerContext)
|
||||
}
|
||||
}
|
||||
}()
|
||||
go a.runAIReplyWorker(workerContext)
|
||||
go a.runAIMaintenance(workerContext)
|
||||
server := rest.MustNewServer(rest.RestConf{
|
||||
Host: a.config.Host,
|
||||
Port: a.config.Port,
|
||||
@@ -156,6 +173,7 @@ func (a *App) routes() []rest.Route {
|
||||
{Method: http.MethodPost, Path: "/api/v1/auth/login/sms", Handler: a.loginSMS},
|
||||
{Method: http.MethodGet, Path: "/api/v1/auth/oauth/providers", Handler: a.userOAuthProviders},
|
||||
{Method: http.MethodPost, Path: "/api/v1/auth/oauth/start", Handler: a.userOAuthStart},
|
||||
{Method: http.MethodPost, Path: "/api/v1/auth/oauth/native", Handler: a.userOAuthNative},
|
||||
{Method: http.MethodGet, Path: "/api/v1/auth/oauth/callback", Handler: a.oauthCallback},
|
||||
{Method: http.MethodPost, Path: "/api/v1/auth/oauth/exchange", Handler: a.userOAuthExchange},
|
||||
{Method: http.MethodPost, Path: "/api/v1/auth/oauth/link", Handler: a.userOAuthLink},
|
||||
@@ -169,6 +187,7 @@ func (a *App) routes() []rest.Route {
|
||||
{Method: http.MethodHead, Path: "/uploads/:name", Handler: a.serveMedia},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/auth/login", Handler: a.adminLogin},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/auth/refresh", Handler: a.adminRefresh},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/auth/logout", Handler: a.adminLogout},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/auth/oauth/providers", Handler: a.adminOAuthProviders},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/auth/oauth/start", Handler: a.adminOAuthStart},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/auth/oauth/callback", Handler: a.oauthCallback},
|
||||
@@ -187,13 +206,32 @@ func (a *App) userRoutes() []rest.Route {
|
||||
{Method: http.MethodGet, Path: "/api/v1/me", Handler: auth(a.me)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/profile", Handler: auth(a.me)},
|
||||
{Method: http.MethodPatch, Path: "/api/v1/me/profile", Handler: auth(a.updateProfile)},
|
||||
{Method: http.MethodPut, Path: "/api/v1/me/password", Handler: auth(a.changeUserPassword)},
|
||||
{Method: http.MethodPut, Path: "/api/v1/me/phone", Handler: auth(a.changeUserPhone)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/devices", Handler: auth(a.myDevices)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/me/devices/:id", Handler: auth(a.revokeDevice)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/blocks", Handler: auth(a.blockedUsers)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/notification-settings", Handler: auth(a.notificationSettings)},
|
||||
{Method: http.MethodPut, Path: "/api/v1/me/notification-settings", Handler: auth(a.updateNotificationSettings)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/me/push-tokens", Handler: auth(a.registerPushToken)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/me/push-tokens", Handler: auth(a.deletePushToken)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/feedback", Handler: auth(a.feedback)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/me/feedback", Handler: auth(a.feedback)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/account-closure", Handler: auth(a.accountClosure)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/me/account-closure", Handler: auth(a.accountClosure)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/me/account-closure", Handler: auth(a.accountClosure)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/data-export", Handler: auth(a.exportMyData)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/me/consents", Handler: auth(a.recordConsent)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/users/search", Handler: auth(a.searchUsers)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/tags", Handler: auth(a.availableTags)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/users/:id", Handler: auth(a.userProfile)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/following", Handler: auth(a.followingList)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/followers", Handler: auth(a.followerList)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/visitors", Handler: auth(a.visitorList)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/privacy", Handler: auth(a.getPrivacy)},
|
||||
{Method: http.MethodPut, Path: "/api/v1/me/privacy", Handler: auth(a.updatePrivacy)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/verification", Handler: auth(a.myVerification)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/me/verification", Handler: auth(a.submitVerification)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/discover/recommendations", Handler: auth(a.discover)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/nearby/users", Handler: auth(a.nearby)},
|
||||
{Method: http.MethodPut, Path: "/api/v1/location", Handler: auth(a.updateLocation)},
|
||||
@@ -207,76 +245,114 @@ func (a *App) userRoutes() []rest.Route {
|
||||
{Method: http.MethodGet, Path: "/api/v1/posts/:id", Handler: auth(a.postDetail)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/users/:id/posts", Handler: auth(a.userPosts)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/posts", Handler: auth(a.createPost)},
|
||||
{Method: http.MethodPatch, Path: "/api/v1/posts/:id", Handler: auth(a.updatePost)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/posts/:id", Handler: auth(a.deleteOwnPost)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/media/upload", Handler: auth(a.uploadMedia)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/posts/:id/like", Handler: auth(a.likePost)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/posts/:id/like", Handler: auth(a.unlikePost)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/posts/:id/comments", Handler: auth(a.comments)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/posts/:id/comments", Handler: auth(a.createComment)},
|
||||
{Method: http.MethodDelete, Path: "/api/v1/comments/:id", Handler: auth(a.deleteOwnComment)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/im/conversations/direct", Handler: auth(a.directConversation)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/im/conversations", Handler: auth(a.conversations)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/im/conversations/:id/messages", Handler: auth(a.messages)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/im/conversations/:id/messages", Handler: auth(a.sendMessageHTTP)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/im/messages/:id/recall", Handler: auth(a.recallMessage)},
|
||||
{Method: http.MethodPatch, Path: "/api/v1/im/conversations/:id/settings", Handler: auth(a.conversationSettings)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/membership/status", Handler: auth(a.membershipStatus)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/orders", Handler: auth(a.createOrder)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/orders/:id/pay", Handler: auth(a.payOrder)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/orders/:id", Handler: auth(a.orderStatus)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/orders/:id/close", Handler: auth(a.closeOwnOrder)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/orders/:id/refund", Handler: auth(a.requestOrderRefund)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/orders", Handler: auth(a.myOrders)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/notifications", Handler: auth(a.notifications)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/notifications/read-all", Handler: auth(a.readAllNotifications)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/notifications/:id/read", Handler: auth(a.readNotification)},
|
||||
{Method: http.MethodPost, Path: "/api/v1/reports", Handler: auth(a.createReport)},
|
||||
{Method: http.MethodGet, Path: "/api/v1/me/reports", Handler: auth(a.myReports)},
|
||||
{Method: http.MethodGet, Path: "/ws", Handler: a.websocket},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) adminRoutes() []rest.Route {
|
||||
auth := func(next http.HandlerFunc) http.HandlerFunc { return a.requireAuth("admin", next) }
|
||||
permit := func(permission string, next http.HandlerFunc) http.HandlerFunc {
|
||||
return a.requireAdminPermission(permission, next)
|
||||
}
|
||||
return []rest.Route{
|
||||
{Method: http.MethodPost, Path: "/admin/v1/auth/logout", Handler: auth(a.adminLogout)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/me/password", Handler: auth(a.adminChangePassword)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/auth/codes", Handler: auth(a.adminCodes)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/user/info", Handler: auth(a.adminInfo)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/dashboard/overview", Handler: auth(a.dashboard)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users", Handler: auth(a.adminUsers)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users/:id", Handler: auth(a.adminUserDetail)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/profile", Handler: auth(a.adminUpdateUserProfile)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/verification", Handler: auth(a.adminUpdateVerification)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/membership", Handler: auth(a.adminUpdateMembership)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/password-reset", Handler: auth(a.adminResetUserPassword)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/force-logout", Handler: auth(a.adminForceLogout)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users/:id/sanctions", Handler: auth(a.adminUserSanctions)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/sanctions", Handler: auth(a.adminUserSanctions)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/sanctions/:id/revoke", Handler: auth(a.adminRevokeSanction)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/freeze", Handler: auth(a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/unfreeze", Handler: auth(a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/ban", Handler: auth(a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/unban", Handler: auth(a.adminUserStatus)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/posts", Handler: auth(a.adminPosts)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/posts/:id", Handler: auth(a.adminPostDetail)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/posts/:id", Handler: auth(a.adminDeletePost)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/reports", Handler: auth(a.adminReports)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/reports/:id/handle", Handler: auth(a.adminHandleReport)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/risk/users", Handler: auth(a.adminRiskUsers)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/risk/events", Handler: auth(a.adminRiskEvents)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/membership/plans", Handler: auth(a.adminPlans)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/membership/plans", Handler: auth(a.adminCreatePlan)},
|
||||
{Method: http.MethodPatch, Path: "/admin/v1/membership/plans/:id", Handler: auth(a.adminUpdatePlan)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/membership/plans/:id", Handler: auth(a.adminUpdatePlan)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/membership/plans/:id", Handler: auth(a.adminDeletePlan)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/orders", Handler: auth(a.adminOrders)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/orders/:id", Handler: auth(a.adminUpdateOrder)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/orders/:id", Handler: auth(a.adminDeleteOrder)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/pay", Handler: auth(a.adminMarkOrderPaid)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/close", Handler: auth(a.adminCloseOrder)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/refund", Handler: auth(a.adminRefund)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/messages", Handler: auth(a.adminMessages)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/system/configs", Handler: auth(a.adminConfigs)},
|
||||
{Method: http.MethodPatch, Path: "/admin/v1/system/configs/:key", Handler: auth(a.adminUpdateConfig)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/system/configs/:key", Handler: auth(a.adminUpdateConfig)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/integrations/:group", Handler: auth(a.adminIntegration)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/integrations/:group", Handler: auth(a.adminUpdateIntegration)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/integrations/:group/test", Handler: auth(a.adminTestIntegration)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/audit-logs", Handler: auth(a.adminAuditLogs)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/dashboard/overview", Handler: permit("dashboard:view", a.dashboard)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users", Handler: permit("users:view", a.adminUsers)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users", Handler: permit("users:create", a.adminCreateUser)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users/:id", Handler: permit("users:view", a.adminUserDetail)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/profile", Handler: permit("users:manage", a.adminUpdateUserProfile)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/verification", Handler: permit("verification:manage", a.adminUpdateVerification)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/users/:id/membership", Handler: permit("users:manage", a.adminUpdateMembership)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/password-reset", Handler: permit("users:security", a.adminResetUserPassword)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/force-logout", Handler: permit("users:security", a.adminForceLogout)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/users/:id/sanctions", Handler: permit("violations:manage", a.adminUserSanctions)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/sanctions", Handler: permit("violations:manage", a.adminUserSanctions)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/sanctions/:id/revoke", Handler: permit("violations:manage", a.adminRevokeSanction)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/freeze", Handler: permit("violations:manage", a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/unfreeze", Handler: permit("violations:manage", a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/ban", Handler: permit("violations:manage", a.adminUserStatus)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/users/:id/unban", Handler: permit("violations:manage", a.adminUserStatus)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/posts", Handler: permit("content:view", a.adminPosts)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/posts/:id", Handler: permit("content:view", a.adminPostDetail)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/posts/:id", Handler: permit("content:manage", a.adminDeletePost)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/reports", Handler: permit("reports:handle", a.adminReports)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/reports/:id/handle", Handler: permit("reports:handle", a.adminHandleReport)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/risk/users", Handler: permit("risk:view", a.adminRiskUsers)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/risk/events", Handler: permit("risk:view", a.adminRiskEvents)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/membership/plans", Handler: permit("membership:manage", a.adminPlans)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/membership/plans", Handler: permit("membership:manage", a.adminCreatePlan)},
|
||||
{Method: http.MethodPatch, Path: "/admin/v1/membership/plans/:id", Handler: permit("membership:manage", a.adminUpdatePlan)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/membership/plans/:id", Handler: permit("membership:manage", a.adminUpdatePlan)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/membership/plans/:id", Handler: permit("membership:manage", a.adminDeletePlan)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/orders", Handler: permit("orders:view", a.adminOrders)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/orders/:id", Handler: permit("orders:manage", a.adminUpdateOrder)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/orders/:id", Handler: permit("orders:manage", a.adminDeleteOrder)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/pay", Handler: permit("orders:manage", a.adminMarkOrderPaid)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/close", Handler: permit("orders:manage", a.adminCloseOrder)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/refund", Handler: permit("orders:manage", a.adminRefund)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/messages", Handler: permit("messages:view", a.adminMessages)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/messages/:id/moderate", Handler: permit("messages:manage", a.adminModerateMessage)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/client-feedback", Handler: permit("users:view", a.adminFeedback)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/client-feedback/:id", Handler: permit("users:manage", a.adminFeedback)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/account-closures", Handler: permit("users:view", a.adminAccountClosures)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/account-closures/:id", Handler: permit("users:manage", a.adminAccountClosures)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/app-versions", Handler: permit("system:manage", a.adminAppVersions)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/app-versions", Handler: permit("system:manage", a.adminAppVersions)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/app-versions/:id", Handler: permit("system:manage", a.adminAppVersions)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/app-versions/:id", Handler: permit("system:manage", a.adminAppVersions)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/system/configs", Handler: permit("system:manage", a.adminConfigs)},
|
||||
{Method: http.MethodPatch, Path: "/admin/v1/system/configs/:key", Handler: permit("system:manage", a.adminUpdateConfig)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/system/configs/:key", Handler: permit("system:manage", a.adminUpdateConfig)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/integrations/:group", Handler: permit("system:manage", a.adminIntegration)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/integrations/:group", Handler: permit("system:manage", a.adminUpdateIntegration)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/integrations/:group/test", Handler: permit("system:manage", a.adminTestIntegration)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/ai/models", Handler: permit("system:manage", a.adminAIModels)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/ai/models", Handler: permit("system:manage", a.adminCreateAIModel)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/ai/models/:id", Handler: permit("system:manage", a.adminUpdateAIModel)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/ai/models/:id", Handler: permit("system:manage", a.adminDeleteAIModel)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/ai/models/:id/test", Handler: permit("system:manage", a.adminTestAIModel)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/ai/agents", Handler: permit("system:manage", a.adminAIAgents)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/ai/agents", Handler: permit("system:manage", a.adminUpdateAIAgents)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/ai/reply-jobs", Handler: permit("system:manage", a.adminAIReplyJobs)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/ai/logs", Handler: permit("system:manage", a.adminAILogs)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/ai/usage", Handler: permit("system:manage", a.adminAIUsage)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/audit-logs", Handler: permit("system:manage", a.adminAuditLogs)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/admin-users", Handler: permit("system:manage", a.adminAccounts)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/admin-users", Handler: permit("system:manage", a.adminCreateAccount)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/admin-users/:id", Handler: permit("system:manage", a.adminUpdateAccount)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/admin-roles", Handler: permit("system:manage", a.adminRoles)},
|
||||
{Method: http.MethodPost, Path: "/admin/v1/admin-roles", Handler: permit("system:manage", a.adminCreateRole)},
|
||||
{Method: http.MethodPut, Path: "/admin/v1/admin-roles/:id", Handler: permit("system:manage", a.adminUpdateRole)},
|
||||
{Method: http.MethodDelete, Path: "/admin/v1/admin-roles/:id", Handler: permit("system:manage", a.adminDeleteRole)},
|
||||
{Method: http.MethodGet, Path: "/admin/v1/admin-permissions", Handler: permit("system:manage", a.adminPermissions)},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,32 +470,9 @@ func validateConfig(config Config) error {
|
||||
return fmt.Errorf("生产环境来源必须使用 HTTPS: %s", origin)
|
||||
}
|
||||
}
|
||||
if config.BootstrapAdminPassword != "" && !strongAdminPassword(config.BootstrapAdminPassword) {
|
||||
return fmt.Errorf("IM_BOOTSTRAP_ADMIN_PASSWORD 至少 12 位,且必须包含大小写字母、数字和特殊字符")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func strongAdminPassword(value string) bool {
|
||||
if len(value) < 12 {
|
||||
return false
|
||||
}
|
||||
var lower, upper, digit, special bool
|
||||
for _, char := range value {
|
||||
switch {
|
||||
case char >= 'a' && char <= 'z':
|
||||
lower = true
|
||||
case char >= 'A' && char <= 'Z':
|
||||
upper = true
|
||||
case char >= '0' && char <= '9':
|
||||
digit = true
|
||||
default:
|
||||
special = true
|
||||
}
|
||||
}
|
||||
return lower && upper && digit && special
|
||||
}
|
||||
|
||||
func (a *App) originAllowed(origin string) bool {
|
||||
origin = strings.TrimRight(strings.TrimSpace(origin), "/")
|
||||
if origin == "" {
|
||||
|
||||
Reference in New Issue
Block a user