Skip to content
This repository was archived by the owner on Feb 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 25 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { LoggerService } from './common/logger.service';
import { RequestLoggingMiddleware } from './infrastructure/middleware/request-logging.middleware';
import { PrismaService } from './infrastructure/database/prisma';
import { AppController } from './presentation/controllers/app.controller';

import { LocationRepository } from './infrastructure/database/repositories/location.repository';
import { LocationService } from './application/services/location.service';
import { LocationController } from './presentation/controllers/location.controller';
import { ModuleTagRepository } from './infrastructure/database/repositories/moduletag.repository';
class SessionActivityMiddleware {
use(req: any, res: any, next: () => void) {
// no-op middleware to track session activity (placeholder implementation)
Expand All @@ -18,6 +21,9 @@ import { ChoiceModulesRepository } from './infrastructure/database/repositories/
import { UserRepository } from './infrastructure/database/repositories/user.repository';
import { ModuleService } from './application/services/module.service';
import { ModulesController } from './presentation/controllers/modules.controller';
import { ModuleTagService } from './application/services/moduletag.service';
import { ModuleTagController } from './presentation/controllers/moduletag.controller';

@Module({
imports: [],
providers: [
Expand All @@ -43,12 +49,30 @@ import { ModulesController } from './presentation/controllers/modules.controller
provide: 'SERVICE.MODULE',
useClass: ModuleService,
},
{
provide: 'REPO.LOCATION',
useClass: LocationRepository,
},
{
provide: 'SERVICE.LOCATION',
useClass: LocationService,
},
{
provide: 'REPO.MODULETAG',
useClass: ModuleTagRepository,
},
{
provide: 'SERVICE.MODULETAG',
useClass: ModuleTagService,
},
],
controllers: [
AuthController,
UserController,
ModulesController,
AppController,
LocationController,
ModuleTagController,
],
})
export class AppModule implements NestModule {
Expand Down
4 changes: 4 additions & 0 deletions src/application/ports/location.port.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Location } from '@/domain/locations/location.model';
export interface ILocationService {
getAllLocation(): Promise<Location[]>;
}
9 changes: 7 additions & 2 deletions src/application/ports/module.port.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Module } from '@/domain/modules/module.model';
import {
Module,
moduleDetail,
createModule,
} from '@/domain/modules/module.model';
export interface IModuleService {
getAllModules(): Promise<Module[]>;
// findById(id: string): Promise<any>;
findById(id: string): Promise<moduleDetail>;
createModule(module: createModule): Promise<moduleDetail>;
}
4 changes: 4 additions & 0 deletions src/application/ports/moduletag.port.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ModuleTag } from '@/domain/moduletags/moduletag.model';
export interface IModuleTagService {
getAllModuleTags(): Promise<ModuleTag[]>;
}
16 changes: 16 additions & 0 deletions src/application/services/location.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Injectable, Inject } from '@nestjs/common';
import type { Location } from '@/domain/locations/location.model';
import { ILocationService } from '../ports/location.port';
import { ILocationRepository } from '@/domain/locations/location-repository.interface';
@Injectable()
export class LocationService implements ILocationService {
private readonly locationRepository: ILocationRepository;
constructor(
@Inject('REPO.LOCATION') locationRepository: ILocationRepository,
) {
this.locationRepository = locationRepository;
}
async getAllLocation(): Promise<Location[]> {
return await this.locationRepository.getAllLocation();
}
}
18 changes: 13 additions & 5 deletions src/application/services/module.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { Injectable, Inject } from '@nestjs/common';
import { IModuleService } from '@/application/ports/module.port';
import { type IModuleRepository } from '@/domain/modules/module-repository.interface';
import { Module, moduleDetail } from '@/domain/modules/module.model';
import {
Module,
moduleDetail,
createModule,
} from '@/domain/modules/module.model';

