trails/apps/journal/app/lib/crypto.server.ts
Ullrich Schäfer 5fd60ba07d WIP: Komoot import integration
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>
2026-04-13 20:10:00 +02:00

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");
}