Fixed auth and regained account access

This commit is contained in:
blaisadmin
2026-04-09 00:04:48 -04:00
parent a7d70cb177
commit 5b0304ee93
2 changed files with 59 additions and 12 deletions
+45 -12
View File
@@ -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<WorkspaceMemberRow>(
`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<WorkspaceMemberRow>(
`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<WorkspaceMemberRow>(
`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,
+14
View File
@@ -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