@Injectable()
export class ModuleService implements IModuleService {
private readonly moduleRepository: IModuleRepository;
constructor(@Inject('REPO.MODULE') _moduleRepository: IModuleRepository) {
this.moduleRepository = _moduleRepository;
}
getAllModules(): Promise<Module[]> {
return this.moduleRepository.getAllModules();
async getAllModules(): Promise<Module[]> {
return await this.moduleRepository.getAllModules();
}
findById(id: string): Promise<moduleDetail> {
return this.moduleRepository.findById(id);
async findById(id: string): Promise<moduleDetail> {
return await this.moduleRepository.findById(id);
}
async createModule(module: createModule): Promise<moduleDetail> {
console.log(`Creating module: ${JSON.stringify(module)}`);
return await this.moduleRepository.createModule(module);
}
}
16 changes: 16 additions & 0 deletions src/application/services/moduletag.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Inject, Injectable } from '@nestjs/common';
import { IModuleTagService } from '../ports/moduletag.port';
import { IModuleTagRepository } from '@/domain/moduletags/moduletag-repository';
import { ModuleTag } from '@/domain/moduletags/moduletag.model';

@Injectable()
export class ModuleTagService implements IModuleTagService {
constructor(
@Inject('REPO.MODULETAG')
private readonly moduleTagRepository: IModuleTagRepository,
) {}

async getAllModuleTags(): Promise<ModuleTag[]> {
return await this.moduleTagRepository.getAllModuleTags();
}
}
4 changes: 4 additions & 0 deletions src/domain/locations/location-repository.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Location } from './location.model';
export interface ILocationRepository {
getAllLocation(): Promise<Location[]>;
}
4 changes: 4 additions & 0 deletions src/domain/locations/location.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Location {
id: string;
name: string;
}
9 changes: 7 additions & 2 deletions src/domain/modules/module-repository.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Module } from '@/domain/modules/module.model';
import {
Module,
moduleDetail,
createModule,
} from '@/domain/modules/module.model';
export interface IModuleRepository {
getAllModules(): Promise<Module[]>;
findById(id: string): Promise<any>;
findById(id: string): Promise<moduleDetail>;
createModule(module: createModule): Promise<moduleDetail>;
}
13 changes: 13 additions & 0 deletions src/domain/modules/module.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ export interface Module {
location: { id: string; name: string }[];
startDate: string;
}
export interface createModule {
name: string;
description: string;
content: string;
level: string;
studyCredits: number;
location: { id: string; name: string }[];
moduleTags: { id: string; name: string }[];
learningOutcomes: string;
availableSpots: number;
startDate: string;
shortdescription: string;
}
4 changes: 4 additions & 0 deletions src/domain/moduletags/moduletag-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ModuleTag } from '@/domain/moduletags/moduletag.model';
export interface IModuleTagRepository {
getAllModuleTags(): Promise<ModuleTag[]>;
}
4 changes: 4 additions & 0 deletions src/domain/moduletags/moduletag.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ModuleTag {
id: string;
name: string;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Injectable } from '@nestjs/common';
import type { Module, moduleDetail } from '@/domain/modules/module.model';
import type {
createModule,
Module,
moduleDetail,
} from '@/domain/modules/module.model';
import { PrismaService } from '../prisma';
import { IModuleRepository } from '@/domain/modules/module-repository.interface';

