Infrastructure build to support third-party app integrations
This commit is contained in:
95
backend/config/providers.ts
Normal file
95
backend/config/providers.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type {
|
||||
NormalizedDelivery,
|
||||
ProviderSlug,
|
||||
SignatureScheme,
|
||||
} from '../types/integration.js';
|
||||
import { normalizeLinear, normalizeRest } from './normalizers.js';
|
||||
|
||||
export type ChallengeResponse = { status: number; body: unknown };
|
||||
|
||||
export type ProviderConfig = {
|
||||
slug: ProviderSlug;
|
||||
signatureScheme: SignatureScheme;
|
||||
/** Header carrying the signature. Empty only when the scheme is 'none'. */
|
||||
signatureHeader: string;
|
||||
timestampHeader?: string;
|
||||
/** Environment variable holding the shared secret when the install has none. */
|
||||
secretEnvVar?: string;
|
||||
/** Returns a response when the request is a handshake rather than an event. */
|
||||
challenge?: (body: unknown) => ChallengeResponse | null;
|
||||
/**
|
||||
* Absent until that provider's integration is built. The transport above is
|
||||
* infrastructure; the payload mapping belongs to the integration itself.
|
||||
*/
|
||||
normalize?: (payload: unknown) => NormalizedDelivery;
|
||||
oauth?: {
|
||||
authorizeUrl: string;
|
||||
tokenUrl: string;
|
||||
scopes: string[];
|
||||
};
|
||||
};
|
||||
|
||||
const slackChallenge = (body: unknown): ChallengeResponse | null => {
|
||||
if (typeof body !== 'object' || body === null) return null;
|
||||
const candidate = body as { type?: unknown; challenge?: unknown };
|
||||
if (candidate.type !== 'url_verification') return null;
|
||||
if (typeof candidate.challenge !== 'string') return null;
|
||||
return { status: 200, body: { challenge: candidate.challenge } };
|
||||
};
|
||||
|
||||
export const providers: Record<ProviderSlug, ProviderConfig> = {
|
||||
rest: {
|
||||
slug: 'rest',
|
||||
signatureScheme: 'none',
|
||||
signatureHeader: '',
|
||||
normalize: normalizeRest,
|
||||
},
|
||||
linear: {
|
||||
slug: 'linear',
|
||||
signatureScheme: 'linear-sha256',
|
||||
signatureHeader: 'linear-signature',
|
||||
secretEnvVar: 'LINEAR_SIGNING_SECRET',
|
||||
normalize: normalizeLinear,
|
||||
oauth: {
|
||||
authorizeUrl: 'https://linear.app/oauth/authorize',
|
||||
tokenUrl: 'https://api.linear.app/oauth/token',
|
||||
scopes: ['read'],
|
||||
},
|
||||
},
|
||||
slack: {
|
||||
slug: 'slack',
|
||||
signatureScheme: 'slack-v0',
|
||||
signatureHeader: 'x-slack-signature',
|
||||
timestampHeader: 'x-slack-request-timestamp',
|
||||
secretEnvVar: 'SLACK_SIGNING_SECRET',
|
||||
challenge: slackChallenge,
|
||||
oauth: {
|
||||
authorizeUrl: 'https://slack.com/oauth/v2/authorize',
|
||||
tokenUrl: 'https://slack.com/api/oauth.v2.access',
|
||||
scopes: ['channels:history', 'reactions:read', 'users:read'],
|
||||
},
|
||||
},
|
||||
github: {
|
||||
slug: 'github',
|
||||
signatureScheme: 'github-sha256',
|
||||
signatureHeader: 'x-hub-signature-256',
|
||||
secretEnvVar: 'GITHUB_WEBHOOK_SECRET',
|
||||
oauth: {
|
||||
authorizeUrl: 'https://github.com/login/oauth/authorize',
|
||||
tokenUrl: 'https://github.com/login/oauth/access_token',
|
||||
scopes: ['repo', 'read:discussion'],
|
||||
},
|
||||
},
|
||||
jira: {
|
||||
slug: 'jira',
|
||||
signatureScheme: 'none',
|
||||
signatureHeader: '',
|
||||
oauth: {
|
||||
authorizeUrl: 'https://auth.atlassian.com/authorize',
|
||||
tokenUrl: 'https://auth.atlassian.com/oauth/token',
|
||||
scopes: ['read:jira-work', 'offline_access'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const getProvider = (slug: ProviderSlug): ProviderConfig => providers[slug];
|
||||
Reference in New Issue
Block a user