add + remove users from organisation

This commit is contained in:
Oliver Bryan
2026-01-01 10:46:54 +00:00
parent 8511c6472c
commit 63fef4a0e9
20 changed files with 560 additions and 32 deletions

View File

@@ -23,6 +23,7 @@ import projectDelete from "./project/delete";
import projectUpdate from "./project/update";
import projectWithCreator from "./project/with-creator";
import projectsWithCreators from "./project/with-creators";
import userByUsername from "./user/by-username";
import userUpdate from "./user/update";
import userUploadAvatar from "./user/upload-avatar";
@@ -31,6 +32,7 @@ export const routes = {
authLogin,
authMe,
userByUsername,
userUpdate,
userUploadAvatar,

View File

@@ -1,8 +1,13 @@
import type { BunRequest } from "bun";
import { createOrganisationMember, getOrganisationById, getUserById } from "../../db/queries";
import type { AuthedRequest } from "../../auth/middleware";
import {
createOrganisationMember,
getOrganisationById,
getOrganisationMemberRole,
getUserById,
} from "../../db/queries";
// /organisation/add-member?organisationId=1&userId=2&role=member
export default async function organisationAddMember(req: BunRequest) {
export default async function organisationAddMember(req: AuthedRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
const userId = url.searchParams.get("userId");
@@ -32,6 +37,20 @@ export default async function organisationAddMember(req: BunRequest) {
return new Response(`user with id ${userId} not found`, { status: 404 });
}
const existingMember = await getOrganisationMemberRole(orgIdNumber, userIdNumber);
if (existingMember) {
return new Response("User is already a member of this organisation", { status: 409 });
}
const requesterMember = await getOrganisationMemberRole(orgIdNumber, req.userId);
if (!requesterMember) {
return new Response("You are not a member of this organisation", { status: 403 });
}
if (requesterMember.role !== "owner" && requesterMember.role !== "admin") {
return new Response("Only owners and admins can add members", { status: 403 });
}
const member = await createOrganisationMember(orgIdNumber, userIdNumber, role);
return Response.json(member);

View File

@@ -1,8 +1,8 @@
import type { BunRequest } from "bun";
import { getOrganisationById, getUserById, removeOrganisationMember } from "../../db/queries";
import type { AuthedRequest } from "../../auth/middleware";
import { getOrganisationById, getOrganisationMemberRole, removeOrganisationMember } from "../../db/queries";
// /organisation/remove-member?organisationId=1&userId=2
export default async function organisationRemoveMember(req: BunRequest) {
export default async function organisationRemoveMember(req: AuthedRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
const userId = url.searchParams.get("userId");
@@ -26,9 +26,22 @@ export default async function organisationRemoveMember(req: BunRequest) {
return new Response(`organisation with id ${organisationId} not found`, { status: 404 });
}
const user = await getUserById(userIdNumber);
if (!user) {
return new Response(`user with id ${userId} not found`, { status: 404 });
const memberToRemove = await getOrganisationMemberRole(orgIdNumber, userIdNumber);
if (!memberToRemove) {
return new Response("User is not a member of this organisation", { status: 404 });
}
if (memberToRemove.role === "owner") {
return new Response("Cannot remove the organisation owner", { status: 403 });
}
const requesterMember = await getOrganisationMemberRole(orgIdNumber, req.userId);
if (!requesterMember) {
return new Response("You are not a member of this organisation", { status: 403 });
}
if (requesterMember.role !== "owner" && requesterMember.role !== "admin") {
return new Response("Only owners and admins can remove members", { status: 403 });
}
await removeOrganisationMember(orgIdNumber, userIdNumber);

View File

@@ -0,0 +1,19 @@
import type { BunRequest } from "bun";
import { getUserByUsername } from "../../db/queries";
// /user/by-username?username=someusername
export default async function userByUsername(req: BunRequest) {
const url = new URL(req.url);
const username = url.searchParams.get("username");
if (!username) {
return new Response("username is required", { status: 400 });
}
const user = await getUserByUsername(username);
if (!user) {
return new Response(`User with username '${username}' not found`, { status: 404 });
}
return Response.json(user);
}