-
Notifications
You must be signed in to change notification settings - Fork 1
User N8N workflows [WIP] #599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,9 +5,6 @@ object_relationships: | |||||||||||
| - name: complexityEnum | ||||||||||||
| using: | ||||||||||||
| foreign_key_constraint_on: default_complexity | ||||||||||||
| - name: department | ||||||||||||
| using: | ||||||||||||
| foreign_key_constraint_on: department_id | ||||||||||||
| - name: lengthEnum | ||||||||||||
| using: | ||||||||||||
| foreign_key_constraint_on: default_length | ||||||||||||
|
|
@@ -39,13 +36,6 @@ array_relationships: | |||||||||||
| table: | ||||||||||||
| name: chatbot_domain | ||||||||||||
| schema: public | ||||||||||||
| - name: organization_chatbots | ||||||||||||
| using: | ||||||||||||
| foreign_key_constraint_on: | ||||||||||||
| column: chatbot_id | ||||||||||||
| table: | ||||||||||||
| name: organization_chatbot | ||||||||||||
| schema: public | ||||||||||||
| - name: prompts | ||||||||||||
| using: | ||||||||||||
| foreign_key_constraint_on: | ||||||||||||
|
|
@@ -66,6 +56,7 @@ select_permissions: | |||||||||||
| columns: | ||||||||||||
| - avatar | ||||||||||||
| - chatbot_id | ||||||||||||
| - chatbot_id | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove duplicate column entry. The 🔎 Proposed fix columns:
- avatar
- chatbot_id
- - chatbot_id
- created_by📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| - created_by | ||||||||||||
| - default_complexity | ||||||||||||
| - default_length | ||||||||||||
|
|
@@ -84,20 +75,20 @@ select_permissions: | |||||||||||
| - role: moderator | ||||||||||||
| permission: | ||||||||||||
| columns: | ||||||||||||
| - disabled | ||||||||||||
| - is_pro | ||||||||||||
| - pro_exclusive | ||||||||||||
| - avatar | ||||||||||||
| - chatbot_id | ||||||||||||
| - department_id | ||||||||||||
| - order | ||||||||||||
| - avatar | ||||||||||||
| - created_by | ||||||||||||
| - default_complexity | ||||||||||||
| - default_length | ||||||||||||
| - default_tone | ||||||||||||
| - default_type | ||||||||||||
| - department_id | ||||||||||||
| - description | ||||||||||||
| - disabled | ||||||||||||
| - name | ||||||||||||
| - order | ||||||||||||
| filter: {} | ||||||||||||
| allow_aggregations: true | ||||||||||||
| comment: "" | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| table: | ||
| name: n8n_credentials | ||
| schema: public | ||
|
Comment on lines
+1
to
+3
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Add strict permissions for credential data. This table stores n8n credentials, which are highly sensitive. This MUST have proper permissions before merging to any non-development environment:
🔎 Example with strict security controlstable:
name: n8n_credentials
schema: public
object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- user_id
- provider
- service
- n8n_credential_id
- created_at
filter:
user_id:
_eq: X-Hasura-User-Id
insert_permissions:
- role: user
permission:
check:
user_id:
_eq: X-Hasura-User-Id
columns:
- provider
- service
- n8n_credential_id
delete_permissions:
- role: user
permission:
filter:
user_id:
_eq: X-Hasura-User-Id🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,8 +24,6 @@ insert_permissions: | |||||||||
| columns: | ||||||||||
| - deep_expertise | ||||||||||
| - favorite | ||||||||||
| - font_size | ||||||||||
| - lang | ||||||||||
| - preferred_complexity | ||||||||||
| - preferred_length | ||||||||||
| - preferred_tone | ||||||||||
|
|
@@ -35,16 +33,10 @@ insert_permissions: | |||||||||
| comment: "" | ||||||||||
| - role: user | ||||||||||
| permission: | ||||||||||
| check: | ||||||||||
| user_id: | ||||||||||
| _eq: X-Hasura-User-Id | ||||||||||
| set: | ||||||||||
| user_id: X-Hasura-User-Id | ||||||||||
| check: {} | ||||||||||
|
||||||||||
| check: {} | |
| check: | |
| user_id: | |
| _eq: X-Hasura-User-Id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical security vulnerability: Empty check allows privilege escalation.
The empty check: {} for the user role permits any authenticated user to insert preferences with any user_id, effectively allowing users to create or modify preferences for other users.
This should enforce row-level security:
check:
user_id:
_eq: X-Hasura-User-Id🔎 Proposed fix
- role: user
permission:
- check: {}
+ check:
+ user_id:
+ _eq: X-Hasura-User-Id
columns:🤖 Prompt for AI Agents
In apps/hasura/metadata/databases/masterbots/tables/public_preference.yaml
around line 36, the `check: {}` for the `user` role allows any authenticated
user to insert or modify rows for arbitrary `user_id`; replace the empty check
with a row-level security condition that enforces user_id equals the Hasura
session variable (X-Hasura-User-Id) for inserts/updates (e.g. set check to
require user_id _eq to the X-Hasura-User-Id session variable), ensuring the
comparison type matches the column type (cast the session var if needed) and
keep the rest of the permission block intact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): Removing the user_id-based row filter exposes all preferences across users to each other.
With check: {} (and no set.user_id), any authenticated user can now insert/select preference rows for any user_id, not just their own. If these are intended to remain per-user preferences, we should reinstate the row-level constraint tied to X-Hasura-User-Id or enforce equivalent restrictions elsewhere.
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filter: {} permission allows any authenticated user to read all preferences from all users, which is a privacy violation. The original permission with user_id filter should be restored to ensure users can only access their own preferences.
| filter: {} | |
| filter: | |
| user_id: | |
| _eq: X-Hasura-User-Id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical security vulnerability: Empty filter exposes all user preferences.
The empty filter: {} for the user role allows any authenticated user to query all preferences across all users, leaking sensitive user data.
This should enforce row-level security to restrict users to their own preferences.
🔎 Proposed fix
- role: user
permission:
columns:
- deep_expertise
- favorite
- font_size
- lang
- preference_id
- preferred_complexity
- preferred_length
- preferred_tone
- preferred_type
- user_id
- web_search
- filter: {}
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
comment: ""🤖 Prompt for AI Agents
In apps/hasura/metadata/databases/masterbots/tables/public_preference.yaml
around line 94, the permission filter is currently empty (filter: {}), exposing
all preferences; replace it with a row-level filter that restricts results to
the logged-in user by comparing the preference's user_id to the Hasura session
variable (for example: set filter to {"user_id": {"_eq": "X-Hasura-User-Id"}} or
to the correct JWT claim/key your app uses, e.g. "x-hasura-user-id"), ensuring
the permission only returns rows where user_id equals the session user id.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| table: | ||
| name: user_oauth_connections | ||
| schema: public | ||
|
Comment on lines
+1
to
+3
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Add permissions and relationships with privacy controls. This table stores OAuth connection data, which is sensitive user information. Before merging, you should add:
🔎 Example with privacy controlstable:
name: user_oauth_connections
schema: public
object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- user_id
- provider
- service
- scopes
- status
- connected_at
- revoked_at
filter:
user_id:
_eq: X-Hasura-User-Id
insert_permissions:
- role: user
permission:
check:
user_id:
_eq: X-Hasura-User-Id
columns:
- provider
- service
- scopes
- status
update_permissions:
- role: user
permission:
columns:
- status
- revoked_at
filter:
user_id:
_eq: X-Hasura-User-Id🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| table: | ||
| name: user_workflows | ||
| schema: public | ||
|
Comment on lines
+1
to
+3
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Consider adding permissions and relationships before merging. This metadata file only defines the table name and schema. For a production-ready table, consider adding:
Given this is marked [WIP], these can be added in a follow-up commit before merging. 🔎 Example permissions and relationshipstable:
name: user_workflows
schema: public
object_relationships:
- name: user
using:
foreign_key_constraint_on: user_id
select_permissions:
- role: user
permission:
columns:
- id
- user_id
- workflow_name
- workflow_id
- service
- folder_path
- created_at
filter:
user_id:
_eq: X-Hasura-User-Id
allow_aggregations: true🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DROP TABLE "public"."user_workflows"; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||||||||||||||
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at")); | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question (bug_risk): The uniqueness constraints on
|
||||||||||||||||||
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at")); | |
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id", "workflow_id"), UNIQUE ("created_at")); |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unique constraint on created_at in the user_workflows table is problematic. Using a timestamp as a unique constraint can cause insertion failures if multiple workflows are created at the same time (which is possible within the same millisecond). Consider removing this constraint or using a composite unique constraint if you need to prevent duplicate workflows per user.
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at")); | |
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: UNIQUE constraints severely limit functionality.
Two critical constraint issues:
UNIQUE ("user_id")- Limits each user to only ONE workflow total, which defeats the purpose of a workflows table.UNIQUE ("created_at")- Prevents multiple workflows from being created at the same timestamp (even by different users), which will cause frequent insertion failures.
These should be replaced with UNIQUE ("user_id", "workflow_id") to allow multiple workflows per user while preventing duplicate workflow IDs for the same user.
🔎 Proposed fix
-CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at"));
+CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE cascade, UNIQUE ("user_id", "workflow_id"));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at")); | |
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE cascade, UNIQUE ("user_id", "workflow_id")); |
🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766065373919_create_table_public_user_workflows/up.sql
around line 1, the table definition wrongly adds UNIQUE("user_id") and
UNIQUE("created_at") which prevent multiple workflows per user and block inserts
with identical timestamps; remove those two UNIQUE constraints and instead add a
composite UNIQUE constraint UNIQUE("user_id", "workflow_id") so a user can have
many workflows but cannot have duplicate workflow_id entries for the same user;
update the CREATE TABLE statement to drop UNIQUE("user_id") and
UNIQUE("created_at") and include UNIQUE("user_id", "workflow_id").
🛠️ Refactor suggestion | 🟠 Major
Add timestamp defaults and updated_at column.
Missing defaults for id and created_at require manual value insertion. An updated_at column is also needed to track modifications to workflow metadata.
🔎 Proposed improvements
-CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at"));
+CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("user_id", "workflow_id"));Consider adding a trigger for automatic updated_at updates:
CREATE TRIGGER set_updated_at
BEFORE UPDATE ON public.user_workflows
FOR EACH ROW
EXECUTE FUNCTION public.set_current_timestamp_updated_at();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("created_at")); | |
| CREATE TABLE "public"."user_workflows" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "workflow_name" text NOT NULL, "workflow_id" text NOT NULL, "service" text NOT NULL, "folder_path" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("user_id", "workflow_id")); |
🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766065373919_create_table_public_user_workflows/up.sql
lines 1-1, add sensible defaults and an updated_at column: set "id" to DEFAULT
uuid_generate_v4() (ensure the uuid extension is enabled or use
gen_random_uuid()), set "created_at" to DEFAULT now(), add "updated_at"
timestamptz NOT NULL DEFAULT now(), and remove redundant UNIQUE on "id" (PK
covers it); optionally keep or revisit UNIQUE on "user_id" if you intend to
allow only one workflow per user. Also add a BEFORE UPDATE trigger that calls
public.set_current_timestamp_updated_at() to auto-update updated_at (create or
reuse the function as needed).
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DROP TABLE "public"."user_oauth_connections"; |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||||||||||
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); | ||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Two points to reconsider:
|
||||||||||||||
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); | |
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id", "provider", "service"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add timestamp defaults and updated_at column.
The connected_at column lacks a default value. Additionally, tracking when connection details (like scopes or status) change requires an updated_at column.
🔎 Proposed improvements
-CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"));
+CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL DEFAULT now(), "revoked_at" timestamptz, "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"));🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766066591567_create_table_public_user_oauth_connections/up.sql
around line 1, the connected_at column has no default and there's no updated_at
column; alter the CREATE TABLE to set connected_at timestamptz NOT NULL DEFAULT
now() and add updated_at timestamptz NOT NULL DEFAULT now(); optionally add a DB
trigger or application logic to set updated_at on row updates if you want
automatic updates (but at minimum add the column with a default).
Critical constraint: UNIQUE user_id prevents multiple OAuth connections.
The UNIQUE ("user_id") constraint allows only one OAuth connection per user across all providers and services. This is severely limiting—users cannot connect both Google and another provider, or even multiple Google services.
This should likely be a composite unique constraint on (user_id, provider, service) to allow multiple connections:
🔎 Proposed fix
-CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"));
+CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL DEFAULT now(), "revoked_at" timestamptz, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE cascade, UNIQUE ("user_id", "provider", "service"));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); | |
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL DEFAULT now(), "revoked_at" timestamptz, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE cascade, UNIQUE ("user_id", "provider", "service")); |
🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766066591567_create_table_public_user_oauth_connections/up.sql
around line 1, the migration defines UNIQUE("user_id") which wrongly prevents a
user from having multiple OAuth connections; remove that single-column unique
constraint and replace it with a composite unique constraint on (user_id,
provider, service) so a user can have multiple provider/service connections but
still avoid duplicate entries for the same provider+service combination.
Incorrect nullability: revoked_at should be nullable.
The revoked_at column is defined as NOT NULL, but it should only be populated when a connection is revoked. For active connections, this field should be NULL.
This forces inserting a placeholder value (like a distant past/future date) for active connections, which is an anti-pattern.
🔎 Proposed fix
-CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"));
+CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL DEFAULT now(), "revoked_at" timestamptz, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL, "revoked_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); | |
| CREATE TABLE "public"."user_oauth_connections" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "scopes" text NOT NULL, "status" text NOT NULL, "connected_at" timestamptz NOT NULL DEFAULT now(), "revoked_at" timestamptz, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id")); |
🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766066591567_create_table_public_user_oauth_connections/up.sql
around line 1, the revoked_at column is declared as NOT NULL but should be
nullable; update the CREATE TABLE statement to remove the NOT NULL from
revoked_at so it becomes nullable, and if applying to an existing DB include a
migration step to ALTER TABLE ... ALTER COLUMN revoked_at DROP NOT NULL and
ensure any existing placeholder values are corrected to NULL as appropriate.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DROP TABLE "public"."n8n_credentials"; |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||||||
| CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("n8n_credential_id")); | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Uniqueness on This constraint allows only one
|
||||||||||
| CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("n8n_credential_id")); | |
| CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id", "provider", "service"), UNIQUE ("n8n_credential_id")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UNIQUE constraint on user_id limits users to one n8n credential.
The UNIQUE ("user_id") constraint prevents users from having multiple n8n credentials. This is overly restrictive if users need credentials for multiple services or multiple credentials for the same service.
Consider removing this constraint or making it a composite unique key with service if you want to allow one credential per service per user:
UNIQUE ("user_id", "service")🔎 Proposed fix
-CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("n8n_credential_id"));
+CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE cascade, UNIQUE ("n8n_credential_id"), UNIQUE ("user_id", "service"));🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766066981705_create_table_public_n8n_credentials/up.sql
around line 1, the current UNIQUE("user_id") constraint prevents a user from
having multiple n8n credentials; remove the standalone UNIQUE("user_id") or
replace it with a composite unique constraint such as
UNIQUE("user_id","service") to allow multiple credentials per user while still
enforcing one credential per service per user; update the CREATE TABLE statement
to drop the single-column unique and add the composite unique (or omit
uniqueness entirely) and ensure any downstream references or migrations are
adjusted accordingly.
🛠️ Refactor suggestion | 🟠 Major
Add default timestamp and updated_at column.
The created_at column lacks a default value, requiring manual timestamp insertion on every insert. Additionally, there's no updated_at column to track modifications.
🔎 Proposed improvements
-CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("n8n_credential_id"));
+CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("user_id"), UNIQUE ("n8n_credential_id"));Also consider adding a trigger to automatically update updated_at:
CREATE TRIGGER set_updated_at
BEFORE UPDATE ON public.n8n_credentials
FOR EACH ROW
EXECUTE FUNCTION public.set_current_timestamp_updated_at();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL, "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("user_id"), UNIQUE ("n8n_credential_id")); | |
| CREATE TABLE "public"."n8n_credentials" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "user_id" uuid NOT NULL, "provider" text NOT NULL, "service" text NOT NULL, "n8n_credential_id" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "public"."user"("user_id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("user_id"), UNIQUE ("n8n_credential_id")); |
🤖 Prompt for AI Agents
In
apps/hasura/migrations/masterbots/1766066981705_create_table_public_n8n_credentials/up.sql
around line 1, the CREATE TABLE lacks a default for created_at and omits an
updated_at column; modify the table definition so created_at has a default of
now() and add an updated_at timestamptz column also defaulting to now() (both
NOT NULL), and after the table creation add a trigger (or trigger creation
statement) to set updated_at on each UPDATE using the existing
public.set_current_timestamp_updated_at() function (or create that function if
missing).
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { getHasuraClient, getUserWorkflowByService } from '@/services/hasura' | ||
| import type { NextApiRequest, NextApiResponse } from 'next' | ||
|
|
||
| export default async function handler( | ||
| req: NextApiRequest, | ||
| res: NextApiResponse, | ||
| ) { | ||
| try { | ||
| const { userId, jwt } = req.body | ||
| if (!userId) return res.status(400).json({ error: 'Missing userId' }) | ||
|
|
||
| const client = getHasuraClient({ jwt }) | ||
|
|
||
| const userWorkflows = await getUserWorkflowByService(userId, 'gmail', jwt) | ||
| const workflowId = userWorkflows?.workflow_id | ||
|
|
||
| if (userWorkflows) { | ||
| return res.status(200).json({ | ||
| message: 'Workflow already enabled', | ||
| workflowId: userWorkflows.workflow_id, | ||
| }) | ||
| } | ||
|
|
||
| if (!userWorkflows) { | ||
| return res.status(400).json({ error: 'No workflow found for this user' }) | ||
| } | ||
|
|
||
| // Delete the workflow record from Hasura | ||
| await client.mutation({ | ||
| deleteUserWorkflows: { | ||
| __args: { where: { workflowId: { _eq: workflowId } } }, | ||
| affected_rows: true, | ||
| }, | ||
| }) | ||
|
|
||
| res.status(200).json({ message: 'Workflow disabled successfully' }) | ||
|
Comment on lines
+14
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical logic error: disable endpoint never actually disables. The conditional logic is inverted and makes the delete operation unreachable:
This means the disable endpoint can never actually delete a workflow. 🔎 Proposed fix export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
try {
const { userId, jwt } = req.body
if (!userId) return res.status(400).json({ error: 'Missing userId' })
const client = getHasuraClient({ jwt })
const userWorkflows = await getUserWorkflowByService(userId, 'gmail', jwt)
- const workflowId = userWorkflows?.workflow_id
-
- if (userWorkflows) {
- return res.status(200).json({
- message: 'Workflow already enabled',
- workflowId: userWorkflows.workflow_id,
- })
- }
if (!userWorkflows) {
return res.status(400).json({ error: 'No workflow found for this user' })
}
+ const workflowId = userWorkflows.workflow_id
+
// Delete the workflow record from Hasura
await client.mutation({
deleteUserWorkflows: {
- __args: { where: { workflowId: { _eq: workflowId } } },
+ __args: { where: { workflow_id: { _eq: workflowId } } },
affected_rows: true,
},
})
res.status(200).json({ message: 'Workflow disabled successfully' })🤖 Prompt for AI Agents |
||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||
| } catch (error: any) { | ||
| console.error('Error disabling Gmail workflow:', error) | ||
| res.status(500).json({ error: error.message || 'Internal server error' }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||||||
| import { duplicateN8nWorkflow } from '@/lib/n8n' | ||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||
| getUserCredentialByService, | ||||||||||||||||||||||||||||||||||
| getUserWorkflowByService, | ||||||||||||||||||||||||||||||||||
| insertUserWorkflow, | ||||||||||||||||||||||||||||||||||
| } from '@/services/hasura' | ||||||||||||||||||||||||||||||||||
| import type { NextApiRequest, NextApiResponse } from 'next' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const N8N_TEMPLATE_WORKFLOW_ID = | ||||||||||||||||||||||||||||||||||
| process.env.N8N_TEMPLATE_WORKFLOW_ID || | ||||||||||||||||||||||||||||||||||
| (() => { | ||||||||||||||||||||||||||||||||||
| throw new Error('N8N_TEMPLATE_WORKFLOW_ID is not defined') | ||||||||||||||||||||||||||||||||||
| })() | ||||||||||||||||||||||||||||||||||
| const N8N_MASTERBOTS_EMAIL_FOLDER_ID = | ||||||||||||||||||||||||||||||||||
| process.env.N8N_MASTERBOTS_EMAIL_FOLDER_ID || | ||||||||||||||||||||||||||||||||||
| (() => { | ||||||||||||||||||||||||||||||||||
| throw new Error('N8N_MASTERBOTS_EMAIL_FOLDER_ID is not defined') | ||||||||||||||||||||||||||||||||||
| })() | ||||||||||||||||||||||||||||||||||
| const N8N_WEBHOOK_BASE_URL = | ||||||||||||||||||||||||||||||||||
| process.env.N8N_WEBHOOK_BASE_URL || | ||||||||||||||||||||||||||||||||||
| (() => { | ||||||||||||||||||||||||||||||||||
| throw new Error('N8N_WEBHOOK_BASE_URL is not defined') | ||||||||||||||||||||||||||||||||||
| })() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export default async function handler( | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use named export instead of default export. The coding guidelines specify: "Avoid default exports; prefer named exports." 🔎 Proposed fix-export default async function handler(
+export async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {Based on coding guidelines.
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| req: NextApiRequest, | ||||||||||||||||||||||||||||||||||
| res: NextApiResponse, | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| const { userId, jwt } = req.body | ||||||||||||||||||||||||||||||||||
| if (!userId) return res.status(400).json({ error: 'Missing userId' }) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const existingWorkflow = await getUserWorkflowByService( | ||||||||||||||||||||||||||||||||||
| userId, | ||||||||||||||||||||||||||||||||||
| 'gmail', | ||||||||||||||||||||||||||||||||||
| jwt, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| if (existingWorkflow) { | ||||||||||||||||||||||||||||||||||
| return res.status(200).json({ | ||||||||||||||||||||||||||||||||||
| message: 'Workflow already enabled', | ||||||||||||||||||||||||||||||||||
| workflowId: existingWorkflow.workflow_id, | ||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const existingCredential = await getUserCredentialByService( | ||||||||||||||||||||||||||||||||||
| userId, | ||||||||||||||||||||||||||||||||||
| 'gmail', | ||||||||||||||||||||||||||||||||||
| jwt, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| if (!existingCredential) { | ||||||||||||||||||||||||||||||||||
| return res.status(400).json({ | ||||||||||||||||||||||||||||||||||
| error: | ||||||||||||||||||||||||||||||||||
| 'User has not connected Gmail OAuth yet. Please connect via /api/oauth/google/start', | ||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const workflow = await duplicateN8nWorkflow( | ||||||||||||||||||||||||||||||||||
| userId, | ||||||||||||||||||||||||||||||||||
| existingCredential.n8n_credential_id, | ||||||||||||||||||||||||||||||||||
| N8N_TEMPLATE_WORKFLOW_ID, | ||||||||||||||||||||||||||||||||||
| N8N_MASTERBOTS_EMAIL_FOLDER_ID, | ||||||||||||||||||||||||||||||||||
| N8N_WEBHOOK_BASE_URL, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await insertUserWorkflow( | ||||||||||||||||||||||||||||||||||
| userId, | ||||||||||||||||||||||||||||||||||
| workflow.id, | ||||||||||||||||||||||||||||||||||
| workflow.name, | ||||||||||||||||||||||||||||||||||
| 'gmail', | ||||||||||||||||||||||||||||||||||
| 'Masterbots/Users Workflows/Email', | ||||||||||||||||||||||||||||||||||
| jwt, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+65
to
+72
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Parameter order mismatch causes runtime error. The call to 🔎 Proposed fix await insertUserWorkflow(
+ jwt,
userId,
workflow.id,
workflow.name,
'gmail',
'Masterbots/Users Workflows/Email',
- jwt,
)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| res.status(200).json({ | ||||||||||||||||||||||||||||||||||
| message: 'Workflow enabled successfully', | ||||||||||||||||||||||||||||||||||
| webhookUrl: workflow.webhookUrl, | ||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||||||||||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||||||||||
| console.error('Error enabling Gmail workflow:', error) | ||||||||||||||||||||||||||||||||||
| res.status(500).json({ error: error.message || 'Internal server error' }) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getHasuraClient, getUserWorkflowByService } from '@/services/hasura' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { NextApiRequest, NextApiResponse } from 'next' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import fetch from 'node-fetch' | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| export default async function handler( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| req: NextApiRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| res: NextApiResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const { userId, payload, jwt } = req.body | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!userId) return res.status(400).json({ error: 'Missing userId' }) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const userWorkflows = await getUserWorkflowByService(userId, 'gmail', jwt) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!userWorkflows) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.status(400).json({ error: 'No workflow found for this user' }) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const webhookUrl = `${process.env.N8N_WEBHOOK_BASE_URL}/webhook/${userWorkflows.workflow_id}` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2️⃣ Trigger workflow via POST | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const triggerRes = await fetch(webhookUrl, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: { 'Content-Type': 'application/json' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify(payload || {}), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const triggerData = await triggerRes.json() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(200).json({ message: 'Workflow executed', data: triggerData }) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling for webhook trigger response. The code assumes the webhook call succeeds without checking 🔎 Proposed fix // 2️⃣ Trigger workflow via POST
const triggerRes = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload || {}),
})
const triggerData = await triggerRes.json()
+ if (!triggerRes.ok) {
+ return res.status(triggerRes.status).json({
+ error: 'Failed to trigger workflow',
+ details: triggerData,
+ })
+ }
res.status(200).json({ message: 'Workflow executed', data: triggerData })📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Error executing Gmail workflow:', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(500).json({ error: error.message || 'Internal server error' }) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate column
chatbot_idappears twice in the select permissions. This appears to be an accidental duplication that should be removed.