mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-24 02:57:13 +00:00
fix(wizard): repair HTTP bot inbound test setup
This commit is contained in:
@@ -48,6 +48,11 @@ import {
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import {
|
||||
ensureHttpBotSigningSecret,
|
||||
getErrorMessage,
|
||||
} from '@/app/wizard/utils';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
@@ -177,15 +182,30 @@ export default function WizardPage() {
|
||||
const botData = await httpClient.getBot(progress.created_bot_uuid);
|
||||
if (cancelled) return;
|
||||
|
||||
setSelectedAdapter(progress.selected_adapter);
|
||||
const restoredAdapter =
|
||||
progress.selected_adapter ?? botData.bot.adapter;
|
||||
const restoredConfig = (botData.bot.adapter_config ?? {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const configToRestore = ensureHttpBotSigningSecret(
|
||||
restoredAdapter,
|
||||
restoredConfig,
|
||||
);
|
||||
const configNeedsSave = configToRestore !== restoredConfig;
|
||||
|
||||
setSelectedAdapter(restoredAdapter);
|
||||
setCreatedBotUuid(progress.created_bot_uuid);
|
||||
setCreatedPipelineUuid(progress.created_pipeline_uuid ?? null);
|
||||
setBotSaved(progress.bot_saved ?? false);
|
||||
setBotSaved(
|
||||
configNeedsSave ? false : (progress.bot_saved ?? false),
|
||||
);
|
||||
setMessageReceived(progress.message_received ?? false);
|
||||
setSelectedRunner(progress.selected_runner);
|
||||
|
||||
// Restore bot name from fetched bot data
|
||||
setBotName(botData.bot.name);
|
||||
setAdapterConfig(configToRestore);
|
||||
|
||||
// Restore webhook URLs
|
||||
const runtimeValues = botData.bot.adapter_runtime_values as
|
||||
@@ -360,12 +380,17 @@ export default function WizardPage() {
|
||||
const defaultConfig = adapter
|
||||
? getDefaultValues(adapter.spec.config)
|
||||
: {};
|
||||
const initialConfig = ensureHttpBotSigningSecret(
|
||||
selectedAdapter,
|
||||
defaultConfig,
|
||||
);
|
||||
setAdapterConfig(initialConfig);
|
||||
|
||||
const bot: Bot = {
|
||||
name: defaultName,
|
||||
description: '',
|
||||
adapter: selectedAdapter,
|
||||
adapter_config: defaultConfig,
|
||||
adapter_config: initialConfig,
|
||||
enable: false,
|
||||
};
|
||||
const resp = await httpClient.createBot(bot);
|
||||
@@ -454,11 +479,17 @@ export default function WizardPage() {
|
||||
setCreatedPipelineUuid(pipelineUuid);
|
||||
}
|
||||
|
||||
const configToSave = ensureHttpBotSigningSecret(
|
||||
selectedAdapter,
|
||||
adapterConfig,
|
||||
);
|
||||
setAdapterConfig(configToSave);
|
||||
|
||||
await httpClient.updateBot(createdBotUuid, {
|
||||
name: botName,
|
||||
description: botDescription || '',
|
||||
adapter: selectedAdapter,
|
||||
adapter_config: adapterConfig,
|
||||
adapter_config: configToSave,
|
||||
enable: true,
|
||||
use_pipeline_uuid: pipelineUuid,
|
||||
});
|
||||
@@ -1063,7 +1094,7 @@ function StepBotConfig({
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
t('wizard.botConfig.httpTestFailed', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
error: getErrorMessage(error),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error) return error.message;
|
||||
|
||||
if (typeof error === 'object' && error !== null && 'msg' in error) {
|
||||
const message = (error as { msg?: unknown }).msg;
|
||||
if (typeof message === 'string') return message;
|
||||
}
|
||||
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function createSigningSecret(): string {
|
||||
const bytes = new Uint8Array(32);
|
||||
crypto.getRandomValues(bytes);
|
||||
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
export function ensureHttpBotSigningSecret(
|
||||
adapterName: string,
|
||||
config: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
if (
|
||||
adapterName !== 'http_bot' ||
|
||||
config.signature_required === false ||
|
||||
(typeof config.inbound_secret === 'string' && config.inbound_secret)
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
inbound_secret: createSigningSecret(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import ts from 'typescript';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
|
||||
const sourcePath = path.resolve(
|
||||
currentDirectory,
|
||||
'../../src/app/wizard/utils.ts',
|
||||
);
|
||||
|
||||
function loadWizardUtils() {
|
||||
const source = fs.readFileSync(sourcePath, 'utf8');
|
||||
const compiled = ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS },
|
||||
}).outputText;
|
||||
const loadedModule = { exports: {} };
|
||||
new Function('require', 'module', 'exports', compiled)(
|
||||
() => {
|
||||
throw new Error('Wizard utils must not have runtime imports');
|
||||
},
|
||||
loadedModule,
|
||||
loadedModule.exports,
|
||||
);
|
||||
return loadedModule.exports;
|
||||
}
|
||||
|
||||
const { ensureHttpBotSigningSecret, getErrorMessage } = loadWizardUtils();
|
||||
|
||||
test('generates an HTTP Bot signing secret when signatures are enabled', () => {
|
||||
const config = ensureHttpBotSigningSecret('http_bot', {
|
||||
signature_required: true,
|
||||
inbound_secret: '',
|
||||
});
|
||||
|
||||
assert.match(config.inbound_secret, /^[a-f0-9]{64}$/);
|
||||
});
|
||||
|
||||
test('preserves existing or intentionally disabled HTTP Bot signing config', () => {
|
||||
const existing = { signature_required: true, inbound_secret: 'keep-me' };
|
||||
const disabled = { signature_required: false, inbound_secret: '' };
|
||||
|
||||
assert.equal(ensureHttpBotSigningSecret('http_bot', existing), existing);
|
||||
assert.equal(ensureHttpBotSigningSecret('http_bot', disabled), disabled);
|
||||
});
|
||||
|
||||
test('does not add signing config to other adapters', () => {
|
||||
const config = {};
|
||||
|
||||
assert.equal(ensureHttpBotSigningSecret('web_page_bot', config), config);
|
||||
});
|
||||
|
||||
test('extracts the backend message from structured API errors', () => {
|
||||
assert.equal(
|
||||
getErrorMessage({ code: 400, msg: 'Signing secret is required' }),
|
||||
'Signing secret is required',
|
||||
);
|
||||
assert.equal(getErrorMessage(new Error('Network failed')), 'Network failed');
|
||||
});
|
||||
Reference in New Issue
Block a user