Skip to content

Commit

Permalink
Merge pull request #61 from Z00One/feat/quiz
Browse files Browse the repository at this point in the history
クイズCRUDの実装
  • Loading branch information
Z00One authored Apr 6, 2024
2 parents e4e6f60 + 6633b4c commit 913935e
Show file tree
Hide file tree
Showing 25 changed files with 1,182 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Warnings:
- The primary key for the `ClassUserQuiz` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `SetQuiz` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `id` on the `SetQuiz` table. All the data in the column will be lost.
- Added the required column `updated_at` to the `ClassUserQuiz` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "ClassUserQuiz" DROP CONSTRAINT "ClassUserQuiz_q_id_fkey";

-- DropForeignKey
ALTER TABLE "ClassUserQuiz" DROP CONSTRAINT "ClassUserQuiz_s_id_fkey";

-- DropForeignKey
ALTER TABLE "QuizFeedback" DROP CONSTRAINT "QuizFeedback_s_id_fkey";

-- AlterTable
ALTER TABLE "ClassUserQuiz" DROP CONSTRAINT "ClassUserQuiz_pkey",
ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL,
ADD CONSTRAINT "ClassUserQuiz_pkey" PRIMARY KEY ("u_id", "c_id");

-- AlterTable
ALTER TABLE "SetQuiz" DROP CONSTRAINT "SetQuiz_pkey",
DROP COLUMN "id",
ADD CONSTRAINT "SetQuiz_pkey" PRIMARY KEY ("m_id");

-- CreateTable
CREATE TABLE "QuizList" (
"q_id" BIGINT NOT NULL,
"s_id" BIGINT NOT NULL,

CONSTRAINT "QuizList_pkey" PRIMARY KEY ("q_id","s_id")
);

