@codelusitan/website-integrations
v0.1.0
Published
Shared service-client layer for CodeLusitan client-website backends: Keycloak M2M tokens, Oathkeeper Gateway fetch, LusoMail/LusoLog/LusoAudit clients, Cal.com booking, and a generic rate limiter. Extracted from CodeLusitan/Home when a second client websi
Readme
@codelusitan/website-integrations
Shared service-client layer for CodeLusitan client-website backends — the small, independent Express apps behind sites like codelusitan.me that authenticate to the platform as an M2M Keycloak client (ADR-0053) and call Oathkeeper-routed Luso* services, distinct from @codelusitan/genericfunctions (which the Luso* services themselves use to build their own CRUD APIs).
Extracted from CodeLusitan/Home when a second client website needed the exact same pattern (Keycloak M2M token → Oathkeeper Gateway → LusoMail/LusoLog/LusoAudit, plus Cal.com booking) — every module here is a thin, protocol-level client with no site-specific business logic. What each site actually does with them (which fields a contact form has, what's allowed to be booked, its own validation rules) stays in that site's own repository, same split @codelusitan/genericfunctions already uses between its generic building blocks and each service's own routes/controllers.
Installation
npm install @codelusitan/website-integrationsUsage
Each client is a factory function — call it once at startup with your site's own config, wire the pieces together, then use the returned object everywhere:
const {
createKeycloakClient,
createGatewayClient,
createLusoMailClient,
createLusoLogClient,
createLusoAuditClient,
createCalComClient,
rateLimit,
} = require("@codelusitan/website-integrations");
const keycloak = createKeycloakClient({
tokenUrl: "https://auth.codelusitan.me/realms/codelusitan/protocol/openid-connect/token",
clientId: "my-site-backend",
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
});
const { gatewayFetch } = createGatewayClient({ gatewayUrl: "https://apps.codelusitan.me", keycloakClient: keycloak });
const lusoMail = createLusoMailClient({ gatewayFetch });
const lusoLog = createLusoLogClient({ gatewayFetch, projectId: process.env.PROJECT_ID, logger: myLogger });
const lusoAudit = createLusoAuditClient({ gatewayFetch, logger: myLogger, service: "my-site", actorId: "my-site-backend" });
const cal = createCalComClient({ apiUrl: "http://calcom-api:18002/v2", username: "my-cal-username" });Modules
| Module | What it does |
|---|---|
| createKeycloakClient | M2M client_credentials token, cached until near expiry, single in-flight request under concurrent callers. |
| createGatewayClient | Authenticated fetch through Oathkeeper; retries once with a fresh token on a 401. |
| createLusoMailClient | scheduleEmail({ templateCode, toEmail, templateData, language }) — resolves a template's id from its code once, caches it. The caller never creates or edits templates from a site backend; that stays a LusoAdmin/LusoMail concern. |
| createLusoLogClient | insert({ level, message, details }) — always logs locally (via the logger you pass in), best-effort remote insert if projectId is set. |
| createLusoAuditClient | record({ action, resourceType, resourceId, metadata }) — best-effort, tenant comes from the token (Oathkeeper/ADR-0055), never record personal data about your own visitors in metadata. |
| createCalComClient | getSlots/createBooking against Cal.com's public v2 API (no token needed — reached container-to-container, not through Oathkeeper). |
| rateLimit({ max, windowMs }) | Fixed-window, per-IP, in-memory Express middleware. No shared store; fine for a single container. |
See CodeLusitan/Home's server/src/services.js for a complete real wiring example.
