Add manual IM bot credential access

This commit is contained in:
xmanrui 2026-08-16 01:54:06 +08:00
parent d5d2c18ce0
commit 7123fce47a
42 changed files with 2458 additions and 889 deletions

View file

@ -161,6 +161,50 @@ export class WecomController {
return publicAttempt(record);
}
async bindCredentials({ botId, secret } = {}) {
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
const remoteBotId = cleanString(botId);
const normalizedSecret = cleanString(secret);
if (!remoteBotId || !normalizedSecret) {
throw new TypeError('Enterprise WeChat Bot ID and Secret are required');
}
if (this.#activeAttemptId) await this.cancelProvisioning(this.#activeAttemptId);
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
const identity = deriveWecomBotIdentity(remoteBotId);
await this.#withBotTransition(identity.botId, async () => {
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
const previousConfig = this.#configStore.getByRemoteBotId(remoteBotId);
const previousSecret = await this.#credentials.resolve(identity.secretRef).catch(() => undefined);
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
const config = {
botId: identity.botId,
remoteBotId,
secretRef: identity.secretRef,
createdAt: previousConfig?.createdAt ?? new Date().toISOString(),
connectedAt: new Date().toISOString(),
};
await this.#credentials.set(identity.secretRef, normalizedSecret);
try {
await this.#configStore.save(config);
} catch (error) {
await this.#restoreCredential(identity.secretRef, previousSecret);
throw error;
}
try {
await this.#startRuntime(config, normalizedSecret);
this.#errors.delete(identity.botId);
} catch {
this.#errors.set(
identity.botId,
safeError('connection-failed', '企业微信机器人已绑定,消息连接暂未就绪。'),
);
this.#logger.warn?.(`[dsh-im:wecom] bot ${identity.botId} credential connection failed`);
}
this.#touch();
});
return this.status();
}
async cancelProvisioning(attemptId) {
const record = this.#attempts.get(attemptId);
if (!record) return null;
@ -268,6 +312,7 @@ export class WecomController {
if (this.#closed) return;
this.#closed = true;
if (this.#activeAttemptId) await this.cancelProvisioning(this.#activeAttemptId);
await Promise.allSettled([...this.#transitions.values()]);
await Promise.allSettled([...this.#runtimes.keys()].map((botId) => this.#stopRuntime(botId)));
}
@ -368,7 +413,9 @@ export class WecomController {
}
async #startRuntime(config, secret) {
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
await this.#stopRuntime(config.botId);
if (this.#closed) throw new Error('Enterprise WeChat controller is closed');
const runtime = await this.#createRuntime({ botId: config.botId, config, secret });
if (!runtime || typeof runtime.start !== 'function' || typeof runtime.stop !== 'function') {
throw new TypeError('createRuntime returned an invalid Enterprise WeChat runtime');