Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
2.7 kB ยท 73 lines
TypeScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374import { and, eq } from "@openstatus/db";import { usersToWorkspaces } from "@openstatus/db/src/schema";
import { requireScope } from "../auth";import { type ServiceContext, withTransaction } from "../context";import { NotFoundError, PreconditionFailedError } from "../errors";import { removeMemberInWorkspace } from "./internal";import { DeleteMemberInput } from "./schemas";
/** * Delete a member's workspace association. Idempotent on the target row โ * if the target user has no membership in the caller's workspace (wrong id, * already removed, or scoped to a different workspace), the call succeeds * silently and no audit row is emitted. This matches the workspace-filtered * DELETE semantics inherited from the legacy router. * * Authorization rules preserved from the legacy router: * - only the caller's own membership row is consulted; the `owner` role is * required to remove anyone else; * - an owner cannot remove themselves (would orphan the workspace). * * Both role-failure and self-removal surface as `PRECONDITION_FAILED` to * match the existing tRPC contract โ these are state-based rejections, not * authz failures in the sense of "you lack any access to this workspace." * * Only fires when the actor is an openstatus user: removing another user is * not something a system / apiKey / webhook actor should do today. */// The delete and its `member.delete` audit row live in// `removeMemberInWorkspace`; this verb only adds the owner / self-removal guards.// oxlint-disable-next-line openstatus/services-mutation-guardsexport async function deleteMember(args: { ctx: ServiceContext; input: DeleteMemberInput;}): Promise<void> { const { ctx } = args; requireScope(ctx, "write"); const input = DeleteMemberInput.parse(args.input);
if (ctx.actor.type !== "user") { throw new PreconditionFailedError( "Only user actors can remove workspace members", ); } const callerId = ctx.actor.userId;
await withTransaction(ctx, async (tx) => { const caller = await tx.query.usersToWorkspaces.findFirst({ where: and( eq(usersToWorkspaces.userId, callerId), eq(usersToWorkspaces.workspaceId, ctx.workspace.id), ), });
if (!caller) { throw new NotFoundError("membership", callerId); }
if (caller.role !== "owner") { throw new PreconditionFailedError( "Not authorized to remove user from workspace", ); }
if (input.userId === callerId) { throw new PreconditionFailedError( "Cannot remove yourself from workspace", ); }
await removeMemberInWorkspace({ tx, ctx, userId: input.userId }); });}