-
-
Notifications
You must be signed in to change notification settings - Fork 8
fix(auth): migrate user IDs to Clerk IDs and resolve connection errors #709
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
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 |
|---|---|---|
|
|
@@ -50,39 +50,40 @@ export async function POST(req: Request) { | |
| } | ||
|
|
||
| // Do something with the payload | ||
| // For this guide, you can log the payload to your console | ||
| const { id } = evt.data | ||
| const eventType = evt.type | ||
|
|
||
| if (eventType === 'user.created' || eventType === 'user.updated') { | ||
| const { id, email_addresses, first_name, last_name } = evt.data as any | ||
| const { id, email_addresses } = evt.data as any | ||
| const email = email_addresses[0]?.email_address | ||
|
|
||
| if (id) { | ||
| // Upsert user | ||
| // Upsert user using Clerk ID as the primary key 'id' | ||
| const [existingUser] = await db.select() | ||
| .from(users) | ||
| .where(eq(users.clerkUserId, id)) | ||
| .where(eq(users.id, id)) | ||
| .limit(1); | ||
|
|
||
| if (existingUser) { | ||
| await db.update(users) | ||
| .set({ email }) | ||
| .where(eq(users.clerkUserId, id)); | ||
| .where(eq(users.id, id)); | ||
| } else { | ||
| // Check by email first to link | ||
| // Check by email first (in case of migration or previous Supabase user) | ||
| const [existingEmailUser] = await db.select() | ||
| .from(users) | ||
| .where(eq(users.email, email)) | ||
| .limit(1); | ||
|
|
||
| if (existingEmailUser) { | ||
| await db.update(users) | ||
| .set({ clerkUserId: id }) | ||
| .where(eq(users.id, existingEmailUser.id)); | ||
| // If found by email but ID is different, we have a conflict. | ||
| // For a clean transition, we could update the ID, but primary keys are stable. | ||
| // In this simplified model, we'll just insert if not exact ID match. | ||
| // Or we update the email to null on the old user if we want to "take over" the email. | ||
| await db.update(users).set({ id: id }).where(eq(users.id, existingEmailUser.id)); | ||
|
Comment on lines
+79
to
+83
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. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Comments contradict the code. The block states primary keys are stable and that it will "just insert if not exact ID match," yet the next line actually executes an 🤖 Prompt for AI Agents |
||
| } else { | ||
| await db.insert(users).values({ | ||
| clerkUserId: id, | ||
| id: id, | ||
| email, | ||
| role: 'viewer' | ||
| }); | ||
|
|
@@ -93,7 +94,6 @@ export async function POST(req: Request) { | |
|
|
||
| if (eventType === 'user.deleted') { | ||
| // Optionally handle user deletion | ||
| // For safety, we might not want to delete user data entirely | ||
| } | ||
|
|
||
| return new Response('', { status: 200 }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| ALTER TABLE "calendar_notes" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "chat_participants" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "chats" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "locations" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "messages" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "prompt_generation_jobs" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "system_prompts" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "users" ALTER COLUMN "id" SET DATA TYPE text;--> statement-breakpoint | ||
| ALTER TABLE "users" ALTER COLUMN "id" DROP DEFAULT;--> statement-breakpoint | ||
| ALTER TABLE "visualizations" ALTER COLUMN "user_id" SET DATA TYPE text; | ||
|
Comment on lines
+1
to
+10
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. 🧹 Nitpick | 🔵 Trivial Type change takes an As flagged by Squawk, each 🧰 Tools🪛 Squawk (2.59.0)[warning] 1-1: Changing a column type requires an (changing-column-type) [warning] 2-2: Changing a column type requires an (changing-column-type) [warning] 3-3: Changing a column type requires an (changing-column-type) [warning] 4-4: Changing a column type requires an (changing-column-type) [warning] 5-5: Changing a column type requires an (changing-column-type) [warning] 6-6: Changing a column type requires an (changing-column-type) [warning] 7-7: Changing a column type requires an (changing-column-type) [warning] 8-8: Changing a column type requires an (changing-column-type) [warning] 10-10: Changing a column type requires an (changing-column-type) 🤖 Prompt for AI AgentsSource: Linters/SAST tools 🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift 🧩 Analysis chain🌐 Web query:
💡 Result: In PostgreSQL, you cannot directly alter a column's data type if it is involved in a foreign key constraint [1][2][3]. The database prevents this to ensure referential integrity, as the referenced and referencing columns must have compatible data types [2]. To change the data type of such a column, you must follow these steps [3]: 1. Drop the foreign key constraint from the referencing table [2][3]. 2. Alter the column type on both the referencing table and the referenced (parent) table as needed [3]. 3. Add the foreign key constraint back to the referencing table [3]. Example workflow: -- 1. Drop the existing foreign key constraint ALTER TABLE child_table DROP CONSTRAINT constraint_name; -- 2. Alter the columns to the new type -- Use a USING clause if an implicit cast is not possible ALTER TABLE parent_table ALTER COLUMN id TYPE new_type USING id::new_type; ALTER TABLE child_table ALTER COLUMN fk_column TYPE new_type USING fk_column::new_type; -- 3. Re-add the foreign key constraint ALTER TABLE child_table ADD CONSTRAINT constraint_name FOREIGN KEY (fk_column) REFERENCES parent_table(id); It is recommended to perform these operations within a single transaction (BEGIN...COMMIT) to maintain consistency throughout the process [2][4]. Note that if your column is also used by views or other objects, those dependencies may also need to be dropped and recreated similarly, as PostgreSQL often restricts structural changes to columns with such dependencies [5][4][6]. Citations:
🏁 Script executed: sed -n '1,220p' drizzle/migrations/0003_complex_sir_ram.sqlRepository: QueueLab/QCX Length of output: 1042 🏁 Script executed: rg -n --context 3 'calendar_notes|chat_participants|chats|locations|messages|prompt_generation_jobs|system_prompts|visualizations|users' drizzle src .Repository: QueueLab/QCX Length of output: 50371 🌐 Web query:
💡 Result: To alter a column type from uuid to text in PostgreSQL, you can use the ALTER TABLE command with a USING clause to specify the casting expression [1][2]. The syntax is as follows: ALTER TABLE table_name ALTER COLUMN column_name TYPE text USING column_name::text; In this command: - table_name is the name of your table [2]. - column_name is the name of the column you are modifying [2]. - The USING clause is required because PostgreSQL requires an explicit instruction for data conversion when it cannot automatically determine how to perform the cast, or when it requires specific casting behavior [1][3][2]. Key Considerations: - If your column has a default value, constraint, or index that depends on the current type, the ALTER TABLE operation might fail [1][3]. It is often best practice to drop such dependencies (like defaults or constraints) before altering the type and recreate them afterward [1][3]. - The USING clause allows for any valid SQL expression, meaning you can perform complex transformations if needed, not just simple casting [1][2]. - If you omit the USING clause and an implicit or assignment cast is not available, PostgreSQL will return an error and prompt you to provide one [1][2]. Citations:
Drop and recreate the FK constraints around this type change. PostgreSQL will reject these 🧰 Tools🪛 Squawk (2.59.0)[warning] 1-1: Changing a column type requires an (changing-column-type) [warning] 2-2: Changing a column type requires an (changing-column-type) [warning] 3-3: Changing a column type requires an (changing-column-type) [warning] 4-4: Changing a column type requires an (changing-column-type) [warning] 5-5: Changing a column type requires an (changing-column-type) [warning] 6-6: Changing a column type requires an (changing-column-type) [warning] 7-7: Changing a column type requires an (changing-column-type) [warning] 8-8: Changing a column type requires an (changing-column-type) [warning] 10-10: Changing a column type requires an (changing-column-type) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: QueueLab/QCX
Length of output: 4516
🏁 Script executed:
Repository: QueueLab/QCX
Length of output: 305
🏁 Script executed:
Repository: QueueLab/QCX
Length of output: 754
🏁 Script executed:
Repository: QueueLab/QCX
Length of output: 11902
🏁 Script executed:
Repository: QueueLab/QCX
Length of output: 11072
🌐 Web query:
Drizzle ORM undefined values insert where eq column undefined behavior💡 Result:
In Drizzle ORM, the handling of undefined values depends on the context of the query (e.g., updates, filters, or inserts) and can lead to specific behaviors that developers should be aware of to avoid logical or security issues. Insert and Update Operations: In Drizzle's.update.set operations, undefined values are explicitly ignored [1]. This allows you to update only selected fields in an object without accidentally overwriting other columns with null or undefined. To explicitly set a column to NULL, you must pass the null value directly [1]. For.insert.values, passing undefined can sometimes lead to type errors or unexpected behavior depending on the specific Drizzle version and configuration (e.g., TypeScript strict mode settings) [2][3][4]. There have been reported issues where Drizzle may include columns in an INSERT statement that were not provided in.values [5]. Filters and Conditions (eq): When using query builders like.where or relational queries, Drizzle's operator functions such as and and or are designed to automatically ignore undefined conditions [6]. While this is convenient for building dynamic queries, it can be a source of unintended behavior if a filter that was intended to be restrictive is passed an undefined value, effectively becoming an unconstrained query [7]. For example, if you use a filter like { token: undefined } in a relational query, Drizzle may skip that field, potentially returning data that should have been restricted [7]. Recommendations: 1. Data Sanitization: Do not pass untrusted or potentially undefined input directly into filters [7]. Ensure that you validate or filter your inputs before passing them to query methods. 2. Explicit Nulls: If you intend to set a database column to NULL, always use the null literal rather than undefined [1]. 3. Configuration: Ensure your project has TypeScript's strict mode enabled (specifically strictNullChecks), as this often resolves type-inference issues related to nullable columns and undefined values in Drizzle [3][4]. 4. Defensive Querying: When building dynamic filters, explicitly check for the presence of values rather than relying solely on Drizzle's automatic skipping of undefined to ensure your business logic remains secure [7].
Citations:
undefinedas the field value into insert.values() / update.set() drizzle-team/drizzle-orm#375undefinedvalues can lead to security vulnerabilities drizzle-team/drizzle-orm#5636🌐 Web query:
Drizzle ORM eq undefined semantics where clause nullable column💡 Result:
In Drizzle ORM, the semantics of
undefinedinWHEREclauses vary significantly depending on whether you are using SQL-like (builder) syntax or Relational Query Builder (RQB) syntax [1][2]. SQL Builder (e.g.,db.select().where(...)) When using the core SQL builder withand()oror()functions,undefinedis explicitly treated as a "no-op" [3][4]. These functions filter outundefinedvalues from their arguments, allowing for easy conditional query building [4][5][6]. Passingundefinedtoeq()directly as a parameter may result in runtime errors or unexpected SQL generation, as SQL has no concept ofundefined[1]. To check forNULLvalues in a column, you should use theisNull()operator rather thaneq(column, null)oreq(column, undefined)[3][4]. Relational Query Builder (RQB) In RQB, providingundefinedas a value in a filter object may cause Drizzle to skip that field entirely [2]. This behavior has been identified as a potential security risk, as it effectively removes the filter instead of applying it, which may result in unintended queries (e.g., returning all records instead of a filtered subset) [2]. Summary of Best Practices 1. Conditional Filtering: Useand()oror()wrappers to build queries conditionally [5]. These wrappers are designed to ignoreundefinedinputs, making them the standard way to handle optional filters [3][4]. 2. Checking for NULL: Always use the dedicatedisNull()orisNotNull()operators when querying for null-valued columns, as SQL behavior forNULLequality is not the same as standard value comparison [3][4]. 3. Security Warning: Exercise caution when passing untrusted user input into RQB filter objects, asundefinedmay be treated as "no filter applied" rather than "filter by null" [2]. Always validate that your filters contain the expected values before passing them to the query builder [2].Citations:
undefinedvalues can lead to security vulnerabilities drizzle-team/drizzle-orm#5636Guard the Clerk email branch when no email is present.
email_addresses[0]?.email_addresscan be undefined;eq(users.email, email)is unsafe with that value. Bail out or skip the email-based branch before looking up by email.🤖 Prompt for AI Agents