From 5b0304ee938509f645ae9443a7fbad862f323e5d Mon Sep 17 00:00:00 2001 From: blaisadmin Date: Thu, 9 Apr 2026 00:04:48 -0400 Subject: [PATCH] Fixed auth and regained account access --- backend/src/app.ts | 57 ++++++++++++++++++++++++++++++++++++---------- docker-compose.yml | 14 ++++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/backend/src/app.ts b/backend/src/app.ts index d483171..bf494cd 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -452,6 +452,37 @@ const ensureSchema = async () => { created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); + ALTER TABLE workspace_members + ADD COLUMN IF NOT EXISTS email VARCHAR(255), + ADD COLUMN IF NOT EXISTS user_id UUID REFERENCES users(id) ON DELETE CASCADE, + ADD COLUMN IF NOT EXISTS invite_email VARCHAR(255), + ADD COLUMN IF NOT EXISTS accepted_at TIMESTAMPTZ; + + DO $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name = 'workspace_members' + AND column_name = 'email' + ) THEN + UPDATE workspace_members + SET invite_email = COALESCE(invite_email, email) + WHERE invite_email IS NULL; + END IF; + END $$; + + UPDATE workspace_members + SET invite_email = '' + WHERE invite_email IS NULL; + + UPDATE workspace_members + SET email = invite_email + WHERE email IS NULL; + + ALTER TABLE workspace_members + ALTER COLUMN invite_email SET NOT NULL; + CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_members_workspace_email ON workspace_members (workspace_id, invite_email); @@ -586,7 +617,7 @@ const getWorkspaceById = async (workspaceId: number) => { const getMembershipForUser = async (userId: string, workspaceId: number) => { const result = await pool.query( - `SELECT id, workspace_id, user_id, invite_email, name, role, accepted_at::text, created_at + `SELECT id, workspace_id, user_id, COALESCE(invite_email, email) AS invite_email, name, role, accepted_at::text, created_at FROM workspace_members WHERE workspace_id = $1 AND user_id = $2`, @@ -611,7 +642,7 @@ const listMembershipsForUser = async (userId: string) => { workspace_members.id, workspace_members.workspace_id, workspace_members.user_id, - workspace_members.invite_email, + COALESCE(workspace_members.invite_email, workspace_members.email) AS invite_email, workspace_members.name, workspace_members.role, workspace_members.accepted_at::text, @@ -691,10 +722,11 @@ const ensurePersonalWorkspaceForUser = async (user: UserRow) => { } await pool.query( - `INSERT INTO workspace_members (workspace_id, user_id, invite_email, name, role, accepted_at) - VALUES ($1, $2, $3, $4, 'owner', CURRENT_TIMESTAMP) + `INSERT INTO workspace_members (workspace_id, user_id, email, invite_email, name, role, accepted_at) + VALUES ($1, $2, $3, $3, $4, 'owner', CURRENT_TIMESTAMP) ON CONFLICT (workspace_id, invite_email) DO UPDATE SET user_id = EXCLUDED.user_id, + email = EXCLUDED.email, name = EXCLUDED.name, role = 'owner', accepted_at = CURRENT_TIMESTAMP`, @@ -709,7 +741,7 @@ const claimWorkspaceInvites = async (user: UserRow) => { `UPDATE workspace_members SET user_id = $1, accepted_at = CURRENT_TIMESTAMP - WHERE LOWER(invite_email) = LOWER($2) + WHERE LOWER(COALESCE(invite_email, email)) = LOWER($2) AND user_id IS NULL`, [user.id, user.email], ); @@ -862,7 +894,7 @@ const resolveAuth = async (token: string) => { workspace_members.id AS membership_id_row, workspace_members.workspace_id AS membership_workspace_id, workspace_members.user_id AS membership_user_id, - workspace_members.invite_email AS membership_invite_email, + COALESCE(workspace_members.invite_email, workspace_members.email) AS membership_invite_email, workspace_members.name AS membership_name, workspace_members.role AS membership_role, workspace_members.accepted_at::text AS membership_accepted_at, @@ -1390,8 +1422,8 @@ app.post('/api/workspaces', requireAuth, async (req: Request, res: Response, nex ); await pool.query( - `INSERT INTO workspace_members (workspace_id, user_id, invite_email, name, role, accepted_at) - VALUES ($1, $2, $3, $4, 'owner', CURRENT_TIMESTAMP)`, + `INSERT INTO workspace_members (workspace_id, user_id, email, invite_email, name, role, accepted_at) + VALUES ($1, $2, $3, $3, $4, 'owner', CURRENT_TIMESTAMP)`, [workspaceId, req.auth!.user.id, req.auth!.user.email, req.auth!.user.name], ); @@ -1438,7 +1470,7 @@ app.put('/api/workspace', requireAuth, requireWorkspaceRole(['owner', 'manager'] app.get('/api/workspace/members', requireAuth, async (req: Request, res: Response, next: NextFunction) => { try { const result = await pool.query( - `SELECT id, workspace_id, user_id, invite_email, name, role, accepted_at::text, created_at + `SELECT id, workspace_id, user_id, COALESCE(invite_email, email) AS invite_email, name, role, accepted_at::text, created_at FROM workspace_members WHERE workspace_id = $1 ORDER BY created_at ASC`, @@ -1470,14 +1502,15 @@ app.post('/api/workspace/members', requireAuth, requireWorkspaceRole(['owner', ' const existingUserRow = existingUser.rows[0]; const result = await pool.query( - `INSERT INTO workspace_members (workspace_id, user_id, invite_email, name, role, accepted_at) - VALUES ($1, $2, $3, $4, $5, $6) + `INSERT INTO workspace_members (workspace_id, user_id, email, invite_email, name, role, accepted_at) + VALUES ($1, $2, $3, $3, $4, $5, $6) ON CONFLICT (workspace_id, invite_email) DO UPDATE SET name = EXCLUDED.name, role = EXCLUDED.role, + email = EXCLUDED.email, user_id = COALESCE(workspace_members.user_id, EXCLUDED.user_id), accepted_at = COALESCE(workspace_members.accepted_at, EXCLUDED.accepted_at) - RETURNING id, workspace_id, user_id, invite_email, name, role, accepted_at::text, created_at`, + RETURNING id, workspace_id, user_id, COALESCE(invite_email, email) AS invite_email, name, role, accepted_at::text, created_at`, [ req.auth!.workspace.id, existingUserRow?.id ?? null, diff --git a/docker-compose.yml b/docker-compose.yml index 39ffa93..0b688d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,20 @@ services: POSTGRES_USER: ${POSTGRES_USER:-flockpal} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-flockpal_dev_password} FRONTEND_URL: ${FRONTEND_URL:-http://localhost:3000} + BACKEND_URL: ${BACKEND_URL:-http://localhost:5000} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} + MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} + MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} + APPLE_CLIENT_ID: ${APPLE_CLIENT_ID:-} + APPLE_CLIENT_SECRET: ${APPLE_CLIENT_SECRET:-} + SMTP_HOST: ${SMTP_HOST:-} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_SECURE: ${SMTP_SECURE:-false} + SMTP_USER: ${SMTP_USER:-} + SMTP_PASS: ${SMTP_PASS:-} + SMTP_FROM_EMAIL: ${SMTP_FROM_EMAIL:-} + SMTP_FROM_NAME: ${SMTP_FROM_NAME:-FlockPal} depends_on: postgres: condition: service_healthy