package admin import ( "context" "errors" "fmt" "net/http" "os" "os/signal" "syscall" "time" chatmw "git.imall.cloud/openim/chat/internal/api/mw" "git.imall.cloud/openim/chat/internal/api/util" "git.imall.cloud/openim/chat/pkg/common/config" "git.imall.cloud/openim/chat/pkg/common/imapi" "git.imall.cloud/openim/chat/pkg/common/kdisc" disetcd "git.imall.cloud/openim/chat/pkg/common/kdisc/etcd" adminclient "git.imall.cloud/openim/chat/pkg/protocol/admin" chatclient "git.imall.cloud/openim/chat/pkg/protocol/chat" "github.com/gin-gonic/gin" "github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery/etcd" "github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/mw" "github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/runtimeenv" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) type Config struct { *config.AllConfig RuntimeEnv string ConfigPath string } func Start(ctx context.Context, index int, config *Config) error { config.RuntimeEnv = runtimeenv.PrintRuntimeEnvironment() if len(config.Share.ChatAdmin) == 0 { return errs.New("share chat admin not configured") } apiPort, err := datautil.GetElemByIndex(config.AdminAPI.Api.Ports, index) if err != nil { return err } client, err := kdisc.NewDiscoveryRegister(&config.Discovery, config.RuntimeEnv, nil) if err != nil { return err } chatConn, err := client.GetConn(ctx, config.Discovery.RpcService.Chat, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient()) if err != nil { return err } adminConn, err := client.GetConn(ctx, config.Discovery.RpcService.Admin, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient()) if err != nil { return err } chatClient := chatclient.NewChatClient(chatConn) adminClient := adminclient.NewAdminClient(adminConn) im := imapi.New(config.Share.OpenIM.ApiURL, config.Share.OpenIM.Secret, config.Share.OpenIM.AdminUserID) base := util.Api{ ImUserID: config.Share.OpenIM.AdminUserID, ProxyHeader: config.Share.ProxyHeader, ChatAdminUserID: config.Share.ChatAdmin[0], } adminApi := New(chatClient, adminClient, im, &base) mwApi := chatmw.New(adminClient) gin.SetMode(gin.ReleaseMode) engine := gin.New() engine.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID(), func(c *gin.Context) { // 确保 operationID 被正确设置到 context 中 operationID := c.GetHeader("operationid") if operationID != "" { c.Set("operationID", operationID) } c.Next() }) SetAdminRoute(engine, adminApi, mwApi, config, client) if config.Discovery.Enable == kdisc.ETCDCONST { cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), config.GetConfigNames()) cm.Watch(ctx) } var ( netDone = make(chan struct{}, 1) netErr error ) server := http.Server{Addr: fmt.Sprintf(":%d", apiPort), Handler: engine} go func() { err = server.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr)) netDone <- struct{}{} } }() shutdown := func() error { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() err := server.Shutdown(ctx) if err != nil { return errs.WrapMsg(err, "shutdown err") } return nil } disetcd.RegisterShutDown(shutdown) sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGTERM) select { case <-sigs: program.SIGTERMExit() if err := shutdown(); err != nil { return err } case <-netDone: close(netDone) return netErr } return nil } func SetAdminRoute(router gin.IRouter, admin *Api, mw *chatmw.MW, cfg *Config, client discovery.SvcDiscoveryRegistry) { adminRouterGroup := router.Group("/account") adminRouterGroup.POST("/login", admin.AdminLogin) // Login adminRouterGroup.POST("/update", mw.CheckAdmin, admin.AdminUpdateInfo) // Modify information adminRouterGroup.POST("/info", mw.CheckAdmin, admin.AdminInfo) // Get information adminRouterGroup.POST("/change_password", mw.CheckAdmin, admin.ChangeAdminPassword) // Change admin account's password adminRouterGroup.POST("/change_operation_password", mw.CheckAdmin, admin.ChangeOperationPassword) // Change operation password adminRouterGroup.POST("/set_google_auth_key", mw.CheckAdmin, admin.SetGoogleAuthKey) // Set Google Authenticator key adminRouterGroup.POST("/add_admin", mw.CheckAdmin, admin.AddAdminAccount) // Add admin account adminRouterGroup.POST("/add_user", mw.CheckAdmin, admin.AddUserAccount) // Add user account adminRouterGroup.POST("/del_admin", mw.CheckAdmin, admin.DelAdminAccount) // Delete admin adminRouterGroup.POST("/search", mw.CheckAdmin, admin.SearchAdminAccount) // Get admin list adminRouterGroup.POST("/statistics", mw.CheckAdmin, admin.GetStatistics) // Get system statistics //account.POST("/add_notification_account") importGroup := router.Group("/user/import") importGroup.POST("/json", mw.CheckAdmin, admin.ImportUserByJson) importGroup.POST("/xlsx", mw.CheckAdmin, admin.ImportUserByXlsx) importGroup.GET("/xlsx", admin.BatchImportTemplate) allowRegisterGroup := router.Group("/user/allow_register", mw.CheckAdmin) allowRegisterGroup.POST("/get", admin.GetAllowRegister) allowRegisterGroup.POST("/set", admin.SetAllowRegister) defaultRouter := router.Group("/default", mw.CheckAdmin) defaultUserRouter := defaultRouter.Group("/user") defaultUserRouter.POST("/add", admin.AddDefaultFriend) // Add default friend at registration defaultUserRouter.POST("/del", admin.DelDefaultFriend) // Delete default friend at registration defaultUserRouter.POST("/find", admin.FindDefaultFriend) // Default friend list defaultUserRouter.POST("/search", admin.SearchDefaultFriend) // Search default friend list at registration defaultGroupRouter := defaultRouter.Group("/group") defaultGroupRouter.POST("/add", admin.AddDefaultGroup) // Add default group at registration defaultGroupRouter.POST("/del", admin.DelDefaultGroup) // Delete default group at registration defaultGroupRouter.POST("/find", admin.FindDefaultGroup) // Get default group list at registration defaultGroupRouter.POST("/search", admin.SearchDefaultGroup) // Search default group list at registration invitationCodeRouter := router.Group("/invitation_code", mw.CheckAdmin) invitationCodeRouter.POST("/add", admin.AddInvitationCode) // Add invitation code invitationCodeRouter.POST("/gen", admin.GenInvitationCode) // Generate invitation code invitationCodeRouter.POST("/del", admin.DelInvitationCode) // Delete invitation code invitationCodeRouter.POST("/search", admin.SearchInvitationCode) // Search invitation code forbiddenRouter := router.Group("/forbidden", mw.CheckAdmin) ipForbiddenRouter := forbiddenRouter.Group("/ip") ipForbiddenRouter.POST("/add", admin.AddIPForbidden) // Add forbidden IP for registration/login ipForbiddenRouter.POST("/del", admin.DelIPForbidden) // Delete forbidden IP for registration/login ipForbiddenRouter.POST("/search", admin.SearchIPForbidden) // Search forbidden IPs for registration/login userForbiddenRouter := forbiddenRouter.Group("/user") userForbiddenRouter.POST("/add", admin.AddUserIPLimitLogin) // Add limit for user login on specific IP userForbiddenRouter.POST("/del", admin.DelUserIPLimitLogin) // Delete user limit on specific IP for login userForbiddenRouter.POST("/search", admin.SearchUserIPLimitLogin) // Search limit for user login on specific IP appletRouterGroup := router.Group("/applet", mw.CheckAdmin) appletRouterGroup.POST("/add", admin.AddApplet) // Add applet appletRouterGroup.POST("/del", admin.DelApplet) // Delete applet appletRouterGroup.POST("/update", admin.UpdateApplet) // Modify applet appletRouterGroup.POST("/search", admin.SearchApplet) // Search applet blockRouter := router.Group("/block", mw.CheckAdmin) blockRouter.POST("/add", admin.BlockUser) // Block user blockRouter.POST("/del", admin.UnblockUser) // Unblock user blockRouter.POST("/search", admin.SearchBlockUser) // Search blocked users userRouter := router.Group("/user", mw.CheckAdmin) userRouter.POST("/password/reset", admin.ResetUserPassword) // Reset user password initGroup := router.Group("/client_config", mw.CheckAdmin) initGroup.POST("/get", admin.GetClientConfig) // Get client initialization configuration initGroup.POST("/set", admin.SetClientConfig) // Set client initialization configuration initGroup.POST("/del", admin.DelClientConfig) // Delete client initialization configuration statistic := router.Group("/statistic", mw.CheckAdmin) statistic.POST("/new_user_count", admin.NewUserCount) statistic.POST("/login_user_count", admin.LoginUserCount) statistic.POST("/online_user_count", admin.OnlineUserCount) statistic.POST("/online_user_count_trend", admin.OnlineUserCountTrend) statistic.POST("/user_send_msg_count", admin.UserSendMsgCount) statistic.POST("/user_send_msg_count_trend", admin.UserSendMsgCountTrend) statistic.POST("/user_send_msg_query", admin.UserSendMsgQuery) applicationGroup := router.Group("application") applicationGroup.POST("/add_version", mw.CheckAdmin, admin.AddApplicationVersion) applicationGroup.POST("/update_version", mw.CheckAdmin, admin.UpdateApplicationVersion) applicationGroup.POST("/delete_version", mw.CheckAdmin, admin.DeleteApplicationVersion) applicationGroup.POST("/latest_version", admin.LatestApplicationVersion) applicationGroup.POST("/page_versions", admin.PageApplicationVersion) var etcdClient *clientv3.Client if cfg.Discovery.Enable == kdisc.ETCDCONST { etcdClient = client.(*etcd.SvcDiscoveryRegistryImpl).GetClient() } cm := NewConfigManager(cfg.AllConfig, etcdClient, cfg.ConfigPath, cfg.RuntimeEnv) { configGroup := router.Group("/config", mw.CheckAdmin) configGroup.POST("/get_config_list", cm.GetConfigList) configGroup.POST("/get_config", cm.GetConfig) configGroup.POST("/set_config", cm.SetConfig) configGroup.POST("/set_configs", cm.SetConfigs) configGroup.POST("/reset_config", cm.ResetConfig) configGroup.POST("/get_enable_config_manager", cm.GetEnableConfigManager) configGroup.POST("/set_enable_config_manager", cm.SetEnableConfigManager) } { router.POST("/restart", mw.CheckAdmin, cm.Restart) } // ==================== 敏感词管理路由 ==================== sensitiveWordRouter := router.Group("/sensitive_word") // 暂时注释掉 mw.CheckAdmin 用于测试 // 敏感词管理 sensitiveWordRouter.POST("/add", mw.CheckAdmin, admin.AddSensitiveWord) // 添加敏感词 sensitiveWordRouter.POST("/update", mw.CheckAdmin, admin.UpdateSensitiveWord) // 更新敏感词 sensitiveWordRouter.POST("/delete", mw.CheckAdmin, admin.DeleteSensitiveWord) // 删除敏感词 sensitiveWordRouter.POST("/get", mw.CheckAdmin, admin.GetSensitiveWord) // 获取敏感词 sensitiveWordRouter.POST("/search", mw.CheckAdmin, admin.SearchSensitiveWords) // 搜索敏感词 sensitiveWordRouter.POST("/batch_add", mw.CheckAdmin, admin.BatchAddSensitiveWords) // 批量添加敏感词 sensitiveWordRouter.POST("/batch_update", mw.CheckAdmin, admin.BatchUpdateSensitiveWords) // 批量更新敏感词 sensitiveWordRouter.POST("/batch_delete", mw.CheckAdmin, admin.BatchDeleteSensitiveWords) // 批量删除敏感词 // 敏感词分组管理 groupRouter := sensitiveWordRouter.Group("/group") groupRouter.POST("/add", mw.CheckAdmin, admin.AddSensitiveWordGroup) // 添加敏感词分组 groupRouter.POST("/update", mw.CheckAdmin, admin.UpdateSensitiveWordGroup) // 更新敏感词分组 groupRouter.POST("/delete", mw.CheckAdmin, admin.DeleteSensitiveWordGroup) // 删除敏感词分组 groupRouter.POST("/get", mw.CheckAdmin, admin.GetSensitiveWordGroup) // 获取敏感词分组 groupRouter.POST("/list", mw.CheckAdmin, admin.GetAllSensitiveWordGroups) // 获取所有敏感词分组 // 敏感词配置管理 configRouter := sensitiveWordRouter.Group("/config") configRouter.GET("/", mw.CheckAdmin, admin.GetSensitiveWordConfig) // 获取敏感词配置 configRouter.POST("/get", mw.CheckAdmin, admin.GetSensitiveWordConfig) // 获取敏感词配置 configRouter.POST("/update", mw.CheckAdmin, admin.UpdateSensitiveWordConfig) // 更新敏感词配置 // 敏感词日志管理 logRouter := sensitiveWordRouter.Group("/log") logRouter.POST("/list", mw.CheckAdmin, admin.GetSensitiveWordLogs) // 获取敏感词日志 // 用户登录记录管理 loginRecordRouter := router.Group("/user_login_record", mw.CheckAdmin) loginRecordRouter.POST("/list", admin.GetUserLoginRecords) // 查询用户登录记录 logRouter.POST("/delete", mw.CheckAdmin, admin.DeleteSensitiveWordLogs) // 删除敏感词日志 // 敏感词统计 statsRouter := sensitiveWordRouter.Group("/stats") statsRouter.GET("/", mw.CheckAdmin, admin.GetSensitiveWordStats) // 获取敏感词统计 statsRouter.POST("/word_stats", mw.CheckAdmin, admin.GetSensitiveWordStats) // 获取敏感词统计 statsRouter.POST("/log_stats", mw.CheckAdmin, admin.GetSensitiveWordLogStats) // 获取敏感词日志统计 // ==================== 定时任务管理路由 ==================== scheduledTaskRouter := router.Group("/scheduled_task", mw.CheckAdmin) scheduledTaskRouter.POST("/list", admin.GetScheduledTasks) // 获取定时任务列表 scheduledTaskRouter.POST("/delete", admin.DeleteScheduledTask) // 删除定时任务 // ==================== 系统配置管理路由 ==================== systemConfigRouter := router.Group("/system_config", mw.CheckAdmin) systemConfigRouter.POST("/create", admin.CreateSystemConfig) // 创建系统配置 systemConfigRouter.POST("/get", admin.GetSystemConfig) // 获取系统配置详情 systemConfigRouter.POST("/list", admin.GetAllSystemConfigs) // 获取所有系统配置(分页) systemConfigRouter.POST("/update", admin.UpdateSystemConfig) // 更新系统配置 systemConfigRouter.POST("/update_value", admin.UpdateSystemConfigValue) // 更新系统配置值 systemConfigRouter.POST("/update_enabled", admin.UpdateSystemConfigEnabled) // 更新系统配置启用状态 systemConfigRouter.POST("/delete", admin.DeleteSystemConfig) // 删除系统配置 systemConfigRouter.POST("/get_enabled", admin.GetEnabledSystemConfigs) // 获取所有已启用的配置 // ==================== 钱包管理路由 ==================== walletRouter := router.Group("/wallet", mw.CheckAdmin) walletRouter.POST("/get", admin.GetUserWallet) // 获取用户钱包信息 walletRouter.POST("/get_wallets", admin.GetWallets) // 获取钱包列表 walletRouter.POST("/update_balance", admin.UpdateUserWalletBalance) // 更新用户余额(后台充值/扣款) walletRouter.POST("/batch_update_balance", admin.BatchUpdateWalletBalance) // 批量更新用户余额(后台批量充值/扣款) walletRouter.POST("/balance_records", admin.GetUserWalletBalanceRecords) // 获取用户余额变动记录列表 walletRouter.POST("/update_payment_password", admin.UpdateUserPaymentPassword) // 修改用户支付密码 walletRouter.POST("/set_withdraw_account", admin.SetUserWithdrawAccount) // 设置用户提款账号 // ==================== 提现管理路由(操作 withdraw_applications)==================== withdrawRouter := router.Group("/withdraw", mw.CheckAdmin) withdrawRouter.POST("/get", admin.GetWithdraw) // 获取提现申请详情 withdrawRouter.POST("/list", admin.GetWithdraws) // 获取提现申请列表(支持按状态筛选) withdrawRouter.POST("/user_list", admin.GetUserWithdraws) // 获取用户的提现申请列表 withdrawRouter.POST("/audit", admin.AuditWithdraw) // 审核提现申请 // ==================== 实名认证审核路由 ==================== realNameAuthRouter := router.Group("/real_name_auth", mw.CheckAdmin) realNameAuthRouter.POST("/list", admin.GetRealNameAuths) // 获取实名认证列表(支持按审核状态筛选) realNameAuthRouter.POST("/audit", admin.AuditRealNameAuth) // 审核实名认证(通过/拒绝) }