mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 10:33:01 +00:00
add + remove users from organisation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Organisation, OrganisationMember } from "@issue/shared";
|
||||
import { Organisation, OrganisationMember, User } from "@issue/shared";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
|
||||
@@ -103,10 +103,21 @@ export async function getOrganisationMembers(organisationId: number) {
|
||||
.select()
|
||||
.from(OrganisationMember)
|
||||
.where(eq(OrganisationMember.organisationId, organisationId))
|
||||
.innerJoin(Organisation, eq(OrganisationMember.organisationId, Organisation.id));
|
||||
.innerJoin(Organisation, eq(OrganisationMember.organisationId, Organisation.id))
|
||||
.innerJoin(User, eq(OrganisationMember.userId, User.id));
|
||||
return members;
|
||||
}
|
||||
|
||||
export async function getOrganisationMemberRole(organisationId: number, userId: number) {
|
||||
const [member] = await db
|
||||
.select()
|
||||
.from(OrganisationMember)
|
||||
.where(
|
||||
and(eq(OrganisationMember.organisationId, organisationId), eq(OrganisationMember.userId, userId)),
|
||||
);
|
||||
return member;
|
||||
}
|
||||
|
||||
export async function removeOrganisationMember(organisationId: number, userId: number) {
|
||||
await db
|
||||
.delete(OrganisationMember)
|
||||
|
||||
@@ -18,6 +18,7 @@ const main = async () => {
|
||||
"/auth/login": withCors(routes.authLogin),
|
||||
"/auth/me": withCors(withAuth(routes.authMe)),
|
||||
|
||||
"/user/by-username": withCors(withAuth(routes.userByUsername)),
|
||||
"/user/update": withCors(withAuth(routes.userUpdate)),
|
||||
"/user/upload-avatar": withCors(routes.userUploadAvatar),
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
19
packages/backend/src/routes/user/by-username.ts
Normal file
19
packages/backend/src/routes/user/by-username.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user