mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
all fixes for Project.creatorId and Organisation tables
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
export * from "./users";
|
||||
export * from "./projects";
|
||||
export * from "./issues";
|
||||
export * from "./organisations";
|
||||
export * from "./projects";
|
||||
export * from "./users";
|
||||
|
||||
45
packages/backend/src/db/queries/organisations.ts
Normal file
45
packages/backend/src/db/queries/organisations.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Organisation, OrganisationMember } from "@issue/shared";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
|
||||
export async function createOrganisation(name: string, slug: string, description?: string) {
|
||||
const [organisation] = await db
|
||||
.insert(Organisation)
|
||||
.values({
|
||||
name,
|
||||
slug,
|
||||
description,
|
||||
})
|
||||
.returning();
|
||||
return organisation;
|
||||
}
|
||||
|
||||
export async function createOrganisationMember(
|
||||
organisationId: number,
|
||||
userId: number,
|
||||
role: string = "member",
|
||||
) {
|
||||
const [member] = await db
|
||||
.insert(OrganisationMember)
|
||||
.values({
|
||||
organisationId,
|
||||
userId,
|
||||
role,
|
||||
})
|
||||
.returning();
|
||||
return member;
|
||||
}
|
||||
|
||||
export async function getOrganisationById(id: number) {
|
||||
const [organisation] = await db.select().from(Organisation).where(eq(Organisation.id, id));
|
||||
return organisation;
|
||||
}
|
||||
|
||||
export async function getOrganisationsByUserId(userId: number) {
|
||||
const organisations = await db
|
||||
.select()
|
||||
.from(OrganisationMember)
|
||||
.where(eq(OrganisationMember.userId, userId))
|
||||
.innerJoin(Organisation, eq(OrganisationMember.organisationId, Organisation.id));
|
||||
return organisations;
|
||||
}
|
||||
@@ -2,13 +2,14 @@ import { Issue, Project, User } from "@issue/shared";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../client";
|
||||
|
||||
export async function createProject(blob: string, name: string, ownerId: number) {
|
||||
export async function createProject(blob: string, name: string, creatorId: number, organisationId: number) {
|
||||
const [project] = await db
|
||||
.insert(Project)
|
||||
.values({
|
||||
blob,
|
||||
name,
|
||||
ownerId,
|
||||
creatorId,
|
||||
organisationId,
|
||||
})
|
||||
.returning();
|
||||
return project;
|
||||
@@ -16,7 +17,7 @@ export async function createProject(blob: string, name: string, ownerId: number)
|
||||
|
||||
export async function updateProject(
|
||||
projectId: number,
|
||||
updates: { blob?: string; name?: string; ownerId?: number },
|
||||
updates: { blob?: string; name?: string; creatorId?: number; organisationId?: number },
|
||||
) {
|
||||
const [project] = await db.update(Project).set(updates).where(eq(Project.id, projectId)).returning();
|
||||
return project;
|
||||
@@ -39,13 +40,13 @@ export async function getProjectByBlob(projectBlob: string) {
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function getProjectsByOwnerID(ownerId: number) {
|
||||
const projectsWithOwners = await db
|
||||
export async function getProjectsByCreatorID(creatorId: number) {
|
||||
const projectsWithCreators = await db
|
||||
.select()
|
||||
.from(Project)
|
||||
.where(eq(Project.ownerId, ownerId))
|
||||
.leftJoin(User, eq(Project.ownerId, User.id));
|
||||
return projectsWithOwners;
|
||||
.where(eq(Project.creatorId, creatorId))
|
||||
.leftJoin(User, eq(Project.creatorId, User.id));
|
||||
return projectsWithCreators;
|
||||
}
|
||||
|
||||
export async function getAllProjects() {
|
||||
@@ -53,16 +54,19 @@ export async function getAllProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
export async function getProjectsWithOwners() {
|
||||
const projectsWithOwners = await db.select().from(Project).leftJoin(User, eq(Project.ownerId, User.id));
|
||||
return projectsWithOwners;
|
||||
}
|
||||
|
||||
export async function getProjectWithOwnerByID(projectId: number) {
|
||||
const [projectWithOwner] = await db
|
||||
export async function getProjectsWithCreators() {
|
||||
const projectsWithCreators = await db
|
||||
.select()
|
||||
.from(Project)
|
||||
.leftJoin(User, eq(Project.ownerId, User.id))
|
||||
.where(eq(Project.id, projectId));
|
||||
return projectWithOwner;
|
||||
.leftJoin(User, eq(Project.creatorId, User.id));
|
||||
return projectsWithCreators;
|
||||
}
|
||||
|
||||
export async function getProjectWithCreatorByID(projectId: number) {
|
||||
const [projectWithCreator] = await db
|
||||
.select()
|
||||
.from(Project)
|
||||
.leftJoin(User, eq(Project.creatorId, User.id))
|
||||
.where(eq(Project.id, projectId));
|
||||
return projectWithCreator;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ const main = async () => {
|
||||
"/project/create": withCors(withAuth(routes.projectCreate)),
|
||||
"/project/update": withCors(withAuth(routes.projectUpdate)),
|
||||
"/project/delete": withCors(withAuth(routes.projectDelete)),
|
||||
"/projects/by-owner": withCors(withAuth(routes.projectsByOwner)),
|
||||
"/projects/by-creator": withCors(withAuth(routes.projectsByCreator)),
|
||||
"/projects/all": withCors(withAuth(routes.projectsAll)),
|
||||
"/projects/with-owners": withCors(withAuth(routes.projectsWithOwners)),
|
||||
"/project/with-owner": withCors(withAuth(routes.projectWithOwner)),
|
||||
"/projects/with-creators": withCors(withAuth(routes.projectsWithCreators)),
|
||||
"/project/with-creator": withCors(withAuth(routes.projectWithCreator)),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import issueUpdate from "./issue/update";
|
||||
import issuesInProject from "./issues/[projectBlob]";
|
||||
import issues from "./issues/all";
|
||||
import projectsAll from "./project/all";
|
||||
import projectsByOwner from "./project/by-owner";
|
||||
import projectsByCreator from "./project/by-creator";
|
||||
import projectCreate from "./project/create";
|
||||
import projectDelete from "./project/delete";
|
||||
import projectUpdate from "./project/update";
|
||||
import projectWithOwner from "./project/with-owner";
|
||||
import projectsWithOwners from "./project/with-owners";
|
||||
import projectWithCreator from "./project/with-creator";
|
||||
import projectsWithCreators from "./project/with-creators";
|
||||
|
||||
export const routes = {
|
||||
issueCreate,
|
||||
@@ -25,10 +25,10 @@ export const routes = {
|
||||
projectCreate,
|
||||
projectUpdate,
|
||||
projectDelete,
|
||||
projectsByOwner,
|
||||
projectsByCreator,
|
||||
projectsAll,
|
||||
projectsWithOwners,
|
||||
projectWithOwner,
|
||||
projectsWithCreators,
|
||||
projectWithCreator,
|
||||
|
||||
authRegister,
|
||||
authLogin,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectsWithOwners } from "../../db/queries";
|
||||
import { getProjectsWithCreators } from "../../db/queries";
|
||||
|
||||
// /projects/all
|
||||
export default async function projectsAll(req: BunRequest) {
|
||||
const projects = await getProjectsWithOwners();
|
||||
const projects = await getProjectsWithCreators();
|
||||
|
||||
return Response.json(projects);
|
||||
}
|
||||
|
||||
26
packages/backend/src/routes/project/by-creator.ts
Normal file
26
packages/backend/src/routes/project/by-creator.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectsByCreatorID, getUserById } from "../../db/queries";
|
||||
|
||||
// /projects/by-creator?creatorId=1
|
||||
export default async function projectsByCreator(req: BunRequest) {
|
||||
const url = new URL(req.url);
|
||||
const creatorId = url.searchParams.get("creatorId");
|
||||
|
||||
if (!creatorId) {
|
||||
return new Response("creatorId is required", { status: 400 });
|
||||
}
|
||||
|
||||
const creatorIdNumber = Number(creatorId);
|
||||
if (!Number.isInteger(creatorIdNumber)) {
|
||||
return new Response("creatorId must be an integer", { status: 400 });
|
||||
}
|
||||
|
||||
const creator = await getUserById(creatorIdNumber);
|
||||
if (!creator) {
|
||||
return new Response(`user with id ${creatorId} not found`, { status: 404 });
|
||||
}
|
||||
|
||||
const projects = await getProjectsByCreatorID(creator.id);
|
||||
|
||||
return Response.json(projects);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectsByOwnerID, getUserById } from "../../db/queries";
|
||||
|
||||
// /projects/by-owner?ownerId=1
|
||||
export default async function projectsByOwner(req: BunRequest) {
|
||||
const url = new URL(req.url);
|
||||
const ownerId = url.searchParams.get("ownerId");
|
||||
|
||||
if (!ownerId) {
|
||||
return new Response("ownerId is required", { status: 400 });
|
||||
}
|
||||
|
||||
const ownerIdNumber = Number(ownerId);
|
||||
if (!Number.isInteger(ownerIdNumber)) {
|
||||
return new Response("ownerId must be an integer", { status: 400 });
|
||||
}
|
||||
|
||||
const owner = await getUserById(ownerIdNumber);
|
||||
if (!owner) {
|
||||
return new Response(`user with id ${ownerId} not found`, { status: 404 });
|
||||
}
|
||||
|
||||
const projects = await getProjectsByOwnerID(owner.id);
|
||||
|
||||
return Response.json(projects);
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { createProject, getUserById, getProjectByBlob } from "../../db/queries";
|
||||
import { createProject, getProjectByBlob, getUserById } from "../../db/queries";
|
||||
|
||||
// /project/create?blob=BLOB&name=Testing&ownerId=1
|
||||
// /project/create?blob=BLOB&name=Testing&creatorId=1&organisationId=1
|
||||
export default async function projectCreate(req: BunRequest) {
|
||||
const url = new URL(req.url);
|
||||
const blob = url.searchParams.get("blob");
|
||||
const name = url.searchParams.get("name");
|
||||
const ownerId = url.searchParams.get("ownerId");
|
||||
const creatorId = url.searchParams.get("creatorId");
|
||||
const organisationId = url.searchParams.get("organisationId");
|
||||
|
||||
if (!blob || !name || !ownerId) {
|
||||
if (!blob || !name || !creatorId || !organisationId) {
|
||||
return new Response(
|
||||
`missing parameters: ${!blob ? "blob " : ""}${!name ? "name " : ""}${!ownerId ? "ownerId" : ""}`,
|
||||
`missing parameters: ${!blob ? "blob " : ""}${!name ? "name " : ""}${!creatorId ? "creatorId " : ""}${!organisationId ? "organisationId" : ""}`,
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
@@ -21,12 +22,12 @@ export default async function projectCreate(req: BunRequest) {
|
||||
return new Response(`project with blob ${blob} already exists`, { status: 400 });
|
||||
}
|
||||
|
||||
const owner = await getUserById(parseInt(ownerId, 10));
|
||||
if (!owner) {
|
||||
return new Response(`owner with id ${ownerId} not found`, { status: 404 });
|
||||
const creator = await getUserById(parseInt(creatorId, 10));
|
||||
if (!creator) {
|
||||
return new Response(`creator with id ${creatorId} not found`, { status: 404 });
|
||||
}
|
||||
|
||||
const project = await createProject(blob, name, owner.id);
|
||||
const project = await createProject(blob, name, creator.id, parseInt(organisationId, 10));
|
||||
|
||||
return Response.json(project);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectByBlob, getProjectByID, getUserById, updateProject } from "../../db/queries";
|
||||
|
||||
// /project/update?id=1&blob=NEW&name=new%20name&ownerId=1
|
||||
// /project/update?id=1&blob=NEW&name=new%20name&creatorId=1&organisationId=1
|
||||
export default async function projectUpdate(req: BunRequest) {
|
||||
const url = new URL(req.url);
|
||||
const id = url.searchParams.get("id");
|
||||
const blob = url.searchParams.get("blob") || undefined;
|
||||
const name = url.searchParams.get("name") || undefined;
|
||||
const ownerId = url.searchParams.get("ownerId") || undefined;
|
||||
const creatorId = url.searchParams.get("creatorId") || undefined;
|
||||
const organisationId = url.searchParams.get("organisationId") || undefined;
|
||||
|
||||
if (!id) {
|
||||
return new Response(`project id is required`, { status: 400 });
|
||||
@@ -18,8 +19,8 @@ export default async function projectUpdate(req: BunRequest) {
|
||||
return new Response(`project with id ${id} does not exist`, { status: 404 });
|
||||
}
|
||||
|
||||
if (!blob && !name && !ownerId) {
|
||||
return new Response(`at least one of blob, name, or ownerId must be provided`, {
|
||||
if (!blob && !name && !creatorId && !organisationId) {
|
||||
return new Response(`at least one of blob, name, creatorId, or organisationId must be provided`, {
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
@@ -29,15 +30,16 @@ export default async function projectUpdate(req: BunRequest) {
|
||||
return new Response(`a project with blob "${blob}" already exists`, { status: 400 });
|
||||
}
|
||||
|
||||
const newOwner = ownerId ? await getUserById(Number(ownerId)) : null;
|
||||
if (ownerId && !newOwner) {
|
||||
return new Response(`user with id ${ownerId} does not exist`, { status: 400 });
|
||||
const newCreator = creatorId ? await getUserById(Number(creatorId)) : null;
|
||||
if (creatorId && !newCreator) {
|
||||
return new Response(`user with id ${creatorId} does not exist`, { status: 400 });
|
||||
}
|
||||
|
||||
const project = await updateProject(Number(id), {
|
||||
blob: blob,
|
||||
name: name,
|
||||
ownerId: newOwner?.id,
|
||||
creatorId: newCreator?.id,
|
||||
organisationId: organisationId ? Number(organisationId) : undefined,
|
||||
});
|
||||
|
||||
return Response.json(project);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectWithOwnerByID } from "../../db/queries";
|
||||
import { getProjectWithCreatorByID } from "../../db/queries";
|
||||
|
||||
// /project/with-owner?id=1
|
||||
export default async function projectWithOwnerByID(req: BunRequest) {
|
||||
// /project/with-creator?id=1
|
||||
export default async function projectWithCreatorByID(req: BunRequest) {
|
||||
const url = new URL(req.url);
|
||||
const id = url.searchParams.get("id");
|
||||
|
||||
@@ -15,10 +15,10 @@ export default async function projectWithOwnerByID(req: BunRequest) {
|
||||
return new Response("project id must be an integer", { status: 400 });
|
||||
}
|
||||
|
||||
const projectWithOwner = await getProjectWithOwnerByID(projectId);
|
||||
if (!projectWithOwner || !projectWithOwner.Project) {
|
||||
const projectWithCreator = await getProjectWithCreatorByID(projectId);
|
||||
if (!projectWithCreator || !projectWithCreator.Project) {
|
||||
return new Response(`project with id ${id} not found`, { status: 404 });
|
||||
}
|
||||
|
||||
return Response.json(projectWithOwner);
|
||||
return Response.json(projectWithCreator);
|
||||
}
|
||||
9
packages/backend/src/routes/project/with-creators.ts
Normal file
9
packages/backend/src/routes/project/with-creators.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectsWithCreators } from "../../db/queries";
|
||||
|
||||
// /projects/with-creators
|
||||
export default async function projectsWithCreators(req: BunRequest) {
|
||||
const projects = await getProjectsWithCreators();
|
||||
|
||||
return Response.json(projects);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { BunRequest } from "bun";
|
||||
import { getProjectsWithOwners } from "../../db/queries";
|
||||
|
||||
// /projects/with-owners
|
||||
export default async function projectsWithOwners(req: BunRequest) {
|
||||
const projects = await getProjectsWithOwners();
|
||||
|
||||
return Response.json(projects);
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
import { hashPassword } from "./auth/utils";
|
||||
import { createIssue, createProject, createUser } from "./db/queries";
|
||||
import {
|
||||
createIssue,
|
||||
createOrganisation,
|
||||
createOrganisationMember,
|
||||
createProject,
|
||||
createUser,
|
||||
} from "./db/queries";
|
||||
|
||||
export const createDemoData = async () => {
|
||||
const passwordHash = await hashPassword("changeme");
|
||||
@@ -8,10 +14,23 @@ export const createDemoData = async () => {
|
||||
throw new Error("failed to create demo user");
|
||||
}
|
||||
|
||||
// create demo organisation
|
||||
const organisation = await createOrganisation(
|
||||
"Demo Organisation",
|
||||
"demo-org",
|
||||
"A demo organisation for testing",
|
||||
);
|
||||
if (!organisation) {
|
||||
throw new Error("failed to create demo organisation");
|
||||
}
|
||||
|
||||
// add user as owner of the organisation
|
||||
await createOrganisationMember(organisation.id, user.id, "owner");
|
||||
|
||||
const projectNames = ["PROJ", "TEST", "SAMPLE"];
|
||||
let issuesToCreate = 3;
|
||||
for (const name of projectNames) {
|
||||
const project = await createProject(name.slice(0, 4), name, user.id);
|
||||
const project = await createProject(name.slice(0, 4), name, user.id, organisation.id);
|
||||
if (!project) {
|
||||
throw new Error(`failed to create demo project: ${name}`);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function Index() {
|
||||
if (projectsRef.current) return;
|
||||
projectsRef.current = true;
|
||||
|
||||
fetch(`${serverURL}/projects/by-owner?ownerId=${user.id}`, { headers: getAuthHeaders() })
|
||||
fetch(`${serverURL}/projects/by-creator?creatorId=${user.id}`, { headers: getAuthHeaders() })
|
||||
.then((res) => res.json())
|
||||
.then((data: ProjectResponse[]) => {
|
||||
setProjects(data);
|
||||
@@ -88,7 +88,7 @@ function Index() {
|
||||
</Select>
|
||||
{selectedProject && (
|
||||
<div className="flex items-center gap-2">
|
||||
Owner: <SmallUserDisplay user={selectedProject?.User} />
|
||||
Creator: <SmallUserDisplay user={selectedProject?.User} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// Drizzle tables
|
||||
export { User, Project, Issue } from "./schema";
|
||||
export { User, Project, Issue, Organisation, OrganisationMember } from "./schema";
|
||||
|
||||
// Types
|
||||
export type {
|
||||
UserRecord,
|
||||
UserInsert,
|
||||
OrganisationRecord,
|
||||
OrganisationInsert,
|
||||
OrganisationMemberRecord,
|
||||
OrganisationMemberInsert,
|
||||
ProjectRecord,
|
||||
ProjectInsert,
|
||||
IssueRecord,
|
||||
@@ -15,6 +19,10 @@ export type {
|
||||
export {
|
||||
UserSelectSchema,
|
||||
UserInsertSchema,
|
||||
OrganisationSelectSchema,
|
||||
OrganisationInsertSchema,
|
||||
OrganisationMemberSelectSchema,
|
||||
OrganisationMemberInsertSchema,
|
||||
ProjectSelectSchema,
|
||||
ProjectInsertSchema,
|
||||
IssueSelectSchema,
|
||||
@@ -25,4 +33,4 @@ export {
|
||||
export type {
|
||||
IssueResponse,
|
||||
ProjectResponse,
|
||||
} from "./schema"
|
||||
} from "./schema";
|
||||
|
||||
@@ -108,5 +108,5 @@ export type IssueResponse = {
|
||||
export type ProjectResponse = {
|
||||
Project: ProjectRecord;
|
||||
Organisation: OrganisationRecord;
|
||||
Creator: UserRecord;
|
||||
User: UserRecord;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user