Mattermost: refresh interaction callback URL cache

This commit is contained in:
Muhammed Mukhthar CM
2026-03-06 09:53:04 +00:00
parent 9d8950a051
commit 4d593731be
3 changed files with 33 additions and 9 deletions

View File

@@ -5,6 +5,7 @@ import { resolveMattermostAccount } from "./accounts.js";
import type { MattermostClient } from "./client.js";
import {
buildButtonAttachments,
computeInteractionCallbackUrl,
createMattermostInteractionHandler,
generateInteractionToken,
getInteractionCallbackUrl,
@@ -136,7 +137,9 @@ describe("callback URL registry", () => {
describe("resolveInteractionCallbackUrl", () => {
afterEach(() => {
setInteractionCallbackUrl("resolve-test", "");
for (const accountId of ["cached", "default", "acct", "myaccount"]) {
setInteractionCallbackUrl(accountId, "");
}
});
it("prefers cached URL from registry", () => {
@@ -144,6 +147,14 @@ describe("resolveInteractionCallbackUrl", () => {
expect(resolveInteractionCallbackUrl("cached")).toBe("http://cached:1234/path");
});
it("recomputes from config when bypassing the cache explicitly", () => {
setInteractionCallbackUrl("acct", "http://cached:1234/path");
const url = computeInteractionCallbackUrl("acct", {
gateway: { port: 9999, customBindHost: "gateway.internal" },
});
expect(url).toBe("http://gateway.internal:9999/mattermost/interactions/acct");
});
it("uses interactions.callbackBaseUrl when configured", () => {
const url = resolveInteractionCallbackUrl("default", {
channels: {

View File

@@ -67,17 +67,12 @@ function normalizeCallbackBaseUrl(baseUrl: string): string {
/**
* Resolve the interaction callback URL for an account.
* Prefers the in-memory registered URL (set by the gateway monitor).
* Falls back to computing it from interactions.callbackBaseUrl or gateway host config.
*/
export function resolveInteractionCallbackUrl(
export function computeInteractionCallbackUrl(
accountId: string,
cfg?: InteractionCallbackConfig,
): string {
const cached = callbackUrls.get(accountId);
if (cached) {
return cached;
}
const path = resolveInteractionCallbackPath(accountId);
// Prefer merged per-account config when available, but keep the top-level path for
// callers/tests that still pass the root Mattermost config shape directly.
@@ -101,6 +96,22 @@ export function resolveInteractionCallbackUrl(
return `http://${host}:${port}${path}`;
}
/**
* Resolve the interaction callback URL for an account.
* Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the
* monitor lifecycle can reuse the runtime-validated callback destination.
*/
export function resolveInteractionCallbackUrl(
accountId: string,
cfg?: InteractionCallbackConfig,
): string {
const cached = callbackUrls.get(accountId);
if (cached) {
return cached;
}
return computeInteractionCallbackUrl(accountId, cfg);
}
// ── HMAC token management ──────────────────────────────────────────────
// Secret is derived from the bot token so it's stable across CLI and gateway processes.

View File

@@ -44,8 +44,8 @@ import {
type MattermostUser,
} from "./client.js";
import {
computeInteractionCallbackUrl,
createMattermostInteractionHandler,
resolveInteractionCallbackUrl,
resolveInteractionCallbackPath,
setInteractionCallbackUrl,
setInteractionSecret,
@@ -456,7 +456,9 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
// Register HTTP callback endpoint for interactive button clicks.
// Mattermost POSTs to this URL when a user clicks a button action.
const interactionPath = resolveInteractionCallbackPath(account.accountId);
const callbackUrl = resolveInteractionCallbackUrl(account.accountId, {
// Recompute from config on each monitor start so reconnects or config reloads can refresh the
// cached callback URL for downstream callers such as `message action=send`.
const callbackUrl = computeInteractionCallbackUrl(account.accountId, {
gateway: cfg.gateway,
interactions: account.config.interactions,
});