Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
DB_HOST=""
DB_PORT=""
DB_NAME=""
DB_USERNAME=""
DB_PASSWORD=""
DB_HOST="localhost"
DB_PORT="5432"
DB_NAME="study"
DB_USERNAME="postgres"
DB_PASSWORD="admin"

ACCESS_TOKEN_SECRET=""
PUBLIC_APP_URL=""
ACCESS_TOKEN_SECRET="arbitrary"
PUBLIC_APP_URL="http://localhost:3000"

NEXT_PUBLIC_SENTRY_DSN=""
NEXT_PUBLIC_SENTRY_DSN=""

#cid from app.code.berlin
LP_ACCESS_TOKEN=""
19 changes: 14 additions & 5 deletions app/api/auth/login/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import dayjs from "dayjs";
import { NextRequest, NextResponse } from "next/server";

import { AppDataSource, connectToDatabase } from "@/backend/datasource";
import { CollaboratorRole } from "@/backend/entities/enums";
import { ModuleHandbook } from "@/backend/entities/moduleHandbook.entity";
import { Semester } from "@/backend/entities/semester.entity";
import { StudyPlan } from "@/backend/entities/studyPlan.entity";
import { StudyPlan, StudyPlanScope } from "@/backend/entities/studyPlan.entity";
import { StudyPlanCollaborator } from "@/backend/entities/studyPlanCollaborator.entity";
import { User } from "@/backend/entities/user.entity";
import { issueAccessToken } from "@/backend/jwt";
import { isDefined } from "@/services/learningPlatform/util/isDefined";
Expand Down Expand Up @@ -75,20 +77,27 @@ export async function POST(req: NextRequest) {
if (isSignup) {
await AppDataSource.transaction(async (transaction) => {
newUser = new User();

newUser.lpId = learningPlatformUser.me.id;

const studyPlan = new StudyPlan();
await transaction.getRepository(User).save(newUser);

const studyPlan = new StudyPlan();
studyPlan.moduleHandbookId = moduleHandbook.id;
studyPlan.subjectId = newUser.id;
studyPlan.scope = StudyPlanScope.Private;

const newStudyPlan = await transaction
.getRepository(StudyPlan)
.save(studyPlan);

newUser.studyPlanId = newStudyPlan.id;
const studyPlanCollaborator = new StudyPlanCollaborator();
studyPlanCollaborator.role = CollaboratorRole.Owner;
studyPlanCollaborator.studyPlanId = newStudyPlan.id;
studyPlanCollaborator.userId = newUser.id;

await transaction.getRepository(User).save(newUser);
await transaction
.getRepository(StudyPlanCollaborator)
.save(studyPlanCollaborator);

const myStudiesData = await learningPlatform.raw.query(
`query myStudies($filter: ModuleFilter) {
Expand Down
4 changes: 2 additions & 2 deletions app/api/learning-platform-proxy/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export async function POST(req: NextRequest) {

if (init?.body) {
// @ts-ignore
console.log("fetched learning platform:", JSON.parse(init.body), data);
//console.log("fetched learning platform:", JSON.parse(init.body), data);
} else {
console.log("fetched learning platform:", data);
//console.log("fetched learning platform:", data);
}

const res = NextResponse.json(data, {
Expand Down
50 changes: 50 additions & 0 deletions app/api/study-plan/[id]/collaborators/[collabId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { NextRequest } from "next/server";

import {
internalServerErrorResponse,
successResponse,
unauthorizedResponse,
} from "@/app/api/utils";
import { StudyPlanCollaboratorPutDTO } from "@/backend/dtos/study-plan-collaborator.dto";
import {
deleteCollaboratorById,
getCollaborator,
updateCollaboratorById,
} from "@/backend/queries/study-plan-collaborator.query";

export type CollaborationParams = {
params: {
id: string;
collabId: string;
};
};

export async function DELETE(
req: NextRequest,
{ params: { id: studyPlanId, collabId } }: CollaborationParams,
) {
const collaborator = await getCollaborator(req, studyPlanId);

if (!collaborator?.canManageCollaborators) return unauthorizedResponse();

const deleteCollaborator = await deleteCollaboratorById(collabId);
if (!deleteCollaborator) return internalServerErrorResponse();

return successResponse();
}

export async function PUT(
req: NextRequest,
{ params: { id: studyPlanId, collabId } }: CollaborationParams,
) {
const collaborator = await getCollaborator(req, studyPlanId);

if (!collaborator?.canManageCollaborators) return unauthorizedResponse();

const body: StudyPlanCollaboratorPutDTO = await req.json();
const updatedCollaborator = await updateCollaboratorById(collabId, body);

if (!updatedCollaborator) return internalServerErrorResponse();

return successResponse();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { NextRequest } from "next/server";

import {
badRequestResponse,
successResponse,
unauthorizedResponse,
} from "@/app/api/utils";
import { InviteStatus } from "@/backend/entities/invite.entity";
import { getUser } from "@/backend/getUser";
import {
getInviteById,
updateInviteStatus,
} from "@/backend/queries/invite.query";
import { createStudyPlanCollaborator } from "@/backend/queries/study-plan-collaborator.query";

import { CollabParams } from "../utils";

export async function PUT(
req: NextRequest,
{ params: { id, inviteId } }: CollabParams,
) {
const user = await getUser(req);
if (!user) return unauthorizedResponse();

const invite = await getInviteById(inviteId);
if (!invite) return badRequestResponse();

if (user.lpId !== invite.inviteeLpId) return unauthorizedResponse();

const updatedInvite = await updateInviteStatus(
inviteId,
InviteStatus.Accepted,
);
if (!updatedInvite) return badRequestResponse();

const studyPlanCollaborator = await createStudyPlanCollaborator(
user.id,
id,
updatedInvite.role,
);
if (!studyPlanCollaborator) return badRequestResponse();

return successResponse();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NextRequest } from "next/server";

import {
badRequestResponse,
successResponse,
unauthorizedResponse,
} from "@/app/api/utils";
import { InviteStatus } from "@/backend/entities/invite.entity";
import { getUser } from "@/backend/getUser";
import {
getInviteById,
updateInviteStatus,
} from "@/backend/queries/invite.query";

import { CollabParams } from "../utils";

export async function PUT(
req: NextRequest,
{ params: { inviteId } }: CollabParams,
) {
const user = await getUser(req);

if (!user) return unauthorizedResponse();

const invite = await getInviteById(inviteId);

if (!invite) return badRequestResponse();

if (user.lpId !== invite.inviteeLpId) return unauthorizedResponse();

const updatedInvite = await updateInviteStatus(
inviteId,
InviteStatus.Declined,
);
if (!updatedInvite) return badRequestResponse();

return successResponse();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type CollabParams = {
params: {
id: string;
inviteId: string;
};
};
30 changes: 30 additions & 0 deletions app/api/study-plan/[id]/collaborators/invites/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NextRequest } from "next/server";

import {
badRequestResponse,
StudyPlanParams,
successResponse,
unauthorizedResponse,
} from "@/app/api/utils";
import { InvitePostDTO } from "@/backend/dtos/invite.dto";
import { createInvite } from "@/backend/queries/invite.query";
import { getCollaborator } from "@/backend/queries/study-plan-collaborator.query";

export async function POST(req: NextRequest, { params }: StudyPlanParams) {
const collaborator = await getCollaborator(req, params.id);

if (!collaborator?.canManageCollaborators) return unauthorizedResponse();

const { inviteeLpId, role }: InvitePostDTO = await req.json();

const invite = await createInvite({
invitedById: collaborator.id,
studyPlanId: params.id,
inviteeLpId,
role,
});

if (!invite) return badRequestResponse();

return successResponse();
}
22 changes: 22 additions & 0 deletions app/api/study-plan/[id]/collaborators/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NextRequest, NextResponse } from "next/server";

import { StudyPlanParams, unauthorizedResponse } from "@/app/api/utils";
import { StudyPlanCollaboratorDTO } from "@/backend/dtos/study-plan-collaborator.dto";
import {
getAllCollaboratorsByStudyPlanId,
getCollaborator,
} from "@/backend/queries/study-plan-collaborator.query";

export async function DELETE(req: NextRequest) {}
export async function GET(req: NextRequest, { params }: StudyPlanParams) {
const collaborator = await getCollaborator(req, params.id);

const studyPlanCollaborators = await getAllCollaboratorsByStudyPlanId(
params.id,
);
if (!collaborator?.canViewCollaborators) return unauthorizedResponse();

const collabs: StudyPlanCollaboratorDTO[] = studyPlanCollaborators;

return NextResponse.json(collabs);
}
107 changes: 107 additions & 0 deletions app/api/study-plan/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import dayjs from "dayjs";
import { NextRequest, NextResponse } from "next/server";

import { SemesterDTO } from "@/backend/dtos/semester.dto";
import { StudyPlanDTO, StudyPlanPutDTO } from "@/backend/dtos/study-plan.dto";
import { Semester } from "@/backend/entities/semester.entity";
import { SemesterModule } from "@/backend/entities/semesterModule.entity";
import { getSemesterByStudyPlanId } from "@/backend/queries/semester.query";
import { getCollaborator } from "@/backend/queries/study-plan-collaborator.query";
import {
getStudyPlanByCollaboratorId,
updateStudyPlanScopeByCollabId,
} from "@/backend/queries/study-plan.query";

import {
internalServerErrorResponse,
StudyPlanParams,
successResponse,
unauthorizedResponse,
} from "../../utils";

const byIndex = (a: SemesterModule, b: SemesterModule) => a.index - b.index;

const toModule = (module: SemesterModule) => ({ moduleId: module.module.lpId });

const mapSemster = (semesters: Semester[]): SemesterDTO[] => {
return semesters
.toSorted((a, b) => dayjs(a.startDate).unix() - dayjs(b.startDate).unix())
.map((semester) => {
return {
id: semester.id,
lpId: semester.lpId,
startDate: semester.startDate,
modules: {
earlyAssessments: semester.semesterModules
.filter((module) => module.assessmentType === "earlyAssessments")
.sort(byIndex)
.map(toModule),
standardAssessments: semester.semesterModules
.filter((module) => module.assessmentType === "standardAssessments")
.sort(byIndex)
.map(toModule),
alternativeAssessments: semester.semesterModules
.filter(
(module) => module.assessmentType === "alternativeAssessments",
)
.sort(byIndex)
.map(toModule),
reassessments: semester.semesterModules
.filter((module) => module.assessmentType === "reassessments")
.sort(byIndex)
.map(toModule),
},
};
});
};

export async function GET(req: NextRequest, { params }: StudyPlanParams) {
const collaborator = await getCollaborator(req, params.id);

if (!collaborator?.canViewStudyPlan) {
return unauthorizedResponse();
}

const currentStudyPlan = await getStudyPlanByCollaboratorId(collaborator.id);

if (!currentStudyPlan) {
return unauthorizedResponse();
}

/*
TODO: maybe add error if list is empty or return something currently if find function errors returns empty list
Prev. on error it would automatically return response 500 rn this wouldnt happen
*/
const semesters = await getSemesterByStudyPlanId(currentStudyPlan.id);

const mappedSemesters = mapSemster(semesters);

const studyPlan: StudyPlanDTO = {
scope: currentStudyPlan.scope,
studyPlanCollaborators: currentStudyPlan.studyPlanCollaborators,
semesters: mappedSemesters,
};

return NextResponse.json(studyPlan);
}

/**
* Sucessfull response for PUT/POST/DELETE is {ok: true}
*/
export async function PUT(req: NextRequest, { params }: StudyPlanParams) {
const collaborator = await getCollaborator(req, params.id);

if (!collaborator?.canChangeStudyPlanScope) return unauthorizedResponse();

const body: StudyPlanPutDTO = await req.json();

const updatePlan = await updateStudyPlanScopeByCollabId(
collaborator.id,
body,
);

// TODO: Think about better error handling lol
if (!updatePlan) return internalServerErrorResponse();

return successResponse();
}
Loading