added full api
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
import { db } from '../db/client.js';
|
||||
import type { BillingPlan, UserRow, WorkspaceMemberRow, WorkspaceRow, WorkspaceType } from '../types.js';
|
||||
|
||||
export const getNextWorkspaceId = async () => {
|
||||
const result = await db.query<{ next_id: number }>('SELECT COALESCE(MAX(id), 0) + 1 AS next_id FROM workspaces');
|
||||
return Number(result.rows[0]?.next_id ?? 1);
|
||||
};
|
||||
|
||||
export const getWorkspaceById = async (workspaceId: number) => {
|
||||
const result = await db.query<WorkspaceRow>(
|
||||
`SELECT id, name, workspace_type, billing_email, billing_plan, created_at, updated_at
|
||||
FROM workspaces
|
||||
WHERE id = $1`,
|
||||
[workspaceId],
|
||||
);
|
||||
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const getMembershipForUser = async (userId: string, workspaceId: number) => {
|
||||
const result = await db.query<WorkspaceMemberRow>(
|
||||
`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`,
|
||||
[workspaceId, userId],
|
||||
);
|
||||
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const listMembershipsForUser = async (userId: string) => {
|
||||
const result = await db.query<
|
||||
WorkspaceMemberRow & {
|
||||
workspace_name: string;
|
||||
workspace_type: WorkspaceType;
|
||||
billing_email: string | null;
|
||||
billing_plan: BillingPlan;
|
||||
workspace_created_at: string;
|
||||
workspace_updated_at: string;
|
||||
}
|
||||
>(
|
||||
`SELECT
|
||||
workspace_members.id,
|
||||
workspace_members.workspace_id,
|
||||
workspace_members.user_id,
|
||||
COALESCE(workspace_members.invite_email, workspace_members.email) AS invite_email,
|
||||
workspace_members.name,
|
||||
workspace_members.role,
|
||||
workspace_members.accepted_at::text,
|
||||
workspace_members.created_at,
|
||||
workspaces.name AS workspace_name,
|
||||
workspaces.workspace_type,
|
||||
workspaces.billing_email,
|
||||
workspaces.billing_plan,
|
||||
workspaces.created_at AS workspace_created_at,
|
||||
workspaces.updated_at AS workspace_updated_at
|
||||
FROM workspace_members
|
||||
INNER JOIN workspaces ON workspaces.id = workspace_members.workspace_id
|
||||
WHERE workspace_members.user_id = $1
|
||||
ORDER BY workspaces.created_at ASC`,
|
||||
[userId],
|
||||
);
|
||||
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
export const ensurePersonalWorkspaceForUser = async (user: UserRow) => {
|
||||
const existing = await db.query<{ workspace_id: number }>(
|
||||
`SELECT workspace_id
|
||||
FROM workspace_members
|
||||
INNER JOIN workspaces ON workspaces.id = workspace_members.workspace_id
|
||||
WHERE workspace_members.user_id = $1
|
||||
AND workspaces.workspace_type = 'standard'
|
||||
ORDER BY workspaces.created_at ASC
|
||||
LIMIT 1`,
|
||||
[user.id],
|
||||
);
|
||||
|
||||
if (existing.rowCount) {
|
||||
return Number(existing.rows[0].workspace_id);
|
||||
}
|
||||
|
||||
const unclaimed = await db.query<{ workspace_id: number }>(
|
||||
`SELECT workspaces.id AS workspace_id
|
||||
FROM workspaces
|
||||
LEFT JOIN workspace_members ON workspace_members.workspace_id = workspaces.id
|
||||
WHERE workspaces.id = 1
|
||||
GROUP BY workspaces.id
|
||||
HAVING COUNT(workspace_members.id) = 0
|
||||
LIMIT 1`,
|
||||
);
|
||||
|
||||
const workspaceId = unclaimed.rowCount ? Number(unclaimed.rows[0].workspace_id) : await getNextWorkspaceId();
|
||||
|
||||
if (!unclaimed.rowCount) {
|
||||
await db.query(
|
||||
`INSERT INTO workspaces (id, name, workspace_type, billing_plan, billing_email)
|
||||
VALUES ($1, $2, 'standard', 'household_basic', $3)`,
|
||||
[workspaceId, `${user.name}'s Flock`, user.email],
|
||||
);
|
||||
} else {
|
||||
await db.query(
|
||||
`UPDATE workspaces
|
||||
SET name = $2,
|
||||
workspace_type = 'standard',
|
||||
billing_plan = 'household_basic',
|
||||
billing_email = $3,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1`,
|
||||
[workspaceId, `${user.name}'s Flock`, user.email],
|
||||
);
|
||||
}
|
||||
|
||||
await db.query(
|
||||
`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`,
|
||||
[workspaceId, user.id, user.email, user.name],
|
||||
);
|
||||
|
||||
return workspaceId;
|
||||
};
|
||||
|
||||
export const claimWorkspaceInvites = async (user: UserRow) => {
|
||||
await db.query(
|
||||
`UPDATE workspace_members
|
||||
SET user_id = $1,
|
||||
accepted_at = CURRENT_TIMESTAMP
|
||||
WHERE LOWER(COALESCE(invite_email, email)) = LOWER($2)
|
||||
AND user_id IS NULL`,
|
||||
[user.id, user.email],
|
||||
);
|
||||
};
|
||||
|
||||
export const createWorkspace = async ({
|
||||
id,
|
||||
name,
|
||||
workspaceType,
|
||||
billingEmail,
|
||||
billingPlan,
|
||||
owner,
|
||||
}: {
|
||||
id: number;
|
||||
name: string;
|
||||
workspaceType: WorkspaceType;
|
||||
billingEmail: string | null;
|
||||
billingPlan: BillingPlan;
|
||||
owner: UserRow;
|
||||
}) => {
|
||||
await db.query(
|
||||
`INSERT INTO workspaces (id, name, workspace_type, billing_email, billing_plan)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[id, name, workspaceType, billingEmail, billingPlan],
|
||||
);
|
||||
|
||||
await db.query(
|
||||
`INSERT INTO workspace_members (workspace_id, user_id, email, invite_email, name, role, accepted_at)
|
||||
VALUES ($1, $2, $3, $3, $4, 'owner', CURRENT_TIMESTAMP)`,
|
||||
[id, owner.id, owner.email, owner.name],
|
||||
);
|
||||
|
||||
return getWorkspaceById(id);
|
||||
};
|
||||
|
||||
export const updateWorkspace = async ({
|
||||
workspaceId,
|
||||
name,
|
||||
workspaceType,
|
||||
billingEmail,
|
||||
billingPlan,
|
||||
}: {
|
||||
workspaceId: number;
|
||||
name: string;
|
||||
workspaceType: WorkspaceType;
|
||||
billingEmail: string | null;
|
||||
billingPlan: BillingPlan;
|
||||
}) => {
|
||||
const result = await db.query<WorkspaceRow>(
|
||||
`UPDATE workspaces
|
||||
SET name = $2,
|
||||
workspace_type = $3,
|
||||
billing_email = $4,
|
||||
billing_plan = $5,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
RETURNING id, name, workspace_type, billing_email, billing_plan, created_at, updated_at`,
|
||||
[workspaceId, name, workspaceType, billingEmail, billingPlan],
|
||||
);
|
||||
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const listWorkspaceMembers = async (workspaceId: number) => {
|
||||
const result = await db.query<WorkspaceMemberRow>(
|
||||
`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`,
|
||||
[workspaceId],
|
||||
);
|
||||
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
export const upsertWorkspaceMember = async ({
|
||||
workspaceId,
|
||||
inviteEmail,
|
||||
name,
|
||||
role,
|
||||
existingUser,
|
||||
}: {
|
||||
workspaceId: number;
|
||||
inviteEmail: string;
|
||||
name: string;
|
||||
role: WorkspaceMemberRow['role'];
|
||||
existingUser: UserRow | null;
|
||||
}) => {
|
||||
const result = await db.query<WorkspaceMemberRow>(
|
||||
`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, COALESCE(invite_email, email) AS invite_email, name, role, accepted_at::text, created_at`,
|
||||
[workspaceId, existingUser?.id ?? null, inviteEmail, name, role, existingUser ? new Date().toISOString() : null],
|
||||
);
|
||||
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const deleteWorkspaceMember = async (memberId: string, workspaceId: number) => {
|
||||
const result = await db.query<{ id: string }>(
|
||||
`DELETE FROM workspace_members
|
||||
WHERE id = $1
|
||||
AND workspace_id = $2
|
||||
AND role <> 'owner'
|
||||
RETURNING id`,
|
||||
[memberId, workspaceId],
|
||||
);
|
||||
|
||||
return Boolean(result.rowCount);
|
||||
};
|
||||
Reference in New Issue
Block a user