diff --git a/.gitignore b/.gitignore index 68d0225..7ae61b6 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ scripts/set-azure-env-vars.sh NODEBASE_CONTEXT.md tsc-errors.txt *.tsc-errors.txt +github-node.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b26ff09..5b7c55a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,9 @@ ENV NEXT_TELEMETRY_DISABLED=1 ENV DATABASE_URL="postgresql://dummy:dummy@dummy:5432/dummy" ENV DIRECT_DATABASE_URL="postgresql://dummy:dummy@dummy:5432/dummy" ENV BETTER_AUTH_SECRET="dummy-secret-for-build-only" -ENV BETTER_AUTH_URL="https://nodebase.tech" -ENV NEXT_PUBLIC_BETTER_AUTH_URL="https://nodebase.tech" -ENV NEXT_PUBLIC_APP_URL="https://nodebase.tech" +ENV BETTER_AUTH_URL="https://nodebase.mayanksaraswal.in" +ENV NEXT_PUBLIC_BETTER_AUTH_URL="https://nodebase.mayanksaraswal.in" +ENV NEXT_PUBLIC_APP_URL="https://nodebase.mayanksaraswal.in" ENV GOOGLE_CLIENT_ID="dummy" ENV GOOGLE_CLIENT_SECRET="dummy" ENV GOOGLE_GMAIL_CLIENT_ID="dummy" diff --git a/next-sitemap.config.js b/next-sitemap.config.js index c1c543f..be9432a 100644 --- a/next-sitemap.config.js +++ b/next-sitemap.config.js @@ -1,6 +1,6 @@ /** @type {import('next-sitemap').IConfig} */ module.exports = { - siteUrl: "https://nodebase.tech", + siteUrl: "https://nodebase.mayanksaraswal.in", generateRobotsTxt: true, generateIndexSitemap: false, changefreq: "weekly", diff --git a/package.json b/package.json index 2cfe8cd..9c46d33 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "test": "vitest run", "test:watch": "vitest", "migrate:razorpay-secrets": "tsx scripts/migrate-razorpay-secrets.ts", - "migrate:whatsapp-tokens": "tsx scripts/migrate-whatsapp-tokens.ts" + "migrate:whatsapp-tokens": "tsx scripts/migrate-whatsapp-tokens.ts", + "migrate:webhook-secrets": "tsx scripts/migrate-webhook-secrets.ts" }, "dependencies": { "@ai-sdk/anthropic": "^2.0.37", diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..a35a4db --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from '@prisma/config' +import 'dotenv/config' + +// Fallback for DIRECT_DATABASE_URL if it's not provided +process.env.DIRECT_DATABASE_URL = process.env.DIRECT_DATABASE_URL || process.env.DATABASE_URL; + +export default defineConfig({ + schema: "prisma/schema", +}) diff --git a/prisma/schema.prisma b/prisma/schema.prisma.bak similarity index 100% rename from prisma/schema.prisma rename to prisma/schema.prisma.bak diff --git a/prisma/schema/auth.prisma b/prisma/schema/auth.prisma new file mode 100644 index 0000000..cc7df0a --- /dev/null +++ b/prisma/schema/auth.prisma @@ -0,0 +1,99 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Auth & Billing Models +// User, Session, Account, Verification, BillingEvent +// ───────────────────────────────────────────────────────────── + +model User { + id String @id + name String + email String + emailVerified Boolean @default(false) + emailVerifyToken String? @unique + emailVerifyExpiry DateTime? + emailVerifyAttempts Int @default(0) + image String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + sessions Session[] + accounts Account[] + workflows workflow[] + credentials Credential[] + executionCount Int @default(0) + + // ── Subscription / Billing ─────────────────────────────────────────────── + plan String @default("FREE") // FREE|STARTER|PRO|TEAM + planStatus String @default("active") // active|past_due|cancelled|paused + razorpayCustomerId String? @unique // cust_xxxx + razorpaySubId String? @unique // sub_xxxx + currentPeriodEnd DateTime? + cancelAtPeriodEnd Boolean @default(false) + workflowRunsUsed Int @default(0) + workflowRunsReset DateTime @default(now()) + billingEvents BillingEvent[] + + @@unique([email]) + @@map("user") +} + +model BillingEvent { + id String @id @default(cuid()) + userId String + type String // subscription.activated | payment.captured | subscription.cancelled | etc. + razorpayEventId String? @unique + amount Int? // paise + currency String @default("INR") + plan String? + status String // success | failed | pending + rawPayload String @db.Text // full Razorpay webhook JSON + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([type]) +} + +model Session { + id String @id + expiresAt DateTime + token String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ipAddress String? + userAgent String? + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([token]) + @@map("session") +} + +model Account { + id String @id + accountId String + providerId String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + accessToken String? + refreshToken String? + idToken String? + accessTokenExpiresAt DateTime? + refreshTokenExpiresAt DateTime? + scope String? + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("account") +} + +model Verification { + id String @id + identifier String + value String + expiresAt DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@map("verification") +} diff --git a/prisma/schema/base.prisma b/prisma/schema/base.prisma new file mode 100644 index 0000000..b76d153 --- /dev/null +++ b/prisma/schema/base.prisma @@ -0,0 +1,15 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Base Configuration +// Generator and datasource definitions +// ───────────────────────────────────────────────────────────── + +generator client { + provider = "prisma-client-js" + output = "../../src/generated/prisma" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_DATABASE_URL") +} diff --git a/prisma/schema/core.prisma b/prisma/schema/core.prisma new file mode 100644 index 0000000..a687cf9 --- /dev/null +++ b/prisma/schema/core.prisma @@ -0,0 +1,160 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Core Models +// workflow, Node, Connection, Credential, Execution, NodeExecution +// ───────────────────────────────────────────────────────────── + +model workflow { + id String @id @default(cuid()) + name String + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + nodes Node[] + connections Connection[] + executions Execution[] + webhookTrigger WebhookTrigger? + scheduleTrigger ScheduleTrigger? + ifElseNodes IfElseNode[] + gmailNodes GmailNode[] + setVariableNodes SetVariableNode[] + googleSheetsNodes GoogleSheetsNode[] + googleDriveNodes GoogleDriveNode[] + codeNodes CodeNode[] + whatsAppNodes WhatsAppNode[] + loopNodes LoopNode[] + notionNodes NotionNode[] + razorpayNodes RazorpayNode[] + slackNodes SlackNode[] + gmailWatchers GmailWatcher[] + switchNodes SwitchNode[] + waitNodes WaitNode[] + mergeNodes MergeNode[] + errorTriggerNodes ErrorTriggerNode[] + razorpayTriggers RazorpayTrigger[] + whatsappTriggers WhatsAppTrigger[] + msg91Nodes Msg91Node[] + shiprocketNodes ShiprocketNode[] + zohoCrmNodes ZohoCrmNode[] + hubspotNodes HubspotNode[] + freshdeskNodes FreshdeskNode[] + aiNodes AINode[] + mediaUploadNodes MediaUploadNode[] + sortNodes SortNode[] + filterNodes FilterNode[] + cashfreeNodes CashfreeNode[] + aggregateNodes AggregateNode[] + postgresNodes PostgresNode[] + githubNodes GitHubNode[] + githubTriggerNodes GitHubTriggerNode[] + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model Credential { + id String @id @default(cuid()) + name String + value String + userId String + type CredentialType + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + Node Node[] + shiprocketNodes ShiprocketNode[] + zohoCrmNodes ZohoCrmNode[] + hubspotNodes HubspotNode[] + freshdeskNodes FreshdeskNode[] + cashfreeNodes CashfreeNode[] + postgresNodes PostgresNode[] + githubNodes GitHubNode[] + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + razorpayNodes RazorpayNode[] + gmailNodes GmailNode[] + googleSheetsNodes GoogleSheetsNode[] + googleDriveNodes GoogleDriveNode[] + slackNodes SlackNode[] + whatsAppNodes WhatsAppNode[] + notionNodes NotionNode[] + msg91Nodes Msg91Node[] + ainodes AINode[] + mediaUploadNodes MediaUploadNode[] + + // TODO: @@map("Credenial") has a typo — should be "credential" + // Fixing requires: ALTER TABLE "Credenial" RENAME TO "credential"; + // plus updating all existing FK references. Do in a dedicated migration sprint. + @@map("credential") +} + +model Node { + id String @id @default(cuid()) + name String + type NodeType + workflowId String + position Json + data Json @default("{}") + outputConnections Connection[] @relation("FromNode") + inputConnections Connection[] @relation("ToNode") + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt +} + +model Connection { + id String @id @default(cuid()) + workflowId String + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + fromNodeId String + fromNode Node @relation("FromNode", fields: [fromNodeId], references: [id], onDelete: Cascade) + toNodeId String + toNode Node @relation("ToNode", fields: [toNodeId], references: [id], onDelete: Cascade) + fromOutput String @default("main") + toInput String @default("main") + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@unique([fromNodeId, toNodeId, fromOutput, toInput]) +} + +model Execution { + id String @id @default(cuid()) + workflowId String + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + startedAt DateTime @default(now()) + completedAt DateTime? + inngestEventId String @unique + output Json? + error String? @db.Text + errorStack String? @db.Text + status ExecutionStatus @default(RUNNING) + nodeExecutions NodeExecution[] +} + +model NodeExecution { + id String @id @default(cuid()) + executionId String + execution Execution @relation(fields: [executionId], references: [id], onDelete: Cascade) + + nodeId String + nodeName String @default("") + nodeType String @default("") + + status String @default("success") + + inputJson String @default("") @db.Text + outputJson String @default("") @db.Text + + errorMessage String @default("") + + durationMs Int @default(0) + + executionOrder Int @default(0) + + createdAt DateTime @default(now()) + + @@index([executionId]) + @@index([nodeId]) + @@index([executionId, executionOrder]) +} diff --git a/prisma/schema/enums.prisma b/prisma/schema/enums.prisma new file mode 100644 index 0000000..0770169 --- /dev/null +++ b/prisma/schema/enums.prisma @@ -0,0 +1,779 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: All Enums +// ───────────────────────────────────────────────────────────── + +enum NodeType { + INITIAL + MANUAL_TRIGGER + HTTP_REQUEST + GOOGLE_FORM_TRIGGER + STRIPE_TRIGGER + WEBHOOK_TRIGGER + SCHEDULE_TRIGGER + ANTHROPIC + GEMINI + OPENAI + GROQ + XAI + DISCORD + SLACK + DEEPSEEK + PERPLEXITY + TELEGRAM + X + WORKDAY + IF_ELSE + GMAIL + SET_VARIABLE + GOOGLE_SHEETS + GOOGLE_DRIVE + CODE + WHATSAPP + LOOP + NOTION + RAZORPAY + SWITCH + WAIT + MERGE + ERROR_TRIGGER + RAZORPAY_TRIGGER + WHATSAPP_TRIGGER + MSG91 + SHIPROCKET + ZOHO_CRM + HUBSPOT + FRESHDESK + MEDIA_UPLOAD + SORT + FILTER + CASHFREE + CASHFREE_TRIGGER + AGGREGATE + POSTGRES + GITHUB + GITHUB_TRIGGER + // In a future sprint: ZOHO_CRM_TRIGGER + // Registers a Zoho Notification API webhook for lead/contact/deal events. + // Ref: https://www.zoho.com/crm/developer/docs/api/v6/notifications/overview.html +} + +enum AIProvider { + OPENAI + ANTHROPIC + GEMINI + GROQ + XAI + DEEPSEEK + PERPLEXITY +} + +enum AIOperation { + CHAT + CHAT_WITH_HISTORY + STRUCTURED_OUTPUT + TOOL_USE + VISION + EMBED + TRANSCRIBE + GENERATE_IMAGE + CLASSIFY +} + +enum HttpMethod { + GET + POST + PUT + PATCH +} + +enum ExecutionStatus { + RUNNING + SUCCESS + FAILED +} + +enum IfElseOperator { + EQUALS + NOT_EQUALS + CONTAINS + NOT_CONTAINS + STARTS_WITH + ENDS_WITH + GREATER_THAN + LESS_THAN + GREATER_THAN_OR_EQUAL + LESS_THAN_OR_EQUAL + IS_EMPTY + IS_NOT_EMPTY + IS_TRUE + IS_FALSE + REGEX_MATCH +} + +enum CredentialType { + OPENAI + ANTHROPIC + GEMINI + DEEPSEEK + PERPLEXITY + XAI + GROQ + GMAIL + GMAIL_OAUTH + GOOGLE_SHEETS + GOOGLE_DRIVE + WHATSAPP + NOTION + RAZORPAY + SLACK + MSG91 + SHIPROCKET + ZOHO_CRM + HUBSPOT + FRESHDESK + CASHFREE + POSTGRES + GITHUB + GITHUB_APP +} + +enum GmailOperation { + SEND + REPLY + FORWARD + GET_MESSAGE + LIST_MESSAGES + SEARCH_MESSAGES + ADD_LABEL + REMOVE_LABEL + MARK_READ + MARK_UNREAD + MOVE_TO_TRASH + CREATE_DRAFT + GET_ATTACHMENT + GET_THREAD + LIST_LABELS + CREATE_LABEL + LIST_DRAFTS + SEND_DRAFT +} + +enum GoogleSheetsOp { + APPEND_ROW + READ_ROWS + UPDATE_ROW + UPDATE_ROWS_BY_QUERY + DELETE_ROW + GET_ROW_BY_NUMBER + SEARCH_ROWS + CLEAR_RANGE + CREATE_SHEET + GET_SHEET_INFO +} + +enum GoogleDriveOperation { + UPLOAD_FILE + DOWNLOAD_FILE + LIST_FILES + CREATE_FOLDER +} + +enum WhatsAppOperation { + SEND_TEXT + SEND_TEMPLATE + SEND_IMAGE + SEND_DOCUMENT + SEND_REACTION +} + +enum NotionOperation { + QUERY_DATABASE + CREATE_DATABASE_PAGE + UPDATE_DATABASE_PAGE + GET_PAGE + ARCHIVE_PAGE + APPEND_BLOCK + GET_BLOCK_CHILDREN + SEARCH + GET_DATABASE + GET_USER + GET_USERS +} + +enum RazorpayOperation { + ORDER_CREATE + ORDER_FETCH + ORDER_FETCH_PAYMENTS + ORDER_LIST + PAYMENT_FETCH + PAYMENT_CAPTURE + PAYMENT_LIST + PAYMENT_UPDATE + REFUND_CREATE + REFUND_FETCH + REFUND_LIST + CUSTOMER_CREATE + CUSTOMER_FETCH + CUSTOMER_UPDATE + SUBSCRIPTION_CREATE + SUBSCRIPTION_FETCH + SUBSCRIPTION_CANCEL + INVOICE_CREATE + INVOICE_FETCH + INVOICE_SEND + INVOICE_CANCEL + PAYMENT_LINK_CREATE + PAYMENT_LINK_FETCH + PAYMENT_LINK_UPDATE + PAYMENT_LINK_CANCEL + PAYOUT_CREATE + PAYOUT_FETCH + VERIFY_PAYMENT_SIGNATURE +} + +enum SlackOperation { + MESSAGE_SEND_WEBHOOK + MESSAGE_SEND + MESSAGE_UPDATE + MESSAGE_DELETE + MESSAGE_GET_PERMALINK + MESSAGE_SCHEDULE + // legacy — kept for backward compat + MESSAGE_SEARCH + CHANNEL_GET + CHANNEL_LIST + CHANNEL_CREATE + CHANNEL_ARCHIVE + CHANNEL_UNARCHIVE + CHANNEL_INVITE + CHANNEL_KICK + CHANNEL_SET_TOPIC + CHANNEL_SET_PURPOSE + // legacy — kept for backward compat + CHANNEL_HISTORY + CHANNEL_INFO + CHANNEL_RENAME + USER_GET + USER_GET_BY_EMAIL + USER_LIST + USER_SET_STATUS + // legacy — kept for backward compat + USER_INFO + USER_GET_PRESENCE + REACTION_ADD + REACTION_REMOVE + REACTION_GET + FILE_UPLOAD + FILE_GET + FILE_DELETE + // legacy — kept for backward compat + FILE_LIST + FILE_INFO + CONVERSATION_OPEN +} + +enum Msg91Operation { + SEND_SMS + SEND_BULK_SMS + SEND_TRANSACTIONAL + SCHEDULE_SMS + SEND_OTP + VERIFY_OTP + RESEND_OTP + INVALIDATE_OTP + SEND_WHATSAPP + SEND_WHATSAPP_MEDIA + SEND_VOICE_OTP + SEND_EMAIL + GET_BALANCE + GET_REPORT +} + +enum ShiprocketOperation { + CREATE_ORDER + GET_ORDER + CANCEL_ORDER + UPDATE_ORDER + GET_ORDER_TRACKING + CLONE_ORDER + GENERATE_AWB + GET_ORDERS_LIST + TRACK_SHIPMENT + ASSIGN_COURIER + GENERATE_LABEL + GENERATE_MANIFEST + REQUEST_PICKUP + GET_COURIER_LIST + GET_RATE + CHECK_SERVICEABILITY + CREATE_RETURN + GET_RETURN_REASONS + TRACK_RETURN + CREATE_PRODUCT + GET_PRODUCTS + GET_PICKUP_LOCATIONS + CREATE_PICKUP_LOCATION +} + +// In a future sprint: ZOHO_CRM_TRIGGER +// Registers Zoho Notification API watches and receives callbacks at /api/webhooks/zoho/{webhookId}. +enum ZohoCrmOperation { + CREATE_LEAD + GET_LEAD + UPDATE_LEAD + DELETE_LEAD + SEARCH_LEADS + CONVERT_LEAD + + CREATE_CONTACT + GET_CONTACT + UPDATE_CONTACT + DELETE_CONTACT + SEARCH_CONTACTS + GET_CONTACT_DEALS + + CREATE_DEAL + GET_DEAL + UPDATE_DEAL + DELETE_DEAL + SEARCH_DEALS + UPDATE_DEAL_STAGE + + CREATE_ACCOUNT + GET_ACCOUNT + UPDATE_ACCOUNT + DELETE_ACCOUNT + SEARCH_ACCOUNTS + + CREATE_TASK + CREATE_CALL_LOG + CREATE_MEETING + GET_ACTIVITIES + + ADD_NOTE + GET_NOTES + + UPSERT_RECORD + SEARCH_RECORDS + GET_FIELDS +} + +enum HubspotOperation { + // ── CONTACTS ────────────────────────────────────────────── + CREATE_CONTACT + GET_CONTACT + UPDATE_CONTACT + DELETE_CONTACT + SEARCH_CONTACTS + GET_CONTACT_PROPERTIES + UPSERT_CONTACT + GET_CONTACT_ASSOCIATIONS + + // ── COMPANIES ───────────────────────────────────────────── + CREATE_COMPANY + GET_COMPANY + UPDATE_COMPANY + DELETE_COMPANY + SEARCH_COMPANIES + + // ── DEALS ───────────────────────────────────────────────── + CREATE_DEAL + GET_DEAL + UPDATE_DEAL + DELETE_DEAL + SEARCH_DEALS + UPDATE_DEAL_STAGE + + // ── TICKETS ─────────────────────────────────────────────── + CREATE_TICKET + GET_TICKET + UPDATE_TICKET + DELETE_TICKET + SEARCH_TICKETS + + // ── ACTIVITIES ──────────────────────────────────────────── + CREATE_NOTE + CREATE_TASK + CREATE_CALL + CREATE_EMAIL_LOG + + // ── ASSOCIATIONS ────────────────────────────────────────── + CREATE_ASSOCIATION + DELETE_ASSOCIATION + + // ── LISTS ───────────────────────────────────────────────── + ADD_CONTACT_TO_LIST + REMOVE_CONTACT_FROM_LIST + GET_LIST_CONTACTS + + // ── GENERIC ─────────────────────────────────────────────── + SEARCH_OBJECTS + GET_PROPERTIES +} + +enum FreshdeskOperation { + // ── TICKETS ────────────────────────────────────────────── + CREATE_TICKET + GET_TICKET + UPDATE_TICKET + DELETE_TICKET + LIST_TICKETS + SEARCH_TICKETS + GET_TICKET_FIELDS + RESTORE_TICKET + + // ── TICKET NOTES ───────────────────────────────────────── + ADD_NOTE + LIST_NOTES + UPDATE_NOTE + DELETE_NOTE + + // ── CONTACTS ───────────────────────────────────────────── + CREATE_CONTACT + GET_CONTACT + UPDATE_CONTACT + DELETE_CONTACT + LIST_CONTACTS + SEARCH_CONTACTS + MERGE_CONTACT + + // ── COMPANIES ──────────────────────────────────────────── + CREATE_COMPANY + GET_COMPANY + UPDATE_COMPANY + DELETE_COMPANY + LIST_COMPANIES + + // ── AGENTS ─────────────────────────────────────────────── + LIST_AGENTS + GET_AGENT + UPDATE_AGENT + + // ── CONVERSATIONS ──────────────────────────────────────── + LIST_CONVERSATIONS + SEND_REPLY + CREATE_OUTBOUND_EMAIL + + // ── GENERIC ────────────────────────────────────────────── + GET_TICKET_STATS +} + +enum MediaUploadSource { + URL + BASE64 + GOOGLE_DRIVE +} + +enum CashfreeOperation { + // ── ORDERS (Payment Gateway) ────────────────────────────── + CREATE_ORDER + GET_ORDER + TERMINATE_ORDER + PAY_ORDER + + // ── PAYMENTS ────────────────────────────────────────────── + GET_PAYMENTS_FOR_ORDER + GET_PAYMENT_BY_ID + + // ── REFUNDS ─────────────────────────────────────────────── + CREATE_REFUND + GET_REFUND + GET_ALL_REFUNDS_FOR_ORDER + + // ── SETTLEMENTS ─────────────────────────────────────────── + GET_SETTLEMENTS_FOR_ORDER + GET_ALL_SETTLEMENTS + GET_SETTLEMENT_RECON + + // ── PAYMENT LINKS ───────────────────────────────────────── + CREATE_PAYMENT_LINK + GET_PAYMENT_LINK + CANCEL_PAYMENT_LINK + GET_ORDERS_FOR_LINK + + // ── SUBSCRIPTIONS ───────────────────────────────────────── + CREATE_SUBSCRIPTION_PLAN + GET_SUBSCRIPTION_PLAN + CREATE_SUBSCRIPTION + GET_SUBSCRIPTION + MANAGE_SUBSCRIPTION + GET_SUBSCRIPTION_PAYMENTS + + // ── PAYOUTS ─────────────────────────────────────────────── + GET_PAYOUT_BALANCE + ADD_BENEFICIARY + GET_BENEFICIARY + REMOVE_BENEFICIARY + TRANSFER_TO_BENEFICIARY + GET_TRANSFER_STATUS + BULK_TRANSFER + GET_BATCH_TRANSFER_STATUS + + // ── UPI ─────────────────────────────────────────────────── + VALIDATE_UPI_ID + CREATE_UPI_PAYMENT_LINK + + // ── OFFERS ──────────────────────────────────────────────── + CREATE_OFFER + GET_OFFER + + // ── WEBHOOKS ────────────────────────────────────────────── + VERIFY_WEBHOOK_SIGNATURE +} + +enum GitHubOperation { + // Repository Operations + REPOSITORY_CREATE + REPOSITORY_GET + REPOSITORY_LIST + REPOSITORY_UPDATE + REPOSITORY_DELETE + REPOSITORY_FORK + REPOSITORY_LIST_CONTRIBUTORS + REPOSITORY_LIST_LANGUAGES + REPOSITORY_TRANSFER + REPOSITORY_DISPATCH + REPOSITORY_CHECK_COLLABORATOR + REPOSITORY_LIST_EVENTS + + // Issue Operations + ISSUE_CREATE + ISSUE_GET + ISSUE_UPDATE + ISSUE_CLOSE + ISSUE_REOPEN + ISSUE_LIST + ISSUE_ADD_LABELS + ISSUE_REMOVE_LABEL + ISSUE_CREATE_COMMENT + ISSUE_LIST_COMMENTS + ISSUE_UPDATE_COMMENT + ISSUE_DELETE_COMMENT + ISSUE_ASSIGN + ISSUE_UNASSIGN + + // Pull Request Operations + PULL_REQUEST_CREATE + PULL_REQUEST_GET + PULL_REQUEST_LIST + PULL_REQUEST_UPDATE + PULL_REQUEST_MERGE + PULL_REQUEST_CLOSE + PULL_REQUEST_REOPEN + PULL_REQUEST_LIST_REVIEWS + PULL_REQUEST_CREATE_REVIEW + PULL_REQUEST_REQUEST_REVIEWERS + PULL_REQUEST_LIST_COMMITS + PULL_REQUEST_LIST_FILES + PULL_REQUEST_CREATE_REVIEW_COMMENT + + // File Operations + FILE_GET + FILE_CREATE + FILE_UPDATE + FILE_DELETE + FILE_LIST + FILE_GET_CONTENTS + FILE_CREATE_OR_UPDATE + + // Branch Operations + BRANCH_LIST + BRANCH_GET + BRANCH_CREATE + BRANCH_DELETE + BRANCH_RENAME + BRANCH_SET_PROTECTION + + // Release Operations + RELEASE_CREATE + RELEASE_GET + RELEASE_LIST + RELEASE_UPDATE + RELEASE_DELETE + RELEASE_UPLOAD_ASSET + + // Workflow Operations + WORKFLOW_LIST + WORKFLOW_GET + WORKFLOW_DISPATCH + WORKFLOW_RUN_LIST + WORKFLOW_RUN_GET + WORKFLOW_RUN_CANCEL + WORKFLOW_RUN_RE_RUN + WORKFLOW_RUN_LIST_JOBS + + // Gist Operations + GIST_CREATE + GIST_GET + GIST_LIST + GIST_UPDATE + GIST_DELETE + GIST_STAR + + // Search Operations + SEARCH_REPOSITORIES + SEARCH_ISSUES + SEARCH_PULL_REQUESTS + SEARCH_CODE + SEARCH_USERS + SEARCH_COMMITS + SEARCH_TOPICS + + // User Operations + USER_GET + USER_GET_CURRENT + USER_LIST_REPOS + USER_LIST_FOLLOWERS + USER_LIST_FOLLOWING + + // Organization Operations + ORG_GET + ORG_LIST + ORG_LIST_MEMBERS + ORG_LIST_TEAMS + ORG_LIST_REPOS + ORG_CREATE_REPO + ORG_INVITE_MEMBER + ORG_REMOVE_MEMBER + + // Discussion Operations + DISCUSSION_CREATE + DISCUSSION_GET + DISCUSSION_LIST + DISCUSSION_UPDATE + DISCUSSION_DELETE + + // Codespaces (NEW) + CODESPACES_LIST + CODESPACES_GET + CODESPACES_CREATE + CODESPACES_START + CODESPACES_STOP + CODESPACES_DELETE + CODESPACES_UPDATE + CODESPACES_EXPORT + CODESPACES_LIST_FOR_REPO + CODESPACES_LIST_MACHINE_TYPES + CODESPACES_LIST_SECRETS + CODESPACES_CREATE_SECRET + + // Copilot (NEW) + COPILOT_LIST_SEATS + COPILOT_ADD_SEAT + COPILOT_REMOVE_SEAT + COPILOT_GET_SETTINGS + COPILOT_UPDATE_SETTINGS + COPILOT_LIST_SPACES + COPILOT_GET_SPACE + COPILOT_CREATE_SPACE + COPILOT_UPDATE_SPACE + COPILOT_DELETE_SPACE + COPILOT_LIST_SPACE_RESOURCES + COPILOT_ADD_SPACE_RESOURCE + COPILOT_REMOVE_SPACE_RESOURCE + COPILOT_GET_AGENT_POLICY + COPILOT_UPDATE_AGENT_POLICY + + // Rulesets (NEW) + RULESET_LIST + RULESET_GET + RULESET_CREATE + RULESET_UPDATE + RULESET_DELETE + RULESET_LIST_RULE_EVALUATIONS + RULESET_GET_RULE_SUITE + RULESET_LIST_ORG_RULESETS + + // Environments (NEW) + ENVIRONMENT_LIST + ENVIRONMENT_GET + ENVIRONMENT_CREATE + ENVIRONMENT_UPDATE + ENVIRONMENT_DELETE + ENVIRONMENT_LIST_SECRETS + ENVIRONMENT_GET_SECRET + ENVIRONMENT_CREATE_SECRET + ENVIRONMENT_DELETE_SECRET + ENVIRONMENT_LIST_PROTECTION_RULES + + // Secrets (NEW) + SECRET_LIST_REPO + SECRET_GET_REPO + SECRET_CREATE_REPO + SECRET_DELETE_REPO + SECRET_LIST_ORG + SECRET_GET_ORG + SECRET_CREATE_ORG + SECRET_DELETE_ORG + SECRET_GET_PUBLIC_KEY + SECRET_GET_ORG_PUBLIC_KEY + SECRET_LIST_SELECTED_REPOS + SECRET_SET_SELECTED_REPOS + + // Deployments (NEW) + DEPLOYMENT_LIST + DEPLOYMENT_GET + DEPLOYMENT_CREATE + DEPLOYMENT_LIST_STATUSES + DEPLOYMENT_GET_STATUS + DEPLOYMENT_CREATE_STATUS + DEPLOYMENT_DELETE + DEPLOYMENT_SET_STATUS + + // Packages (NEW) + PACKAGE_LIST_FOR_USER + PACKAGE_LIST_FOR_ORG + PACKAGE_GET + PACKAGE_DELETE + PACKAGE_RESTORE + PACKAGE_LIST_VERSIONS + PACKAGE_GET_VERSION + PACKAGE_DELETE_VERSION + PACKAGE_RESTORE_VERSION + PACKAGE_LIST_DOCKER_IMAGES + + // Attestations (NEW) + ATTESTATION_LIST_FOR_REPO + ATTESTATION_GET + ATTESTATION_CREATE + ATTESTATION_LIST_FOR_ORG + ATTESTATION_VERIFY + + // Agent Tasks (NEW) + AGENT_TASK_LIST + AGENT_TASK_GET + AGENT_TASK_CREATE + AGENT_TASK_UPDATE + AGENT_TASK_CANCEL + AGENT_TASK_GET_LOGS + + // Artifacts (NEW) + ARTIFACT_LIST + ARTIFACT_GET + ARTIFACT_DELETE + ARTIFACT_DOWNLOAD + ARTIFACT_UPLOAD + ARTIFACT_GET_ARCHIVE_URL + + // Projects V2 (NEW) + PROJECT_V2_LIST + PROJECT_V2_GET + PROJECT_V2_CREATE + PROJECT_V2_UPDATE + PROJECT_V2_DELETE + PROJECT_V2_LIST_ITEMS + PROJECT_V2_ADD_ITEM + PROJECT_V2_UPDATE_ITEM + PROJECT_V2_DELETE_ITEM + PROJECT_V2_LIST_FIELDS + PROJECT_V2_CREATE_FIELD + PROJECT_V2_UPDATE_FIELD_VALUE + + // Security Advisories (NEW) + ADVISORY_LIST + ADVISORY_GET + ADVISORY_CREATE + ADVISORY_UPDATE + ADVISORY_LIST_REPOS_AFFECTED + ADVISORY_REPORT +} diff --git a/prisma/schema/migrations/20260604130200_schema_refactoring/migration.sql b/prisma/schema/migrations/20260604130200_schema_refactoring/migration.sql new file mode 100644 index 0000000..105c392 --- /dev/null +++ b/prisma/schema/migrations/20260604130200_schema_refactoring/migration.sql @@ -0,0 +1,1956 @@ +-- CreateEnum +CREATE TYPE "NodeType" AS ENUM ('INITIAL', 'MANUAL_TRIGGER', 'HTTP_REQUEST', 'GOOGLE_FORM_TRIGGER', 'STRIPE_TRIGGER', 'WEBHOOK_TRIGGER', 'SCHEDULE_TRIGGER', 'ANTHROPIC', 'GEMINI', 'OPENAI', 'GROQ', 'XAI', 'DISCORD', 'SLACK', 'DEEPSEEK', 'PERPLEXITY', 'TELEGRAM', 'X', 'WORKDAY', 'IF_ELSE', 'GMAIL', 'SET_VARIABLE', 'GOOGLE_SHEETS', 'GOOGLE_DRIVE', 'CODE', 'WHATSAPP', 'LOOP', 'NOTION', 'RAZORPAY', 'SWITCH', 'WAIT', 'MERGE', 'ERROR_TRIGGER', 'RAZORPAY_TRIGGER', 'WHATSAPP_TRIGGER', 'MSG91', 'SHIPROCKET', 'ZOHO_CRM', 'HUBSPOT', 'FRESHDESK', 'MEDIA_UPLOAD', 'SORT', 'FILTER', 'CASHFREE', 'CASHFREE_TRIGGER', 'AGGREGATE', 'POSTGRES'); + +-- CreateEnum +CREATE TYPE "AIProvider" AS ENUM ('OPENAI', 'ANTHROPIC', 'GEMINI', 'GROQ', 'XAI', 'DEEPSEEK', 'PERPLEXITY'); + +-- CreateEnum +CREATE TYPE "AIOperation" AS ENUM ('CHAT', 'CHAT_WITH_HISTORY', 'STRUCTURED_OUTPUT', 'TOOL_USE', 'VISION', 'EMBED', 'TRANSCRIBE', 'GENERATE_IMAGE', 'CLASSIFY'); + +-- CreateEnum +CREATE TYPE "HttpMethod" AS ENUM ('GET', 'POST', 'PUT', 'PATCH'); + +-- CreateEnum +CREATE TYPE "ExecutionStatus" AS ENUM ('RUNNING', 'SUCCESS', 'FAILED'); + +-- CreateEnum +CREATE TYPE "IfElseOperator" AS ENUM ('EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'STARTS_WITH', 'ENDS_WITH', 'GREATER_THAN', 'LESS_THAN', 'GREATER_THAN_OR_EQUAL', 'LESS_THAN_OR_EQUAL', 'IS_EMPTY', 'IS_NOT_EMPTY', 'IS_TRUE', 'IS_FALSE', 'REGEX_MATCH'); + +-- CreateEnum +CREATE TYPE "CredentialType" AS ENUM ('OPENAI', 'ANTHROPIC', 'GEMINI', 'DEEPSEEK', 'PERPLEXITY', 'XAI', 'GROQ', 'GMAIL', 'GMAIL_OAUTH', 'GOOGLE_SHEETS', 'GOOGLE_DRIVE', 'WHATSAPP', 'NOTION', 'RAZORPAY', 'SLACK', 'MSG91', 'SHIPROCKET', 'ZOHO_CRM', 'HUBSPOT', 'FRESHDESK', 'CASHFREE', 'POSTGRES'); + +-- CreateEnum +CREATE TYPE "GmailOperation" AS ENUM ('SEND', 'REPLY', 'FORWARD', 'GET_MESSAGE', 'LIST_MESSAGES', 'SEARCH_MESSAGES', 'ADD_LABEL', 'REMOVE_LABEL', 'MARK_READ', 'MARK_UNREAD', 'MOVE_TO_TRASH', 'CREATE_DRAFT', 'GET_ATTACHMENT', 'GET_THREAD', 'LIST_LABELS', 'CREATE_LABEL', 'LIST_DRAFTS', 'SEND_DRAFT'); + +-- CreateEnum +CREATE TYPE "GoogleSheetsOp" AS ENUM ('APPEND_ROW', 'READ_ROWS', 'UPDATE_ROW', 'UPDATE_ROWS_BY_QUERY', 'DELETE_ROW', 'GET_ROW_BY_NUMBER', 'SEARCH_ROWS', 'CLEAR_RANGE', 'CREATE_SHEET', 'GET_SHEET_INFO'); + +-- CreateEnum +CREATE TYPE "GoogleDriveOperation" AS ENUM ('UPLOAD_FILE', 'DOWNLOAD_FILE', 'LIST_FILES', 'CREATE_FOLDER'); + +-- CreateEnum +CREATE TYPE "WhatsAppOperation" AS ENUM ('SEND_TEXT', 'SEND_TEMPLATE', 'SEND_IMAGE', 'SEND_DOCUMENT', 'SEND_REACTION'); + +-- CreateEnum +CREATE TYPE "NotionOperation" AS ENUM ('QUERY_DATABASE', 'CREATE_DATABASE_PAGE', 'UPDATE_DATABASE_PAGE', 'GET_PAGE', 'ARCHIVE_PAGE', 'APPEND_BLOCK', 'GET_BLOCK_CHILDREN', 'SEARCH', 'GET_DATABASE', 'GET_USER', 'GET_USERS'); + +-- CreateEnum +CREATE TYPE "RazorpayOperation" AS ENUM ('ORDER_CREATE', 'ORDER_FETCH', 'ORDER_FETCH_PAYMENTS', 'ORDER_LIST', 'PAYMENT_FETCH', 'PAYMENT_CAPTURE', 'PAYMENT_LIST', 'PAYMENT_UPDATE', 'REFUND_CREATE', 'REFUND_FETCH', 'REFUND_LIST', 'CUSTOMER_CREATE', 'CUSTOMER_FETCH', 'CUSTOMER_UPDATE', 'SUBSCRIPTION_CREATE', 'SUBSCRIPTION_FETCH', 'SUBSCRIPTION_CANCEL', 'INVOICE_CREATE', 'INVOICE_FETCH', 'INVOICE_SEND', 'INVOICE_CANCEL', 'PAYMENT_LINK_CREATE', 'PAYMENT_LINK_FETCH', 'PAYMENT_LINK_UPDATE', 'PAYMENT_LINK_CANCEL', 'PAYOUT_CREATE', 'PAYOUT_FETCH', 'VERIFY_PAYMENT_SIGNATURE'); + +-- CreateEnum +CREATE TYPE "SlackOperation" AS ENUM ('MESSAGE_SEND_WEBHOOK', 'MESSAGE_SEND', 'MESSAGE_UPDATE', 'MESSAGE_DELETE', 'MESSAGE_GET_PERMALINK', 'MESSAGE_SCHEDULE', 'MESSAGE_SEARCH', 'CHANNEL_GET', 'CHANNEL_LIST', 'CHANNEL_CREATE', 'CHANNEL_ARCHIVE', 'CHANNEL_UNARCHIVE', 'CHANNEL_INVITE', 'CHANNEL_KICK', 'CHANNEL_SET_TOPIC', 'CHANNEL_SET_PURPOSE', 'CHANNEL_HISTORY', 'CHANNEL_INFO', 'CHANNEL_RENAME', 'USER_GET', 'USER_GET_BY_EMAIL', 'USER_LIST', 'USER_SET_STATUS', 'USER_INFO', 'USER_GET_PRESENCE', 'REACTION_ADD', 'REACTION_REMOVE', 'REACTION_GET', 'FILE_UPLOAD', 'FILE_GET', 'FILE_DELETE', 'FILE_LIST', 'FILE_INFO', 'CONVERSATION_OPEN'); + +-- CreateEnum +CREATE TYPE "Msg91Operation" AS ENUM ('SEND_SMS', 'SEND_BULK_SMS', 'SEND_TRANSACTIONAL', 'SCHEDULE_SMS', 'SEND_OTP', 'VERIFY_OTP', 'RESEND_OTP', 'INVALIDATE_OTP', 'SEND_WHATSAPP', 'SEND_WHATSAPP_MEDIA', 'SEND_VOICE_OTP', 'SEND_EMAIL', 'GET_BALANCE', 'GET_REPORT'); + +-- CreateEnum +CREATE TYPE "ShiprocketOperation" AS ENUM ('CREATE_ORDER', 'GET_ORDER', 'CANCEL_ORDER', 'UPDATE_ORDER', 'GET_ORDER_TRACKING', 'CLONE_ORDER', 'GENERATE_AWB', 'GET_ORDERS_LIST', 'TRACK_SHIPMENT', 'ASSIGN_COURIER', 'GENERATE_LABEL', 'GENERATE_MANIFEST', 'REQUEST_PICKUP', 'GET_COURIER_LIST', 'GET_RATE', 'CHECK_SERVICEABILITY', 'CREATE_RETURN', 'GET_RETURN_REASONS', 'TRACK_RETURN', 'CREATE_PRODUCT', 'GET_PRODUCTS', 'GET_PICKUP_LOCATIONS', 'CREATE_PICKUP_LOCATION'); + +-- CreateEnum +CREATE TYPE "ZohoCrmOperation" AS ENUM ('CREATE_LEAD', 'GET_LEAD', 'UPDATE_LEAD', 'DELETE_LEAD', 'SEARCH_LEADS', 'CONVERT_LEAD', 'CREATE_CONTACT', 'GET_CONTACT', 'UPDATE_CONTACT', 'DELETE_CONTACT', 'SEARCH_CONTACTS', 'GET_CONTACT_DEALS', 'CREATE_DEAL', 'GET_DEAL', 'UPDATE_DEAL', 'DELETE_DEAL', 'SEARCH_DEALS', 'UPDATE_DEAL_STAGE', 'CREATE_ACCOUNT', 'GET_ACCOUNT', 'UPDATE_ACCOUNT', 'DELETE_ACCOUNT', 'SEARCH_ACCOUNTS', 'CREATE_TASK', 'CREATE_CALL_LOG', 'CREATE_MEETING', 'GET_ACTIVITIES', 'ADD_NOTE', 'GET_NOTES', 'UPSERT_RECORD', 'SEARCH_RECORDS', 'GET_FIELDS'); + +-- CreateEnum +CREATE TYPE "HubspotOperation" AS ENUM ('CREATE_CONTACT', 'GET_CONTACT', 'UPDATE_CONTACT', 'DELETE_CONTACT', 'SEARCH_CONTACTS', 'GET_CONTACT_PROPERTIES', 'UPSERT_CONTACT', 'GET_CONTACT_ASSOCIATIONS', 'CREATE_COMPANY', 'GET_COMPANY', 'UPDATE_COMPANY', 'DELETE_COMPANY', 'SEARCH_COMPANIES', 'CREATE_DEAL', 'GET_DEAL', 'UPDATE_DEAL', 'DELETE_DEAL', 'SEARCH_DEALS', 'UPDATE_DEAL_STAGE', 'CREATE_TICKET', 'GET_TICKET', 'UPDATE_TICKET', 'DELETE_TICKET', 'SEARCH_TICKETS', 'CREATE_NOTE', 'CREATE_TASK', 'CREATE_CALL', 'CREATE_EMAIL_LOG', 'CREATE_ASSOCIATION', 'DELETE_ASSOCIATION', 'ADD_CONTACT_TO_LIST', 'REMOVE_CONTACT_FROM_LIST', 'GET_LIST_CONTACTS', 'SEARCH_OBJECTS', 'GET_PROPERTIES'); + +-- CreateEnum +CREATE TYPE "FreshdeskOperation" AS ENUM ('CREATE_TICKET', 'GET_TICKET', 'UPDATE_TICKET', 'DELETE_TICKET', 'LIST_TICKETS', 'SEARCH_TICKETS', 'GET_TICKET_FIELDS', 'RESTORE_TICKET', 'ADD_NOTE', 'LIST_NOTES', 'UPDATE_NOTE', 'DELETE_NOTE', 'CREATE_CONTACT', 'GET_CONTACT', 'UPDATE_CONTACT', 'DELETE_CONTACT', 'LIST_CONTACTS', 'SEARCH_CONTACTS', 'MERGE_CONTACT', 'CREATE_COMPANY', 'GET_COMPANY', 'UPDATE_COMPANY', 'DELETE_COMPANY', 'LIST_COMPANIES', 'LIST_AGENTS', 'GET_AGENT', 'UPDATE_AGENT', 'LIST_CONVERSATIONS', 'SEND_REPLY', 'CREATE_OUTBOUND_EMAIL', 'GET_TICKET_STATS'); + +-- CreateEnum +CREATE TYPE "MediaUploadSource" AS ENUM ('URL', 'BASE64', 'GOOGLE_DRIVE'); + +-- CreateEnum +CREATE TYPE "CashfreeOperation" AS ENUM ('CREATE_ORDER', 'GET_ORDER', 'TERMINATE_ORDER', 'PAY_ORDER', 'GET_PAYMENTS_FOR_ORDER', 'GET_PAYMENT_BY_ID', 'CREATE_REFUND', 'GET_REFUND', 'GET_ALL_REFUNDS_FOR_ORDER', 'GET_SETTLEMENTS_FOR_ORDER', 'GET_ALL_SETTLEMENTS', 'GET_SETTLEMENT_RECON', 'CREATE_PAYMENT_LINK', 'GET_PAYMENT_LINK', 'CANCEL_PAYMENT_LINK', 'GET_ORDERS_FOR_LINK', 'CREATE_SUBSCRIPTION_PLAN', 'GET_SUBSCRIPTION_PLAN', 'CREATE_SUBSCRIPTION', 'GET_SUBSCRIPTION', 'MANAGE_SUBSCRIPTION', 'GET_SUBSCRIPTION_PAYMENTS', 'GET_PAYOUT_BALANCE', 'ADD_BENEFICIARY', 'GET_BENEFICIARY', 'REMOVE_BENEFICIARY', 'TRANSFER_TO_BENEFICIARY', 'GET_TRANSFER_STATUS', 'BULK_TRANSFER', 'GET_BATCH_TRANSFER_STATUS', 'VALIDATE_UPI_ID', 'CREATE_UPI_PAYMENT_LINK', 'CREATE_OFFER', 'GET_OFFER', 'VERIFY_WEBHOOK_SIGNATURE'); + +-- CreateTable +CREATE TABLE "user" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "emailVerified" BOOLEAN NOT NULL DEFAULT false, + "emailVerifyToken" TEXT, + "emailVerifyExpiry" TIMESTAMP(3), + "emailVerifyAttempts" INTEGER NOT NULL DEFAULT 0, + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "executionCount" INTEGER NOT NULL DEFAULT 0, + "plan" TEXT NOT NULL DEFAULT 'FREE', + "planStatus" TEXT NOT NULL DEFAULT 'active', + "razorpayCustomerId" TEXT, + "razorpaySubId" TEXT, + "currentPeriodEnd" TIMESTAMP(3), + "cancelAtPeriodEnd" BOOLEAN NOT NULL DEFAULT false, + "workflowRunsUsed" INTEGER NOT NULL DEFAULT 0, + "workflowRunsReset" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "user_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "BillingEvent" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "razorpayEventId" TEXT, + "amount" INTEGER, + "currency" TEXT NOT NULL DEFAULT 'INR', + "plan" TEXT, + "status" TEXT NOT NULL, + "rawPayload" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "BillingEvent_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "session" ( + "id" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "token" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "ipAddress" TEXT, + "userAgent" TEXT, + "userId" TEXT NOT NULL, + + CONSTRAINT "session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account" ( + "id" TEXT NOT NULL, + "accountId" TEXT NOT NULL, + "providerId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "accessToken" TEXT, + "refreshToken" TEXT, + "idToken" TEXT, + "accessTokenExpiresAt" TIMESTAMP(3), + "refreshTokenExpiresAt" TIMESTAMP(3), + "scope" TEXT, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "verification" ( + "id" TEXT NOT NULL, + "identifier" TEXT NOT NULL, + "value" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "verification_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "workflow" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "userId" TEXT NOT NULL, + + CONSTRAINT "workflow_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "credential" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "value" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "type" "CredentialType" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "credential_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Node" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "type" "NodeType" NOT NULL, + "workflowId" TEXT NOT NULL, + "position" JSONB NOT NULL, + "data" JSONB NOT NULL DEFAULT '{}', + "credentialId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Node_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Connection" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "fromNodeId" TEXT NOT NULL, + "toNodeId" TEXT NOT NULL, + "fromOutput" TEXT NOT NULL DEFAULT 'main', + "toInput" TEXT NOT NULL DEFAULT 'main', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Connection_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Execution" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "completedAt" TIMESTAMP(3), + "inngestEventId" TEXT NOT NULL, + "output" JSONB, + "error" TEXT, + "errorStack" TEXT, + "status" "ExecutionStatus" NOT NULL DEFAULT 'RUNNING', + + CONSTRAINT "Execution_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "NodeExecution" ( + "id" TEXT NOT NULL, + "executionId" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "nodeName" TEXT NOT NULL DEFAULT '', + "nodeType" TEXT NOT NULL DEFAULT '', + "status" TEXT NOT NULL DEFAULT 'success', + "inputJson" TEXT NOT NULL DEFAULT '', + "outputJson" TEXT NOT NULL DEFAULT '', + "errorMessage" TEXT NOT NULL DEFAULT '', + "durationMs" INTEGER NOT NULL DEFAULT 0, + "executionOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "NodeExecution_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RazorpayNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "RazorpayOperation" NOT NULL DEFAULT 'ORDER_CREATE', + "variableName" TEXT NOT NULL DEFAULT 'razorpay', + "amount" TEXT NOT NULL DEFAULT '', + "currency" TEXT NOT NULL DEFAULT 'INR', + "description" TEXT NOT NULL DEFAULT '', + "receipt" TEXT NOT NULL DEFAULT '', + "notes" TEXT NOT NULL DEFAULT '', + "partialPayment" BOOLEAN NOT NULL DEFAULT false, + "orderId" TEXT NOT NULL DEFAULT '', + "paymentId" TEXT NOT NULL DEFAULT '', + "captureAmount" TEXT NOT NULL DEFAULT '', + "refundAmount" TEXT NOT NULL DEFAULT '', + "refundSpeed" TEXT NOT NULL DEFAULT 'normal', + "refundId" TEXT NOT NULL DEFAULT '', + "customerId" TEXT NOT NULL DEFAULT '', + "customerName" TEXT NOT NULL DEFAULT '', + "customerEmail" TEXT NOT NULL DEFAULT '', + "customerContact" TEXT NOT NULL DEFAULT '', + "failExisting" BOOLEAN NOT NULL DEFAULT false, + "planId" TEXT NOT NULL DEFAULT '', + "totalCount" TEXT NOT NULL DEFAULT '', + "quantity" TEXT NOT NULL DEFAULT '1', + "startAt" TEXT NOT NULL DEFAULT '', + "subscriptionId" TEXT NOT NULL DEFAULT '', + "cancelAtCycleEnd" BOOLEAN NOT NULL DEFAULT false, + "invoiceType" TEXT NOT NULL DEFAULT 'invoice', + "lineItems" TEXT NOT NULL DEFAULT '', + "expireBy" TEXT NOT NULL DEFAULT '', + "smsNotify" BOOLEAN NOT NULL DEFAULT true, + "emailNotify" BOOLEAN NOT NULL DEFAULT true, + "invoiceId" TEXT NOT NULL DEFAULT '', + "paymentLinkId" TEXT NOT NULL DEFAULT '', + "referenceId" TEXT NOT NULL DEFAULT '', + "reminderEnable" BOOLEAN NOT NULL DEFAULT true, + "callbackUrl" TEXT NOT NULL DEFAULT '', + "callbackMethod" TEXT NOT NULL DEFAULT '', + "accountNumber" TEXT NOT NULL DEFAULT '', + "fundAccountId" TEXT NOT NULL DEFAULT '', + "payoutMode" TEXT NOT NULL DEFAULT '', + "payoutPurpose" TEXT NOT NULL DEFAULT 'payout', + "narration" TEXT NOT NULL DEFAULT '', + "queueIfLowBalance" BOOLEAN NOT NULL DEFAULT false, + "payoutId" TEXT NOT NULL DEFAULT '', + "signature" TEXT NOT NULL DEFAULT '', + "throwOnInvalid" BOOLEAN NOT NULL DEFAULT true, + "count" TEXT NOT NULL DEFAULT '', + "skip" TEXT NOT NULL DEFAULT '', + "fromDate" TEXT NOT NULL DEFAULT '', + "toDate" TEXT NOT NULL DEFAULT '', + "authorized" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "RazorpayNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ShiprocketNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "ShiprocketOperation" NOT NULL DEFAULT 'CREATE_ORDER', + "variableName" TEXT NOT NULL DEFAULT 'shiprocket', + "orderId" TEXT NOT NULL DEFAULT '', + "orderDate" TEXT NOT NULL DEFAULT '', + "channelId" TEXT NOT NULL DEFAULT '', + "billingName" TEXT NOT NULL DEFAULT '', + "billingAddress" TEXT NOT NULL DEFAULT '', + "billingAddress2" TEXT NOT NULL DEFAULT '', + "billingCity" TEXT NOT NULL DEFAULT '', + "billingState" TEXT NOT NULL DEFAULT '', + "billingCountry" TEXT NOT NULL DEFAULT 'India', + "billingPincode" TEXT NOT NULL DEFAULT '', + "billingEmail" TEXT NOT NULL DEFAULT '', + "billingPhone" TEXT NOT NULL DEFAULT '', + "billingAlternatePhone" TEXT NOT NULL DEFAULT '', + "shippingIsBilling" BOOLEAN NOT NULL DEFAULT true, + "shippingName" TEXT NOT NULL DEFAULT '', + "shippingAddress" TEXT NOT NULL DEFAULT '', + "shippingAddress2" TEXT NOT NULL DEFAULT '', + "shippingCity" TEXT NOT NULL DEFAULT '', + "shippingState" TEXT NOT NULL DEFAULT '', + "shippingCountry" TEXT NOT NULL DEFAULT 'India', + "shippingPincode" TEXT NOT NULL DEFAULT '', + "shippingEmail" TEXT NOT NULL DEFAULT '', + "shippingPhone" TEXT NOT NULL DEFAULT '', + "orderItems" TEXT NOT NULL DEFAULT '[]', + "paymentMethod" TEXT NOT NULL DEFAULT 'prepaid', + "subTotal" TEXT NOT NULL DEFAULT '', + "codAmount" TEXT NOT NULL DEFAULT '0', + "length" TEXT NOT NULL DEFAULT '', + "breadth" TEXT NOT NULL DEFAULT '', + "height" TEXT NOT NULL DEFAULT '', + "weight" TEXT NOT NULL DEFAULT '', + "shiprocketOrderId" TEXT NOT NULL DEFAULT '', + "shipmentId" TEXT NOT NULL DEFAULT '', + "awbCode" TEXT NOT NULL DEFAULT '', + "courierId" TEXT NOT NULL DEFAULT '', + "courierName" TEXT NOT NULL DEFAULT '', + "pickupLocation" TEXT NOT NULL DEFAULT '', + "pickupPostcode" TEXT NOT NULL DEFAULT '', + "deliveryPostcode" TEXT NOT NULL DEFAULT '', + "cod" TEXT NOT NULL DEFAULT '0', + "returnOrderId" TEXT NOT NULL DEFAULT '', + "returnReason" TEXT NOT NULL DEFAULT '', + "returnPickupLocation" TEXT NOT NULL DEFAULT '', + "productName" TEXT NOT NULL DEFAULT '', + "productSku" TEXT NOT NULL DEFAULT '', + "productMrp" TEXT NOT NULL DEFAULT '', + "productSellingPrice" TEXT NOT NULL DEFAULT '', + "productWeight" TEXT NOT NULL DEFAULT '', + "productCategory" TEXT NOT NULL DEFAULT '', + "productHsn" TEXT NOT NULL DEFAULT '', + "filterStatus" TEXT NOT NULL DEFAULT '', + "pageNo" INTEGER NOT NULL DEFAULT 1, + "perPage" INTEGER NOT NULL DEFAULT 10, + "warehouseName" TEXT NOT NULL DEFAULT '', + "warehouseEmail" TEXT NOT NULL DEFAULT '', + "warehousePhone" TEXT NOT NULL DEFAULT '', + "warehouseAddress" TEXT NOT NULL DEFAULT '', + "warehouseCity" TEXT NOT NULL DEFAULT '', + "warehouseState" TEXT NOT NULL DEFAULT '', + "warehousePincode" TEXT NOT NULL DEFAULT '', + "warehouseCountry" TEXT NOT NULL DEFAULT 'India', + "cancelReason" TEXT NOT NULL DEFAULT '', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ShiprocketNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CashfreeNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "CashfreeOperation" NOT NULL DEFAULT 'CREATE_ORDER', + "variableName" TEXT NOT NULL DEFAULT 'cashfree', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "orderId" TEXT NOT NULL DEFAULT '', + "orderAmount" TEXT NOT NULL DEFAULT '', + "orderCurrency" TEXT NOT NULL DEFAULT 'INR', + "orderNote" TEXT NOT NULL DEFAULT '', + "orderMeta" TEXT NOT NULL DEFAULT '{}', + "customerId" TEXT NOT NULL DEFAULT '', + "customerEmail" TEXT NOT NULL DEFAULT '', + "customerPhone" TEXT NOT NULL DEFAULT '', + "customerName" TEXT NOT NULL DEFAULT '', + "customerBankAccountNumber" TEXT NOT NULL DEFAULT '', + "customerBankIfsc" TEXT NOT NULL DEFAULT '', + "customerBankCode" TEXT NOT NULL DEFAULT '', + "cfPaymentId" TEXT NOT NULL DEFAULT '', + "cfOrderId" TEXT NOT NULL DEFAULT '', + "paymentMethod" TEXT NOT NULL DEFAULT '', + "refundId" TEXT NOT NULL DEFAULT '', + "refundAmount" TEXT NOT NULL DEFAULT '', + "refundNote" TEXT NOT NULL DEFAULT '', + "refundSpeed" TEXT NOT NULL DEFAULT 'STANDARD', + "refundSplits" TEXT NOT NULL DEFAULT '[]', + "settlementId" TEXT NOT NULL DEFAULT '', + "startDate" TEXT NOT NULL DEFAULT '', + "endDate" TEXT NOT NULL DEFAULT '', + "cursor" TEXT NOT NULL DEFAULT '', + "limit" INTEGER NOT NULL DEFAULT 10, + "linkId" TEXT NOT NULL DEFAULT '', + "linkAmount" TEXT NOT NULL DEFAULT '', + "linkCurrency" TEXT NOT NULL DEFAULT 'INR', + "linkPurpose" TEXT NOT NULL DEFAULT '', + "linkDescription" TEXT NOT NULL DEFAULT '', + "linkExpiryTime" TEXT NOT NULL DEFAULT '', + "linkNotifyPhone" BOOLEAN NOT NULL DEFAULT true, + "linkNotifyEmail" BOOLEAN NOT NULL DEFAULT true, + "linkAutoReminders" BOOLEAN NOT NULL DEFAULT true, + "linkMinPartialAmount" TEXT NOT NULL DEFAULT '', + "linkMeta" TEXT NOT NULL DEFAULT '{}', + "subscriptionId" TEXT NOT NULL DEFAULT '', + "planId" TEXT NOT NULL DEFAULT '', + "planName" TEXT NOT NULL DEFAULT '', + "planType" TEXT NOT NULL DEFAULT 'PERIODIC', + "planIntervalType" TEXT NOT NULL DEFAULT 'MONTH', + "planIntervals" INTEGER NOT NULL DEFAULT 1, + "planMaxCycles" INTEGER NOT NULL DEFAULT 0, + "planMaxAmount" TEXT NOT NULL DEFAULT '', + "subscriptionFirstChargeTime" TEXT NOT NULL DEFAULT '', + "subscriptionExpiryTime" TEXT NOT NULL DEFAULT '', + "subscriptionReturnUrl" TEXT NOT NULL DEFAULT '', + "subscriptionNotifyUrl" TEXT NOT NULL DEFAULT '', + "subscriptionAction" TEXT NOT NULL DEFAULT 'PAUSE', + "beneId" TEXT NOT NULL DEFAULT '', + "beneName" TEXT NOT NULL DEFAULT '', + "beneEmail" TEXT NOT NULL DEFAULT '', + "benePhone" TEXT NOT NULL DEFAULT '', + "beneBankAccount" TEXT NOT NULL DEFAULT '', + "beneBankIfsc" TEXT NOT NULL DEFAULT '', + "beneVpa" TEXT NOT NULL DEFAULT '', + "beneAddress" TEXT NOT NULL DEFAULT '', + "beneCity" TEXT NOT NULL DEFAULT '', + "beneState" TEXT NOT NULL DEFAULT 'India', + "benePincode" TEXT NOT NULL DEFAULT '', + "transferId" TEXT NOT NULL DEFAULT '', + "transferAmount" TEXT NOT NULL DEFAULT '', + "transferRemarks" TEXT NOT NULL DEFAULT '', + "transferMode" TEXT NOT NULL DEFAULT 'banktransfer', + "batchTransferId" TEXT NOT NULL DEFAULT '', + "batchEntries" TEXT NOT NULL DEFAULT '[]', + "upiVpa" TEXT NOT NULL DEFAULT '', + "upiAmount" TEXT NOT NULL DEFAULT '', + "upiDescription" TEXT NOT NULL DEFAULT '', + "offerId" TEXT NOT NULL DEFAULT '', + "offerMeta" TEXT NOT NULL DEFAULT '{}', + "offerValidations" TEXT NOT NULL DEFAULT '{}', + "offerDetails" TEXT NOT NULL DEFAULT '{}', + "webhookSignature" TEXT NOT NULL DEFAULT '', + "webhookTimestamp" TEXT NOT NULL DEFAULT '', + "webhookRawBody" TEXT NOT NULL DEFAULT '', + "webhookThrowOnFail" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "CashfreeNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ZohoCrmNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "ZohoCrmOperation" NOT NULL DEFAULT 'CREATE_LEAD', + "variableName" TEXT NOT NULL DEFAULT 'zoho', + "module" TEXT NOT NULL DEFAULT 'Leads', + "recordId" TEXT NOT NULL DEFAULT '', + "firstName" TEXT NOT NULL DEFAULT '', + "lastName" TEXT NOT NULL DEFAULT '', + "email" TEXT NOT NULL DEFAULT '', + "phone" TEXT NOT NULL DEFAULT '', + "mobile" TEXT NOT NULL DEFAULT '', + "company" TEXT NOT NULL DEFAULT '', + "title" TEXT NOT NULL DEFAULT '', + "website" TEXT NOT NULL DEFAULT '', + "leadSource" TEXT NOT NULL DEFAULT '', + "leadStatus" TEXT NOT NULL DEFAULT '', + "industry" TEXT NOT NULL DEFAULT '', + "annualRevenue" TEXT NOT NULL DEFAULT '', + "noOfEmployees" TEXT NOT NULL DEFAULT '', + "rating" TEXT NOT NULL DEFAULT '', + "description" TEXT NOT NULL DEFAULT '', + "street" TEXT NOT NULL DEFAULT '', + "city" TEXT NOT NULL DEFAULT '', + "state" TEXT NOT NULL DEFAULT '', + "country" TEXT NOT NULL DEFAULT 'India', + "zipCode" TEXT NOT NULL DEFAULT '', + "dealName" TEXT NOT NULL DEFAULT '', + "dealStage" TEXT NOT NULL DEFAULT '', + "dealAmount" TEXT NOT NULL DEFAULT '', + "closingDate" TEXT NOT NULL DEFAULT '', + "accountName" TEXT NOT NULL DEFAULT '', + "contactName" TEXT NOT NULL DEFAULT '', + "probability" TEXT NOT NULL DEFAULT '', + "dealType" TEXT NOT NULL DEFAULT '', + "accountOwner" TEXT NOT NULL DEFAULT '', + "billingCity" TEXT NOT NULL DEFAULT '', + "billingState" TEXT NOT NULL DEFAULT '', + "subject" TEXT NOT NULL DEFAULT '', + "dueDate" TEXT NOT NULL DEFAULT '', + "priority" TEXT NOT NULL DEFAULT 'High', + "status" TEXT NOT NULL DEFAULT 'Not Started', + "whoId" TEXT NOT NULL DEFAULT '', + "whatId" TEXT NOT NULL DEFAULT '', + "whoModule" TEXT NOT NULL DEFAULT 'Contacts', + "whatModule" TEXT NOT NULL DEFAULT 'Deals', + "callDuration" TEXT NOT NULL DEFAULT '', + "callDirection" TEXT NOT NULL DEFAULT 'Outbound', + "callResult" TEXT NOT NULL DEFAULT '', + "callStartTime" TEXT NOT NULL DEFAULT '', + "callDescription" TEXT NOT NULL DEFAULT '', + "meetingStart" TEXT NOT NULL DEFAULT '', + "meetingEnd" TEXT NOT NULL DEFAULT '', + "meetingAgenda" TEXT NOT NULL DEFAULT '', + "participants" TEXT NOT NULL DEFAULT '[]', + "noteTitle" TEXT NOT NULL DEFAULT '', + "noteContent" TEXT NOT NULL DEFAULT '', + "parentModule" TEXT NOT NULL DEFAULT 'Leads', + "searchTerm" TEXT NOT NULL DEFAULT '', + "searchField" TEXT NOT NULL DEFAULT 'Email', + "criteria" TEXT NOT NULL DEFAULT '', + "page" INTEGER NOT NULL DEFAULT 1, + "perPage" INTEGER NOT NULL DEFAULT 10, + "createDeal" BOOLEAN NOT NULL DEFAULT false, + "overwrite" BOOLEAN NOT NULL DEFAULT false, + "customFields" TEXT NOT NULL DEFAULT '{}', + "duplicateCheckField" TEXT NOT NULL DEFAULT 'Email', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ZohoCrmNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HubspotNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "HubspotOperation" NOT NULL DEFAULT 'CREATE_CONTACT', + "variableName" TEXT NOT NULL DEFAULT 'hubspot', + "objectType" TEXT NOT NULL DEFAULT 'contacts', + "recordId" TEXT NOT NULL DEFAULT '', + "email" TEXT NOT NULL DEFAULT '', + "firstName" TEXT NOT NULL DEFAULT '', + "lastName" TEXT NOT NULL DEFAULT '', + "phone" TEXT NOT NULL DEFAULT '', + "website" TEXT NOT NULL DEFAULT '', + "company" TEXT NOT NULL DEFAULT '', + "jobTitle" TEXT NOT NULL DEFAULT '', + "lifecycleStage" TEXT NOT NULL DEFAULT '', + "leadStatus" TEXT NOT NULL DEFAULT '', + "companyName" TEXT NOT NULL DEFAULT '', + "domain" TEXT NOT NULL DEFAULT '', + "industry" TEXT NOT NULL DEFAULT '', + "annualRevenue" TEXT NOT NULL DEFAULT '', + "numberOfEmployees" TEXT NOT NULL DEFAULT '', + "city" TEXT NOT NULL DEFAULT '', + "state" TEXT NOT NULL DEFAULT '', + "country" TEXT NOT NULL DEFAULT 'India', + "dealName" TEXT NOT NULL DEFAULT '', + "dealStage" TEXT NOT NULL DEFAULT '', + "pipeline" TEXT NOT NULL DEFAULT 'default', + "amount" TEXT NOT NULL DEFAULT '', + "closeDate" TEXT NOT NULL DEFAULT '', + "dealType" TEXT NOT NULL DEFAULT '', + "priority" TEXT NOT NULL DEFAULT '', + "ticketName" TEXT NOT NULL DEFAULT '', + "ticketPipeline" TEXT NOT NULL DEFAULT '0', + "ticketStatus" TEXT NOT NULL DEFAULT '', + "ticketPriority" TEXT NOT NULL DEFAULT '', + "ticketDescription" TEXT NOT NULL DEFAULT '', + "ticketSource" TEXT NOT NULL DEFAULT '', + "noteBody" TEXT NOT NULL DEFAULT '', + "taskSubject" TEXT NOT NULL DEFAULT '', + "taskBody" TEXT NOT NULL DEFAULT '', + "taskStatus" TEXT NOT NULL DEFAULT 'NOT_STARTED', + "taskPriority" TEXT NOT NULL DEFAULT 'NONE', + "taskDueDate" TEXT NOT NULL DEFAULT '', + "callBody" TEXT NOT NULL DEFAULT '', + "callDuration" TEXT NOT NULL DEFAULT '', + "callDirection" TEXT NOT NULL DEFAULT 'OUTBOUND', + "callDisposition" TEXT NOT NULL DEFAULT '', + "emailSubject" TEXT NOT NULL DEFAULT '', + "emailBody" TEXT NOT NULL DEFAULT '', + "emailFrom" TEXT NOT NULL DEFAULT '', + "emailTo" TEXT NOT NULL DEFAULT '', + "fromObjectType" TEXT NOT NULL DEFAULT 'contacts', + "fromObjectId" TEXT NOT NULL DEFAULT '', + "toObjectType" TEXT NOT NULL DEFAULT 'deals', + "toObjectId" TEXT NOT NULL DEFAULT '', + "associationType" TEXT NOT NULL DEFAULT '', + "listId" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "filterProperty" TEXT NOT NULL DEFAULT '', + "filterOperator" TEXT NOT NULL DEFAULT 'EQ', + "filterValue" TEXT NOT NULL DEFAULT '', + "sortProperty" TEXT NOT NULL DEFAULT 'createdate', + "sortDirection" TEXT NOT NULL DEFAULT 'DESCENDING', + "limit" INTEGER NOT NULL DEFAULT 10, + "after" TEXT NOT NULL DEFAULT '', + "customProperties" TEXT NOT NULL DEFAULT '{}', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HubspotNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "FreshdeskNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "FreshdeskOperation" NOT NULL DEFAULT 'CREATE_TICKET', + "variableName" TEXT NOT NULL DEFAULT 'freshdesk', + "subject" TEXT NOT NULL DEFAULT '', + "description" TEXT NOT NULL DEFAULT '', + "descriptionHtml" TEXT NOT NULL DEFAULT '', + "email" TEXT NOT NULL DEFAULT '', + "name" TEXT NOT NULL DEFAULT '', + "phone" TEXT NOT NULL DEFAULT '', + "mobilePhone" TEXT NOT NULL DEFAULT '', + "subject2" TEXT NOT NULL DEFAULT '', + "ticketId" TEXT NOT NULL DEFAULT '', + "priority" INTEGER NOT NULL DEFAULT 2, + "status" INTEGER NOT NULL DEFAULT 2, + "source" INTEGER NOT NULL DEFAULT 2, + "ticketType" TEXT NOT NULL DEFAULT '', + "responderId" TEXT NOT NULL DEFAULT '', + "groupId" TEXT NOT NULL DEFAULT '', + "productId" TEXT NOT NULL DEFAULT '', + "companyId" TEXT NOT NULL DEFAULT '', + "fwdEmail" TEXT NOT NULL DEFAULT '', + "tags" TEXT NOT NULL DEFAULT '', + "customFields" TEXT NOT NULL DEFAULT '{}', + "noteId" TEXT NOT NULL DEFAULT '', + "noteBody" TEXT NOT NULL DEFAULT '', + "notePrivate" BOOLEAN NOT NULL DEFAULT false, + "noteUserId" TEXT NOT NULL DEFAULT '', + "contactId" TEXT NOT NULL DEFAULT '', + "contactEmail" TEXT NOT NULL DEFAULT '', + "contactName" TEXT NOT NULL DEFAULT '', + "contactPhone" TEXT NOT NULL DEFAULT '', + "contactMobile" TEXT NOT NULL DEFAULT '', + "contactJobTitle" TEXT NOT NULL DEFAULT '', + "contactTimeZone" TEXT NOT NULL DEFAULT '', + "contactLanguage" TEXT NOT NULL DEFAULT '', + "contactTags" TEXT NOT NULL DEFAULT '', + "contactCompanyId" TEXT NOT NULL DEFAULT '', + "mergeTargetId" TEXT NOT NULL DEFAULT '', + "companyName" TEXT NOT NULL DEFAULT '', + "companyDomain" TEXT NOT NULL DEFAULT '', + "companyDescription" TEXT NOT NULL DEFAULT '', + "companyNote" TEXT NOT NULL DEFAULT '', + "agentId" TEXT NOT NULL DEFAULT '', + "replyBody" TEXT NOT NULL DEFAULT '', + "replyFrom" TEXT NOT NULL DEFAULT '', + "replyTo" TEXT NOT NULL DEFAULT '', + "replyCc" TEXT NOT NULL DEFAULT '', + "replyBcc" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "filterBy" TEXT NOT NULL DEFAULT '', + "orderBy" TEXT NOT NULL DEFAULT 'created_at', + "orderType" TEXT NOT NULL DEFAULT 'desc', + "page" INTEGER NOT NULL DEFAULT 1, + "perPage" INTEGER NOT NULL DEFAULT 30, + "updatedSince" TEXT NOT NULL DEFAULT '', + "includeStats" BOOLEAN NOT NULL DEFAULT false, + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "FreshdeskNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "IfElseNode" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "field" TEXT NOT NULL DEFAULT '', + "operator" "IfElseOperator" NOT NULL DEFAULT 'EQUALS', + "value" TEXT NOT NULL DEFAULT '', + "conditionsJson" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "IfElseNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "SwitchNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "variableName" TEXT NOT NULL DEFAULT 'switch', + "casesJson" TEXT NOT NULL DEFAULT '[]', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SwitchNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "SetVariableNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "pairs" JSONB NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SetVariableNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CodeNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "code" TEXT NOT NULL, + "language" TEXT NOT NULL DEFAULT 'javascript', + "outputMode" TEXT NOT NULL DEFAULT 'append', + "timeout" INTEGER NOT NULL DEFAULT 5000, + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "allowedDomains" TEXT NOT NULL DEFAULT '', + "variableName" TEXT NOT NULL DEFAULT 'codeOutput', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "CodeNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "LoopNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "inputPath" TEXT NOT NULL DEFAULT 'googleSheets.rows', + "itemVariable" TEXT NOT NULL DEFAULT 'item', + "maxIterations" INTEGER NOT NULL DEFAULT 100, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "LoopNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WaitNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "waitMode" TEXT NOT NULL DEFAULT 'duration', + "duration" INTEGER NOT NULL DEFAULT 30, + "durationUnit" TEXT NOT NULL DEFAULT 'minutes', + "untilDatetime" TEXT NOT NULL DEFAULT '', + "timezone" TEXT NOT NULL DEFAULT 'UTC', + "timeoutDuration" INTEGER NOT NULL DEFAULT 24, + "timeoutUnit" TEXT NOT NULL DEFAULT 'hours', + "continueOnTimeout" BOOLEAN NOT NULL DEFAULT true, + "variableName" TEXT NOT NULL DEFAULT 'wait', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "WaitNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "MergeNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "inputCount" INTEGER NOT NULL DEFAULT 2, + "mergeMode" TEXT NOT NULL DEFAULT 'combine', + "matchKey1" TEXT NOT NULL DEFAULT '', + "matchKey2" TEXT NOT NULL DEFAULT '', + "positionFill" TEXT NOT NULL DEFAULT 'shortest', + "branchKey1" TEXT NOT NULL DEFAULT '', + "branchKey2" TEXT NOT NULL DEFAULT '', + "branchKeys" TEXT NOT NULL DEFAULT '', + "waitForAll" BOOLEAN NOT NULL DEFAULT true, + "variableName" TEXT NOT NULL DEFAULT 'merge', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MergeNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "GmailNode" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "GmailOperation" NOT NULL DEFAULT 'SEND', + "variableName" TEXT NOT NULL DEFAULT 'gmail', + "to" TEXT NOT NULL DEFAULT '', + "subject" TEXT NOT NULL DEFAULT '', + "body" TEXT NOT NULL DEFAULT '', + "isHtml" BOOLEAN NOT NULL DEFAULT false, + "cc" TEXT NOT NULL DEFAULT '', + "bcc" TEXT NOT NULL DEFAULT '', + "replyTo" TEXT NOT NULL DEFAULT '', + "messageId" TEXT NOT NULL DEFAULT '', + "threadId" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "maxResults" INTEGER NOT NULL DEFAULT 10, + "labelIds" TEXT NOT NULL DEFAULT '', + "includeBody" BOOLEAN NOT NULL DEFAULT true, + "includeHeaders" BOOLEAN NOT NULL DEFAULT false, + "pageToken" TEXT NOT NULL DEFAULT '', + "attachmentData" TEXT NOT NULL DEFAULT '', + "attachmentName" TEXT NOT NULL DEFAULT '', + "attachmentMime" TEXT NOT NULL DEFAULT 'application/octet-stream', + "attachmentId" TEXT NOT NULL DEFAULT '', + "attachmentOutputFormat" TEXT NOT NULL DEFAULT 'base64', + "removeLabelIds" TEXT NOT NULL DEFAULT '', + "labelName" TEXT NOT NULL DEFAULT '', + "draftId" TEXT NOT NULL DEFAULT '', + "messageIds" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "GmailNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "GoogleSheetsNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "operation" "GoogleSheetsOp" NOT NULL DEFAULT 'APPEND_ROW', + "spreadsheetId" TEXT NOT NULL DEFAULT '', + "sheetName" TEXT NOT NULL DEFAULT 'Sheet1', + "range" TEXT NOT NULL DEFAULT 'A:Z', + "rowData" JSONB NOT NULL DEFAULT '[]', + "credentialId" TEXT, + "variableName" TEXT NOT NULL DEFAULT 'googleSheets', + "headerRow" BOOLEAN NOT NULL DEFAULT true, + "rowNumber" TEXT NOT NULL DEFAULT '', + "rowValues" TEXT NOT NULL DEFAULT '', + "searchColumn" TEXT NOT NULL DEFAULT '', + "searchValue" TEXT NOT NULL DEFAULT '', + "clearRange" TEXT NOT NULL DEFAULT '', + "newSheetName" TEXT NOT NULL DEFAULT '', + "valueInputOption" TEXT NOT NULL DEFAULT 'USER_ENTERED', + "matchColumn" TEXT NOT NULL DEFAULT '', + "matchValue" TEXT NOT NULL DEFAULT '', + "updateValues" TEXT NOT NULL DEFAULT '', + "maxResults" INTEGER NOT NULL DEFAULT 100, + "includeEmptyRows" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "GoogleSheetsNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "GoogleDriveNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "GoogleDriveOperation" NOT NULL DEFAULT 'UPLOAD_FILE', + "folderId" TEXT, + "fileId" TEXT, + "fileName" TEXT, + "mimeType" TEXT, + "query" TEXT, + "maxResults" INTEGER NOT NULL DEFAULT 10, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "GoogleDriveNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "SlackNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "SlackOperation" NOT NULL DEFAULT 'MESSAGE_SEND_WEBHOOK', + "variableName" TEXT NOT NULL DEFAULT 'slack', + "channel" TEXT NOT NULL DEFAULT '', + "message" TEXT NOT NULL DEFAULT '', + "threadTs" TEXT NOT NULL DEFAULT '', + "messageTs" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "channelName" TEXT NOT NULL DEFAULT '', + "channelTopic" TEXT NOT NULL DEFAULT '', + "channelPurpose" TEXT NOT NULL DEFAULT '', + "userId" TEXT NOT NULL DEFAULT '', + "emoji" TEXT NOT NULL DEFAULT '', + "fileComment" TEXT NOT NULL DEFAULT '', + "blockKit" TEXT NOT NULL DEFAULT '', + "botName" TEXT NOT NULL DEFAULT '', + "iconEmoji" TEXT NOT NULL DEFAULT '', + "unfurlLinks" BOOLEAN NOT NULL DEFAULT true, + "channelTypes" TEXT NOT NULL DEFAULT 'public_channel,private_channel', + "limit" INTEGER NOT NULL DEFAULT 100, + "excludeArchived" BOOLEAN NOT NULL DEFAULT true, + "isPrivate" BOOLEAN NOT NULL DEFAULT false, + "filename" TEXT NOT NULL DEFAULT '', + "fileType" TEXT NOT NULL DEFAULT '', + "title" TEXT NOT NULL DEFAULT '', + "initialComment" TEXT NOT NULL DEFAULT '', + "email" TEXT NOT NULL DEFAULT '', + "statusText" TEXT NOT NULL DEFAULT '', + "statusEmoji" TEXT NOT NULL DEFAULT '', + "statusExpiration" TEXT NOT NULL DEFAULT '', + "sendAt" TEXT NOT NULL DEFAULT '', + "content" TEXT NOT NULL DEFAULT '', + "fileId" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SlackNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WhatsAppNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "WhatsAppOperation" NOT NULL DEFAULT 'SEND_TEXT', + "to" TEXT NOT NULL DEFAULT '', + "body" TEXT NOT NULL DEFAULT '', + "templateName" TEXT NOT NULL DEFAULT '', + "templateLang" TEXT NOT NULL DEFAULT 'en_US', + "templateParams" TEXT NOT NULL DEFAULT '[]', + "mediaUrl" TEXT NOT NULL DEFAULT '', + "mediaCaption" TEXT NOT NULL DEFAULT '', + "reactionEmoji" TEXT NOT NULL DEFAULT '', + "reactionMsgId" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "WhatsAppNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "NotionNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "NotionOperation" NOT NULL DEFAULT 'QUERY_DATABASE', + "databaseId" TEXT NOT NULL DEFAULT '', + "pageId" TEXT NOT NULL DEFAULT '', + "blockContent" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "filterJson" TEXT NOT NULL DEFAULT '{}', + "sortsJson" TEXT NOT NULL DEFAULT '[]', + "propertiesJson" TEXT NOT NULL DEFAULT '{}', + "notionUserId" TEXT NOT NULL DEFAULT '', + "pageSize" INTEGER NOT NULL DEFAULT 100, + "startCursor" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "NotionNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Msg91Node" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "Msg91Operation" NOT NULL DEFAULT 'SEND_OTP', + "variableName" TEXT NOT NULL DEFAULT 'msg91', + "mobile" TEXT NOT NULL DEFAULT '', + "senderId" TEXT NOT NULL DEFAULT '', + "flowId" TEXT NOT NULL DEFAULT '', + "smsVariables" TEXT NOT NULL DEFAULT '{}', + "message" TEXT NOT NULL DEFAULT '', + "route" TEXT NOT NULL DEFAULT '4', + "bulkData" TEXT NOT NULL DEFAULT '[]', + "scheduleTime" TEXT NOT NULL DEFAULT '', + "otpTemplateId" TEXT NOT NULL DEFAULT '', + "otpExpiry" INTEGER NOT NULL DEFAULT 10, + "otpLength" INTEGER NOT NULL DEFAULT 6, + "otpValue" TEXT NOT NULL DEFAULT '', + "retryType" TEXT NOT NULL DEFAULT 'text', + "whatsappTemplate" TEXT NOT NULL DEFAULT '', + "whatsappLang" TEXT NOT NULL DEFAULT 'en', + "whatsappParams" TEXT NOT NULL DEFAULT '[]', + "integratedNumber" TEXT NOT NULL DEFAULT '', + "mediaType" TEXT NOT NULL DEFAULT 'image', + "mediaUrl" TEXT NOT NULL DEFAULT '', + "mediaCaption" TEXT NOT NULL DEFAULT '', + "voiceMessage" TEXT NOT NULL DEFAULT '', + "toEmail" TEXT NOT NULL DEFAULT '', + "subject" TEXT NOT NULL DEFAULT '', + "emailBody" TEXT NOT NULL DEFAULT '', + "fromEmail" TEXT NOT NULL DEFAULT '', + "fromName" TEXT NOT NULL DEFAULT '', + "requestId" TEXT NOT NULL DEFAULT '', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Msg91Node_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "AINode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "provider" "AIProvider" NOT NULL, + "operation" "AIOperation" NOT NULL DEFAULT 'CHAT', + "variableName" TEXT NOT NULL DEFAULT 'ai', + "model" TEXT NOT NULL DEFAULT '', + "systemPrompt" TEXT NOT NULL DEFAULT '', + "userPrompt" TEXT NOT NULL DEFAULT '', + "temperature" DOUBLE PRECISION NOT NULL DEFAULT 0.7, + "maxTokens" INTEGER NOT NULL DEFAULT 1000, + "topP" DOUBLE PRECISION NOT NULL DEFAULT 1.0, + "frequencyPenalty" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "presencePenalty" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "responseFormat" TEXT NOT NULL DEFAULT 'text', + "jsonSchema" TEXT NOT NULL DEFAULT '', + "toolsJson" TEXT NOT NULL DEFAULT '[]', + "toolChoice" TEXT NOT NULL DEFAULT 'auto', + "imageUrl" TEXT NOT NULL DEFAULT '', + "imageUrls" TEXT NOT NULL DEFAULT '[]', + "imageDetail" TEXT NOT NULL DEFAULT 'auto', + "historyKey" TEXT NOT NULL DEFAULT '', + "maxHistory" INTEGER NOT NULL DEFAULT 10, + "embeddingInput" TEXT NOT NULL DEFAULT '', + "audioUrl" TEXT NOT NULL DEFAULT '', + "audioLanguage" TEXT NOT NULL DEFAULT '', + "transcriptionFormat" TEXT NOT NULL DEFAULT 'text', + "imagePrompt" TEXT NOT NULL DEFAULT '', + "imageSize" TEXT NOT NULL DEFAULT '1024x1024', + "imageQuality" TEXT NOT NULL DEFAULT 'standard', + "imageStyle" TEXT NOT NULL DEFAULT 'vivid', + "imageCount" INTEGER NOT NULL DEFAULT 1, + "classifyLabels" TEXT NOT NULL DEFAULT '', + "classifyExamples" TEXT NOT NULL DEFAULT '[]', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "timeout" INTEGER NOT NULL DEFAULT 60000, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "AINode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "MediaUploadNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "source" "MediaUploadSource" NOT NULL DEFAULT 'URL', + "inputField" TEXT NOT NULL DEFAULT '', + "mimeTypeHint" TEXT NOT NULL DEFAULT 'image/png', + "filename" TEXT NOT NULL DEFAULT '', + "credentialId" TEXT, + "variableName" TEXT NOT NULL DEFAULT 'media', + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MediaUploadNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "SortNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "operation" TEXT NOT NULL DEFAULT 'SORT_ARRAY', + "sortKeys" TEXT NOT NULL DEFAULT '[]', + "inputPath" TEXT NOT NULL DEFAULT '', + "variableName" TEXT NOT NULL DEFAULT 'sort', + "inPlace" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SortNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "FilterNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "operation" TEXT NOT NULL DEFAULT 'FILTER_ARRAY', + "inputArray" TEXT NOT NULL DEFAULT '', + "inputObject" TEXT NOT NULL DEFAULT '', + "conditionGroups" TEXT NOT NULL DEFAULT '[]', + "rootLogic" TEXT NOT NULL DEFAULT 'AND', + "outputMode" TEXT NOT NULL DEFAULT 'filtered', + "variableName" TEXT NOT NULL DEFAULT 'filter', + "stopOnEmpty" BOOLEAN NOT NULL DEFAULT false, + "includeMetadata" BOOLEAN NOT NULL DEFAULT false, + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "keyFilterMode" TEXT NOT NULL DEFAULT 'key_name', + "keepMatching" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "FilterNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "AggregateNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "operation" TEXT NOT NULL DEFAULT 'COUNT', + "inputPath" TEXT NOT NULL DEFAULT '', + "field" TEXT NOT NULL DEFAULT '', + "groupByField" TEXT NOT NULL DEFAULT '', + "pivotRowField" TEXT NOT NULL DEFAULT '', + "pivotColField" TEXT NOT NULL DEFAULT '', + "pivotValueField" TEXT NOT NULL DEFAULT '', + "pivotValueOp" TEXT NOT NULL DEFAULT 'SUM', + "separator" TEXT NOT NULL DEFAULT ', ', + "percentile" DOUBLE PRECISION NOT NULL DEFAULT 90, + "countFilter" TEXT NOT NULL DEFAULT '', + "multiOps" TEXT NOT NULL DEFAULT '[]', + "groupAggOps" TEXT NOT NULL DEFAULT '[]', + "variableName" TEXT NOT NULL DEFAULT 'aggregate', + "includeInput" BOOLEAN NOT NULL DEFAULT false, + "sortOutput" BOOLEAN NOT NULL DEFAULT true, + "topN" INTEGER NOT NULL DEFAULT 0, + "nullHandling" TEXT NOT NULL DEFAULT 'exclude', + "roundDecimals" INTEGER NOT NULL DEFAULT 2, + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "AggregateNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PostgresNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "operation" TEXT NOT NULL DEFAULT 'EXECUTE_QUERY', + "credentialId" TEXT, + "tableName" TEXT NOT NULL DEFAULT '', + "schemaName" TEXT NOT NULL DEFAULT 'public', + "selectColumns" TEXT NOT NULL DEFAULT '[]', + "whereConditions" TEXT NOT NULL DEFAULT '[]', + "orderBy" TEXT NOT NULL DEFAULT '[]', + "limitRows" INTEGER NOT NULL DEFAULT 0, + "offsetRows" INTEGER NOT NULL DEFAULT 0, + "joins" TEXT NOT NULL DEFAULT '[]', + "insertData" TEXT NOT NULL DEFAULT '', + "conflictColumns" TEXT NOT NULL DEFAULT '[]', + "updateOnConflict" TEXT NOT NULL DEFAULT '[]', + "updateData" TEXT NOT NULL DEFAULT '', + "insertManyPath" TEXT NOT NULL DEFAULT '', + "insertManyColumns" TEXT NOT NULL DEFAULT '[]', + "query" TEXT NOT NULL DEFAULT '', + "queryParams" TEXT NOT NULL DEFAULT '[]', + "transactionStatements" TEXT NOT NULL DEFAULT '[]', + "functionName" TEXT NOT NULL DEFAULT '', + "functionArgs" TEXT NOT NULL DEFAULT '[]', + "searchColumn" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "searchLanguage" TEXT NOT NULL DEFAULT 'english', + "searchLimit" INTEGER NOT NULL DEFAULT 10, + "jsonColumn" TEXT NOT NULL DEFAULT '', + "jsonPath" TEXT NOT NULL DEFAULT '', + "jsonSetColumn" TEXT NOT NULL DEFAULT '', + "jsonSetPath" TEXT NOT NULL DEFAULT '', + "jsonSetValue" TEXT NOT NULL DEFAULT '', + "columnDefinitions" TEXT NOT NULL DEFAULT '[]', + "createTableIfNotExists" BOOLEAN NOT NULL DEFAULT true, + "variableName" TEXT NOT NULL DEFAULT 'postgres', + "returnData" BOOLEAN NOT NULL DEFAULT true, + "continueOnFail" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "PostgresNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WebhookTrigger" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "webhookId" TEXT NOT NULL, + "secretToken" TEXT, + "secretTokenEncrypted" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "httpMethod" "HttpMethod" NOT NULL DEFAULT 'POST', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "WebhookTrigger_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ScheduleTrigger" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "cronExpression" TEXT NOT NULL, + "timezone" TEXT NOT NULL DEFAULT 'UTC', + "isActive" BOOLEAN NOT NULL DEFAULT true, + "inngestFunctionId" TEXT NOT NULL, + "lastRunAt" TIMESTAMP(3), + "nextRunAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ScheduleTrigger_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ErrorTriggerNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "variableName" TEXT NOT NULL DEFAULT 'errorTrigger', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ErrorTriggerNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RazorpayTrigger" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "webhookId" TEXT NOT NULL, + "webhookSecret" TEXT, + "webhookSecretEncrypted" TEXT, + "activeEvents" TEXT NOT NULL DEFAULT '[]', + "variableName" TEXT NOT NULL DEFAULT 'razorpayTrigger', + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "RazorpayTrigger_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WhatsAppTrigger" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "webhookId" TEXT NOT NULL, + "verifyToken" TEXT, + "verifyTokenEncrypted" TEXT, + "phoneNumberId" TEXT NOT NULL DEFAULT '', + "activeEvents" TEXT NOT NULL DEFAULT '[]', + "messageTypes" TEXT NOT NULL DEFAULT '[]', + "ignoreOwnMessages" BOOLEAN NOT NULL DEFAULT true, + "variableName" TEXT NOT NULL DEFAULT 'whatsappTrigger', + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "WhatsAppTrigger_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "GmailWatcher" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "credentialId" TEXT NOT NULL, + "email" TEXT NOT NULL, + "active" BOOLEAN NOT NULL DEFAULT true, + "lastHistoryId" TEXT NOT NULL DEFAULT '', + "expiration" TEXT NOT NULL DEFAULT '', + "filterQuery" TEXT NOT NULL DEFAULT '', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "GmailWatcher_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "user_emailVerifyToken_key" ON "user"("emailVerifyToken"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_razorpayCustomerId_key" ON "user"("razorpayCustomerId"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_razorpaySubId_key" ON "user"("razorpaySubId"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "BillingEvent_razorpayEventId_key" ON "BillingEvent"("razorpayEventId"); + +-- CreateIndex +CREATE INDEX "BillingEvent_userId_idx" ON "BillingEvent"("userId"); + +-- CreateIndex +CREATE INDEX "BillingEvent_type_idx" ON "BillingEvent"("type"); + +-- CreateIndex +CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); + +-- CreateIndex +CREATE UNIQUE INDEX "Connection_fromNodeId_toNodeId_fromOutput_toInput_key" ON "Connection"("fromNodeId", "toNodeId", "fromOutput", "toInput"); + +-- CreateIndex +CREATE UNIQUE INDEX "Execution_inngestEventId_key" ON "Execution"("inngestEventId"); + +-- CreateIndex +CREATE INDEX "NodeExecution_executionId_idx" ON "NodeExecution"("executionId"); + +-- CreateIndex +CREATE INDEX "NodeExecution_nodeId_idx" ON "NodeExecution"("nodeId"); + +-- CreateIndex +CREATE INDEX "NodeExecution_executionId_executionOrder_idx" ON "NodeExecution"("executionId", "executionOrder"); + +-- CreateIndex +CREATE UNIQUE INDEX "RazorpayNode_nodeId_key" ON "RazorpayNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "RazorpayNode_workflowId_idx" ON "RazorpayNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "RazorpayNode_nodeId_idx" ON "RazorpayNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ShiprocketNode_nodeId_key" ON "ShiprocketNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "ShiprocketNode_workflowId_idx" ON "ShiprocketNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "ShiprocketNode_nodeId_idx" ON "ShiprocketNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "CashfreeNode_nodeId_key" ON "CashfreeNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "CashfreeNode_workflowId_idx" ON "CashfreeNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "CashfreeNode_nodeId_idx" ON "CashfreeNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ZohoCrmNode_nodeId_key" ON "ZohoCrmNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "ZohoCrmNode_workflowId_idx" ON "ZohoCrmNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "ZohoCrmNode_nodeId_idx" ON "ZohoCrmNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "HubspotNode_nodeId_key" ON "HubspotNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "HubspotNode_workflowId_idx" ON "HubspotNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "HubspotNode_nodeId_idx" ON "HubspotNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "FreshdeskNode_nodeId_key" ON "FreshdeskNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "FreshdeskNode_workflowId_idx" ON "FreshdeskNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "FreshdeskNode_nodeId_idx" ON "FreshdeskNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "IfElseNode_nodeId_key" ON "IfElseNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "IfElseNode_workflowId_idx" ON "IfElseNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "IfElseNode_nodeId_idx" ON "IfElseNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "SwitchNode_nodeId_key" ON "SwitchNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "SwitchNode_nodeId_idx" ON "SwitchNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "SetVariableNode_nodeId_key" ON "SetVariableNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "SetVariableNode_workflowId_idx" ON "SetVariableNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "SetVariableNode_nodeId_idx" ON "SetVariableNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "CodeNode_nodeId_key" ON "CodeNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "CodeNode_workflowId_idx" ON "CodeNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "CodeNode_nodeId_idx" ON "CodeNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "LoopNode_nodeId_key" ON "LoopNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "LoopNode_workflowId_idx" ON "LoopNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "LoopNode_nodeId_idx" ON "LoopNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WaitNode_nodeId_key" ON "WaitNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "WaitNode_workflowId_idx" ON "WaitNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "WaitNode_nodeId_idx" ON "WaitNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "MergeNode_nodeId_key" ON "MergeNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "MergeNode_workflowId_idx" ON "MergeNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "MergeNode_nodeId_idx" ON "MergeNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GmailNode_nodeId_key" ON "GmailNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "GmailNode_workflowId_idx" ON "GmailNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "GmailNode_nodeId_idx" ON "GmailNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GoogleSheetsNode_nodeId_key" ON "GoogleSheetsNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "GoogleSheetsNode_workflowId_idx" ON "GoogleSheetsNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "GoogleSheetsNode_nodeId_idx" ON "GoogleSheetsNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GoogleDriveNode_nodeId_key" ON "GoogleDriveNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "SlackNode_nodeId_key" ON "SlackNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "SlackNode_workflowId_idx" ON "SlackNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "SlackNode_nodeId_idx" ON "SlackNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WhatsAppNode_nodeId_key" ON "WhatsAppNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "NotionNode_nodeId_key" ON "NotionNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "NotionNode_workflowId_idx" ON "NotionNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "NotionNode_nodeId_idx" ON "NotionNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Msg91Node_nodeId_key" ON "Msg91Node"("nodeId"); + +-- CreateIndex +CREATE INDEX "Msg91Node_workflowId_idx" ON "Msg91Node"("workflowId"); + +-- CreateIndex +CREATE INDEX "Msg91Node_nodeId_idx" ON "Msg91Node"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "AINode_nodeId_key" ON "AINode"("nodeId"); + +-- CreateIndex +CREATE INDEX "AINode_workflowId_idx" ON "AINode"("workflowId"); + +-- CreateIndex +CREATE INDEX "AINode_nodeId_idx" ON "AINode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "MediaUploadNode_nodeId_key" ON "MediaUploadNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "MediaUploadNode_workflowId_idx" ON "MediaUploadNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "MediaUploadNode_nodeId_idx" ON "MediaUploadNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "SortNode_nodeId_key" ON "SortNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "FilterNode_nodeId_key" ON "FilterNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "FilterNode_workflowId_idx" ON "FilterNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "FilterNode_nodeId_idx" ON "FilterNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "AggregateNode_nodeId_key" ON "AggregateNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "AggregateNode_workflowId_idx" ON "AggregateNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "AggregateNode_nodeId_idx" ON "AggregateNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PostgresNode_nodeId_key" ON "PostgresNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "PostgresNode_workflowId_idx" ON "PostgresNode"("workflowId"); + +-- CreateIndex +CREATE INDEX "PostgresNode_nodeId_idx" ON "PostgresNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "PostgresNode_credentialId_idx" ON "PostgresNode"("credentialId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WebhookTrigger_workflowId_key" ON "WebhookTrigger"("workflowId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WebhookTrigger_webhookId_key" ON "WebhookTrigger"("webhookId"); + +-- CreateIndex +CREATE INDEX "WebhookTrigger_webhookId_idx" ON "WebhookTrigger"("webhookId"); + +-- CreateIndex +CREATE INDEX "WebhookTrigger_workflowId_idx" ON "WebhookTrigger"("workflowId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ScheduleTrigger_workflowId_key" ON "ScheduleTrigger"("workflowId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ScheduleTrigger_inngestFunctionId_key" ON "ScheduleTrigger"("inngestFunctionId"); + +-- CreateIndex +CREATE INDEX "ScheduleTrigger_workflowId_idx" ON "ScheduleTrigger"("workflowId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ErrorTriggerNode_nodeId_key" ON "ErrorTriggerNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "ErrorTriggerNode_nodeId_idx" ON "ErrorTriggerNode"("nodeId"); + +-- CreateIndex +CREATE INDEX "ErrorTriggerNode_workflowId_idx" ON "ErrorTriggerNode"("workflowId"); + +-- CreateIndex +CREATE UNIQUE INDEX "RazorpayTrigger_nodeId_key" ON "RazorpayTrigger"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "RazorpayTrigger_webhookId_key" ON "RazorpayTrigger"("webhookId"); + +-- CreateIndex +CREATE INDEX "RazorpayTrigger_nodeId_idx" ON "RazorpayTrigger"("nodeId"); + +-- CreateIndex +CREATE INDEX "RazorpayTrigger_workflowId_idx" ON "RazorpayTrigger"("workflowId"); + +-- CreateIndex +CREATE INDEX "RazorpayTrigger_webhookId_idx" ON "RazorpayTrigger"("webhookId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WhatsAppTrigger_nodeId_key" ON "WhatsAppTrigger"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WhatsAppTrigger_webhookId_key" ON "WhatsAppTrigger"("webhookId"); + +-- CreateIndex +CREATE INDEX "WhatsAppTrigger_nodeId_idx" ON "WhatsAppTrigger"("nodeId"); + +-- CreateIndex +CREATE INDEX "WhatsAppTrigger_workflowId_idx" ON "WhatsAppTrigger"("workflowId"); + +-- CreateIndex +CREATE INDEX "WhatsAppTrigger_webhookId_idx" ON "WhatsAppTrigger"("webhookId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GmailWatcher_nodeId_key" ON "GmailWatcher"("nodeId"); + +-- CreateIndex +CREATE INDEX "GmailWatcher_email_active_idx" ON "GmailWatcher"("email", "active"); + +-- AddForeignKey +ALTER TABLE "BillingEvent" ADD CONSTRAINT "BillingEvent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "workflow" ADD CONSTRAINT "workflow_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "credential" ADD CONSTRAINT "credential_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Node" ADD CONSTRAINT "Node_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Node" ADD CONSTRAINT "Node_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Connection" ADD CONSTRAINT "Connection_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Connection" ADD CONSTRAINT "Connection_fromNodeId_fkey" FOREIGN KEY ("fromNodeId") REFERENCES "Node"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Connection" ADD CONSTRAINT "Connection_toNodeId_fkey" FOREIGN KEY ("toNodeId") REFERENCES "Node"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Execution" ADD CONSTRAINT "Execution_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "NodeExecution" ADD CONSTRAINT "NodeExecution_executionId_fkey" FOREIGN KEY ("executionId") REFERENCES "Execution"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RazorpayNode" ADD CONSTRAINT "RazorpayNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RazorpayNode" ADD CONSTRAINT "RazorpayNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ShiprocketNode" ADD CONSTRAINT "ShiprocketNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ShiprocketNode" ADD CONSTRAINT "ShiprocketNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CashfreeNode" ADD CONSTRAINT "CashfreeNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CashfreeNode" ADD CONSTRAINT "CashfreeNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ZohoCrmNode" ADD CONSTRAINT "ZohoCrmNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ZohoCrmNode" ADD CONSTRAINT "ZohoCrmNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HubspotNode" ADD CONSTRAINT "HubspotNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HubspotNode" ADD CONSTRAINT "HubspotNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "FreshdeskNode" ADD CONSTRAINT "FreshdeskNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "FreshdeskNode" ADD CONSTRAINT "FreshdeskNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "IfElseNode" ADD CONSTRAINT "IfElseNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "SwitchNode" ADD CONSTRAINT "SwitchNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "SetVariableNode" ADD CONSTRAINT "SetVariableNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CodeNode" ADD CONSTRAINT "CodeNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LoopNode" ADD CONSTRAINT "LoopNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WaitNode" ADD CONSTRAINT "WaitNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "MergeNode" ADD CONSTRAINT "MergeNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GmailNode" ADD CONSTRAINT "GmailNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GmailNode" ADD CONSTRAINT "GmailNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GoogleSheetsNode" ADD CONSTRAINT "GoogleSheetsNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GoogleSheetsNode" ADD CONSTRAINT "GoogleSheetsNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GoogleDriveNode" ADD CONSTRAINT "GoogleDriveNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GoogleDriveNode" ADD CONSTRAINT "GoogleDriveNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "SlackNode" ADD CONSTRAINT "SlackNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "SlackNode" ADD CONSTRAINT "SlackNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WhatsAppNode" ADD CONSTRAINT "WhatsAppNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WhatsAppNode" ADD CONSTRAINT "WhatsAppNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "NotionNode" ADD CONSTRAINT "NotionNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "NotionNode" ADD CONSTRAINT "NotionNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Msg91Node" ADD CONSTRAINT "Msg91Node_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Msg91Node" ADD CONSTRAINT "Msg91Node_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AINode" ADD CONSTRAINT "AINode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AINode" ADD CONSTRAINT "AINode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "MediaUploadNode" ADD CONSTRAINT "MediaUploadNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "MediaUploadNode" ADD CONSTRAINT "MediaUploadNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "SortNode" ADD CONSTRAINT "SortNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "FilterNode" ADD CONSTRAINT "FilterNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AggregateNode" ADD CONSTRAINT "AggregateNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PostgresNode" ADD CONSTRAINT "PostgresNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PostgresNode" ADD CONSTRAINT "PostgresNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WebhookTrigger" ADD CONSTRAINT "WebhookTrigger_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ScheduleTrigger" ADD CONSTRAINT "ScheduleTrigger_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ErrorTriggerNode" ADD CONSTRAINT "ErrorTriggerNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RazorpayTrigger" ADD CONSTRAINT "RazorpayTrigger_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WhatsAppTrigger" ADD CONSTRAINT "WhatsAppTrigger_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GmailWatcher" ADD CONSTRAINT "GmailWatcher_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- ───────────────────────────────────────────────────────────── +-- GitHub Node Schema Objects +-- Added: GITHUB/GITHUB_TRIGGER NodeTypes, GITHUB/GITHUB_APP CredentialTypes, +-- GitHubOperation enum, GitHubNode table, GitHubTriggerNode table +-- ───────────────────────────────────────────────────────────── + +-- AlterEnum: Add GITHUB and GITHUB_TRIGGER to NodeType +ALTER TYPE "NodeType" ADD VALUE 'GITHUB'; +ALTER TYPE "NodeType" ADD VALUE 'GITHUB_TRIGGER'; + +-- AlterEnum: Add GITHUB and GITHUB_APP to CredentialType +ALTER TYPE "CredentialType" ADD VALUE 'GITHUB'; +ALTER TYPE "CredentialType" ADD VALUE 'GITHUB_APP'; + +-- CreateEnum +CREATE TYPE "GitHubOperation" AS ENUM ( + 'REPOSITORY_CREATE', 'REPOSITORY_GET', 'REPOSITORY_LIST', 'REPOSITORY_UPDATE', + 'REPOSITORY_DELETE', 'REPOSITORY_FORK', 'REPOSITORY_LIST_CONTRIBUTORS', + 'REPOSITORY_LIST_LANGUAGES', 'REPOSITORY_TRANSFER', 'REPOSITORY_DISPATCH', + 'REPOSITORY_CHECK_COLLABORATOR', 'REPOSITORY_LIST_EVENTS', + 'ISSUE_CREATE', 'ISSUE_GET', 'ISSUE_UPDATE', 'ISSUE_CLOSE', 'ISSUE_REOPEN', + 'ISSUE_LIST', 'ISSUE_ADD_LABELS', 'ISSUE_REMOVE_LABEL', 'ISSUE_CREATE_COMMENT', + 'ISSUE_LIST_COMMENTS', 'ISSUE_UPDATE_COMMENT', 'ISSUE_DELETE_COMMENT', + 'ISSUE_ASSIGN', 'ISSUE_UNASSIGN', + 'PULL_REQUEST_CREATE', 'PULL_REQUEST_GET', 'PULL_REQUEST_LIST', + 'PULL_REQUEST_UPDATE', 'PULL_REQUEST_MERGE', 'PULL_REQUEST_CLOSE', + 'PULL_REQUEST_REOPEN', 'PULL_REQUEST_LIST_REVIEWS', 'PULL_REQUEST_CREATE_REVIEW', + 'PULL_REQUEST_REQUEST_REVIEWERS', 'PULL_REQUEST_LIST_COMMITS', + 'PULL_REQUEST_LIST_FILES', 'PULL_REQUEST_CREATE_REVIEW_COMMENT', + 'FILE_GET', 'FILE_CREATE', 'FILE_UPDATE', 'FILE_DELETE', 'FILE_LIST', + 'FILE_GET_CONTENTS', 'FILE_CREATE_OR_UPDATE', + 'BRANCH_LIST', 'BRANCH_GET', 'BRANCH_CREATE', 'BRANCH_DELETE', + 'BRANCH_RENAME', 'BRANCH_SET_PROTECTION', + 'RELEASE_CREATE', 'RELEASE_GET', 'RELEASE_LIST', 'RELEASE_UPDATE', + 'RELEASE_DELETE', 'RELEASE_UPLOAD_ASSET', + 'WORKFLOW_LIST', 'WORKFLOW_GET', 'WORKFLOW_DISPATCH', 'WORKFLOW_RUN_LIST', + 'WORKFLOW_RUN_GET', 'WORKFLOW_RUN_CANCEL', 'WORKFLOW_RUN_RE_RUN', + 'WORKFLOW_RUN_LIST_JOBS', + 'GIST_CREATE', 'GIST_GET', 'GIST_LIST', 'GIST_UPDATE', 'GIST_DELETE', 'GIST_STAR', + 'SEARCH_REPOSITORIES', 'SEARCH_ISSUES', 'SEARCH_PULL_REQUESTS', 'SEARCH_CODE', + 'SEARCH_USERS', 'SEARCH_COMMITS', 'SEARCH_TOPICS', + 'USER_GET', 'USER_GET_CURRENT', 'USER_LIST_REPOS', 'USER_LIST_FOLLOWERS', + 'USER_LIST_FOLLOWING', + 'ORG_GET', 'ORG_LIST', 'ORG_LIST_MEMBERS', 'ORG_LIST_TEAMS', 'ORG_LIST_REPOS', + 'ORG_CREATE_REPO', 'ORG_INVITE_MEMBER', 'ORG_REMOVE_MEMBER', + 'DISCUSSION_CREATE', 'DISCUSSION_GET', 'DISCUSSION_LIST', 'DISCUSSION_UPDATE', + 'DISCUSSION_DELETE', + 'CODESPACES_LIST', 'CODESPACES_GET', 'CODESPACES_CREATE', 'CODESPACES_START', + 'CODESPACES_STOP', 'CODESPACES_DELETE', 'CODESPACES_UPDATE', 'CODESPACES_EXPORT', + 'CODESPACES_LIST_FOR_REPO', 'CODESPACES_LIST_MACHINE_TYPES', + 'CODESPACES_LIST_SECRETS', 'CODESPACES_CREATE_SECRET', + 'COPILOT_LIST_SEATS', 'COPILOT_ADD_SEAT', 'COPILOT_REMOVE_SEAT', + 'COPILOT_GET_SETTINGS', 'COPILOT_UPDATE_SETTINGS', 'COPILOT_LIST_SPACES', + 'COPILOT_GET_SPACE', 'COPILOT_CREATE_SPACE', 'COPILOT_UPDATE_SPACE', + 'COPILOT_DELETE_SPACE', 'COPILOT_LIST_SPACE_RESOURCES', + 'COPILOT_ADD_SPACE_RESOURCE', 'COPILOT_REMOVE_SPACE_RESOURCE', + 'COPILOT_GET_AGENT_POLICY', 'COPILOT_UPDATE_AGENT_POLICY', + 'RULESET_LIST', 'RULESET_GET', 'RULESET_CREATE', 'RULESET_UPDATE', + 'RULESET_DELETE', 'RULESET_LIST_RULE_EVALUATIONS', 'RULESET_GET_RULE_SUITE', + 'RULESET_LIST_ORG_RULESETS', + 'ENVIRONMENT_LIST', 'ENVIRONMENT_GET', 'ENVIRONMENT_CREATE', 'ENVIRONMENT_UPDATE', + 'ENVIRONMENT_DELETE', 'ENVIRONMENT_LIST_SECRETS', 'ENVIRONMENT_GET_SECRET', + 'ENVIRONMENT_CREATE_SECRET', 'ENVIRONMENT_DELETE_SECRET', + 'ENVIRONMENT_LIST_PROTECTION_RULES', + 'SECRET_LIST_REPO', 'SECRET_GET_REPO', 'SECRET_CREATE_REPO', 'SECRET_DELETE_REPO', + 'SECRET_LIST_ORG', 'SECRET_GET_ORG', 'SECRET_CREATE_ORG', 'SECRET_DELETE_ORG', + 'SECRET_GET_PUBLIC_KEY', 'SECRET_GET_ORG_PUBLIC_KEY', + 'SECRET_LIST_SELECTED_REPOS', 'SECRET_SET_SELECTED_REPOS', + 'DEPLOYMENT_LIST', 'DEPLOYMENT_GET', 'DEPLOYMENT_CREATE', + 'DEPLOYMENT_LIST_STATUSES', 'DEPLOYMENT_GET_STATUS', 'DEPLOYMENT_CREATE_STATUS', + 'DEPLOYMENT_DELETE', 'DEPLOYMENT_SET_STATUS', + 'PACKAGE_LIST_FOR_USER', 'PACKAGE_LIST_FOR_ORG', 'PACKAGE_GET', 'PACKAGE_DELETE', + 'PACKAGE_RESTORE', 'PACKAGE_LIST_VERSIONS', 'PACKAGE_GET_VERSION', + 'PACKAGE_DELETE_VERSION', 'PACKAGE_RESTORE_VERSION', 'PACKAGE_LIST_DOCKER_IMAGES', + 'ATTESTATION_LIST_FOR_REPO', 'ATTESTATION_GET', 'ATTESTATION_CREATE', + 'ATTESTATION_LIST_FOR_ORG', 'ATTESTATION_VERIFY', + 'AGENT_TASK_LIST', 'AGENT_TASK_GET', 'AGENT_TASK_CREATE', 'AGENT_TASK_UPDATE', + 'AGENT_TASK_CANCEL', 'AGENT_TASK_GET_LOGS', + 'ARTIFACT_LIST', 'ARTIFACT_GET', 'ARTIFACT_DELETE', 'ARTIFACT_DOWNLOAD', + 'ARTIFACT_UPLOAD', 'ARTIFACT_GET_ARCHIVE_URL', + 'PROJECT_V2_LIST', 'PROJECT_V2_GET', 'PROJECT_V2_CREATE', 'PROJECT_V2_UPDATE', + 'PROJECT_V2_DELETE', 'PROJECT_V2_LIST_ITEMS', 'PROJECT_V2_ADD_ITEM', + 'PROJECT_V2_UPDATE_ITEM', 'PROJECT_V2_DELETE_ITEM', 'PROJECT_V2_LIST_FIELDS', + 'PROJECT_V2_CREATE_FIELD', 'PROJECT_V2_UPDATE_FIELD_VALUE', + 'ADVISORY_LIST', 'ADVISORY_GET', 'ADVISORY_CREATE', 'ADVISORY_UPDATE', + 'ADVISORY_LIST_REPOS_AFFECTED', 'ADVISORY_REPORT' +); + +-- CreateTable: GitHubNode +CREATE TABLE "GitHubNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "credentialId" TEXT, + "operation" "GitHubOperation" NOT NULL DEFAULT 'USER_GET_CURRENT', + "owner" TEXT NOT NULL DEFAULT '', + "repo" TEXT NOT NULL DEFAULT '', + "branch" TEXT NOT NULL DEFAULT '', + "filePath" TEXT NOT NULL DEFAULT '', + "fileContent" TEXT NOT NULL DEFAULT '', + "commitMessage" TEXT NOT NULL DEFAULT '', + "issueNumber" TEXT NOT NULL DEFAULT '', + "pullNumber" TEXT NOT NULL DEFAULT '', + "title" TEXT NOT NULL DEFAULT '', + "body" TEXT NOT NULL DEFAULT '', + "state" TEXT NOT NULL DEFAULT '', + "labels" TEXT NOT NULL DEFAULT '', + "assignees" TEXT NOT NULL DEFAULT '', + "headBranch" TEXT NOT NULL DEFAULT '', + "baseBranch" TEXT NOT NULL DEFAULT '', + "workflowId_github" TEXT NOT NULL DEFAULT '', + "eventType" TEXT NOT NULL DEFAULT '', + "clientPayload" TEXT NOT NULL DEFAULT '', + "searchQuery" TEXT NOT NULL DEFAULT '', + "perPage" INTEGER NOT NULL DEFAULT 30, + "tagName" TEXT NOT NULL DEFAULT '', + "releaseName" TEXT NOT NULL DEFAULT '', + "draft" BOOLEAN NOT NULL DEFAULT false, + "prerelease" BOOLEAN NOT NULL DEFAULT false, + "options" JSONB NOT NULL DEFAULT '{}', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "GitHubNode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable: GitHubTriggerNode +CREATE TABLE "GitHubTriggerNode" ( + "id" TEXT NOT NULL, + "nodeId" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "owner" TEXT NOT NULL DEFAULT '', + "repo" TEXT NOT NULL DEFAULT '', + "events" TEXT NOT NULL DEFAULT '["push"]', + "webhookId" TEXT, + "webhookSecret" TEXT, + "webhookSecretEncrypted" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "GitHubTriggerNode_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "GitHubNode_nodeId_key" ON "GitHubNode"("nodeId"); +CREATE INDEX "GitHubNode_workflowId_idx" ON "GitHubNode"("workflowId"); +CREATE INDEX "GitHubNode_nodeId_idx" ON "GitHubNode"("nodeId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GitHubTriggerNode_nodeId_key" ON "GitHubTriggerNode"("nodeId"); +CREATE INDEX "GitHubTriggerNode_workflowId_idx" ON "GitHubTriggerNode"("workflowId"); + +-- AddForeignKey +ALTER TABLE "GitHubNode" ADD CONSTRAINT "GitHubNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GitHubNode" ADD CONSTRAINT "GitHubNode_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "credential"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GitHubTriggerNode" ADD CONSTRAINT "GitHubTriggerNode_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema/migrations/migration_lock.toml b/prisma/schema/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/schema/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema/nodes_commerce.prisma b/prisma/schema/nodes_commerce.prisma new file mode 100644 index 0000000..dd4f83c --- /dev/null +++ b/prisma/schema/nodes_commerce.prisma @@ -0,0 +1,294 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Commerce Node Models +// RazorpayNode, ShiprocketNode, CashfreeNode +// ───────────────────────────────────────────────────────────── + +model RazorpayNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + operation RazorpayOperation @default(ORDER_CREATE) + variableName String @default("razorpay") + amount String @default("") + currency String @default("INR") + description String @default("") + receipt String @default("") + notes String @default("") + partialPayment Boolean @default(false) + orderId String @default("") + paymentId String @default("") + captureAmount String @default("") + refundAmount String @default("") + refundSpeed String @default("normal") + refundId String @default("") + customerId String @default("") + customerName String @default("") + customerEmail String @default("") + customerContact String @default("") + failExisting Boolean @default(false) + planId String @default("") + totalCount String @default("") + quantity String @default("1") + startAt String @default("") + subscriptionId String @default("") + cancelAtCycleEnd Boolean @default(false) + invoiceType String @default("invoice") + lineItems String @default("") + expireBy String @default("") + smsNotify Boolean @default(true) + emailNotify Boolean @default(true) + invoiceId String @default("") + paymentLinkId String @default("") + referenceId String @default("") + reminderEnable Boolean @default(true) + callbackUrl String @default("") + callbackMethod String @default("") + accountNumber String @default("") + fundAccountId String @default("") + payoutMode String @default("") + payoutPurpose String @default("payout") + narration String @default("") + queueIfLowBalance Boolean @default(false) + payoutId String @default("") + signature String @default("") + throwOnInvalid Boolean @default(true) + count String @default("") + skip String @default("") + fromDate String @default("") + toDate String @default("") + authorized String @default("") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model ShiprocketNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + + operation ShiprocketOperation @default(CREATE_ORDER) + variableName String @default("shiprocket") + + // ── ORDER fields ────────────────────────────────────────────────── + orderId String @default("") @db.Text + orderDate String @default("") + channelId String @default("") + + billingName String @default("") @db.Text + billingAddress String @default("") @db.Text + billingAddress2 String @default("") @db.Text + billingCity String @default("") @db.Text + billingState String @default("") @db.Text + billingCountry String @default("India") + billingPincode String @default("") @db.Text + billingEmail String @default("") @db.Text + billingPhone String @default("") @db.Text + billingAlternatePhone String @default("") @db.Text + + shippingIsBilling Boolean @default(true) + shippingName String @default("") @db.Text + shippingAddress String @default("") @db.Text + shippingAddress2 String @default("") @db.Text + shippingCity String @default("") @db.Text + shippingState String @default("") @db.Text + shippingCountry String @default("India") + shippingPincode String @default("") @db.Text + shippingEmail String @default("") @db.Text + shippingPhone String @default("") @db.Text + + orderItems String @default("[]") @db.Text + + paymentMethod String @default("prepaid") + subTotal String @default("") @db.Text + codAmount String @default("0") + + length String @default("") @db.Text + breadth String @default("") @db.Text + height String @default("") @db.Text + weight String @default("") @db.Text + + shiprocketOrderId String @default("") @db.Text + shipmentId String @default("") @db.Text + awbCode String @default("") @db.Text + + courierId String @default("") @db.Text + courierName String @default("") @db.Text + + // ── PICKUP ──────────────────────────────────────────────────────── + pickupLocation String @default("") @db.Text + + // ── RATE/SERVICEABILITY ─────────────────────────────────────────── + pickupPostcode String @default("") @db.Text + deliveryPostcode String @default("") @db.Text + cod String @default("0") + + // ── RETURN ─────────────────────────────────────────────────────── + returnOrderId String @default("") @db.Text + returnReason String @default("") @db.Text + returnPickupLocation String @default("") @db.Text + + // ── PRODUCT ────────────────────────────────────────────────────── + productName String @default("") @db.Text + productSku String @default("") @db.Text + productMrp String @default("") @db.Text + productSellingPrice String @default("") @db.Text + productWeight String @default("") @db.Text + productCategory String @default("") @db.Text + productHsn String @default("") @db.Text + + // ── LIST filters ───────────────────────────────────────────────── + filterStatus String @default("") @db.Text + pageNo Int @default(1) + perPage Int @default(10) + + // ── WAREHOUSE ──────────────────────────────────────────────────── + warehouseName String @default("") @db.Text + warehouseEmail String @default("") @db.Text + warehousePhone String @default("") @db.Text + warehouseAddress String @default("") @db.Text + warehouseCity String @default("") @db.Text + warehouseState String @default("") @db.Text + warehousePincode String @default("") @db.Text + warehouseCountry String @default("India") + + cancelReason String @default("") @db.Text + + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + @@index([workflowId]) + @@index([nodeId]) +} + +model CashfreeNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation CashfreeOperation @default(CREATE_ORDER) + variableName String @default("cashfree") + continueOnFail Boolean @default(false) + + // ── ORDER FIELDS ────────────────────────────────────────── + orderId String @default("") @db.Text + orderAmount String @default("") @db.Text + orderCurrency String @default("INR") + orderNote String @default("") @db.Text + orderMeta String @default("{}") @db.Text + + // ── CUSTOMER FIELDS ─────────────────────────────────────── + customerId String @default("") @db.Text + customerEmail String @default("") @db.Text + customerPhone String @default("") @db.Text + customerName String @default("") @db.Text + customerBankAccountNumber String @default("") @db.Text + customerBankIfsc String @default("") @db.Text + customerBankCode String @default("") + + // ── PAYMENT FIELDS ──────────────────────────────────────── + cfPaymentId String @default("") @db.Text + cfOrderId String @default("") @db.Text + paymentMethod String @default("") @db.Text + + // ── REFUND FIELDS ───────────────────────────────────────── + refundId String @default("") @db.Text + refundAmount String @default("") @db.Text + refundNote String @default("") @db.Text + refundSpeed String @default("STANDARD") + refundSplits String @default("[]") @db.Text + + // ── SETTLEMENT FIELDS ───────────────────────────────────── + settlementId String @default("") @db.Text + startDate String @default("") @db.Text + endDate String @default("") @db.Text + cursor String @default("") + limit Int @default(10) + + // ── PAYMENT LINK FIELDS ─────────────────────────────────── + linkId String @default("") @db.Text + linkAmount String @default("") @db.Text + linkCurrency String @default("INR") + linkPurpose String @default("") @db.Text + linkDescription String @default("") @db.Text + linkExpiryTime String @default("") @db.Text + linkNotifyPhone Boolean @default(true) + linkNotifyEmail Boolean @default(true) + linkAutoReminders Boolean @default(true) + linkMinPartialAmount String @default("") @db.Text + linkMeta String @default("{}") @db.Text + + // ── SUBSCRIPTION FIELDS ─────────────────────────────────── + subscriptionId String @default("") @db.Text + planId String @default("") @db.Text + planName String @default("") @db.Text + planType String @default("PERIODIC") + planIntervalType String @default("MONTH") + planIntervals Int @default(1) + planMaxCycles Int @default(0) + planMaxAmount String @default("") @db.Text + subscriptionFirstChargeTime String @default("") @db.Text + subscriptionExpiryTime String @default("") @db.Text + subscriptionReturnUrl String @default("") @db.Text + subscriptionNotifyUrl String @default("") @db.Text + subscriptionAction String @default("PAUSE") + + // ── PAYOUT FIELDS ───────────────────────────────────────── + beneId String @default("") @db.Text + beneName String @default("") @db.Text + beneEmail String @default("") @db.Text + benePhone String @default("") @db.Text + beneBankAccount String @default("") @db.Text + beneBankIfsc String @default("") @db.Text + beneVpa String @default("") @db.Text + beneAddress String @default("") @db.Text + beneCity String @default("") @db.Text + beneState String @default("India") + benePincode String @default("") @db.Text + + transferId String @default("") @db.Text + transferAmount String @default("") @db.Text + transferRemarks String @default("") @db.Text + transferMode String @default("banktransfer") + batchTransferId String @default("") @db.Text + batchEntries String @default("[]") @db.Text + + // ── UPI FIELDS ──────────────────────────────────────────── + upiVpa String @default("") @db.Text + upiAmount String @default("") @db.Text + upiDescription String @default("") @db.Text + + // ── OFFER FIELDS ────────────────────────────────────────── + offerId String @default("") @db.Text + offerMeta String @default("{}") @db.Text + offerValidations String @default("{}") @db.Text + offerDetails String @default("{}") @db.Text + + // ── WEBHOOK FIELDS ──────────────────────────────────────── + webhookSignature String @default("") @db.Text + webhookTimestamp String @default("") @db.Text + webhookRawBody String @default("") @db.Text + webhookThrowOnFail Boolean @default(true) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/prisma/schema/nodes_crm.prisma b/prisma/schema/nodes_crm.prisma new file mode 100644 index 0000000..7398ad5 --- /dev/null +++ b/prisma/schema/nodes_crm.prisma @@ -0,0 +1,318 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: CRM Node Models +// ZohoCrmNode, HubspotNode, FreshdeskNode +// ───────────────────────────────────────────────────────────── + +model ZohoCrmNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation ZohoCrmOperation @default(CREATE_LEAD) + variableName String @default("zoho") + + module String @default("Leads") + recordId String @default("") @db.Text + + // Lead / Contact shared fields + firstName String @default("") @db.Text + lastName String @default("") @db.Text + email String @default("") @db.Text + phone String @default("") @db.Text + mobile String @default("") @db.Text + company String @default("") @db.Text + title String @default("") @db.Text + website String @default("") @db.Text + leadSource String @default("") + leadStatus String @default("") + industry String @default("") + annualRevenue String @default("") @db.Text + noOfEmployees String @default("") @db.Text + rating String @default("") + description String @default("") @db.Text + + // Address (Lead / Contact / Account) + street String @default("") @db.Text + city String @default("") @db.Text + state String @default("") @db.Text + country String @default("India") + zipCode String @default("") @db.Text + + // Deal fields + dealName String @default("") @db.Text + dealStage String @default("") + dealAmount String @default("") @db.Text + closingDate String @default("") @db.Text + accountName String @default("") @db.Text + contactName String @default("") @db.Text + probability String @default("") @db.Text + dealType String @default("") + + // Account fields + accountOwner String @default("") @db.Text + billingCity String @default("") @db.Text + billingState String @default("") @db.Text + + // Task / Activity fields + subject String @default("") @db.Text + dueDate String @default("") @db.Text + priority String @default("High") + status String @default("Not Started") + whoId String @default("") @db.Text + whatId String @default("") @db.Text + whoModule String @default("Contacts") + whatModule String @default("Deals") + + // Call log fields + callDuration String @default("") @db.Text + callDirection String @default("Outbound") + callResult String @default("") + callStartTime String @default("") @db.Text + callDescription String @default("") @db.Text + + // Meeting fields + meetingStart String @default("") @db.Text + meetingEnd String @default("") @db.Text + meetingAgenda String @default("") @db.Text + participants String @default("[]") @db.Text + + // Note fields + noteTitle String @default("") @db.Text + noteContent String @default("") @db.Text + parentModule String @default("Leads") + + // Search / filter fields + searchTerm String @default("") @db.Text + searchField String @default("Email") + criteria String @default("") @db.Text + page Int @default(1) + perPage Int @default(10) + + // Convert lead + createDeal Boolean @default(false) + overwrite Boolean @default(false) + + // Custom fields JSON — escape hatch for any Zoho field not listed above + customFields String @default("{}") @db.Text + + // Upsert + duplicateCheckField String @default("Email") + + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model HubspotNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation HubspotOperation @default(CREATE_CONTACT) + variableName String @default("hubspot") + + // ── OBJECT TYPE (for generic ops) ──────────────────────── + objectType String @default("contacts") + + // ── RECORD IDENTIFICATION ───────────────────────────────── + recordId String @default("") @db.Text + + // ── CONTACT FIELDS ──────────────────────────────────────── + email String @default("") @db.Text + firstName String @default("") @db.Text + lastName String @default("") @db.Text + phone String @default("") @db.Text + website String @default("") @db.Text + company String @default("") @db.Text + jobTitle String @default("") @db.Text + lifecycleStage String @default("") + leadStatus String @default("") + + // ── COMPANY FIELDS ──────────────────────────────────────── + companyName String @default("") @db.Text + domain String @default("") @db.Text + industry String @default("") + annualRevenue String @default("") @db.Text + numberOfEmployees String @default("") @db.Text + city String @default("") @db.Text + state String @default("") @db.Text + country String @default("India") + + // ── DEAL FIELDS ─────────────────────────────────────────── + dealName String @default("") @db.Text + dealStage String @default("") + pipeline String @default("default") + amount String @default("") @db.Text + closeDate String @default("") @db.Text + dealType String @default("") + priority String @default("") + + // ── TICKET FIELDS ───────────────────────────────────────── + ticketName String @default("") @db.Text + ticketPipeline String @default("0") + ticketStatus String @default("") + ticketPriority String @default("") + ticketDescription String @default("") @db.Text + ticketSource String @default("") + + // ── ACTIVITY FIELDS ─────────────────────────────────────── + noteBody String @default("") @db.Text + taskSubject String @default("") @db.Text + taskBody String @default("") @db.Text + taskStatus String @default("NOT_STARTED") + taskPriority String @default("NONE") + taskDueDate String @default("") @db.Text + callBody String @default("") @db.Text + callDuration String @default("") @db.Text + callDirection String @default("OUTBOUND") + callDisposition String @default("") + emailSubject String @default("") @db.Text + emailBody String @default("") @db.Text + emailFrom String @default("") @db.Text + emailTo String @default("") @db.Text + + // ── ASSOCIATION FIELDS ──────────────────────────────────── + fromObjectType String @default("contacts") + fromObjectId String @default("") @db.Text + toObjectType String @default("deals") + toObjectId String @default("") @db.Text + associationType String @default("") + + // ── LIST FIELDS ─────────────────────────────────────────── + listId String @default("") @db.Text + + // ── SEARCH / FILTER FIELDS ──────────────────────────────── + searchQuery String @default("") @db.Text + filterProperty String @default("") + filterOperator String @default("EQ") + filterValue String @default("") @db.Text + sortProperty String @default("createdate") + sortDirection String @default("DESCENDING") + limit Int @default(10) + after String @default("") + + // ── CUSTOM PROPERTIES (JSON) ────────────────────────────── + // Escape hatch for any HubSpot property not listed above + customProperties String @default("{}") @db.Text + + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model FreshdeskNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation FreshdeskOperation @default(CREATE_TICKET) + variableName String @default("freshdesk") + + // ── TICKET FIELDS ───────────────────────────────────────── + subject String @default("") @db.Text + description String @default("") @db.Text + descriptionHtml String @default("") @db.Text + email String @default("") @db.Text + name String @default("") @db.Text + phone String @default("") @db.Text + mobilePhone String @default("") @db.Text + subject2 String @default("") @db.Text + ticketId String @default("") @db.Text + + // Priority: 1=Low, 2=Medium, 3=High, 4=Urgent + priority Int @default(2) + + // Status: 2=Open, 3=Pending, 4=Resolved, 5=Closed + status Int @default(2) + + // Source: 1=Email, 2=Portal, 3=Phone, 7=Chat, 9=Twitter DM, 10=WhatsApp + source Int @default(2) + + // Type: Question, Incident, Problem, Feature Request, Refund Request, etc. + ticketType String @default("") + + responderId String @default("") @db.Text + groupId String @default("") @db.Text + productId String @default("") @db.Text + companyId String @default("") @db.Text + fwdEmail String @default("") @db.Text + + // Tags — comma-separated list + tags String @default("") @db.Text + + // Custom fields JSON — { "cf_order_id": "ORD-123" } + customFields String @default("{}") @db.Text + + // ── NOTE FIELDS ─────────────────────────────────────────── + noteId String @default("") @db.Text + noteBody String @default("") @db.Text + notePrivate Boolean @default(false) + noteUserId String @default("") @db.Text + + // ── CONTACT FIELDS ──────────────────────────────────────── + contactId String @default("") @db.Text + contactEmail String @default("") @db.Text + contactName String @default("") @db.Text + contactPhone String @default("") @db.Text + contactMobile String @default("") @db.Text + contactJobTitle String @default("") @db.Text + contactTimeZone String @default("") @db.Text + contactLanguage String @default("") @db.Text + contactTags String @default("") @db.Text + contactCompanyId String @default("") @db.Text + mergeTargetId String @default("") @db.Text + + // ── COMPANY FIELDS ──────────────────────────────────────── + companyName String @default("") @db.Text + companyDomain String @default("") @db.Text + companyDescription String @default("") @db.Text + companyNote String @default("") @db.Text + + // ── AGENT FIELDS ────────────────────────────────────────── + agentId String @default("") @db.Text + + // ── REPLY FIELDS ────────────────────────────────────────── + replyBody String @default("") @db.Text + replyFrom String @default("") @db.Text + replyTo String @default("") @db.Text + replyCc String @default("") @db.Text + replyBcc String @default("") @db.Text + + // ── SEARCH / FILTER FIELDS ──────────────────────────────── + searchQuery String @default("") @db.Text + filterBy String @default("") + orderBy String @default("created_at") + orderType String @default("desc") + page Int @default(1) + perPage Int @default(30) + updatedSince String @default("") @db.Text + includeStats Boolean @default(false) + + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/prisma/schema/nodes_flow_control.prisma b/prisma/schema/nodes_flow_control.prisma new file mode 100644 index 0000000..19dcce8 --- /dev/null +++ b/prisma/schema/nodes_flow_control.prisma @@ -0,0 +1,174 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Flow Control Node Models +// IfElseNode, SwitchNode, LoopNode, WaitNode, +// MergeNode, SetVariableNode, CodeNode +// ───────────────────────────────────────────────────────────── + +model IfElseNode { + id String @id @default(cuid()) + workflowId String + nodeId String @unique + + // Condition configuration (legacy single-condition fields, kept for backward compatibility) + field String @default("") + operator IfElseOperator @default(EQUALS) + value String @default("") + + // Compound conditions JSON (AND/OR groups) + conditionsJson String @default("") @db.Text + + // Metadata + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model SwitchNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + variableName String @default("switch") + casesJson String @default("[]") @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([nodeId]) + @@index([workflowId]) +} + +model SetVariableNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + pairs Json // stores array of {key: string, value: string} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model CodeNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + code String @db.Text + language String @default("javascript") + outputMode String @default("append") + timeout Int @default(5000) + continueOnFail Boolean @default(false) + allowedDomains String @default("") + variableName String @default("codeOutput") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model LoopNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + inputPath String @default("googleSheets.rows") + itemVariable String @default("item") + maxIterations Int @default(100) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model WaitNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + // Resume mode: "duration" | "until" | "webhook" + waitMode String @default("duration") + + // Duration mode fields + duration Int @default(30) + durationUnit String @default("minutes") + + // Until mode fields + untilDatetime String @default("") + timezone String @default("UTC") + + // Webhook mode fields + timeoutDuration Int @default(24) + timeoutUnit String @default("hours") + continueOnTimeout Boolean @default(true) + + // Output + variableName String @default("wait") + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model MergeNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + // How many input branches to wait for (2-10) + inputCount Int @default(2) + + // Merge strategy: + // "position" — zip by index position + // "combine" — combine all fields from all branches into one object (default) + // "crossJoin" — cartesian product of all items + // "keyMatch" — inner join by a key field + // "keyDiff" — outer difference — items in branch1 NOT in branch2 + mergeMode String @default("combine") + + // For keyMatch and keyDiff modes: + // The dot-path field to match on e.g. "orderId" or "email" + // Supports {{variables}} — resolved at runtime + matchKey1 String @default("") + matchKey2 String @default("") + + // For position mode: + // "shortest" — zip to shortest branch length + // "longest" — pad shorter branches with null + positionFill String @default("shortest") + + // Branch variable name selection + // branchKey1/branchKey2 — variable names of the first/second branch (e.g. "gmail", "slack") + // branchKeys — comma-separated list for 3+ branch modes (e.g. "gmail,slack,notion") + branchKey1 String @default("") + branchKey2 String @default("") + branchKeys String @default("") + + // Wait for ALL branches to complete before merging + waitForAll Boolean @default(true) + + // Output variable name + variableName String @default("merge") + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/prisma/schema/nodes_github.prisma b/prisma/schema/nodes_github.prisma new file mode 100644 index 0000000..cdda700 --- /dev/null +++ b/prisma/schema/nodes_github.prisma @@ -0,0 +1,89 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: GitHub Nodes +// GitHubNode, GitHubTriggerNode +// ───────────────────────────────────────────────────────────── + +model GitHubNode { + id String @id @default(cuid()) + nodeId String @unique + + workflowId String + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation GitHubOperation @default(USER_GET_CURRENT) + + // Common Fields (Templated) + owner String @default("") + repo String @default("") + branch String @default("") + filePath String @default("") + fileContent String @default("") @db.Text + commitMessage String @default("") + + // Issue / PR Fields + issueNumber String @default("") + pullNumber String @default("") + title String @default("") + body String @default("") @db.Text + state String @default("") + labels String @default("") // JSON string array + assignees String @default("") // JSON string array + headBranch String @default("") + baseBranch String @default("") + + // Workflow / Event Fields + workflowId_github String @default("") // Renamed to avoid collision with Nodebase workflowId + eventType String @default("") + clientPayload String @default("") @db.Text + + // Search / Filter Fields + searchQuery String @default("") + perPage Int @default(30) + + // Other fields specific to releases, codespaces, rulesets, etc. + tagName String @default("") + releaseName String @default("") + draft Boolean @default(false) + prerelease Boolean @default(false) + + // Generic JSON options for all other specific flags and options + options Json @default("{}") + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@index([workflowId]) + @@index([nodeId]) +} + +model GitHubTriggerNode { + id String @id @default(cuid()) + nodeId String @unique + + workflowId String + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + owner String @default("") + repo String @default("") + events String @default("[\"push\"]") // JSON string array of GitHub webhook events + + webhookId String? // The ID of the webhook created in GitHub, to allow cleanup + + // DEPRECATED — plaintext, kept for backward-compat until data migration is complete. + // TODO: Once all rows have webhookSecretEncrypted set and webhookSecret IS NULL, + // create a follow-up migration to: DROP COLUMN webhookSecret and set + // webhookSecretEncrypted NOT NULL. + webhookSecret String? // legacy plaintext (pre-encryption rows) + + // AES-256 encrypted webhook secret. Prefer this over webhookSecret. + // Populated by new saves and by `npm run migrate:github-secrets`. + webhookSecretEncrypted String? // encrypted; null until migrated + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@index([workflowId]) +} diff --git a/prisma/schema/nodes_google.prisma b/prisma/schema/nodes_google.prisma new file mode 100644 index 0000000..f217dbd --- /dev/null +++ b/prisma/schema/nodes_google.prisma @@ -0,0 +1,108 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Google Integration Node Models +// GmailNode, GoogleSheetsNode, GoogleDriveNode +// ───────────────────────────────────────────────────────────── + +model GmailNode { + id String @id @default(cuid()) + workflowId String + nodeId String @unique + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation GmailOperation @default(SEND) + variableName String @default("gmail") + + // Email fields — support template variables like {{body.name}} + to String @default("") + subject String @default("") + body String @default("") @db.Text + isHtml Boolean @default(false) + + // New fields for extended operations + cc String @default("") + bcc String @default("") + replyTo String @default("") + messageId String @default("") + threadId String @default("") + searchQuery String @default("") + maxResults Int @default(10) + labelIds String @default("") + includeBody Boolean @default(true) + includeHeaders Boolean @default(false) + pageToken String @default("") + attachmentData String @default("") @db.Text + attachmentName String @default("") + attachmentMime String @default("application/octet-stream") + + // New fields for extended operations + attachmentId String @default("") + attachmentOutputFormat String @default("base64") + removeLabelIds String @default("") + labelName String @default("") + draftId String @default("") + messageIds String @default("") @db.Text + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model GoogleSheetsNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + operation GoogleSheetsOp @default(APPEND_ROW) + spreadsheetId String @default("") + sheetName String @default("Sheet1") + range String @default("A:Z") + rowData Json @default("[]") + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + variableName String @default("googleSheets") + headerRow Boolean @default(true) + rowNumber String @default("") + rowValues String @default("") @db.Text + searchColumn String @default("") + searchValue String @default("") + clearRange String @default("") + newSheetName String @default("") + valueInputOption String @default("USER_ENTERED") + matchColumn String @default("") + matchValue String @default("") + updateValues String @default("") @db.Text + maxResults Int @default(100) + includeEmptyRows Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model GoogleDriveNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + operation GoogleDriveOperation @default(UPLOAD_FILE) + folderId String? + fileId String? + fileName String? + mimeType String? + query String? + maxResults Int @default(10) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/prisma/schema/nodes_messaging.prisma b/prisma/schema/nodes_messaging.prisma new file mode 100644 index 0000000..c079e77 --- /dev/null +++ b/prisma/schema/nodes_messaging.prisma @@ -0,0 +1,159 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Messaging Node Models +// SlackNode, WhatsAppNode, NotionNode, Msg91Node +// ───────────────────────────────────────────────────────────── + +model SlackNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + operation SlackOperation @default(MESSAGE_SEND_WEBHOOK) + variableName String @default("slack") + channel String @default("") + message String @default("") @db.Text + threadTs String @default("") + messageTs String @default("") + searchQuery String @default("") + channelName String @default("") + channelTopic String @default("") + channelPurpose String @default("") + userId String @default("") + emoji String @default("") + fileComment String @default("") + + blockKit String @default("") @db.Text + botName String @default("") + iconEmoji String @default("") + unfurlLinks Boolean @default(true) + channelTypes String @default("public_channel,private_channel") + limit Int @default(100) + excludeArchived Boolean @default(true) + isPrivate Boolean @default(false) + filename String @default("") + fileType String @default("") + title String @default("") + initialComment String @default("") + email String @default("") + statusText String @default("") + statusEmoji String @default("") + statusExpiration String @default("") + sendAt String @default("") + content String @default("") @db.Text + fileId String @default("") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model WhatsAppNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + operation WhatsAppOperation @default(SEND_TEXT) + to String @default("") + body String @default("") @db.Text + templateName String @default("") + templateLang String @default("en_US") + templateParams String @default("[]") + mediaUrl String @default("") + mediaCaption String @default("") + reactionEmoji String @default("") + reactionMsgId String @default("") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model NotionNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + operation NotionOperation @default(QUERY_DATABASE) + databaseId String @default("") + pageId String @default("") + blockContent String @default("") @db.Text + searchQuery String @default("") + filterJson String @default("{}") @db.Text + sortsJson String @default("[]") @db.Text + propertiesJson String @default("{}") @db.Text + notionUserId String @default("") + pageSize Int @default(100) + startCursor String @default("") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model Msg91Node { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + operation Msg91Operation @default(SEND_OTP) + variableName String @default("msg91") + + mobile String @default("") @db.Text + senderId String @default("") + flowId String @default("") + smsVariables String @default("{}") @db.Text + message String @default("") @db.Text + route String @default("4") + + bulkData String @default("[]") @db.Text + + scheduleTime String @default("") + + otpTemplateId String @default("") + otpExpiry Int @default(10) + otpLength Int @default(6) + otpValue String @default("") + retryType String @default("text") + + whatsappTemplate String @default("") + whatsappLang String @default("en") + whatsappParams String @default("[]") @db.Text + integratedNumber String @default("") + + mediaType String @default("image") + mediaUrl String @default("") + mediaCaption String @default("") + + voiceMessage String @default("") + + toEmail String @default("") + subject String @default("") + emailBody String @default("") @db.Text + fromEmail String @default("") + fromName String @default("") + + requestId String @default("") + + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/prisma/schema/nodes_utility.prisma b/prisma/schema/nodes_utility.prisma new file mode 100644 index 0000000..defb5c1 --- /dev/null +++ b/prisma/schema/nodes_utility.prisma @@ -0,0 +1,315 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Utility & Misc Node Models +// AINode, MediaUploadNode, SortNode, FilterNode, +// AggregateNode, PostgresNode +// ───────────────────────────────────────────────────────────── + +model AINode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + provider AIProvider + operation AIOperation @default(CHAT) + variableName String @default("ai") + + model String @default("") + + systemPrompt String @default("") @db.Text + userPrompt String @default("") @db.Text + + temperature Float @default(0.7) + maxTokens Int @default(1000) + topP Float @default(1.0) + frequencyPenalty Float @default(0.0) + presencePenalty Float @default(0.0) + + responseFormat String @default("text") + jsonSchema String @default("") @db.Text + + toolsJson String @default("[]") @db.Text + toolChoice String @default("auto") + + imageUrl String @default("") + imageUrls String @default("[]") @db.Text + imageDetail String @default("auto") + + historyKey String @default("") + maxHistory Int @default(10) + + embeddingInput String @default("") @db.Text + + audioUrl String @default("") + audioLanguage String @default("") + transcriptionFormat String @default("text") + + imagePrompt String @default("") @db.Text + imageSize String @default("1024x1024") + imageQuality String @default("standard") + imageStyle String @default("vivid") + imageCount Int @default(1) + + classifyLabels String @default("") @db.Text + classifyExamples String @default("[]") @db.Text + + continueOnFail Boolean @default(false) + timeout Int @default(60000) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model MediaUploadNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + source MediaUploadSource @default(URL) + inputField String @default("") + mimeTypeHint String @default("image/png") + filename String @default("") + + // For Google Drive source + credentialId String? + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + variableName String @default("media") + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model SortNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + // Operation mode + // "SORT_ARRAY" — sort array of objects or primitives + // "SORT_KEYS" — sort the keys of an object + // "REVERSE" — reverse array order + // "SHUFFLE" — random order (Fisher-Yates) + operation String @default("SORT_ARRAY") + + // JSON-serialized array of SortKey objects: + // [{ field: string, direction: "asc"|"desc", type: "auto"|"string"| + // "number"|"date"|"boolean", nulls: "first"|"last", + // caseSensitive: boolean, locale: string, natural: boolean }] + sortKeys String @default("[]") @db.Text + + // Input path — dot-notation path to the array inside context + // e.g. "googleSheets.rows" or "razorpayTrigger.payload.items" + // Empty string = use the entire previous node output + inputPath String @default("") + + // Variable name for output + variableName String @default("sort") + + // Whether to sort in-place (mutate) or return new array + // Always false in executor — immutable by default + // Kept for future UI hint + inPlace Boolean @default(false) + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([workflowId]) + @@index([nodeId]) +} + +model FilterNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + operation String @default("FILTER_ARRAY") + inputArray String @default("") @db.Text + inputObject String @default("") @db.Text + conditionGroups String @default("[]") @db.Text + rootLogic String @default("AND") + outputMode String @default("filtered") + variableName String @default("filter") + stopOnEmpty Boolean @default(false) + includeMetadata Boolean @default(false) + continueOnFail Boolean @default(false) + + // FILTER_OBJECT_KEYS specific: + keyFilterMode String @default("key_name") + keepMatching Boolean @default(true) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) + @@index([nodeId]) +} + +model AggregateNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + // Primary operation + operation String @default("COUNT") + + // Dot-notation path to the input array + inputPath String @default("") @db.Text + + // Dot-notation field within each item to aggregate + field String @default("") @db.Text + + // For GROUP_BY: field to group items by + groupByField String @default("") + + // For PIVOT: row field, column field, value field, value operation + pivotRowField String @default("") + pivotColField String @default("") + pivotValueField String @default("") + pivotValueOp String @default("SUM") + + // For CONCATENATE: separator string (default ", ") + separator String @default(", ") + + // For PERCENTILE: the percentile to compute (0–100) + percentile Float @default(90) + + // For COUNT: optional filter condition (JSON FilterCondition[]) + countFilter String @default("") @db.Text + + // For MULTI: JSON array of AggregateOperation objects + multiOps String @default("[]") @db.Text + + // For GROUP_BY with nested aggregation: + groupAggOps String @default("[]") @db.Text + + // Output options + variableName String @default("aggregate") + includeInput Boolean @default(false) + sortOutput Boolean @default(true) + topN Int @default(0) + nullHandling String @default("exclude") + roundDecimals Int @default(2) + continueOnFail Boolean @default(false) + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([workflowId]) + @@index([nodeId]) +} + +model PostgresNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + // The operation to perform + operation String @default("EXECUTE_QUERY") + + // Credential reference + credentialId String? + + // For visual query builder (SELECT, UPDATE, DELETE, COUNT, EXISTS, UPSERT): + tableName String @default("") // target table + schemaName String @default("public") + + // SELECT options: + // JSON array of column names to return. [] = SELECT * + selectColumns String @default("[]") @db.Text + // JSON array of WhereCondition objects + whereConditions String @default("[]") @db.Text + // JSON array of OrderBy objects: [{ column, direction }] + orderBy String @default("[]") @db.Text + // LIMIT rows. 0 = no limit + limitRows Int @default(0) + // OFFSET for pagination + offsetRows Int @default(0) + // JOIN configuration: JSON array of JoinClause objects + joins String @default("[]") @db.Text + + // INSERT / UPSERT: + // JSON object or template string that resolves to row data + insertData String @default("") @db.Text + // For UPSERT: column(s) to check for conflict + conflictColumns String @default("[]") @db.Text + // For UPSERT: columns to update on conflict. [] = all non-conflict columns + updateOnConflict String @default("[]") @db.Text + + // UPDATE: + // JSON object of { column: newValue } — supports template expressions + updateData String @default("") @db.Text + + // INSERT_MANY: + // Template path to array of row objects: "{{filter.items}}" + insertManyPath String @default("") @db.Text + // Column names to insert. [] = infer from first row + insertManyColumns String @default("[]") @db.Text + + // EXECUTE_QUERY: + // Raw SQL with $1, $2... placeholders + query String @default("") @db.Text + // JSON array of query parameters [value1, value2...] + queryParams String @default("[]") @db.Text + + // EXECUTE_TRANSACTION: + // JSON array of { query, params } objects — run atomically + transactionStatements String @default("[]") @db.Text + + // EXECUTE_FUNCTION: + functionName String @default("") + functionArgs String @default("[]") @db.Text // JSON array + + // FULL_TEXT_SEARCH: + searchColumn String @default("") + searchQuery String @default("") + searchLanguage String @default("english") // pg language: "simple", "english", etc. + searchLimit Int @default(10) + + // JSON_PATH_QUERY: + jsonColumn String @default("") + jsonPath String @default("") // e.g. '$.payment.amount' + + // JSON_SET_FIELD: + jsonSetColumn String @default("") + jsonSetPath String @default("") // e.g. '{payment,status}' + jsonSetValue String @default("") // new value (supports template) + + // CREATE_TABLE: + // JSON array of ColumnDefinition objects + columnDefinitions String @default("[]") @db.Text + createTableIfNotExists Boolean @default(true) + + // Output options + variableName String @default("postgres") + returnData Boolean @default(true) // whether to return affected rows + continueOnFail Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) + + @@index([workflowId]) + @@index([nodeId]) + @@index([credentialId]) +} diff --git a/prisma/schema/triggers.prisma b/prisma/schema/triggers.prisma new file mode 100644 index 0000000..5509a23 --- /dev/null +++ b/prisma/schema/triggers.prisma @@ -0,0 +1,146 @@ +// ───────────────────────────────────────────────────────────── +// Nodebase — Prisma Schema: Trigger Models +// WebhookTrigger, ScheduleTrigger, RazorpayTrigger, +// WhatsAppTrigger, ErrorTriggerNode, GmailWatcher +// ───────────────────────────────────────────────────────────── + +model WebhookTrigger { + id String @id @default(cuid()) + workflowId String @unique + webhookId String @unique @default(uuid()) + // DEPRECATED — plaintext, kept for backward-compat until data migration is complete. + // TODO: Once all rows have secretTokenEncrypted set and secretTokenLegacy IS NULL, + // create a follow-up migration to: DROP COLUMN secretTokenLegacy and set + // secretTokenEncrypted NOT NULL. + secretTokenLegacy String? @map("secretToken") // legacy plaintext (pre-encryption rows) + + // AES-256 encrypted webhook secret. Prefer this over secretTokenLegacy. + // Populated by new saves and by `npm run migrate:webhook-secrets`. + secretTokenEncrypted String? // encrypted; null until migrated + isActive Boolean @default(true) + httpMethod HttpMethod @default(POST) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([webhookId]) + @@index([workflowId]) +} + +model ScheduleTrigger { + id String @id @default(cuid()) + workflowId String @unique + cronExpression String + timezone String @default("UTC") + isActive Boolean @default(true) + inngestFunctionId String @unique + lastRunAt DateTime? + nextRunAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([workflowId]) +} + +model ErrorTriggerNode { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + variableName String @default("errorTrigger") + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([nodeId]) + @@index([workflowId]) +} + +model RazorpayTrigger { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + webhookId String @unique @default(uuid()) + + // DEPRECATED — plaintext, kept for backward-compat until data migration is complete. + // TODO: Once all rows have webhookSecretEncrypted set and webhookSecret IS NULL, + // create a follow-up migration to: DROP COLUMN webhookSecret and set + // webhookSecretEncrypted NOT NULL. + webhookSecret String? // legacy plaintext (pre-encryption rows) + + // AES-256 encrypted webhook secret. Prefer this over webhookSecret. + // Populated by new saves and by `npm run migrate:razorpay-secrets`. + webhookSecretEncrypted String? // encrypted; null until migrated + + activeEvents String @default("[]") @db.Text + variableName String @default("razorpayTrigger") + isActive Boolean @default(true) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([nodeId]) + @@index([workflowId]) + @@index([webhookId]) +} + +model WhatsAppTrigger { + id String @id @default(cuid()) + nodeId String @unique + workflowId String + + webhookId String @unique @default(uuid()) + + // DEPRECATED — plaintext, kept for backward-compat until data migration is complete. + // TODO: Once all rows have verifyTokenEncrypted set and verifyToken IS NULL, + // create a follow-up migration to: DROP COLUMN verifyToken and set + // verifyTokenEncrypted NOT NULL. + verifyToken String? // legacy plaintext (pre-encryption rows) + + // AES-256 encrypted verify token. Prefer this over verifyToken. + // Populated by new saves and by `npm run migrate:whatsapp-tokens`. + verifyTokenEncrypted String? // encrypted; null until migrated + + phoneNumberId String @default("") + activeEvents String @default("[]") @db.Text + messageTypes String @default("[]") @db.Text + ignoreOwnMessages Boolean @default(true) + variableName String @default("whatsappTrigger") + isActive Boolean @default(true) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([nodeId]) + @@index([workflowId]) + @@index([webhookId]) +} + +model GmailWatcher { + id String @id @default(cuid()) + workflowId String + nodeId String @unique + credentialId String + email String + active Boolean @default(true) + lastHistoryId String @default("") + expiration String @default("") + filterQuery String @default("") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + workflow workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) + + @@index([email, active]) + @@index([workflowId]) + @@index([nodeId]) +} diff --git a/public/robots.txt b/public/robots.txt index f3d360a..d2ec89a 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -14,7 +14,7 @@ Disallow: /editor/ Disallow: /settings/ # Host -Host: https://nodebase.tech +Host: https://nodebase.mayanksaraswal.in # Sitemaps -Sitemap: https://nodebase.tech/sitemap.xml +Sitemap: https://nodebase.mayanksaraswal.in/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml index 94c903b..4e9cea5 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -1,33 +1,33 @@ -https://nodebase.tech/sentry-example-page2026-06-03T17:28:28.119Zweekly0.7 -https://nodebase.tech/docs/nodes/loop2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/msg912026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/getting-started2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/razorpay-trigger2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/switch2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/code2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/if-else2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/razorpay2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/set-variable2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/error-trigger2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/shiprocket2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/whatsapp-trigger2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/slack2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/ai2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/http-request2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/whatsapp2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/pricing2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/merge2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/google-sheets2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/notion2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/wait2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes/gmail2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/docs/nodes2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/blog2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/integrations2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/login2026-06-03T17:28:28.120Zweekly0.7 -https://nodebase.tech/signup2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/sentry-example-page2026-06-03T17:28:28.119Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/loop2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/msg912026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/getting-started2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/razorpay-trigger2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/switch2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/code2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/if-else2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/razorpay2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/set-variable2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/error-trigger2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/shiprocket2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/whatsapp-trigger2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/slack2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/ai2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/http-request2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/whatsapp2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/pricing2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/merge2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/google-sheets2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/notion2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/wait2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes/gmail2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/docs/nodes2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/blog2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/integrations2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/login2026-06-03T17:28:28.120Zweekly0.7 +https://nodebase.mayanksaraswal.in/signup2026-06-03T17:28:28.120Zweekly0.7 \ No newline at end of file diff --git a/scripts/fix-ts.js b/scripts/fix-ts.js new file mode 100644 index 0000000..2f5266b --- /dev/null +++ b/scripts/fix-ts.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const optHelper = ` +/** Safely cast an options value to string (empty string if null/undefined/object). */ +const opt = (v: unknown): string => (v != null && typeof v !== "object" ? String(v) : ""); + +`; + +function fixFile(file, replacements) { + let code = fs.readFileSync(file, 'utf8'); + if (!code.includes('const opt = ')) { + code = code.replace(/(import .* from "inngest";)/, "$1" + optHelper); + } + for (const [search, replace] of replacements) { + code = code.split(search).join(replace); + } + fs.writeFileSync(file, code); +} + +fixFile('src/features/executions/components/github/executors/issues-prs.ts', [ + ['config.options?.commentId', 'opt(config.options?.commentId)'] +]); + +fixFile('src/features/executions/components/github/executors/repository.ts', [ + ['config.options?.newOwner', 'opt(config.options?.newOwner)'], + ['config.options?.username', 'opt(config.options?.username)'], + ['config.options?.sha', 'opt(config.options?.sha)'] +]); + +fixFile('src/features/executions/components/github/executors/search-misc.ts', [ + ['config.options?.codespaceName', 'opt(config.options?.codespaceName)'], + ['config.options?.secretName', 'opt(config.options?.secretName)'], + ['body: config.options?.resource || {}', 'body: (config.options?.resource as Record) || {}'] +]); + +fixFile('src/features/executions/components/github/executors/workflows.ts', [ + ['encodeURIComponent(config.options.name)', 'encodeURIComponent(opt(config.options.name))'] +]); diff --git a/scripts/migrate-github-secrets.ts b/scripts/migrate-github-secrets.ts new file mode 100644 index 0000000..856da83 --- /dev/null +++ b/scripts/migrate-github-secrets.ts @@ -0,0 +1,80 @@ +/** + * scripts/migrate-github-secrets.ts + * + * One-time data migration: encrypts all plaintext webhookSecret values + * in GitHubTriggerNode and stores them in webhookSecretEncrypted. + * + * Run: npm run migrate:github-secrets + * + * Safe to re-run — skips rows that already have webhookSecretEncrypted set. + */ +import { PrismaClient } from "../src/generated/prisma" +import { encrypt } from "../src/lib/encryption" + +const prisma = new PrismaClient() + +async function main() { + const triggers = await prisma.gitHubTriggerNode.findMany({ + where: { + webhookSecretEncrypted: null, + webhookSecret: { not: null }, + }, + }) + + console.log(`Found ${triggers.length} GitHub trigger(s) to migrate`) + + let migrated = 0 + let skipped = 0 + let failed = 0 + + for (const trigger of triggers) { + try { + if (!trigger.webhookSecret) { + skipped++ + continue + } + + const encrypted = encrypt(trigger.webhookSecret) + + // Use updateMany with conditional where to prevent race conditions: + // only update if webhookSecretEncrypted is still null + const result = await prisma.gitHubTriggerNode.updateMany({ + where: { + id: trigger.id, + webhookSecretEncrypted: null, + }, + data: { + webhookSecretEncrypted: encrypted, + webhookSecret: null, + }, + }) + + if (result.count > 0) { + migrated++ + console.log(` ✓ Migrated trigger ${trigger.id} (workflow: ${trigger.workflowId})`) + } else { + skipped++ + console.log(` – Skipped trigger ${trigger.id} (already migrated by concurrent process)`) + } + } catch (err) { + failed++ + console.error(` ✗ Failed trigger ${trigger.id}:`, err) + } + } + + console.log( + `\nMigration complete: ${migrated} migrated, ${skipped} skipped, ${failed} failed` + ) + + if (failed > 0) { + console.error("Some triggers failed to migrate. Check errors above.") + process.exit(1) + } +} + +main() + .catch((err) => { + console.error("Fatal error:", err) + process.exit(1) + }) + .finally(() => prisma.$disconnect()) diff --git a/scripts/migrate-webhook-secrets.ts b/scripts/migrate-webhook-secrets.ts new file mode 100644 index 0000000..13dab31 --- /dev/null +++ b/scripts/migrate-webhook-secrets.ts @@ -0,0 +1,80 @@ +/** + * scripts/migrate-webhook-secrets.ts + * + * One-time data migration: encrypts all plaintext secretTokenLegacy values + * in WebhookTrigger and stores them in secretTokenEncrypted. + * + * Run: npm run migrate:webhook-secrets + * + * Safe to re-run — skips rows that already have secretTokenEncrypted set. + */ +import { PrismaClient } from "../src/generated/prisma" +import { encrypt } from "../src/lib/encryption" + +const prisma = new PrismaClient() + +async function main() { + const triggers = await prisma.webhookTrigger.findMany({ + where: { + secretTokenEncrypted: null, + secretTokenLegacy: { not: null }, + }, + }) + + console.log(`Found ${triggers.length} trigger(s) to migrate`) + + let migrated = 0 + let skipped = 0 + let failed = 0 + + for (const trigger of triggers) { + try { + if (!trigger.secretTokenLegacy) { + skipped++ + continue + } + + const encrypted = encrypt(trigger.secretTokenLegacy) + + // Use updateMany with conditional where to prevent race conditions: + // only update if secretTokenEncrypted is still null + const result = await prisma.webhookTrigger.updateMany({ + where: { + id: trigger.id, + secretTokenEncrypted: null, + }, + data: { + secretTokenEncrypted: encrypted, + secretTokenLegacy: null, // clear legacy plaintext + }, + }) + + if (result.count > 0) { + migrated++ + console.log(` ✓ Migrated trigger ${trigger.id} (workflow: ${trigger.workflowId})`) + } else { + skipped++ + console.log(` – Skipped trigger ${trigger.id} (already migrated by concurrent process)`) + } + } catch (err) { + failed++ + console.error(` ✗ Failed trigger ${trigger.id}:`, err) + } + } + + console.log( + `\nMigration complete: ${migrated} migrated, ${skipped} skipped (empty secret), ${failed} failed` + ) + + if (failed > 0) { + console.error("Some triggers failed to migrate. Check errors above.") + process.exit(1) + } +} + +main() + .catch((err) => { + console.error("Fatal error:", err) + process.exit(1) + }) + .finally(() => prisma.$disconnect()) diff --git a/src/app/api/auth/custom-signup/route.ts b/src/app/api/auth/custom-signup/route.ts index 54a80ca..c496b09 100644 --- a/src/app/api/auth/custom-signup/route.ts +++ b/src/app/api/auth/custom-signup/route.ts @@ -31,11 +31,9 @@ export async function POST(req: NextRequest) { emailVerifyExpiry: getTokenExpiry(), }, }) - try { - await sendVerificationEmail(email, existing.name || email, token) - } catch (emailError) { + sendVerificationEmail(email, existing.name || email, token).catch(emailError => { console.error("Failed to send verification email:", emailError) - } + }) return NextResponse.json({ success: "VERIFICATION_SENT", email }) } } @@ -74,16 +72,16 @@ export async function POST(req: NextRequest) { }) // 5. Send verification email (non-blocking) - try { - await sendVerificationEmail(email, user.name || email, verifyToken) - } catch (emailError) { + sendVerificationEmail(email, user.name || email, verifyToken).catch(emailError => { console.error("Failed to send verification email:", emailError) - } + }) // 6. DO NOT log them in — return success directive return NextResponse.json({ success: "VERIFICATION_SENT", email }) } catch (error) { console.error("Signup error:", error) - return NextResponse.json({ error: "Internal server error" }, { status: 500 }) + return NextResponse.json({ + error: "Internal server error" + }, { status: 500 }) } } diff --git a/src/app/api/webhooks/github/[webhookId]/route.ts b/src/app/api/webhooks/github/[webhookId]/route.ts new file mode 100644 index 0000000..3f943c8 --- /dev/null +++ b/src/app/api/webhooks/github/[webhookId]/route.ts @@ -0,0 +1,157 @@ +import { sendWorkflowExecution } from "@/inngest/utils"; +import prisma from "@/lib/db"; +import { claimIdempotencyKey } from "@/lib/redis"; +import { decrypt } from "@/lib/encryption"; +import crypto from "crypto"; +import { NextRequest, NextResponse } from "next/server"; + +function verifyGitHubSignature( + secret: string, + payload: string, + signature: string +): boolean { + if (!signature || !secret) return false; + try { + const hmac = crypto.createHmac("sha256", secret); + const digest = "sha256=" + hmac.update(payload).digest("hex"); + return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature)); + } catch (error) { + return false; + } +} + +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ webhookId: string }> } +) { + try { + // Validate content-length: treat missing/invalid/negative as zero + const contentLengthRaw = request.headers.get("content-length"); + const contentLength = contentLengthRaw !== null ? Number(contentLengthRaw) : 0; + if (!Number.isFinite(contentLength) || contentLength < 0) { + return NextResponse.json({ error: "Invalid content-length header" }, { status: 400 }); + } + if (contentLength > 10_000_000) { + return NextResponse.json({ error: "Payload too large" }, { status: 413 }); + } + + const { webhookId } = await params; + + if (!webhookId) { + return NextResponse.json( + { success: false, error: "Missing webhookId" }, + { status: 400 } + ); + } + + const webhookTrigger = await prisma.gitHubTriggerNode.findFirst({ + where: { webhookId }, + }); + + if (!webhookTrigger) { + return NextResponse.json( + { success: false, error: "Webhook not found" }, + { status: 404 } + ); + } + + const rawTextBody = await request.text().catch(() => ""); + const signature = request.headers.get("x-hub-signature-256") || ""; + + // Resolve the webhook secret: prefer encrypted, fall back to legacy plaintext + let webhookSecret: string | null = null; + if (webhookTrigger.webhookSecretEncrypted) { + try { + webhookSecret = decrypt(webhookTrigger.webhookSecretEncrypted); + } catch { + // If decryption fails, fall back to plaintext + webhookSecret = webhookTrigger.webhookSecret; + } + } else { + webhookSecret = webhookTrigger.webhookSecret; + } + + // Verify signature if a secret is configured + if (webhookSecret) { + const isValid = verifyGitHubSignature( + webhookSecret, + rawTextBody, + signature + ); + if (!isValid) { + return NextResponse.json( + { success: false, error: "Invalid signature" }, + { status: 401 } + ); + } + } + + let body = {}; + try { + if (rawTextBody) { + body = JSON.parse(rawTextBody); + } + } catch { + // Keep empty + } + + // Validate required GitHub headers + const githubEvent = request.headers.get("x-github-event"); + const githubDelivery = request.headers.get("x-github-delivery"); + + if (!githubEvent) { + return NextResponse.json( + { success: false, error: "Missing required header: x-github-event" }, + { status: 400 } + ); + } + + if (!githubDelivery) { + return NextResponse.json( + { success: false, error: "Missing required header: x-github-delivery" }, + { status: 400 } + ); + } + + // GitHub's delivery ID acts as a natural idempotency key + const idempotencyKey = `github-webhook:${webhookId}:${githubDelivery}`; + const isFirstDelivery = await claimIdempotencyKey(idempotencyKey, 3600); // 1 hour TTL + + if (!isFirstDelivery) { + console.log( + `[GitHub Webhook] Duplicate delivery ${githubDelivery} discarded for workflow ${webhookTrigger.workflowId}` + ); + return NextResponse.json({ + success: true, + status: "duplicate_discarded", + }); + } + + const receivedAt = new Date().toISOString(); + + await sendWorkflowExecution({ + workflowId: webhookTrigger.workflowId, + inngestId: idempotencyKey, + initialData: { + github: { + event: githubEvent, + deliveryId: githubDelivery, + body, + receivedAt, + }, + }, + }); + + return NextResponse.json({ + success: true, + status: "accepted", + receivedAt, + }); + } catch (error) { + console.error("GitHub webhook trigger error: ", error); + return NextResponse.json( + { success: false, error: "Failed to process webhook" }, + { status: 500 } + ); + } +} diff --git a/src/features/credentials/components/credentials.tsx b/src/features/credentials/components/credentials.tsx index 2e0aae3..80ed049 100644 --- a/src/features/credentials/components/credentials.tsx +++ b/src/features/credentials/components/credentials.tsx @@ -153,7 +153,9 @@ const credentialsLogos: Record = { [CredentialType.HUBSPOT]: "/logos/hubspot.svg", [CredentialType.FRESHDESK]: "/logos/freshdesk.svg", [CredentialType.CASHFREE]: "/logos/cashfree.svg", - [CredentialType.POSTGRES]: "/logos/postgres.svg", + [CredentialType.POSTGRES]: "/logos/postgresql.svg", + [CredentialType.GITHUB]: "/logos/github.svg", + [CredentialType.GITHUB_APP]: "/logos/github.svg", } export const CredentialsItem = ({ diff --git a/src/features/executions/components/github/actions.ts b/src/features/executions/components/github/actions.ts new file mode 100644 index 0000000..fe171cb --- /dev/null +++ b/src/features/executions/components/github/actions.ts @@ -0,0 +1,16 @@ +"use server" + +import { githubChannel } from "@/inngest/channels/github" +import { inngest } from "@/inngest/client" +import { getSubscriptionToken, Realtime } from "@inngest/realtime" + +export type GitHubToken = Realtime.Token, ["status"]> + +export async function fetchGitHubRealtimeToken(): Promise { + const token = await getSubscriptionToken(inngest, { + channel: githubChannel(), + topics: ["status"] + }) + + return token +} diff --git a/src/features/executions/components/github/api-client.ts b/src/features/executions/components/github/api-client.ts new file mode 100644 index 0000000..42d931e --- /dev/null +++ b/src/features/executions/components/github/api-client.ts @@ -0,0 +1,126 @@ +import { NonRetriableError } from "inngest"; + +interface GitHubClientOptions { + accessToken: string; + baseUrl?: string; +} + +interface RequestOptions { + method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; + body?: Record; + headers?: Record; +} + +interface RequestWithHeadersResult { + data: T; + headers: Headers; + status: number; +} + +export class GitHubClient { + private baseUrl: string; + private headers: Record; + + constructor(options: GitHubClientOptions) { + this.baseUrl = options.baseUrl || "https://api.github.com"; + this.headers = { + Authorization: `Bearer ${options.accessToken}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2026-03-10", + }; + } + + async request( + endpoint: string, + options: RequestOptions = {} + ): Promise { + const { data } = await this.requestWithHeaders(endpoint, options); + return data; + } + + /** + * Internal helper that returns both parsed data and response headers. + * Enforces rate-limit checking, error handling, 204/non-JSON responses. + */ + private async requestWithHeaders( + endpoint: string, + options: RequestOptions = {} + ): Promise> { + const url = `${this.baseUrl}${endpoint}`; + const response = await fetch(url, { + method: options.method || "GET", + headers: { ...this.headers, ...options.headers }, + body: options.body ? JSON.stringify(options.body) : undefined, + }); + + // Handle rate limiting + if ((response.status === 403 || response.status === 429) && response.headers.get("x-ratelimit-remaining") === "0") { + const resetRaw = response.headers.get("x-ratelimit-reset"); + const resetTime = resetRaw && Number.isFinite(Number(resetRaw)) + ? new Date(Number(resetRaw) * 1000).toISOString() + : "unknown"; + throw new NonRetriableError( + `GitHub API rate limit exceeded. Resets at ${resetTime}` + ); + } + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new NonRetriableError( + this.getErrorMessage(response.status, error) + ); + } + + // Handle 204 No Content + if (response.status === 204) { + return { data: {} as T, headers: response.headers, status: response.status }; + } + + // For raw contents (like file downloads) + const contentType = response.headers.get("content-type"); + if (contentType && !contentType.includes("application/json")) { + const text = await response.text(); + return { data: text as unknown as T, headers: response.headers, status: response.status }; + } + + const data = await response.json(); + return { data, headers: response.headers, status: response.status }; + } + + private getErrorMessage(status: number, error: any): string { + const message = error.message || error.error || "Unknown error"; + const hints: Record = { + "Not Found": "Repository or resource not found. Check owner/repo.", + "Bad credentials": "Invalid GitHub token. Check your credential.", + "Repository creation failed": "Failed to create repository.", + "Reference already exists": "Branch or tag already exists.", + }; + if (error.errors && Array.isArray(error.errors)) { + hints["Validation Failed"] = `Validation error: ${JSON.stringify(error.errors)}`; + } + return hints[message] || `GitHub API error (${status}): ${message}`; + } + + // Pagination helper — delegates to requestWithHeaders for consistent error/rate-limit handling + async *paginate( + endpoint: string, + perPage: number = 30 + ): AsyncGenerator { + let page = 1; + while (true) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${endpoint}${separator}per_page=${perPage}&page=${page}`; + const { data, headers } = await this.requestWithHeaders(url); + + if (!Array.isArray(data) || data.length === 0) break; + + yield data; + + // Check Link header for next page + const linkHeader = headers.get("link"); + if (!linkHeader?.includes('rel="next"')) break; + + page++; + } + } +} diff --git a/src/features/executions/components/github/components/generic-fields.tsx b/src/features/executions/components/github/components/generic-fields.tsx new file mode 100644 index 0000000..f5fc0c9 --- /dev/null +++ b/src/features/executions/components/github/components/generic-fields.tsx @@ -0,0 +1,151 @@ +"use client" + +import { useEffect, useState } from "react" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { GitHubConfig } from "../types" + +interface GenericFieldsProps { + values: Partial + setValues: (values: Partial) => void +} + +export function GenericFields({ values, setValues }: GenericFieldsProps) { + const op = values.operation || "" + const isSearch = op.startsWith("SEARCH_") + const isUser = op.startsWith("USER_") + const isOrg = op.startsWith("ORG_") + const needsOwnerRepo = op.startsWith("CODESPACES_") || op.startsWith("RULESET_") || + op.startsWith("ENVIRONMENT_") || op.startsWith("SECRET_") || + op.startsWith("PACKAGE_") || op.startsWith("ATTESTATION_") || + op.startsWith("PROJECT_V2_") || op.startsWith("ADVISORY_") + const needsOrgOnly = op.startsWith("COPILOT_") || isOrg + + // Local string state for the JSON Textarea — preserves partial edits + const [optionsText, setOptionsText] = useState( + typeof values.options === "object" ? JSON.stringify(values.options, null, 2) : "{}" + ) + + // Sync optionsText when values.options changes externally (e.g., from DB load) + useEffect(() => { + const externalJson = typeof values.options === "object" + ? JSON.stringify(values.options, null, 2) + : "{}" + setOptionsText(externalJson) + }, [values.options]) + + return ( +
+ {/* Owner / Org field — shown for most ops */} + {(needsOwnerRepo || needsOrgOnly || isUser) && ( +
+ + setValues({ ...values, owner: e.target.value })} + /> +
+ )} + + {/* Repo field — for operations that need a repo context */} + {needsOwnerRepo && ( +
+ + setValues({ ...values, repo: e.target.value })} + /> +
+ )} + + {/* Search query */} + {isSearch && ( +
+ + setValues({ ...values, searchQuery: e.target.value })} + /> +

+ Uses GitHub search syntax. See docs for query qualifiers. +

+
+ )} + + {/* Title — for create operations */} + {(op.includes("CREATE") || op.includes("INVITE") || op.includes("REPORT")) && ( +
+ + setValues({ ...values, title: e.target.value })} + /> +
+ )} + + {/* Body — for create/update operations */} + {(op.includes("CREATE") || op.includes("UPDATE") || op.includes("REPORT")) && ( +
+ +