Expand Down Expand Up @@ -67,4 +71,72 @@ export class ChoiceModulesRepository implements IModuleRepository {
availableSpots: mod.availableSpots!,
} as moduleDetail;
}
async createModule(module: createModule): Promise<moduleDetail> {
const created = await this.prisma.$transaction(async (tx) => {
const baseModule = await tx.choiceModule.create({
data: {
name: module.name,
shortDescription: module.shortdescription,
description: module.description,
content: module.content,
level: module.level,
studyCredits: module.studyCredits,
startDate: module.startDate,
availableSpots: module.availableSpots,
learningOutcomes: module.learningOutcomes,
},
});
console.log('Created base module:', baseModule);

if (module.location.length > 0) {
await tx.choiceModuleLocation.createMany({
data: module.location.map((loc) => ({
choiceModuleId: baseModule.id,
locationId: loc.id,
})),
});
}

if (module.moduleTags.length > 0) {
await tx.choiceModuleTags.createMany({
data: module.moduleTags.map((tag) => ({
choiceModuleId: baseModule.id,
moduleTagsId: tag.id,
})),
});
}

return tx.choiceModule.findUnique({
where: { id: baseModule.id },
include: {
location: { include: { Location: true } },
moduleTags: { include: { ModuleTags: true } },
},
});
});

if (!created) {
throw new Error('Failed to create module');
}

return {
id: created.id,
name: created.name!,
description: created.description!,
content: created.content!,
level: created.level!,
studyCredits: created.studyCredits!,
startDate: created.startDate,
location: created.location.map((cml) => ({
id: cml.Location.id,
name: cml.Location.name,
})),
moduleTags: created.moduleTags.map((cmt) => ({
id: cmt.ModuleTags.id,
name: cmt.ModuleTags.name,
})),
learningOutcomes: created.learningOutcomes!,
availableSpots: created.availableSpots!,
} as moduleDetail;
}
}
17 changes: 17 additions & 0 deletions src/infrastructure/database/repositories/location.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Injectable } from '@nestjs/common';
import { ILocationRepository } from '@/domain/locations/location-repository.interface';
import { Location } from '@/domain/locations/location.model';
import { PrismaService } from '../prisma';

@Injectable()
export class LocationRepository implements ILocationRepository {
constructor(private readonly prisma: PrismaService) {}

async getAllLocation(): Promise<Location[]> {
const locations = await this.prisma.location.findMany();
return locations.map((loc) => ({
id: loc.id,
name: loc.name,
}));
}
}
16 changes: 16 additions & 0 deletions src/infrastructure/database/repositories/moduletag.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { IModuleTagRepository } from '@/domain/moduletags/moduletag-repository';
import { ModuleTag } from '@/domain/moduletags/moduletag.model';
import { PrismaService } from '@/infrastructure/database/prisma';

@Injectable()
export class ModuleTagRepository implements IModuleTagRepository {
constructor(private readonly prisma: PrismaService) {}
async getAllModuleTags(): Promise<ModuleTag[]> {
const moduleTags = await this.prisma.moduleTags.findMany();
return moduleTags.map((mt) => ({
id: mt.id,
name: mt.name,
}));
}
}
16 changes: 16 additions & 0 deletions src/presentation/controllers/location.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Controller, Get } from '@nestjs/common';
import { ILocationService } from '@/application/ports/location.port';
import { Inject } from '@nestjs/common';
import { Location } from '@/domain/locations/location.model';

@Controller('locations')
export class LocationController {
private readonly locationService: ILocationService;
constructor(@Inject('SERVICE.LOCATION') locationService: ILocationService) {
this.locationService = locationService;
}
@Get()
async getAllLocation(): Promise<Location[]> {
return await this.locationService.getAllLocation();
}
}
45 changes: 43 additions & 2 deletions src/presentation/controllers/modules.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { Controller, Get, Param, Session } from '@nestjs/common';
import { SessionData } from 'express-session';
import {
Controller,
Get,
Param,
Post,
Session,
UnauthorizedException,
Body,
} from '@nestjs/common';
import { SessionData } from '@/types/session.types';
import { ModuleService } from '@/application/services/module.service';
import { Inject } from '@nestjs/common';
import {
Module,
moduleDetail,
createModule,
} from '@/domain/modules/module.model';

@Controller('modules')
export class ModulesController {
private readonly moduleService: ModuleService;
Expand Down Expand Up @@ -32,4 +46,31 @@ export class ModulesController {
module: await this.moduleService.findById(id),
};
}
@Post()
async createModule(
@Session() session: SessionData,
@Body() moduleData: createModule,
): Promise<any> {
if (!session) {
throw new UnauthorizedException('No active session');
}
if (!session.user || session.user.role === 'STUDENT') {
console.log(session.user);
return { message: 'Unauthorized' };
}

if (!session.user || session.user.role === 'STUDENT') {
console.log(session.user);
return { message: `Unauthorized ${session.user}` };
}
const module = await this.moduleService.createModule(moduleData);
if (!module) {
console.log(module);
return { message: `Failed to create module` };
}

return {
module,
};
}
}
Loading