From b46d1388f624e489ead0046acab0a714e7ed8361 Mon Sep 17 00:00:00 2001 From: Mariano Fuentes Date: Fri, 7 Mar 2025 09:55:35 -0500 Subject: [PATCH] refactor: update Trigger.dev configuration and encryption strategy - Update trigger configs to include new trigger directories - Modify encryption method to use Node.js native scrypt instead of argon2 - Remove Deel-specific tasks and update workflow to include trigger directory - Add SECRET_KEY to deployment workflow - Simplify key derivation and remove external dependency --- .github/workflows/trigger-tasks-deploy.yml | 2 + apps/app/src/lib/encryption.ts | 28 ++++++----- .../src/{jobs/tasks => trigger}/deel/index.ts | 49 ++++++++++++++++-- .../{jobs/tasks => trigger}/deel/schedule.ts | 2 +- apps/app/trigger.config.ts | 50 +++++++++---------- apps/web/trigger.config.ts | 2 +- 6 files changed, 89 insertions(+), 44 deletions(-) rename apps/app/src/{jobs/tasks => trigger}/deel/index.ts (79%) rename apps/app/src/{jobs/tasks => trigger}/deel/schedule.ts (98%) diff --git a/.github/workflows/trigger-tasks-deploy.yml b/.github/workflows/trigger-tasks-deploy.yml index 8b532ff8b2..00bb375f01 100644 --- a/.github/workflows/trigger-tasks-deploy.yml +++ b/.github/workflows/trigger-tasks-deploy.yml @@ -6,6 +6,7 @@ on: - main paths: - "**/src/jobs/tasks/**" + - "**/src/trigger/**" jobs: deploy: @@ -28,4 +29,5 @@ jobs: - name: 🚀 Deploy Trigger.dev env: TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} + SECRET_KEY: ${{ secrets.SECRET_KEY }} run: bunx trigger.dev@latest deploy diff --git a/apps/app/src/lib/encryption.ts b/apps/app/src/lib/encryption.ts index 43b65ba97b..d35ff90f48 100644 --- a/apps/app/src/lib/encryption.ts +++ b/apps/app/src/lib/encryption.ts @@ -1,5 +1,9 @@ -import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto"; -import argon2 from "argon2"; +import { + createCipheriv, + createDecipheriv, + randomBytes, + scryptSync, +} from "node:crypto"; const ALGORITHM = "aes-256-gcm"; const IV_LENGTH = 12; @@ -14,16 +18,14 @@ export interface EncryptedData { salt: string; } -export async function deriveKey(secret: string, salt: Buffer): Promise { - const hash = await argon2.hash(secret, { - salt, - memoryCost: 19456, - timeCost: 2, - parallelism: 1, - raw: true, +// Simple key derivation using Node's built-in scrypt instead of argon2 +function deriveKey(secret: string, salt: Buffer): Buffer { + return scryptSync(secret, salt, KEY_LENGTH, { + // These are reasonable defaults for scrypt + N: 16384, + r: 8, + p: 1, }); - - return Buffer.from(hash); } export async function encrypt(text: string): Promise { @@ -35,7 +37,7 @@ export async function encrypt(text: string): Promise { const salt = randomBytes(SALT_LENGTH); const iv = randomBytes(IV_LENGTH); - const key = await deriveKey(secretKey, salt); + const key = deriveKey(secretKey, salt); const cipher = createCipheriv(ALGORITHM, key, iv); const encrypted = Buffer.concat([ @@ -64,7 +66,7 @@ export async function decrypt(encryptedData: EncryptedData): Promise { const tag = Buffer.from(encryptedData.tag, "base64"); const salt = Buffer.from(encryptedData.salt, "base64"); - const key = await deriveKey(secretKey, salt); + const key = deriveKey(secretKey, salt); const decipher = createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(tag); diff --git a/apps/app/src/jobs/tasks/deel/index.ts b/apps/app/src/trigger/deel/index.ts similarity index 79% rename from apps/app/src/jobs/tasks/deel/index.ts rename to apps/app/src/trigger/deel/index.ts index edf251a0d6..c5706e7cb2 100644 --- a/apps/app/src/jobs/tasks/deel/index.ts +++ b/apps/app/src/trigger/deel/index.ts @@ -1,7 +1,9 @@ import { db } from "@bubba/db"; import { logger, schemaTask } from "@trigger.dev/sdk/v3"; +import axios from "axios"; import { z } from "zod"; import { Departments } from "@bubba/db"; +import { decrypt } from "@/lib/encryption"; // Define the input schema for the Deel task const deelTaskSchema = z.object({ @@ -127,12 +129,51 @@ export const syncDeelEmployees = schemaTask({ } // Extract access token from user settings - const accessToken = integration.user_settings.accessToken; - if (!accessToken) { + let accessToken: string | undefined; + try { + if ( + integration.user_settings.api_key && + typeof integration.user_settings.api_key === "object" && + "encrypted" in integration.user_settings.api_key + ) { + // Decrypt the access token + try { + accessToken = await decrypt(integration.user_settings.api_key); + logger.info("Successfully decrypted Deel API key"); + } catch (decryptError) { + logger.error(`Failed to decrypt Deel API key: ${decryptError}`); + + // Check if SECRET_KEY is set + if (!process.env.SECRET_KEY) { + return { + success: false, + error: + "Missing SECRET_KEY environment variable required for decryption", + }; + } + + return { + success: false, + error: + "Failed to decrypt API key. Make sure SECRET_KEY is correct.", + }; + } + } else { + // For backward compatibility, in case it's stored as a plain string + accessToken = integration.user_settings.api_key; + } + + if (!accessToken) { + logger.error( + `Deel integration ${integration.name} is missing an access token` + ); + return { success: false, error: "Missing access token" }; + } + } catch (error) { logger.error( - `Deel integration ${integration.name} is missing an access token` + `Failed to decrypt access token for Deel integration ${integration.name}: ${error}` ); - return { success: false, error: "Missing access token" }; + return { success: false, error: "Failed to decrypt access token" }; } // Fetch employees from Deel diff --git a/apps/app/src/jobs/tasks/deel/schedule.ts b/apps/app/src/trigger/deel/schedule.ts similarity index 98% rename from apps/app/src/jobs/tasks/deel/schedule.ts rename to apps/app/src/trigger/deel/schedule.ts index cdc3215128..c8d50cd974 100644 --- a/apps/app/src/jobs/tasks/deel/schedule.ts +++ b/apps/app/src/trigger/deel/schedule.ts @@ -11,7 +11,7 @@ export const deelEmployeeSchedule = schedules.task({ const deelIntegrations = await db.organizationIntegrations.findMany({ where: { - integration_id: "Deel", + integration_id: "deel", }, select: { id: true, diff --git a/apps/app/trigger.config.ts b/apps/app/trigger.config.ts index 0c7bcd4516..2afb9ca51a 100644 --- a/apps/app/trigger.config.ts +++ b/apps/app/trigger.config.ts @@ -5,29 +5,29 @@ import { puppeteer } from "@trigger.dev/build/extensions/puppeteer"; import { defineConfig } from "@trigger.dev/sdk/v3"; export default defineConfig({ - project: "proj_lhxjliiqgcdyqbgtucda", - runtime: "node", - logLevel: "log", - instrumentations: [new PrismaInstrumentation()], - maxDuration: 300, - build: { - extensions: [ - prismaExtension({ - schema: "../../packages/db/prisma/schema/schema.prisma", - }), - puppeteer(), - syncVercelEnvVars(), - ], - }, - retries: { - enabledInDev: true, - default: { - maxAttempts: 3, - minTimeoutInMs: 1000, - maxTimeoutInMs: 10000, - factor: 2, - randomize: true, - }, - }, - dirs: ["./src/jobs"], + project: "proj_lhxjliiqgcdyqbgtucda", + runtime: "node", + logLevel: "log", + instrumentations: [new PrismaInstrumentation()], + maxDuration: 300, // 5 minutes + build: { + extensions: [ + prismaExtension({ + schema: "../../packages/db/prisma/schema/schema.prisma", + }), + puppeteer(), + syncVercelEnvVars(), + ], + }, + retries: { + enabledInDev: true, + default: { + maxAttempts: 3, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + factor: 2, + randomize: true, + }, + }, + dirs: ["./src/jobs", "./src/trigger"], }); diff --git a/apps/web/trigger.config.ts b/apps/web/trigger.config.ts index f5174a3aa3..2b8b777f91 100644 --- a/apps/web/trigger.config.ts +++ b/apps/web/trigger.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ // The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped. // You can override this on an individual task. // See https://trigger.dev/docs/runs/max-duration - maxDuration: 3600, + maxDuration: 3600, // 1 hour retries: { enabledInDev: true, default: {