mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-09-30 07:43:17 +00:00
52 lines
2 KiB
TypeScript
52 lines
2 KiB
TypeScript
|
|
/**
|
||
|
|
* Group-sync Lambda entrypoint — runs every 5 minutes (EventBridge).
|
||
|
|
*
|
||
|
|
* Mirrors Google Group membership into the matching Cognito groups and, on full
|
||
|
|
* success, writes the freshness marker the pre-token Lambda fails closed on. Any
|
||
|
|
* error propagates so the CloudWatch alarm fires and the marker stays stale.
|
||
|
|
*
|
||
|
|
* Reads the Google service-account credentials from Secrets Manager on cold
|
||
|
|
* start (never an env var — secrets-and-config handbook rule).
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
|
||
|
|
|
||
|
|
import { GoogleDirectoryReader, type GoogleServiceAccount } from './clients.js';
|
||
|
|
import { CognitoGroupSync, DynamoSyncStateWriter } from './aws.js';
|
||
|
|
import { runSync } from './sync.js';
|
||
|
|
import { DEFAULT_DOMAIN } from './groups.js';
|
||
|
|
|
||
|
|
let cachedSa: GoogleServiceAccount | undefined;
|
||
|
|
|
||
|
|
async function loadServiceAccount(secretArn: string): Promise<GoogleServiceAccount> {
|
||
|
|
if (cachedSa) return cachedSa;
|
||
|
|
const sm = new SecretsManagerClient({});
|
||
|
|
const res = await sm.send(new GetSecretValueCommand({ SecretId: secretArn }));
|
||
|
|
if (!res.SecretString) throw new Error('Google SA secret has no SecretString');
|
||
|
|
cachedSa = JSON.parse(res.SecretString) as GoogleServiceAccount;
|
||
|
|
return cachedSa;
|
||
|
|
}
|
||
|
|
|
||
|
|
function requireEnv(name: string): string {
|
||
|
|
const v = process.env[name];
|
||
|
|
if (!v) throw new Error(`Missing required env var ${name}`);
|
||
|
|
return v;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function handler(): Promise<{ ok: true; results: unknown }> {
|
||
|
|
const userPoolId = requireEnv('USER_POOL_ID');
|
||
|
|
const syncStateTable = requireEnv('SYNC_STATE_TABLE');
|
||
|
|
const saSecretArn = requireEnv('GOOGLE_SA_SECRET_ARN');
|
||
|
|
const domain = process.env['WORKSPACE_DOMAIN'] ?? DEFAULT_DOMAIN;
|
||
|
|
|
||
|
|
const sa = await loadServiceAccount(saSecretArn);
|
||
|
|
const results = await runSync({
|
||
|
|
directory: new GoogleDirectoryReader(sa),
|
||
|
|
cognito: new CognitoGroupSync(userPoolId),
|
||
|
|
syncState: new DynamoSyncStateWriter(syncStateTable),
|
||
|
|
domain,
|
||
|
|
});
|
||
|
|
console.log(JSON.stringify({ event: 'group_sync_ok', results }));
|
||
|
|
return { ok: true, results };
|
||
|
|
}
|