Organisation and OrganisationMember routes

This commit is contained in:
Oliver Bryan
2025-12-22 20:23:16 +00:00
parent 92866f1017
commit 474a929ffa
12 changed files with 307 additions and 1 deletions

View File

@@ -23,6 +23,16 @@ const main = async () => {
"/issues/:projectBlob": withCors(withAuth(routes.issuesInProject)), "/issues/:projectBlob": withCors(withAuth(routes.issuesInProject)),
"/issues/all": withCors(withAuth(routes.issues)), "/issues/all": withCors(withAuth(routes.issues)),
"/organisation/create": withCors(withAuth(routes.organisationCreate)),
"/organisation/by-id": withCors(withAuth(routes.organisationById)),
"/organisation/by-user": withCors(withAuth(routes.organisationByUser)),
"/organisation/update": withCors(withAuth(routes.organisationUpdate)),
"/organisation/delete": withCors(withAuth(routes.organisationDelete)),
"/organisation/add-member": withCors(withAuth(routes.organisationAddMember)),
"/organisation/members": withCors(withAuth(routes.organisationMembers)),
"/organisation/remove-member": withCors(withAuth(routes.organisationRemoveMember)),
"/organisation/update-member-role": withCors(withAuth(routes.organisationUpdateMemberRole)),
"/project/create": withCors(withAuth(routes.projectCreate)), "/project/create": withCors(withAuth(routes.projectCreate)),
"/project/update": withCors(withAuth(routes.projectUpdate)), "/project/update": withCors(withAuth(routes.projectUpdate)),
"/project/delete": withCors(withAuth(routes.projectDelete)), "/project/delete": withCors(withAuth(routes.projectDelete)),

View File

@@ -6,6 +6,15 @@ import issueDelete from "./issue/delete";
import issueUpdate from "./issue/update"; import issueUpdate from "./issue/update";
import issuesInProject from "./issues/[projectBlob]"; import issuesInProject from "./issues/[projectBlob]";
import issues from "./issues/all"; import issues from "./issues/all";
import organisationAddMember from "./organisation/add-member";
import organisationById from "./organisation/by-id";
import organisationByUser from "./organisation/by-user";
import organisationCreate from "./organisation/create";
import organisationDelete from "./organisation/delete";
import organisationMembers from "./organisation/members";
import organisationRemoveMember from "./organisation/remove-member";
import organisationUpdate from "./organisation/update";
import organisationUpdateMemberRole from "./organisation/update-member-role";
import projectsAll from "./project/all"; import projectsAll from "./project/all";
import projectsByCreator from "./project/by-creator"; import projectsByCreator from "./project/by-creator";
import projectCreate from "./project/create"; import projectCreate from "./project/create";
@@ -22,6 +31,16 @@ export const routes = {
issuesInProject, issuesInProject,
issues, issues,
organisationCreate,
organisationById,
organisationByUser,
organisationUpdate,
organisationDelete,
organisationAddMember,
organisationMembers,
organisationRemoveMember,
organisationUpdateMemberRole,
projectCreate, projectCreate,
projectUpdate, projectUpdate,
projectDelete, projectDelete,

View File

@@ -0,0 +1,38 @@
import type { BunRequest } from "bun";
import { createOrganisationMember, getOrganisationById, getUserById } from "../../db/queries";
// /organisation/add-member?organisationId=1&userId=2&role=member
export default async function organisationAddMember(req: BunRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
const userId = url.searchParams.get("userId");
const role = url.searchParams.get("role") || "member";
if (!organisationId || !userId) {
return new Response(
`missing parameters: ${!organisationId ? "organisationId " : ""}${!userId ? "userId" : ""}`,
{ status: 400 },
);
}
const orgIdNumber = Number(organisationId);
const userIdNumber = Number(userId);
if (!Number.isInteger(orgIdNumber) || !Number.isInteger(userIdNumber)) {
return new Response("organisationId and userId must be integers", { status: 400 });
}
const organisation = await getOrganisationById(orgIdNumber);
if (!organisation) {
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 member = await createOrganisationMember(orgIdNumber, userIdNumber, role);
return Response.json(member);
}

View File

@@ -0,0 +1,24 @@
import type { BunRequest } from "bun";
import { getOrganisationById } from "../../db/queries";
// /organisation/by-id?id=1
export default async function organisationById(req: BunRequest) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (!id) {
return new Response("organisation id is required", { status: 400 });
}
const organisationId = Number(id);
if (!Number.isInteger(organisationId)) {
return new Response("organisation id must be an integer", { status: 400 });
}
const organisation = await getOrganisationById(organisationId);
if (!organisation) {
return new Response(`organisation with id ${id} not found`, { status: 404 });
}
return Response.json(organisation);
}

View File

@@ -0,0 +1,26 @@
import type { BunRequest } from "bun";
import { getOrganisationsByUserId, getUserById } from "../../db/queries";
// /organisation/by-user?userId=1
export default async function organisationsByUser(req: BunRequest) {
const url = new URL(req.url);
const userId = url.searchParams.get("userId");
if (!userId) {
return new Response("userId is required", { status: 400 });
}
const userIdNumber = Number(userId);
if (!Number.isInteger(userIdNumber)) {
return new Response("userId must be an integer", { status: 400 });
}
const user = await getUserById(userIdNumber);
if (!user) {
return new Response(`user with id ${userId} not found`, { status: 404 });
}
const organisations = await getOrganisationsByUserId(user.id);
return Response.json(organisations);
}

View File

@@ -0,0 +1,23 @@
import type { BunRequest } from "bun";
import { createOrganisation } from "../../db/queries";
// /organisation/create?name=Org%20Name&slug=org-name&description=Optional%20description
export default async function organisationCreate(req: BunRequest) {
const url = new URL(req.url);
const name = url.searchParams.get("name");
const slug = url.searchParams.get("slug");
const description = url.searchParams.get("description") || undefined;
if (!name || !slug) {
return new Response(`missing parameters: ${!name ? "name " : ""}${!slug ? "slug" : ""}`, {
status: 400,
});
}
// Check if organisation with slug already exists
// TODO: Add this check when we have a getOrganisationBySlug function
const organisation = await createOrganisation(name, slug, description);
return Response.json(organisation);
}

View File

@@ -0,0 +1,26 @@
import type { BunRequest } from "bun";
import { deleteOrganisation, getOrganisationById } from "../../db/queries";
// /organisation/delete?id=1
export default async function organisationDelete(req: BunRequest) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (!id) {
return new Response("organisation id is required", { status: 400 });
}
const organisationId = Number(id);
if (!Number.isInteger(organisationId)) {
return new Response("organisation id must be an integer", { status: 400 });
}
const organisation = await getOrganisationById(organisationId);
if (!organisation) {
return new Response(`organisation with id ${id} not found`, { status: 404 });
}
await deleteOrganisation(organisationId);
return Response.json({ success: true });
}

View File

@@ -0,0 +1,26 @@
import type { BunRequest } from "bun";
import { getOrganisationById, getOrganisationMembers } from "../../db/queries";
// /organisation/members?organisationId=1
export default async function organisationMembers(req: BunRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
if (!organisationId) {
return new Response("organisationId is required", { status: 400 });
}
const orgIdNumber = Number(organisationId);
if (!Number.isInteger(orgIdNumber)) {
return new Response("organisationId must be an integer", { status: 400 });
}
const organisation = await getOrganisationById(orgIdNumber);
if (!organisation) {
return new Response(`organisation with id ${organisationId} not found`, { status: 404 });
}
const members = await getOrganisationMembers(orgIdNumber);
return Response.json(members);
}

View File

@@ -0,0 +1,37 @@
import type { BunRequest } from "bun";
import { getOrganisationById, getUserById, removeOrganisationMember } from "../../db/queries";
// /organisation/remove-member?organisationId=1&userId=2
export default async function organisationRemoveMember(req: BunRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
const userId = url.searchParams.get("userId");
if (!organisationId || !userId) {
return new Response(
`missing parameters: ${!organisationId ? "organisationId " : ""}${!userId ? "userId" : ""}`,
{ status: 400 },
);
}
const orgIdNumber = Number(organisationId);
const userIdNumber = Number(userId);
if (!Number.isInteger(orgIdNumber) || !Number.isInteger(userIdNumber)) {
return new Response("organisationId and userId must be integers", { status: 400 });
}
const organisation = await getOrganisationById(orgIdNumber);
if (!organisation) {
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 });
}
await removeOrganisationMember(orgIdNumber, userIdNumber);
return Response.json({ success: true });
}

