mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
Organisation and OrganisationMember routes
This commit is contained in:
@@ -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)),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
38
packages/backend/src/routes/organisation/add-member.ts
Normal file
38
packages/backend/src/routes/organisation/add-member.ts
Normal 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);
|
||||||
|
}
|
||||||
24
packages/backend/src/routes/organisation/by-id.ts
Normal file
24
packages/backend/src/routes/organisation/by-id.ts
Normal 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);
|
||||||
|
}
|
||||||
26
packages/backend/src/routes/organisation/by-user.ts
Normal file
26
packages/backend/src/routes/organisation/by-user.ts
Normal 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);
|
||||||
|
}
|
||||||
23
packages/backend/src/routes/organisation/create.ts
Normal file
23
packages/backend/src/routes/organisation/create.ts
Normal 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);
|
||||||
|
}
|
||||||
26
packages/backend/src/routes/organisation/delete.ts
Normal file
26
packages/backend/src/routes/organisation/delete.ts
Normal 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 });
|
||||||
|
}
|
||||||
26
packages/backend/src/routes/organisation/members.ts
Normal file
26
packages/backend/src/routes/organisation/members.ts
Normal 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);
|
||||||
|
}
|
||||||
37
packages/backend/src/routes/organisation/remove-member.ts
Normal file
37
packages/backend/src/routes/organisation/remove-member.ts
Normal 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 });
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
39
packages/backend/src/routes/organisation/update.ts
Normal file
39
packages/backend/src/routes/organisation/update.ts
Normal 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);
|
||||||
|
}
|
||||||
2
todo.md
2
todo.md
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user