Compare commits
10 Commits
main
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
7fa7e4448d | |||
7aab149bb2 | |||
e3365cdff7 | |||
2d35fc413c | |||
95ccfc884c | |||
341faa3bb0 | |||
8e63816616 | |||
db88a24028 | |||
7fc3e14612 | |||
1c19158771 |
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@toogether/server",
|
"name": "@toogether/api",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"build:docker": "docker build --no-cache -t toogether/api .",
|
"build:docker": "docker build -t toogether/api .",
|
||||||
"format": "prettier --write \"src/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "docker compose up --force-recreate -d",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\"",
|
"preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\"",
|
||||||
"migrate:dev": "npx prisma migrate dev",
|
"migrate:dev": "npx prisma migrate dev",
|
||||||
|
@ -10,7 +10,6 @@ datasource db {
|
|||||||
model User {
|
model User {
|
||||||
id String @id
|
id String @id
|
||||||
username String
|
username String
|
||||||
role Role @default(STUDENT)
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
Class Class[]
|
Class Class[]
|
||||||
@ -104,9 +103,4 @@ model RoomSurveyAnswerUser {
|
|||||||
userId String
|
userId String
|
||||||
Answer RoomSurveyAnswer @relation(fields: [answerId], references: [id])
|
Answer RoomSurveyAnswer @relation(fields: [answerId], references: [id])
|
||||||
answerId Int
|
answerId Int
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Role {
|
|
||||||
STUDENT
|
|
||||||
ADMIN
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
import { SetMetadata } from "@nestjs/common";
|
import { SetMetadata } from "@nestjs/common";
|
||||||
import { $Enums } from "@prisma/client";
|
|
||||||
|
|
||||||
export const Roles = (roles: $Enums.Role[]) => SetMetadata("roles", roles);
|
export const Roles = (roles: string[]) => SetMetadata("roles", roles);
|
||||||
|
@ -7,12 +7,13 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
|
import { decode, JwtPayload, UserJwtPayload } from "jsonwebtoken";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RolesGuard implements CanActivate {
|
export class RolesGuard implements CanActivate {
|
||||||
constructor(private readonly reflector: Reflector) {}
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
canActivate(context: ExecutionContext): boolean {
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const RolesHandler = this.reflector.get<string[]>(
|
const RolesHandler = this.reflector.get<string[]>(
|
||||||
"roles",
|
"roles",
|
||||||
context.getHandler(),
|
context.getHandler(),
|
||||||
@ -26,14 +27,24 @@ export class RolesGuard implements CanActivate {
|
|||||||
|
|
||||||
const request = context.switchToHttp().getRequest() as Request;
|
const request = context.switchToHttp().getRequest() as Request;
|
||||||
const user = request.user;
|
const user = request.user;
|
||||||
|
|
||||||
if (!user) throw new ForbiddenException("User not authenticated");
|
if (!user) throw new ForbiddenException("User not authenticated");
|
||||||
|
|
||||||
|
const decodedToken = await this.decodeToken(
|
||||||
|
this.extractTokenFromHeader(request.headers.authorization),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the user has the right role
|
||||||
|
// On the handler level
|
||||||
const hasRoleHandler =
|
const hasRoleHandler =
|
||||||
RolesHandler?.some((role) => user.role?.includes(role)) ??
|
RolesHandler?.some((role) =>
|
||||||
false,
|
decodedToken.realm_access.roles?.includes(role),
|
||||||
|
) ?? false,
|
||||||
|
// On the class level
|
||||||
hasRoleClass =
|
hasRoleClass =
|
||||||
RolesClass?.some((role) => user.role?.includes(role)) ?? false;
|
RolesClass?.some((role) =>
|
||||||
|
decodedToken.realm_access.roles?.includes(role),
|
||||||
|
) ?? false;
|
||||||
|
|
||||||
if (hasRoleHandler) return true;
|
if (hasRoleHandler) return true;
|
||||||
else if (hasRoleClass) return true;
|
else if (hasRoleClass) return true;
|
||||||
@ -42,4 +53,20 @@ export class RolesGuard implements CanActivate {
|
|||||||
`User doesn't have the right role, expected: ${RolesHandler ?? RolesClass}`,
|
`User doesn't have the right role, expected: ${RolesHandler ?? RolesClass}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async decodeToken(token: string) {
|
||||||
|
try {
|
||||||
|
return decode(token) as UserJwtPayload;
|
||||||
|
} catch (error) {
|
||||||
|
throw new UnauthorizedException("Invalid token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extractTokenFromHeader(header: string) {
|
||||||
|
const token = header.split(" ")[1];
|
||||||
|
|
||||||
|
if (!token) throw new UnauthorizedException("Token not found");
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ import { ClassRoomEntity } from "./entities/room.entity";
|
|||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Roles(["ADMIN"])
|
@Roles(["admin"])
|
||||||
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
||||||
export class ClassController {
|
export class ClassController {
|
||||||
constructor(private readonly classService: ClassService) { }
|
constructor(private readonly classService: ClassService) { }
|
||||||
|
@ -8,7 +8,7 @@ import * as moment from "moment";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ClassService {
|
export class ClassService {
|
||||||
constructor(private readonly prisma: PrismaService) { }
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async create(createClassDto: CreateClassDto) {
|
async create(createClassDto: CreateClassDto) {
|
||||||
return await this.prisma.class.create({ data: createClassDto });
|
return await this.prisma.class.create({ data: createClassDto });
|
||||||
@ -20,7 +20,7 @@ export class ClassService {
|
|||||||
cursor,
|
cursor,
|
||||||
where,
|
where,
|
||||||
orderBy,
|
orderBy,
|
||||||
include
|
include,
|
||||||
}: {
|
}: {
|
||||||
skip?: number;
|
skip?: number;
|
||||||
take?: number;
|
take?: number;
|
||||||
@ -35,7 +35,7 @@ export class ClassService {
|
|||||||
cursor,
|
cursor,
|
||||||
where,
|
where,
|
||||||
orderBy,
|
orderBy,
|
||||||
include
|
include,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,12 +68,11 @@ export class ClassService {
|
|||||||
include: { Students: true },
|
include: { Students: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Class)
|
if (!Class) throw new HttpException("Class not found", 404);
|
||||||
throw new HttpException("Class not found", 404);
|
|
||||||
|
|
||||||
const studentIdsToAdd = studentIds.filter(
|
const studentIdsToAdd = studentIds.filter(
|
||||||
(studentId) =>
|
(studentId) =>
|
||||||
!Class.Students.some((student) => student.id === studentId)
|
!Class.Students.some((student) => student.id === studentId),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (studentIdsToAdd.length === 0) return Class;
|
if (studentIdsToAdd.length === 0) return Class;
|
||||||
@ -82,7 +81,9 @@ export class ClassService {
|
|||||||
where: { id: classId },
|
where: { id: classId },
|
||||||
data: {
|
data: {
|
||||||
Students: {
|
Students: {
|
||||||
connect: studentIdsToAdd.map((studentId) => ({ id: studentId })),
|
connect: studentIdsToAdd.map((studentId) => ({
|
||||||
|
id: studentId,
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -97,18 +98,40 @@ export class ClassService {
|
|||||||
|
|
||||||
async createRoom(classId: string, createRoomClassDto: CreateRoomClassDto) {
|
async createRoom(classId: string, createRoomClassDto: CreateRoomClassDto) {
|
||||||
// Check if end time is greater than start time
|
// Check if end time is greater than start time
|
||||||
const invalidTime = createRoomClassDto.times.find(
|
const invalidTime = createRoomClassDto.times.find((time, i) =>
|
||||||
(time) => moment(time.start, "HH:mm").isAfter(moment(time.end, "HH:mm"))
|
moment(time.start, "HH:mm").isAfter(moment(time.end, "HH:mm")),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (invalidTime)
|
if (invalidTime)
|
||||||
throw new HttpException("Invalid time", 400);
|
throw new HttpException(
|
||||||
|
`The end time must be greater than the start time, ${invalidTime.start} - ${invalidTime.end}`,
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
|
||||||
// Check if date is in the past
|
const date = moment(createRoomClassDto.date);
|
||||||
const invalidDate = moment(createRoomClassDto.date).isBefore(moment());
|
|
||||||
|
|
||||||
if (invalidDate)
|
// Check if the date is before the current date
|
||||||
throw new HttpException("Invalid date", 400);
|
if (date.isBefore(moment().startOf("day")))
|
||||||
|
throw new HttpException(
|
||||||
|
"Can't create a room for a class that has passed",
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
|
||||||
|
const parseTimes = createRoomClassDto.times.map((time) => ({
|
||||||
|
start: moment(date).set("hour", moment(time.start, "HH:mm").hour()),
|
||||||
|
end: moment(date).set("hour", moment(time.end, "HH:mm").hour()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const lastTimeEnd = parseTimes.reduce((prev, current) =>
|
||||||
|
moment(current.end).isAfter(moment(prev.end)) ? current : prev,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the last time end is before the current time
|
||||||
|
if (moment(lastTimeEnd.end).isBefore(moment().subtract(5, "minutes")))
|
||||||
|
throw new HttpException(
|
||||||
|
"Can't create a room for a class that has already ended",
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
|
||||||
return await this.prisma.room.create({
|
return await this.prisma.room.create({
|
||||||
include: { Times: true },
|
include: { Times: true },
|
||||||
@ -132,10 +155,14 @@ export class ClassService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getRooms(classId: string) {
|
async getRooms(classId: string) {
|
||||||
return await this.prisma.class.findUnique({
|
return await this.prisma.class
|
||||||
where: { id: classId },
|
.findUnique({
|
||||||
include: { ClassRoom: { include: { Times: true, Presentator: true } } },
|
where: { id: classId },
|
||||||
}).then((class_) => class_.ClassRoom);
|
include: {
|
||||||
|
ClassRoom: { include: { Times: true, Presentator: true } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((class_) => class_.ClassRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserClasses(userId: string) {
|
async getUserClasses(userId: string) {
|
||||||
|
@ -6,6 +6,7 @@ import { JwtAuthGuard } from '../auth/guards/jwt.guard';
|
|||||||
import { RolesGuard } from '../auth/guards/role.guard';
|
import { RolesGuard } from '../auth/guards/role.guard';
|
||||||
import { isUserInClassGuard } from './guards/isUserInClass.guard';
|
import { isUserInClassGuard } from './guards/isUserInClass.guard';
|
||||||
import { MeService } from './me.service';
|
import { MeService } from './me.service';
|
||||||
|
import { UserService } from '../user/user.service';
|
||||||
|
|
||||||
@Controller('@me')
|
@Controller('@me')
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@ -13,7 +14,16 @@ import { MeService } from './me.service';
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
||||||
export class MeController {
|
export class MeController {
|
||||||
constructor(private readonly meService: MeService) { }
|
constructor(
|
||||||
|
private readonly meService: MeService,
|
||||||
|
private readonly userService: UserService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOkResponse({ description: 'Get my profile' })
|
||||||
|
async getMyProfile(@Req() req: Request) {
|
||||||
|
return await this.userService.getProfile(req.user.id);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("/class")
|
@Get("/class")
|
||||||
@ApiOkResponse({ description: 'Get all classes' })
|
@ApiOkResponse({ description: 'Get all classes' })
|
||||||
|
@ -3,7 +3,9 @@ import { ClassService } from '../class/class.service';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MeService {
|
export class MeService {
|
||||||
constructor(private readonly classService: ClassService) { }
|
constructor(
|
||||||
|
private readonly classService: ClassService,
|
||||||
|
) { }
|
||||||
|
|
||||||
async getMyClasses(userId: string) {
|
async getMyClasses(userId: string) {
|
||||||
return await this.classService.getUserClasses(userId);
|
return await this.classService.getUserClasses(userId);
|
||||||
|
@ -2,18 +2,25 @@ import { ApiResponseNoStatusOptions } from "@nestjs/swagger";
|
|||||||
import { UserEntity } from "../entities/user.entity";
|
import { UserEntity } from "../entities/user.entity";
|
||||||
|
|
||||||
export const UserResponse = {
|
export const UserResponse = {
|
||||||
type: UserEntity,
|
type: UserEntity,
|
||||||
description: "The user has been successfully found.",
|
description: "The user has been successfully found.",
|
||||||
examples: {
|
examples: {
|
||||||
example: {
|
example: {
|
||||||
summary: "A user example",
|
summary: "A user example",
|
||||||
value: {
|
value: {
|
||||||
id: "1",
|
id: "1",
|
||||||
role: "ADMIN",
|
role: "ADMIN",
|
||||||
username: "admin",
|
username: "admin",
|
||||||
} as UserEntity,
|
Class: [
|
||||||
},
|
{
|
||||||
},
|
id: "1",
|
||||||
|
name: "Class 1",
|
||||||
|
createdAt: new Date(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as UserEntity,
|
||||||
|
},
|
||||||
|
},
|
||||||
} as ApiResponseNoStatusOptions;
|
} as ApiResponseNoStatusOptions;
|
||||||
|
|
||||||
export const UsersResponse = {
|
export const UsersResponse = {
|
||||||
@ -23,19 +30,19 @@ export const UsersResponse = {
|
|||||||
example: {
|
example: {
|
||||||
summary: "A list of users",
|
summary: "A list of users",
|
||||||
value: [
|
value: [
|
||||||
{ id: "1", role: "ADMIN", username: "admin" },
|
{ id: "1", username: "admin" },
|
||||||
{ id: "2", role: "STUDENT", username: "student" },
|
{ id: "2", username: "student" },
|
||||||
] as UserEntity[],
|
] as UserEntity[],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as ApiResponseNoStatusOptions;
|
} as ApiResponseNoStatusOptions;
|
||||||
|
|
||||||
export const UserCountResponse = {
|
export const UserCountResponse = {
|
||||||
description: "The users count",
|
description: "The users count",
|
||||||
examples: {
|
examples: {
|
||||||
example: {
|
example: {
|
||||||
summary: "A count of users",
|
summary: "A count of users",
|
||||||
value: { count: 2 },
|
value: { count: 2 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as ApiResponseNoStatusOptions;
|
} as ApiResponseNoStatusOptions;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { Role } from "@prisma/client";
|
|
||||||
import { IsString } from "class-validator";
|
import { IsString } from "class-validator";
|
||||||
|
|
||||||
export class CreateUserDTO {
|
export class CreateUserDTO {
|
||||||
@ -10,8 +9,4 @@ export class CreateUserDTO {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
@IsString()
|
|
||||||
@ApiProperty()
|
|
||||||
role: Role;
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { ClassEntity } from "@/modules/class/entities/class.entity";
|
||||||
import { ApiProperty, ApiSchema } from "@nestjs/swagger";
|
import { ApiProperty, ApiSchema } from "@nestjs/swagger";
|
||||||
import { $Enums } from "@prisma/client";
|
|
||||||
import { Expose } from "class-transformer";
|
import { Expose } from "class-transformer";
|
||||||
|
|
||||||
@ApiSchema({ name: "User" })
|
@ApiSchema({ name: "User" })
|
||||||
@ -14,7 +14,7 @@ export class UserEntity {
|
|||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: $Enums.Role;
|
Class?: ClassEntity[];
|
||||||
|
|
||||||
constructor(partial: Partial<UserEntity>) {
|
constructor(partial: Partial<UserEntity>) {
|
||||||
Object.assign(this, partial);
|
Object.assign(this, partial);
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
Patch,
|
Patch,
|
||||||
Query,
|
Query,
|
||||||
Req,
|
Req,
|
||||||
UseGuards
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import {
|
import {
|
||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
@ -19,8 +19,9 @@ import {
|
|||||||
ApiOperation,
|
ApiOperation,
|
||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
ApiQuery,
|
||||||
ApiUnauthorizedResponse
|
ApiUnauthorizedResponse,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
|
import { Request } from "express";
|
||||||
import {
|
import {
|
||||||
UserCountResponse,
|
UserCountResponse,
|
||||||
UserResponse,
|
UserResponse,
|
||||||
@ -29,13 +30,12 @@ import {
|
|||||||
import { UpdateUserDTO } from "./dto/update-user.dto";
|
import { UpdateUserDTO } from "./dto/update-user.dto";
|
||||||
import { UserEntity } from "./entities/user.entity";
|
import { UserEntity } from "./entities/user.entity";
|
||||||
import { UserService } from "./user.service";
|
import { UserService } from "./user.service";
|
||||||
import { Request } from "express";
|
|
||||||
|
|
||||||
@Controller("user")
|
@Controller("user")
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Roles(["ADMIN"])
|
@Roles(["admin"])
|
||||||
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
@ApiUnauthorizedResponse(UnauthorizedResponse)
|
||||||
export class UserController {
|
export class UserController {
|
||||||
constructor(private readonly userService: UserService) {}
|
constructor(private readonly userService: UserService) {}
|
||||||
@ -45,14 +45,21 @@ export class UserController {
|
|||||||
@ApiOperation({ summary: "Get all users" })
|
@ApiOperation({ summary: "Get all users" })
|
||||||
async findAll(): Promise<UserEntity[]> {
|
async findAll(): Promise<UserEntity[]> {
|
||||||
return await this.userService
|
return await this.userService
|
||||||
.findAll()
|
.findAll({
|
||||||
|
include: {
|
||||||
|
Class: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
.then((users) => users.map((user) => new UserEntity(user)));
|
.then((users) => users.map((user) => new UserEntity(user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
@ApiOkResponse(UserResponse)
|
@ApiOkResponse(UserResponse)
|
||||||
@ApiOperation({ summary: "Get user by id" })
|
@ApiOperation({ summary: "Get user by id" })
|
||||||
async findOne(@Param("id") id: string, @Req() req: Request): Promise<UserEntity> {
|
async findOne(
|
||||||
|
@Param("id") id: string,
|
||||||
|
@Req() req: Request,
|
||||||
|
): Promise<UserEntity> {
|
||||||
if (id === "@me") id = req.user.id;
|
if (id === "@me") id = req.user.id;
|
||||||
|
|
||||||
return this.userService
|
return this.userService
|
||||||
|
@ -7,7 +7,7 @@ import { UpdateUserDTO } from "./dto/update-user.dto";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(private readonly prisma: PrismaService) { }
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async findAll({
|
async findAll({
|
||||||
skip,
|
skip,
|
||||||
@ -15,12 +15,14 @@ export class UserService {
|
|||||||
cursor,
|
cursor,
|
||||||
where,
|
where,
|
||||||
orderBy,
|
orderBy,
|
||||||
|
include,
|
||||||
}: {
|
}: {
|
||||||
skip?: number;
|
skip?: number;
|
||||||
take?: number;
|
take?: number;
|
||||||
cursor?: Prisma.UserWhereUniqueInput;
|
cursor?: Prisma.UserWhereUniqueInput;
|
||||||
where?: Prisma.UserWhereInput;
|
where?: Prisma.UserWhereInput;
|
||||||
orderBy?: Record<string, unknown>;
|
orderBy?: Record<string, unknown>;
|
||||||
|
include?: Prisma.UserInclude;
|
||||||
} = {}) {
|
} = {}) {
|
||||||
return await this.prisma.user.findMany({
|
return await this.prisma.user.findMany({
|
||||||
skip,
|
skip,
|
||||||
@ -28,6 +30,7 @@ export class UserService {
|
|||||||
cursor,
|
cursor,
|
||||||
where,
|
where,
|
||||||
orderBy,
|
orderBy,
|
||||||
|
include,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +48,10 @@ export class UserService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
const isFirstUser = (await this.prisma.user.count()) === 0;
|
|
||||||
|
|
||||||
user = await this.prisma.user.create({
|
user = await this.prisma.user.create({
|
||||||
data: {
|
data: {
|
||||||
id,
|
id,
|
||||||
username,
|
username,
|
||||||
role: isFirstUser ? "ADMIN" : "STUDENT",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (user.username !== username) {
|
} else if (user.username !== username) {
|
||||||
@ -80,7 +80,6 @@ export class UserService {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
username: updateUserInput.username,
|
username: updateUserInput.username,
|
||||||
role: updateUserInput.role,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,4 +99,14 @@ export class UserService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getProfile(id: string) {
|
||||||
|
return await this.prisma.user.findUnique({
|
||||||
|
where: { id },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
2
src/types/http.d.ts
vendored
2
src/types/http.d.ts
vendored
@ -4,6 +4,6 @@ import { IncomingMessage } from "http";
|
|||||||
|
|
||||||
declare module "http" {
|
declare module "http" {
|
||||||
interface IncomingMessage {
|
interface IncomingMessage {
|
||||||
user?: UserEntity;
|
user?: User;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/types/jwt.d.ts
vendored
Normal file
14
src/types/jwt.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import * as jwt from "jsonwebtoken";
|
||||||
|
|
||||||
|
declare module "jsonwebtoken" {
|
||||||
|
export interface UserJwtPayload extends jwt.JwtPayload {
|
||||||
|
name: string;
|
||||||
|
preferred_username: string;
|
||||||
|
email: string;
|
||||||
|
given_name: string;
|
||||||
|
family_name: string;
|
||||||
|
realm_access: {
|
||||||
|
roles: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user