diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/settings/api-keys/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/api-keys/page.tsx index fb13e34e96..967d451d82 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/settings/api-keys/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/api-keys/page.tsx @@ -20,7 +20,11 @@ export default async function ApiKeysPage({ return redirect("/"); } - return ; + return ( +
+ +
+ ); } export async function generateMetadata({ diff --git a/apps/app/src/components/integrations/integrations-card.tsx b/apps/app/src/components/integrations/integrations-card.tsx index cb1ac26f2a..b45e765c2d 100644 --- a/apps/app/src/components/integrations/integrations-card.tsx +++ b/apps/app/src/components/integrations/integrations-card.tsx @@ -1,10 +1,10 @@ import { deleteIntegrationConnectionAction } from "@/actions/integrations/delete-integration-connection"; import { retrieveIntegrationSessionTokenAction } from "@/actions/integrations/retrieve-integration-session-token"; import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, } from "@bubba/ui/accordion"; import { Button } from "@bubba/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; @@ -16,195 +16,197 @@ import { parseAsBoolean, parseAsString, useQueryStates } from "nuqs"; import { useState } from "react"; import { toast } from "sonner"; import { - IntegrationSettings, - type IntegrationSettingsItem, + IntegrationSettings, + type IntegrationSettingsItem, } from "./integration-settings"; +import Image from "next/image"; +import type { StaticImageData } from "next/image"; export function IntegrationsCard({ - id, - logo: Logo, - name, - short_description, - description, - settings, - images, - active, - installed, - category, - installedSettings, + id, + logo, + name, + short_description, + description, + settings, + images, + active, + installed, + category, + installedSettings, }: { - id: string; - logo: React.ComponentType; - name: string; - short_description: string; - description: string; - settings: Record; - images: string[]; - active: boolean; - installed: boolean; - category: string; - installedSettings: Record; + id: string; + logo: StaticImageData; + name: string; + short_description: string; + description: string; + settings: Record; + images: string[]; + active: boolean; + installed: boolean; + category: string; + installedSettings: Record; }) { - const router = useRouter(); - - const [params, setParams] = useQueryStates({ - app: parseAsString, - settings: parseAsBoolean, - }); - - const retrieveIntegrationSessionToken = useAction( - retrieveIntegrationSessionTokenAction, - ); - - const deleteIntegrationConnection = useAction( - deleteIntegrationConnectionAction, - { - onSuccess: () => { - toast.success("Integration disconnected successfully"); - }, - onError: () => { - toast.error("Failed to disconnect integration"); - }, - }, - ); - - const [isLoading, setLoading] = useState(false); - - const handleConnect = async () => { - try { - setLoading(true); - - const res = await retrieveIntegrationSessionToken.executeAsync({ - integrationId: id, - }); - } catch (error) { - console.error("Connection error:", error); - toast.error("Failed to connect integration"); - setLoading(false); - } - }; - - return ( - - setParams(null)}> -
- - - {installed && ( -
- Connected -
- )} -
- - -
- - {name} - - {!active && ( - - Coming soon - - )} -
-
- - - {short_description} - - -
- -
- - - -
-
- -
-
-

{name}

- {installed && ( -
- )} -
- - - {category} • Published by Comp AI - -
-
{" "} -
- {installed && ( - - )} -
-
- - - - - How it works - - {description} - - - - {settings && settings.length > 0 && ( - - Settings - - - - - )} - - - -
-

- All integrations on the Comp AI store are open-source and - peer-reviewed. -

- - - Report an issue - -
- - - - ); + const router = useRouter(); + + const [params, setParams] = useQueryStates({ + app: parseAsString, + settings: parseAsBoolean, + }); + + const retrieveIntegrationSessionToken = useAction( + retrieveIntegrationSessionTokenAction, + ); + + const deleteIntegrationConnection = useAction( + deleteIntegrationConnectionAction, + { + onSuccess: () => { + toast.success("Integration disconnected successfully"); + }, + onError: () => { + toast.error("Failed to disconnect integration"); + }, + }, + ); + + const [isLoading, setLoading] = useState(false); + + const handleConnect = async () => { + try { + setLoading(true); + + const res = await retrieveIntegrationSessionToken.executeAsync({ + integrationId: id, + }); + } catch (error) { + console.error("Connection error:", error); + toast.error("Failed to connect integration"); + setLoading(false); + } + }; + + return ( + + setParams(null)}> +
+ {name} + + {installed && ( +
+ Connected +
+ )} +
+ + +
+ + {name} + + {!active && ( + + Coming soon + + )} +
+
+ + + {short_description} + + +
+ +
+ + + +
+
+ {name} +
+
+

