Introduces integration-based webhook registration, message parsing, dry-run monitoring, JSM ticket creation, and OAuth token refresh for DC Ops spaces. Co-authored-by: Cursor <cursoragent@cursor.com>
145 lines
5.5 KiB
JavaScript
145 lines
5.5 KiB
JavaScript
import dotenv from 'dotenv';
|
|
import fs from 'fs';
|
|
|
|
dotenv.config();
|
|
|
|
function envOrConfig(envKey, configValue) {
|
|
const envValue = process.env[envKey];
|
|
if (envValue !== undefined && envValue !== '') {
|
|
return envValue;
|
|
}
|
|
return configValue;
|
|
}
|
|
|
|
function envBool(envKey, defaultValue = false) {
|
|
const envValue = process.env[envKey];
|
|
if (envValue === undefined || envValue === '') {
|
|
return defaultValue;
|
|
}
|
|
return envValue === 'true' || envValue === '1';
|
|
}
|
|
|
|
function resolveJiraHost(fileConfig) {
|
|
const cloudId = envOrConfig('JIRA_CLOUD_ID', fileConfig.jiraCloud?.cloudId)?.trim();
|
|
let baseUrl = envOrConfig('JIRA_BASE_URL', fileConfig.jiraCloud?.host || 'https://aeo.atlassian.net')?.trim();
|
|
|
|
if (cloudId) {
|
|
return `https://api.atlassian.com/ex/jira/${cloudId}`;
|
|
}
|
|
|
|
return baseUrl.replace(/\/$/, '');
|
|
}
|
|
|
|
export function loadConfig() {
|
|
const fileConfig = JSON.parse(fs.readFileSync('./config/config.json'));
|
|
|
|
const config = {
|
|
...fileConfig,
|
|
logLevel: envOrConfig('LOG_LEVEL', fileConfig.logLevel),
|
|
server: {
|
|
...fileConfig.server,
|
|
port: envOrConfig('PORT', fileConfig.server?.port),
|
|
name: envOrConfig('SERVER_NAME', fileConfig.server?.name),
|
|
},
|
|
webex: {
|
|
...fileConfig.webex,
|
|
token: envOrConfig('WEBEX_TOKEN', fileConfig.webex?.token),
|
|
webhookSecret: envOrConfig('WEBEX_WEBHOOK_SECRET', fileConfig.webex?.webhookSecret),
|
|
webhookTargetUrl: envOrConfig(
|
|
'WEBEX_WEBHOOK_TARGET_URL',
|
|
fileConfig.webex?.webhookTargetUrl || 'https://bot.joesjavajoint.com/jiracloud/webex/messages'
|
|
),
|
|
inboundEnabled: envBool('WEBEX_INBOUND_ENABLED', false),
|
|
inboundDryRun: envBool('WEBEX_INBOUND_DRY_RUN', true),
|
|
botPersonId: envOrConfig('WEBEX_BOT_PERSON_ID', fileConfig.webex?.botPersonId),
|
|
integration: {
|
|
clientId: envOrConfig('WEBEX_INTEGRATION_CLIENT_ID', fileConfig.webex?.integration?.clientId),
|
|
clientSecret: envOrConfig('WEBEX_INTEGRATION_CLIENT_SECRET', fileConfig.webex?.integration?.clientSecret),
|
|
redirectUri: envOrConfig(
|
|
'WEBEX_INTEGRATION_REDIRECT_URI',
|
|
fileConfig.webex?.integration?.redirectUri || 'https://bot.joesjavajoint.com/jiracloud/webex/oauth/callback'
|
|
),
|
|
scopes: envOrConfig(
|
|
'WEBEX_INTEGRATION_SCOPES',
|
|
fileConfig.webex?.integration?.scopes || 'spark:messages_read spark:webhooks_read spark:webhooks_write'
|
|
),
|
|
tokenFile: envOrConfig(
|
|
'WEBEX_OAUTH_TOKEN_FILE',
|
|
fileConfig.webex?.integration?.tokenFile || './config/webex-oauth.json'
|
|
),
|
|
},
|
|
room: {
|
|
...fileConfig.webex?.room,
|
|
id: envOrConfig('WEBEX_ROOM_ID', fileConfig.webex?.room?.id),
|
|
},
|
|
},
|
|
jiraNotifier: {
|
|
...fileConfig.jiraNotifier,
|
|
token: envOrConfig('JIRA_NOTIFIER_TOKEN', fileConfig.jiraNotifier?.token),
|
|
},
|
|
jiraCloud: {
|
|
...fileConfig.jiraCloud,
|
|
cloudId: envOrConfig('JIRA_CLOUD_ID', fileConfig.jiraCloud?.cloudId) || null,
|
|
host: resolveJiraHost(fileConfig),
|
|
siteUrl: (envOrConfig('JIRA_BASE_URL', fileConfig.jiraCloud?.host || 'https://aeo.atlassian.net') || '').replace(/\/$/, ''),
|
|
authType: (envOrConfig('JIRA_AUTH_TYPE', fileConfig.jiraCloud?.authType) || 'basic').toLowerCase(),
|
|
email: envOrConfig('JIRA_EMAIL', fileConfig.jiraCloud?.email),
|
|
token: envOrConfig('JIRA_API_TOKEN', fileConfig.jiraCloud?.token),
|
|
},
|
|
rooms: {
|
|
approval: envOrConfig(
|
|
'APPROVAL_ROOM_ID',
|
|
fileConfig.rooms?.approval || 'Y2lzY29zcGFyazovL3VzL1JPT00vZWQ4ZWY3OTAtMGE5NC0xMWVjLWExZDUtM2I5ZWNlNWFlNDI5'
|
|
),
|
|
support: envOrConfig(
|
|
'SUPPORT_ROOM_ID',
|
|
fileConfig.rooms?.support || 'Y2lzY29zcGFyazovL3VzL1JPT00vZWY4MmExNjAtNzYzNS0xMWU4LWFlYWMtNjNjMGMyMTExYTVm'
|
|
),
|
|
},
|
|
};
|
|
|
|
return config;
|
|
}
|
|
|
|
export function loadRequests() {
|
|
return JSON.parse(fs.readFileSync('./config/requests.json'));
|
|
}
|
|
|
|
export function loadWebexRooms() {
|
|
const roomsPath = './config/webexRooms.json';
|
|
if (!fs.existsSync(roomsPath)) {
|
|
return {};
|
|
}
|
|
return JSON.parse(fs.readFileSync(roomsPath));
|
|
}
|
|
|
|
export function findWebexRoomById(webexRooms, roomId) {
|
|
if (!roomId || !webexRooms) {
|
|
return null;
|
|
}
|
|
|
|
for (const [configKey, room] of Object.entries(webexRooms)) {
|
|
if (room.roomId === roomId && room.enabled !== false) {
|
|
return { configKey, ...room };
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getMissingSecrets(config) {
|
|
const missing = [];
|
|
if (!config.jiraCloud?.email) missing.push('JIRA_EMAIL');
|
|
if (!config.jiraCloud?.token) missing.push('JIRA_API_TOKEN');
|
|
if (!config.webex?.token) missing.push('WEBEX_TOKEN');
|
|
if (!config.jiraNotifier?.token) missing.push('JIRA_NOTIFIER_TOKEN');
|
|
return missing;
|
|
}
|
|
|
|
export function describeJiraIdentity(config) {
|
|
const email = config.jiraCloud?.email || '(not set)';
|
|
const host = config.jiraCloud?.host || '(not set)';
|
|
const authType = config.jiraCloud?.authType || 'basic';
|
|
const viaCloudId = config.jiraCloud?.cloudId ? `cloudId=${config.jiraCloud.cloudId}` : 'site URL';
|
|
return `${email} (${authType}, ${viaCloudId}) → ${host}`;
|
|
}
|