View File

@@ -0,0 +1,38 @@
import type { BunRequest } from "bun";
import { getOrganisationById, getUserById, updateOrganisationMemberRole } from "../../db/queries";
// /organisation/update-member-role?organisationId=1&userId=2&role=admin
export default async function organisationUpdateMemberRole(req: BunRequest) {
const url = new URL(req.url);
const organisationId = url.searchParams.get("organisationId");
const userId = url.searchParams.get("userId");
const role = url.searchParams.get("role");
if (!organisationId || !userId || !role) {
return new Response(
`missing parameters: ${!organisationId ? "organisationId " : ""}${!userId ? "userId " : ""}${!role ? "role" : ""}`,
{ status: 400 },
);
}
const orgIdNumber = Number(organisationId);
const userIdNumber = Number(userId);
if (!Number.isInteger(orgIdNumber) || !Number.isInteger(userIdNumber)) {
return new Response("organisationId and userId must be integers", { status: 400 });
}
const organisation = await getOrganisationById(orgIdNumber);
if (!organisation) {
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 member = await updateOrganisationMemberRole(orgIdNumber, userIdNumber, role);
return Response.json(member);
}

View File

@@ -0,0 +1,39 @@
import type { BunRequest } from "bun";
import { getOrganisationById, updateOrganisation } from "../../db/queries";
// /organisation/update?id=1&name=New%20Name&description=New%20Description&slug=new-slug
export default async function organisationUpdate(req: BunRequest) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
const name = url.searchParams.get("name") || undefined;
const description = url.searchParams.get("description") || undefined;
const slug = url.searchParams.get("slug") || undefined;
if (!id) {
return new Response("organisation id is required", { status: 400 });
}
const organisationId = Number(id);
if (!Number.isInteger(organisationId)) {
return new Response("organisation id must be an integer", { status: 400 });
}
const existingOrganisation = await getOrganisationById(organisationId);
if (!existingOrganisation) {
return new Response(`organisation with id ${id} does not exist`, { status: 404 });
}
if (!name && !description && !slug) {
return new Response("at least one of name, description, or slug must be provided", {
status: 400,
});
}
const organisation = await updateOrganisation(organisationId, {
name,
description,
slug,
});
return Response.json(organisation);
}

View File

@@ -1,7 +1,7 @@
- user settings/profile page - user settings/profile page
- organisations system
- create organisation - create organisation
- create project - create project
- create issue - create issue
- add user(s) to project - add user(s) to project
- replace "no projects" text with create project button - replace "no projects" text with create project button
- rename Project.blob to Project.key