Added admin mode, read only status for inactive accounts, and resuce verification
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { db } from '../db/client.js';
|
||||
import type { BillingPlan, UserRow, WorkspaceMemberRow, WorkspaceRow, WorkspaceType } from '../types.js';
|
||||
import type { BillingPlan, RescueVerificationStatus, 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');
|
||||
@@ -8,7 +8,7 @@ export const getNextWorkspaceId = async () => {
|
||||
|
||||
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
|
||||
`SELECT id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at
|
||||
FROM workspaces
|
||||
WHERE id = $1`,
|
||||
[workspaceId],
|
||||
@@ -36,6 +36,8 @@ export const listMembershipsForUser = async (userId: string) => {
|
||||
workspace_type: WorkspaceType;
|
||||
billing_email: string | null;
|
||||
billing_plan: BillingPlan;
|
||||
subscription_status: WorkspaceRow['subscription_status'];
|
||||
rescue_verification_status: RescueVerificationStatus;
|
||||
workspace_created_at: string;
|
||||
workspace_updated_at: string;
|
||||
}
|
||||
@@ -53,6 +55,8 @@ export const listMembershipsForUser = async (userId: string) => {
|
||||
workspaces.workspace_type,
|
||||
workspaces.billing_email,
|
||||
workspaces.billing_plan,
|
||||
workspaces.subscription_status,
|
||||
workspaces.rescue_verification_status,
|
||||
workspaces.created_at AS workspace_created_at,
|
||||
workspaces.updated_at AS workspace_updated_at
|
||||
FROM workspace_members
|
||||
@@ -95,8 +99,8 @@ export const ensurePersonalWorkspaceForUser = async (user: UserRow) => {
|
||||
|
||||
if (!unclaimed.rowCount) {
|
||||
await db.query(
|
||||
`INSERT INTO workspaces (id, name, workspace_type, billing_plan, billing_email)
|
||||
VALUES ($1, $2, 'standard', 'household_basic', $3)`,
|
||||
`INSERT INTO workspaces (id, name, workspace_type, billing_plan, billing_email, subscription_status, rescue_verification_status)
|
||||
VALUES ($1, $2, 'standard', 'household_basic', $3, 'active', 'not_required')`,
|
||||
[workspaceId, `${user.name}'s Flock`, user.email],
|
||||
);
|
||||
} else {
|
||||
@@ -106,6 +110,8 @@ export const ensurePersonalWorkspaceForUser = async (user: UserRow) => {
|
||||
workspace_type = 'standard',
|
||||
billing_plan = 'household_basic',
|
||||
billing_email = $3,
|
||||
subscription_status = 'active',
|
||||
rescue_verification_status = 'not_required',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1`,
|
||||
[workspaceId, `${user.name}'s Flock`, user.email],
|
||||
@@ -154,9 +160,17 @@ export const createWorkspace = async ({
|
||||
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],
|
||||
`INSERT INTO workspaces (id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[
|
||||
id,
|
||||
name,
|
||||
workspaceType,
|
||||
billingEmail,
|
||||
billingPlan,
|
||||
workspaceType === 'rescue' ? 'active' : 'active',
|
||||
workspaceType === 'rescue' ? 'pending' : 'not_required',
|
||||
],
|
||||
);
|
||||
|
||||
await db.query(
|
||||
@@ -187,9 +201,14 @@ export const updateWorkspace = async ({
|
||||
workspace_type = $3,
|
||||
billing_email = $4,
|
||||
billing_plan = $5,
|
||||
rescue_verification_status = CASE
|
||||
WHEN $3 = 'rescue' AND rescue_verification_status = 'not_required' THEN 'pending'
|
||||
WHEN $3 = 'standard' THEN 'not_required'
|
||||
ELSE rescue_verification_status
|
||||
END,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
RETURNING id, name, workspace_type, billing_email, billing_plan, created_at, updated_at`,
|
||||
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at`,
|
||||
[workspaceId, name, workspaceType, billingEmail, billingPlan],
|
||||
);
|
||||
|
||||
@@ -249,3 +268,80 @@ export const deleteWorkspaceMember = async (memberId: string, workspaceId: numbe
|
||||
|
||||
return Boolean(result.rowCount);
|
||||
};
|
||||
|
||||
export const listRescueWorkspacesForAdmin = async () => {
|
||||
const result = await db.query<
|
||||
WorkspaceRow & {
|
||||
owner_email: string | null;
|
||||
bird_count: number;
|
||||
member_count: number;
|
||||
}
|
||||
>(
|
||||
`SELECT
|
||||
workspaces.id,
|
||||
workspaces.name,
|
||||
workspaces.workspace_type,
|
||||
workspaces.billing_email,
|
||||
workspaces.billing_plan,
|
||||
workspaces.subscription_status,
|
||||
workspaces.rescue_verification_status,
|
||||
workspaces.created_at,
|
||||
workspaces.updated_at,
|
||||
owner.invite_email AS owner_email,
|
||||
COUNT(DISTINCT birds.id)::int AS bird_count,
|
||||
COUNT(DISTINCT workspace_members.id)::int AS member_count
|
||||
FROM workspaces
|
||||
LEFT JOIN workspace_members owner
|
||||
ON owner.workspace_id = workspaces.id
|
||||
AND owner.role = 'owner'
|
||||
LEFT JOIN birds ON birds.workspace_id = workspaces.id
|
||||
LEFT JOIN workspace_members ON workspace_members.workspace_id = workspaces.id
|
||||
WHERE workspaces.workspace_type = 'rescue'
|
||||
GROUP BY workspaces.id, owner.invite_email
|
||||
ORDER BY
|
||||
CASE workspaces.rescue_verification_status
|
||||
WHEN 'pending' THEN 0
|
||||
WHEN 'approved' THEN 1
|
||||
WHEN 'rejected' THEN 2
|
||||
ELSE 3
|
||||
END,
|
||||
workspaces.created_at DESC`,
|
||||
);
|
||||
|
||||
return result.rows;
|
||||
};
|
||||
|
||||
export const updateRescueVerificationStatus = async (workspaceId: number, status: RescueVerificationStatus) => {
|
||||
const result = await db.query<WorkspaceRow>(
|
||||
`UPDATE workspaces
|
||||
SET rescue_verification_status = $2,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
AND workspace_type = 'rescue'
|
||||
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at`,
|
||||
[workspaceId, status],
|
||||
);
|
||||
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
|
||||
export const getPlatformAdminSummary = async () => {
|
||||
const result = await db.query<{
|
||||
total_birds: number;
|
||||
total_users: number;
|
||||
total_workspaces: number;
|
||||
rescue_workspaces: number;
|
||||
pending_rescues: number;
|
||||
daily_users: number;
|
||||
}>(
|
||||
`SELECT
|
||||
(SELECT COUNT(*)::int FROM birds) AS total_birds,
|
||||
(SELECT COUNT(*)::int FROM users) AS total_users,
|
||||
(SELECT COUNT(*)::int FROM workspaces) AS total_workspaces,
|
||||
(SELECT COUNT(*)::int FROM workspaces WHERE workspace_type = 'rescue') AS rescue_workspaces,
|
||||
(SELECT COUNT(*)::int FROM workspaces WHERE workspace_type = 'rescue' AND rescue_verification_status = 'pending') AS pending_rescues,
|
||||
(SELECT COUNT(DISTINCT user_id)::int FROM auth_sessions WHERE created_at >= CURRENT_DATE) AS daily_users`,
|
||||
);
|
||||
|
||||
return result.rows[0];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user