Add Komoot API client, import logic, crypto helpers, integration routes, DB schema changes, and i18n strings for the Komoot import feature. Import processing runs as a durable pg-boss background job with retries instead of blocking the HTTP request. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
|
|
|
|
const ALGORITHM = "aes-256-gcm";
|
|
const IV_LENGTH = 12;
|
|
const TAG_LENGTH = 16;
|
|
|
|
function getKey(): Buffer {
|
|
const secret = process.env.INTEGRATION_SECRET;
|
|
if (!secret) throw new Error("INTEGRATION_SECRET environment variable is required");
|
|
return scryptSync(secret, "trails-cool-integrations", 32);
|
|
}
|
|
|
|
export function encrypt(plaintext: string): string {
|
|
const key = getKey();
|
|
const iv = randomBytes(IV_LENGTH);
|
|
const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: TAG_LENGTH });
|
|
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
const tag = cipher.getAuthTag();
|
|
return Buffer.concat([iv, tag, encrypted]).toString("base64");
|
|
}
|
|
|
|
export function decrypt(encoded: string): string {
|
|
const key = getKey();
|
|
const buf = Buffer.from(encoded, "base64");
|
|
const iv = buf.subarray(0, IV_LENGTH);
|
|
const tag = buf.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
|
|
const encrypted = buf.subarray(IV_LENGTH + TAG_LENGTH);
|
|
const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: TAG_LENGTH });
|
|
decipher.setAuthTag(tag);
|
|
return decipher.update(encrypted) + decipher.final("utf8");
|
|
}
|