feat(config): 运行时配置体系②——渠道配置落 channels.json + 面板落盘 + 热更新
- runtimeconfig/channels.go: channels.json 承载 map[platform][]ChannelAccount(token/baseURL/黑白名单/群聊限流), LoadChannels 优先文件/回退env, SaveChannels(0600+tmp原子写), Upsert/Delete/Masked脱敏
- channelbridge/reload.go: adapterManager 热替换——工厂重建新adapter先连接, 再原子断旧adapter(独立readCtx/进行中不中断); Bridge.Reload(platform) 逐账号增删
- 面板 PUT/DELETE /admin/channels/{platform}/config(owner+审计) 落盘channels.json+Reload热更新; GET /admin/channels 附账号掩码不泄裸token
- 前端 Channels.tsx 配置表单(id/token/baseURL/黑白名单/限流+config JSON)+保存热更新提示+掩码展示/删除
- .gitignore 加 channels.json; .env 退兜底(.env.example注明); token不打印/Config()脱敏
- channelbridge.go 拆 logic→reload.go(742→731); 全仓 go build/vet/test绿+前端build绿
- 未做真token端到端(需渠道凭据,不读.env)——如实标注待凭据
This commit is contained in:
1 parent
2d7151f979
commit
aedae2190b
18 files changed
+1743
-126
No files matched your search
@@ -115,6 +115,14 @@ interface Envelope {
|
||||
error?: ApiErrorShape;
|
||||
}
|
||||
|
||||
/** Result of saving/deleting a channel account config (persisted to channels.json). */
|
||||
export interface ChannelConfigSaveResult {
|
||||
persisted: boolean;
|
||||
path: string;
|
||||
platform: string;
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
async function request<T>(
|
||||
path: string,
|
||||
init: RequestInit = {}
|
||||
@@ -491,6 +499,39 @@ export function adminChannelTest(req: {
|
||||
}): Promise<ChannelTestResult> {
|
||||
return request<ChannelTestResult>("/admin/channels/test", { method: "POST", body: JSON.stringify(req) });
|
||||
}
|
||||
/** PUT /api/v1/admin/channels/{platform}/config — save an account config + hot-reload. */
|
||||
export function adminSaveChannelConfig(
|
||||
platform: string,
|
||||
req: {
|
||||
id?: string;
|
||||
token?: string;
|
||||
baseURL?: string;
|
||||
remoteURL?: string;
|
||||
listenAddr?: string;
|
||||
mode?: string;
|
||||
accessToken?: string;
|
||||
selfID?: string;
|
||||
config?: Record<string, unknown>;
|
||||
allowGroups?: string[];
|
||||
denyGroups?: string[];
|
||||
allowUsers?: string[];
|
||||
denyUsers?: string[];
|
||||
groupMsgPerMin?: number;
|
||||
msgPerMin?: number;
|
||||
maxMessages?: number;
|
||||
},
|
||||
): Promise<ChannelConfigSaveResult> {
|
||||
return request<ChannelConfigSaveResult>(`/admin/channels/${platform}/config`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
}
|
||||
/** DELETE /api/v1/admin/channels/{platform}/config?id=… — delete an account + hot-reload. */
|
||||
export function adminDeleteChannelAccount(platform: string, id: string): Promise<ChannelConfigSaveResult> {
|
||||
return request<ChannelConfigSaveResult>(`/admin/channels/${platform}/config?id=${encodeURIComponent(id)}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
/** POST /api/v1/admin/plugins/install — honest 501 (needs a plugin source). */
|
||||
export function adminPluginInstall(req: { name: string; kind: string; description: string }): Promise<AdminPluginRecord> {
|
||||
return request<AdminPluginRecord>("/admin/plugins/install", { method: "POST", body: JSON.stringify(req) });
|
||||
|
||||
@@ -253,6 +253,21 @@ export interface AdminPluginRecord {
|
||||
export interface AdminChannelRecord {
|
||||
name: string;
|
||||
connected: boolean;
|
||||
/** Configured accounts for this channel's platform (token masked). */
|
||||
accounts?: AdminChannelAccount[];
|
||||
}
|
||||
|
||||
/** One configured IM channel account (admin view, token masked). */
|
||||
export interface AdminChannelAccount {
|
||||
id: string;
|
||||
config: Record<string, unknown>;
|
||||
allowGroups?: string[];
|
||||
denyGroups?: string[];
|
||||
allowUsers?: string[];
|
||||
denyUsers?: string[];
|
||||
groupMsgPerMin?: number;
|
||||
msgPerMin?: number;
|
||||
maxMessages?: number;
|
||||
}
|
||||
|
||||
/** An IoT device as returned by GET /api/v1/admin/iot. */
|
||||
|
||||
Reference in new issue
Block a user