jiraCloud/lib/config.js
jmcqueen efc64a227c Add Webex inbound OAuth flow and room-to-Jira job request pipeline.
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>
2026-07-28 08:27:52 -04:00

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}`;
}