{name}

+ {installed && ( +
+ )} +
+ + + {category} • Published by Comp AI + +
+
{" "} +
+ {installed && ( + + )} +
+
+ + + + + How it works + + {description} + + + + {settings && settings.length > 0 && ( + + Settings + + + + + )} + + + +
+

+ All integrations on the Comp AI store are open-source and + peer-reviewed. +

+ + + Report an issue + +
+ + + + ); } diff --git a/apps/app/src/components/tables/api-keys/create-api-key-dialog.tsx b/apps/app/src/components/tables/api-keys/create-api-key-dialog.tsx index b6fd0f1e8c..c4f651b6fd 100644 --- a/apps/app/src/components/tables/api-keys/create-api-key-dialog.tsx +++ b/apps/app/src/components/tables/api-keys/create-api-key-dialog.tsx @@ -1,7 +1,6 @@ "use client"; import { createApiKeyAction } from "@/actions/organization/create-api-key-action"; -import { apiKeySchema } from "@/actions/schema"; import { useI18n } from "@/locales/client"; import { Button } from "@bubba/ui/button"; import { @@ -99,7 +98,7 @@ export function CreateApiKeyDialog({ return ( - + {createdApiKey ? ( <> @@ -128,7 +127,6 @@ export function CreateApiKeyDialog({ size="icon" className="absolute right-1 top-1/2 -translate-y-1/2" onClick={copyToClipboard} - title="Copy" > {copied ? ( @@ -144,7 +142,7 @@ export function CreateApiKeyDialog({
- @@ -171,6 +169,7 @@ export function CreateApiKeyDialog({ onChange={(e) => setName(e.target.value)} placeholder={t("settings.api_keys.name_placeholder")} required + className="w-full" />
@@ -188,7 +187,7 @@ export function CreateApiKeyDialog({ ) } > - +
- + - + + + ))} + + + )} @@ -179,19 +204,20 @@ export function ApiKeysTable() {
- + {t("settings.api_keys.revoke_title")} {t("settings.api_keys.revoke_confirm")} - + @@ -200,6 +226,7 @@ export function ApiKeysTable() { variant="destructive" onClick={handleConfirmRevoke} disabled={isRevoking === "executing"} + className="w-full sm:w-auto" > {isRevoking === "executing" && ( diff --git a/apps/app/src/env.mjs b/apps/app/src/env.mjs index 3eecad3397..afd08c240b 100644 --- a/apps/app/src/env.mjs +++ b/apps/app/src/env.mjs @@ -15,6 +15,8 @@ export const env = createEnv({ STRIPE_WEBHOOK_SECRET: z.string(), DISCORD_WEBHOOK_URL: z.string(), TRIGGER_SECRET_KEY: z.string(), + TRIGGER_API_KEY: z.string().optional(), + TRIGGER_API_URL: z.string().optional(), VERCEL_ACCESS_TOKEN: z.string().optional(), VERCEL_TEAM_ID: z.string().optional(), VERCEL_PROJECT_ID: z.string().optional(), @@ -45,6 +47,8 @@ export const env = createEnv({ STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL, TRIGGER_SECRET_KEY: process.env.TRIGGER_SECRET_KEY, + TRIGGER_API_KEY: process.env.TRIGGER_API_KEY, + TRIGGER_API_URL: process.env.TRIGGER_API_URL, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST, VERCEL_ACCESS_TOKEN: process.env.VERCEL_ACCESS_TOKEN, diff --git a/apps/app/src/trigger/deel/index.ts b/apps/app/src/trigger/deel/index.ts new file mode 100644 index 0000000000..09968a2da2 --- /dev/null +++ b/apps/app/src/trigger/deel/index.ts @@ -0,0 +1,229 @@ +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"; + +// Define the input schema for the Deel task +const deelTaskSchema = z.object({ + integration: z.object({ + id: z.string(), + name: z.string(), + integration_id: z.string(), + settings: z.record(z.any()), + user_settings: z.record(z.any()), + organization: z.object({ + id: z.string(), + name: z.string(), + }), + }), +}); + +// Define the schema for Deel employee data +interface DeelEmployee { + id: string; + firstName: string; + lastName: string; + email: string; + department?: string; + title?: string; + status: string; +} + +/** + * Fetches employees from Deel API + */ +async function fetchDeelEmployees( + accessToken: string +): Promise { + try { + // const response = await axios.get("https://api.deel.com/v1/employees", { + // headers: { + // Authorization: `Bearer ${accessToken}`, + // "Content-Type": "application/json", + // }, + // }); + + // Return some fake data + return [ + { + id: "1", + firstName: "John", + lastName: "Doe", + email: "john.doe@example.com", + department: "Engineering", + title: "Software Engineer", + status: "active", + }, + ]; + + // Return the employees from the response + } catch (error) { + logger.error(`Failed to fetch employees from Deel: ${error}`); + throw error; + } +} + +/** + * Maps Deel department to our system's department enum + */ +function mapDeelDepartment(deelDepartment?: string): Departments { + if (!deelDepartment) return Departments.none; + + // Map Deel departments to our system's department enum + const departmentMap: Record = { + Engineering: Departments.it, + Product: Departments.it, + Marketing: Departments.admin, + Sales: Departments.admin, + HR: Departments.hr, + Finance: Departments.admin, + Legal: Departments.gov, + Operations: Departments.admin, + // Add more mappings as needed + }; + + return departmentMap[deelDepartment] || Departments.none; +} + +/** + * Task to sync employees from Deel to our system + */ +export const syncDeelEmployees = schemaTask({ + id: "sync-deel-employees", + schema: deelTaskSchema, + run: async ({ integration }) => { + logger.info(`Running Deel employee sync for ${integration.name}`); + + try { + // Check if the integration is for Deel + if (integration.integration_id !== "Deel") { + logger.warn( + `Integration ${integration.name} is not a Deel integration` + ); + return { success: false, error: "Not a Deel integration" }; + } + + // Check if we've run this integration within the last 24 hours + const lastRun = await db.integrationLastRun.findUnique({ + where: { + integrationId_organizationId: { + integrationId: integration.id, + organizationId: integration.organization.id, + }, + }, + }); + + const now = new Date(); + if ( + lastRun && + lastRun.lastRunAt > new Date(now.getTime() - 24 * 60 * 60 * 1000) + ) { + logger.info("Skipping Deel sync as it was run less than 24 hours ago"); + return { + success: true, + message: "Skipped - run less than 24 hours ago", + }; + } + + // Extract access token from user settings + const accessToken = integration.user_settings.accessToken; + if (!accessToken) { + logger.error( + `Deel integration ${integration.name} is missing an access token` + ); + return { success: false, error: "Missing access token" }; + } + + // Fetch employees from Deel + const deelEmployees = await fetchDeelEmployees(accessToken); + logger.info(`Fetched ${deelEmployees.length} employees from Deel`); + + // Process each employee + const processedEmployees = []; + for (const deelEmployee of deelEmployees) { + // Skip inactive employees + if (deelEmployee.status !== "active") { + continue; + } + + const name = + `${deelEmployee.firstName} ${deelEmployee.lastName}`.trim(); + const department = mapDeelDepartment(deelEmployee.department); + + // Check if employee already exists + const existingEmployee = await db.employee.findUnique({ + where: { + email_organizationId: { + email: deelEmployee.email, + organizationId: integration.organization.id, + }, + }, + }); + + if (existingEmployee) { + // Update existing employee + await db.employee.update({ + where: { + id: existingEmployee.id, + }, + data: { + name, + department, + externalEmployeeId: deelEmployee.id, + // Keep isActive true as we're filtering inactive employees already + }, + }); + processedEmployees.push({ + id: existingEmployee.id, + action: "updated", + }); + } else { + // Create new employee + const newEmployee = await db.employee.create({ + data: { + name, + email: deelEmployee.email, + department, + isActive: true, + externalEmployeeId: deelEmployee.id, + organizationId: integration.organization.id, + }, + }); + processedEmployees.push({ id: newEmployee.id, action: "created" }); + } + } + + // Update or create the last run record + await db.integrationLastRun.upsert({ + where: { + integrationId_organizationId: { + integrationId: integration.id, + organizationId: integration.organization.id, + }, + }, + create: { + integrationId: integration.id, + organizationId: integration.organization.id, + lastRunAt: now, + }, + update: { + lastRunAt: now, + }, + }); + + logger.info(`Deel employee sync completed for ${integration.name}`); + return { + success: true, + processedCount: processedEmployees.length, + totalFetched: deelEmployees.length, + }; + } catch (error) { + logger.error(`Error running Deel employee sync: ${error}`); + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + }, +}); diff --git a/apps/app/src/trigger/deel/schedule.ts b/apps/app/src/trigger/deel/schedule.ts new file mode 100644 index 0000000000..0668e8fc0e --- /dev/null +++ b/apps/app/src/trigger/deel/schedule.ts @@ -0,0 +1,72 @@ +import { db } from "@bubba/db"; +import { logger, schedules } from "@trigger.dev/sdk/v3"; +import { syncDeelEmployees } from "./index"; + +export const deelEmployeeSchedule = schedules.task({ + id: "deel-employee-schedule", + cron: "0 0 * * *", // Run at midnight every day + run: async () => { + logger.info("Starting Deel employee sync schedule"); + + const deelIntegrations = await db.organizationIntegrations.findMany({ + where: { + integration_id: "Deel", + }, + select: { + id: true, + name: true, + integration_id: true, + settings: true, + user_settings: true, + organization: { + select: { + id: true, + name: true, + }, + }, + }, + }); + + logger.info( + `Found ${deelIntegrations.length} Deel integrations to process` + ); + + const triggerPayloads = deelIntegrations.map((integration) => ({ + payload: { + integration: { + id: integration.id, + name: integration.name, + integration_id: integration.integration_id, + settings: integration.settings as Record, + user_settings: integration.user_settings as Record, + organization: integration.organization, + }, + }, + })); + + if (triggerPayloads.length > 0) { + try { + await syncDeelEmployees.batchTrigger(triggerPayloads); + logger.info( + `Triggered ${triggerPayloads.length} Deel employee sync jobs` + ); + } catch (error) { + logger.error(`Failed to trigger batch Deel sync jobs: ${error}`); + return { + success: false, + totalIntegrations: deelIntegrations.length, + triggeredIntegrations: triggerPayloads.length, + error: error instanceof Error ? error.message : String(error), + }; + } + } else { + logger.info("No Deel integrations found to process"); + } + + return { + success: true, + totalIntegrations: deelIntegrations.length, + triggeredIntegrations: triggerPayloads.length, + }; + }, +}); diff --git a/apps/app/src/trigger/index.ts b/apps/app/src/trigger/index.ts new file mode 100644 index 0000000000..479079265a --- /dev/null +++ b/apps/app/src/trigger/index.ts @@ -0,0 +1,4 @@ +import { env } from "../env.mjs"; + +// Import job schedules +import "./deel/schedule"; diff --git a/bun.lock b/bun.lock index febff8bb65..f66176a54b 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "name": "comp", "dependencies": { "@aws-sdk/client-s3": "^3.758.0", - "@aws-sdk/client-securityhub": "^3.751.0", + "@aws-sdk/client-securityhub": "^3.758.0", "@manypkg/cli": "^0.23.0", "@nangohq/node": "^0.53.2", "@types/d3": "^7.4.3", @@ -19,7 +19,7 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", - "@types/lodash": "^4.17.15", + "@types/lodash": "^4.17.16", "turbo": "^2.4.4", "typescript": "5.7.3", }, diff --git a/packages/db/prisma/migrations/20250306164051_add_last_run_table/migration.sql b/packages/db/prisma/migrations/20250306164051_add_last_run_table/migration.sql new file mode 100644 index 0000000000..e1cddc9a44 --- /dev/null +++ b/packages/db/prisma/migrations/20250306164051_add_last_run_table/migration.sql @@ -0,0 +1,26 @@ +-- CreateTable +CREATE TABLE "IntegrationLastRun" ( + "id" TEXT NOT NULL, + "integrationId" TEXT NOT NULL, + "organizationId" TEXT NOT NULL, + "lastRunAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "IntegrationLastRun_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "IntegrationLastRun_integrationId_idx" ON "IntegrationLastRun"("integrationId"); + +-- CreateIndex +CREATE INDEX "IntegrationLastRun_organizationId_idx" ON "IntegrationLastRun"("organizationId"); + +-- CreateIndex +CREATE UNIQUE INDEX "IntegrationLastRun_integrationId_organizationId_key" ON "IntegrationLastRun"("integrationId", "organizationId"); + +-- AddForeignKey +ALTER TABLE "IntegrationLastRun" ADD CONSTRAINT "IntegrationLastRun_integrationId_fkey" FOREIGN KEY ("integrationId") REFERENCES "OrganizationIntegrations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "IntegrationLastRun" ADD CONSTRAINT "IntegrationLastRun_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/db/prisma/schema/integration.prisma b/packages/db/prisma/schema/integration.prisma new file mode 100644 index 0000000000..3d49b713ca --- /dev/null +++ b/packages/db/prisma/schema/integration.prisma @@ -0,0 +1,14 @@ +model IntegrationLastRun { + id String @id @default(cuid()) + integrationId String + organizationId String + lastRunAt DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + integration OrganizationIntegrations @relation(fields: [integrationId], references: [id], onDelete: Cascade) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + + @@unique([integrationId, organizationId]) + @@index([integrationId]) + @@index([organizationId]) +} diff --git a/packages/db/prisma/schema/schema.prisma b/packages/db/prisma/schema/schema.prisma index 898e0f7e96..7b93b7a772 100644 --- a/packages/db/prisma/schema/schema.prisma +++ b/packages/db/prisma/schema/schema.prisma @@ -103,6 +103,7 @@ model Organization { PortalUser PortalUser[] apiKeys OrganizationApiKey[] OrganizationIntegrationResults OrganizationIntegrationResults[] + IntegrationLastRun IntegrationLastRun[] vendors Vendor[] VendorContact VendorContact[] VendorComment VendorComment[] @@ -167,6 +168,7 @@ model OrganizationIntegrations { organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) results OrganizationIntegrationResults[] + lastRuns IntegrationLastRun[] @@index([organizationId]) } diff --git a/packages/integrations/src/aws/config.ts b/packages/integrations/src/aws/config.ts index b3d153cff2..027210fdce 100644 --- a/packages/integrations/src/aws/config.ts +++ b/packages/integrations/src/aws/config.ts @@ -1,41 +1,40 @@ import image from "./assets/image.png"; -import { Logo } from "./assets/logo"; export default { - name: "AWS", - id: "aws", - active: true, - logo: Logo, - short_description: - "Connect your AWS account to Comp AI to automate evidence collection for cloud resources", - description: - "Integrating with AWS allows you to automate evidence collection. This compliance analysis tool enables organizations to more quickly articulate their compliance posture and also generate supporting evidence artifacts", - images: [image], - settings: [ - { - id: "access_key_id", - label: "AWS access key ID", - description: "The API access key ID for your AWS account", - type: "text", - required: true, - value: "", - }, - { - id: "secret_access_key", - label: "AWS secret access key", - description: "The API secret access key for your AWS account", - type: "text", - required: true, - value: "", - }, - { - id: "session_token", - label: "AWS session token", - description: "The API session token AWS account", - type: "text", - required: true, - value: "", - }, - ], - category: "Cloud", + name: "AWS", + id: "aws", + active: true, + logo: image, + short_description: + "Connect your AWS account to Comp AI to automate evidence collection for cloud resources", + description: + "Integrating with AWS allows you to automate evidence collection. This compliance analysis tool enables organizations to more quickly articulate their compliance posture and also generate supporting evidence artifacts", + images: [image], + settings: [ + { + id: "access_key_id", + label: "AWS access key ID", + description: "The API access key ID for your AWS account", + type: "text", + required: true, + value: "", + }, + { + id: "secret_access_key", + label: "AWS secret access key", + description: "The API secret access key for your AWS account", + type: "text", + required: true, + value: "", + }, + { + id: "session_token", + label: "AWS session token", + description: "The API session token AWS account", + type: "text", + required: true, + value: "", + }, + ], + category: "Cloud", }; diff --git a/packages/integrations/src/deel/config.ts b/packages/integrations/src/deel/config.ts index ae6321ab46..ae6c322f2d 100644 --- a/packages/integrations/src/deel/config.ts +++ b/packages/integrations/src/deel/config.ts @@ -1,11 +1,10 @@ import image from "./assets/image.png"; -import { Logo } from "./assets/logo"; export default { name: "Deel", id: "deel", active: true, - logo: Logo, + logo: image, short_description: "Connect your Deel account to Bubba to automate evidence collection for payroll and contractor management", description: diff --git a/packages/integrations/src/github/assets/image.png b/packages/integrations/src/github/assets/image.png index 1fc008cbc6..c61ab9d058 100644 Binary files a/packages/integrations/src/github/assets/image.png and b/packages/integrations/src/github/assets/image.png differ diff --git a/packages/integrations/src/github/config.ts b/packages/integrations/src/github/config.ts index c407d4b27f..1a10c8d618 100644 --- a/packages/integrations/src/github/config.ts +++ b/packages/integrations/src/github/config.ts @@ -5,7 +5,7 @@ export default { name: "GitHub", id: "github", active: true, - logo: Logo, + logo: image, short_description: "Connect your GitHub account to Bubba to automate evidence collection for cloud resources", description: diff --git a/packages/integrations/src/gusto/assets/image.png b/packages/integrations/src/gusto/assets/image.png index 1fc008cbc6..6865358ae6 100644 Binary files a/packages/integrations/src/gusto/assets/image.png and b/packages/integrations/src/gusto/assets/image.png differ diff --git a/packages/integrations/src/gusto/config.ts b/packages/integrations/src/gusto/config.ts index 6a86988177..d6122502f3 100644 --- a/packages/integrations/src/gusto/config.ts +++ b/packages/integrations/src/gusto/config.ts @@ -5,7 +5,7 @@ export default { name: "Gusto", id: "gusto-demo", active: true, - logo: Logo, + logo: image, short_description: "Connect your Gusto account to Bubba to automate evidence collection for HR resources", description: diff --git a/prisma/schema.prisma b/prisma/schema.prisma deleted file mode 100644 index ee282c7518..0000000000 --- a/prisma/schema.prisma +++ /dev/null @@ -1,14 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} diff --git a/yarn.lock b/yarn.lock index bfd3a6e663..b30dba40f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -321,7 +321,7 @@ "@smithy/util-waiter" "^4.0.2" tslib "^2.6.2" -"@aws-sdk/client-securityhub@^3.751.0": +"@aws-sdk/client-securityhub@^3.758.0": version "3.758.0" resolved "https://registry.npmjs.org/@aws-sdk/client-securityhub/-/client-securityhub-3.758.0.tgz" integrity sha512-J8pmXU5qOoDIzYr3NTOdmPiTRZW65ayW/tkEMCZ+HxEdjpNDjdPJzuqDZW4uSHcrgLwzKmyXmPNpJb2xxXj6HA== @@ -1053,6 +1053,7 @@ resolved "workspace:packages/db" devDependencies: "@bubba/tsconfig" "workspace:*" + devDependencies: typescript "^5.7.3" dependencies: "@auth/prisma-adapter" "^2.7.4" @@ -1072,6 +1073,7 @@ dependencies: "@bubba/ui" "workspace:*" "@bubba/utils" "workspace:*" + dependencies: "@react-email/components" "0.0.31" "@react-email/render" "0.0.10" "@react-email/tailwind" "1.0.4" @@ -5143,7 +5145,7 @@ resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz" integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== -"@types/lodash@^4.17.15": +"@types/lodash@^4.17.16": version "4.17.16" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz" integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== @@ -6299,6 +6301,7 @@ commander@11.1.0: resolved "workspace:apps/app" devDependencies: "@bubba/db" "workspace:*" + devDependencies: "@trigger.dev/build" "3.3.17" "@types/node" "^22.13.2" "@types/react" "19.0.10" @@ -6312,7 +6315,9 @@ commander@11.1.0: "@aws-sdk/client-s3" "^3.758.0" "@aws-sdk/s3-request-presigner" "^3.758.0" "@browserbasehq/sdk" "^2.3.0" + dependencies: "@bubba/notifications" "workspace:*" + dependencies: "@date-fns/tz" "^1.2.0" "@hookform/resolvers" "^3.10.0" "@nangohq/frontend" "^0.53.1" @@ -10834,6 +10839,7 @@ plist@^3.1.0: devDependencies: "@bubba/db" "workspace:*" "@bubba/ui" "workspace:*" + devDependencies: "@types/node" "^22.13.2" "@types/react" "^19.0.8" "@types/react-dom" "^19.0.3" @@ -13461,6 +13467,7 @@ wcwidth@^1.0.1: resolved "workspace:apps/web" devDependencies: "@bubba/analytics" "workspace:*" + devDependencies: "@trigger.dev/build" "^3.3.15" "@types/node" "^22.13.2" "@types/react" "^19.0.8"