-- AddForeignKey
ALTER TABLE "ClassUserQuiz" ADD CONSTRAINT "ClassUserQuiz_q_id_s_id_fkey" FOREIGN KEY ("q_id", "s_id") REFERENCES "QuizList"("q_id", "s_id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "QuizFeedback" ADD CONSTRAINT "QuizFeedback_s_id_fkey" FOREIGN KEY ("s_id") REFERENCES "SetQuiz"("m_id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "QuizList" ADD CONSTRAINT "QuizList_q_id_fkey" FOREIGN KEY ("q_id") REFERENCES "Quiz"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "QuizList" ADD CONSTRAINT "QuizList_s_id_fkey" FOREIGN KEY ("s_id") REFERENCES "SetQuiz"("m_id") ON DELETE CASCADE ON UPDATE CASCADE;
30 changes: 20 additions & 10 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ model ClassUser {
model Material {
id BigInt @id @default(autoincrement())
file File?
setQuizs SetQuiz?
name String @db.VarChar(100)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
prompts Prompt[]
quizs Quiz[]
setQuizs SetQuiz[]
u_id BigInt?
c_id BigInt
Expand Down Expand Up @@ -116,7 +116,7 @@ model Quiz {
content Json
created_at DateTime @default(now())
class_user_quizs ClassUserQuiz[]
quizLists QuizList[]
m_id BigInt
material Material @relation(fields: [m_id], references: [id], onDelete: Cascade)
Expand All @@ -125,31 +125,29 @@ model Quiz {
model ClassUserQuiz {
result Boolean?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
u_id BigInt
c_id BigInt
class_user ClassUser @relation(fields: [u_id, c_id], references: [u_id, c_id], onDelete: Cascade)
q_id BigInt
quiz Quiz @relation(fields: [q_id], references: [id], onDelete: Cascade)
s_id BigInt
setQuiz SetQuiz @relation(fields: [s_id], references: [id], onDelete: Cascade)
quizList QuizList @relation(fields: [q_id, s_id], references: [q_id, s_id], onDelete: Cascade)
@@id([u_id, c_id, q_id, s_id])
@@id([u_id, c_id])
}

model SetQuiz {
id BigInt @id @default(autoincrement())
created_at DateTime @default(now())
updated_at DateTime @updatedAt
deadline DateTime
quizFeedbacks QuizFeedback[]
class_user_quizs ClassUserQuiz[]
quizLists QuizList[]
m_id BigInt
m_id BigInt @id
material Material @relation(fields: [m_id], references: [id], onDelete: Cascade)
}

Expand All @@ -164,5 +162,17 @@ model QuizFeedback {
class_user ClassUser @relation(fields: [u_id, c_id], references: [u_id, c_id], onDelete: Cascade)
s_id BigInt
setQuiz SetQuiz @relation(fields: [s_id], references: [id], onDelete: Cascade)
setQuiz SetQuiz @relation(fields: [s_id], references: [m_id], onDelete: Cascade)
}

model QuizList {
classUserQuizs ClassUserQuiz[]
q_id BigInt
quiz Quiz @relation(fields: [q_id], references: [id], onDelete: Cascade)
s_id BigInt
setQuiz SetQuiz @relation(fields: [s_id], references: [m_id], onDelete: Cascade)
@@id([q_id, s_id])
}
10 changes: 10 additions & 0 deletions src/common/decorators/metadata.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SetMetadata } from '@nestjs/common';

export enum Key {
ROLES = 'roles',
MODELS = 'models',
ID_PARAM = 'id',
}

export const MetaData = <T>(key: Key, models: T) =>
SetMetadata(key, models);
7 changes: 0 additions & 7 deletions src/common/decorators/model.decorator.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/common/decorators/role.decorator.ts

This file was deleted.

10 changes: 7 additions & 3 deletions src/common/decorators/use-owner-guards.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { applyDecorators, UseGuards } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { ApiForbiddenResponse } from '@nestjs/swagger';
import { Models } from './model.decorator';
import { OwnerGuard } from '@common/guards/owner.guard';
import { Key, MetaData } from './metadata.decorator';

export const UseOwnerGuards = (model: Prisma.ModelName) => {
export const UseOwnerGuards = (
model: Prisma.ModelName,
id: string = 'id',
) => {
return applyDecorators(
Models(model),
MetaData<Prisma.ModelName>(Key.MODELS, model),
MetaData<string>(Key.ID_PARAM, id),
UseGuards(OwnerGuard),
ApiForbiddenResponse({
description: 'このアクセスする権限がありません。',
Expand Down
4 changes: 2 additions & 2 deletions src/common/decorators/use-role-guards.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { applyDecorators, UseGuards } from '@nestjs/common';
import { Roles } from './role.decorator';
import { Role } from '@prisma/client';
import { RolesGuard } from '@common/guards/role.guard';
import { ApiForbiddenResponse } from '@nestjs/swagger';
import { Key, MetaData } from './metadata.decorator';

export const UseRoleGuards = (
role: Role[] = [Role.ADMIN, Role.USER, Role.ASSISTANT],
Expand All @@ -16,7 +16,7 @@ export const UseRoleGuards = (
description = '管理者権限が必要です。';
}
return applyDecorators(
Roles(role),
MetaData<Role[]>(Key.ROLES, role),
UseGuards(RolesGuard),
ApiForbiddenResponse({
description:
Expand Down
15 changes: 15 additions & 0 deletions src/common/decorators/use-role-or-owner-guards.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { applyDecorators, UseGuards } from '@nestjs/common';
import { Prisma, Role } from '@prisma/client';
import { RoleOrOwnerGuard } from '@common/guards/role-or-owner.guard';
import { Key, MetaData } from './metadata.decorator';

export const UseRoleOrOwnerGuards = (
role: Role[],
model: Prisma.ModelName,
) => {
return applyDecorators(
MetaData<Prisma.ModelName>(Key.MODELS, model),
MetaData<Role[]>(Key.ROLES, role),
UseGuards(RoleOrOwnerGuard),
);
};
45 changes: 29 additions & 16 deletions src/common/guards/owner.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
import { Reflector } from '@nestjs/core';
import { Prisma } from '@prisma/client';
import { Request } from 'express';
import { KEY_OF_MODELS } from '@common/decorators/model.decorator';
import { PrismaService } from '@modules/prisma/prisma.service';
import { Key } from '@common/decorators/metadata.decorator';

@Injectable()
export class OwnerGuard implements CanActivate {
Expand All @@ -25,35 +25,48 @@ export class OwnerGuard implements CanActivate {
.getRequest<Request>();

const model = this.reflector.get<Prisma.ModelName>(
KEY_OF_MODELS,
Key.MODELS,
context.getHandler(),
);

switch (model) {
case 'Prompt':
return await this.canActivatePrompt(request);
}
const idParamName = this.reflector.get<string>(
Key.ID_PARAM,
context.getHandler(),
);

return await this.checkOwnership(
request,
model,
idParamName,
);
}

async canActivatePrompt(req: Request) {
private async checkOwnership(
req: Request,
modelName: Prisma.ModelName,
idParamName: string,
): Promise<boolean> {
const userId = req['user'];
const promptId = req.params['id'];
const resourceId = req.params[idParamName];

if (!userId || !promptId) {
if (!userId || !resourceId) {
throw new UnauthorizedException(
'Invalid user or prompt id.',
'Invalid user or parameter.',
);
}

const p_id = BigInt(promptId as string);
const prompt = await this.prisma.prompt.findUnique({
where: { id: p_id },
const resource = await this.prisma[
modelName
].findUnique({
where: { id: BigInt(resourceId) },
});

if (!prompt) {
throw new UnauthorizedException('Invalid prompt id.');
if (!resource) {
throw new UnauthorizedException(
`Invalid ${idParamName}.`,
);
}

return prompt.u_id === BigInt(userId);
return resource.u_id === BigInt(userId);
}
}
35 changes: 35 additions & 0 deletions src/common/guards/role-or-owner.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
Injectable,
CanActivate,
ExecutionContext,
} from '@nestjs/common';
import { OwnerGuard } from './owner.guard';
import { RolesGuard } from './role.guard';

@Injectable()
export class RoleOrOwnerGuard implements CanActivate {
constructor(
private readonly ownerGuard: OwnerGuard,
private readonly roleGuard: RolesGuard,
) {}

async canActivate(
context: ExecutionContext,
): Promise<boolean> {
const hasRole = await this.checkUserRole(context);
if (hasRole) return true;

const isOwner = await this.checkOwnership(context);
if (isOwner) return true;

return false;
}

private checkUserRole(context: ExecutionContext) {
return this.roleGuard.canActivate(context);
}

private checkOwnership(context: ExecutionContext) {
return this.ownerGuard.canActivate(context);
}
}
4 changes: 2 additions & 2 deletions src/common/guards/role.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Reflector } from '@nestjs/core';
import { Role } from '@prisma/client';
import { Request } from 'express';
import { AuthService } from '@modules/auth/auth.service';
import { KEY_OF_ROLES } from '@common/decorators/role.decorator';
import { Key } from '@common/decorators/metadata.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
Expand All @@ -25,7 +25,7 @@ export class RolesGuard implements CanActivate {
.getRequest<Request>();

const requiredRoles = this.reflector.get<Role[]>(
KEY_OF_ROLES,
Key.ROLES,
context.getHandler(),
);

Expand Down
2 changes: 2 additions & 0 deletions src/modules/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AuthGuard } from '@common/guards/auth.guard';
import { JwtModule } from '@nestjs/jwt';
import { SecurityConfig } from '@common/configs/config.interface';
import { MaterialFeedbackModule } from '@modules/material-feedback/material-feedback.module';
import { QuizModule } from '@modules/quiz/quiz.module';

@Module({
imports: [
Expand All @@ -44,6 +45,7 @@ import { MaterialFeedbackModule } from '@modules/material-feedback/material-feed
MaterialModule,
PromptModule,
MaterialFeedbackModule,
QuizModule,
],
controllers: [AppController],
providers: [
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/modules/material-feedback/material-feedback.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AuthModule } from '@modules/auth/auth.module';
import { MaterialFeedbackRepository } from './material-feedback.repository';
import { ReferModule } from '@modules/refer/refer.module';
import { LangchainModule } from './langchain/langchain.module';
import { FileModule } from './file/file.module';
import { FileModule } from '../file/file.module';

@Module({
imports: [
Expand Down
2 changes: 1 addition & 1 deletion src/modules/material-feedback/material-feedback.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { MaterialFeedbackRepository } from './material-feedback.repository';
import { ReferRepository } from '@modules/refer/refer.repository';
import { LangchainService } from './langchain/langchain.service';
import { FileService } from './file/file.service';
import { FileService } from '../file/file.service';
import { Response } from 'express';

@Injectable()
Expand Down
Loading

0 comments on commit 913935e

Please sign in to comment.