Skip to content
Open
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
1,745 changes: 1,725 additions & 20 deletions root/week06/server/package-lock.json

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions root/week06/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon --exec tsx src/index.ts"
"swagger": "tsoa spec-and-routes",
"dev": "npm run swagger && nodemon --exec tsx src/index.ts"
},
"keywords": [],
"author": "",
Expand All @@ -14,12 +15,19 @@
"dependencies": {
"@prisma/adapter-mariadb": "^7.8.0",
"@prisma/client": "^7.8.0",
"cookie-parser": "^1.4.7",
"dotenv": "^17.4.2",
"express": "^5.2.1"
"express": "^5.2.1",
"morgan": "^1.10.1",
"swagger-ui-express": "^5.0.1",
"tsoa": "^7.0.0-alpha.0"
},
"devDependencies": {
"@types/cookie-parser": "^1.4.10",
"@types/express": "^5.0.6",
"@types/morgan": "^1.9.10",
"@types/node": "^25.6.2",
"@types/swagger-ui-express": "^4.1.8",
"nodemon": "^3.1.14",
"prisma": "^7.8.0",
"tsx": "^4.21.0",
Expand Down
273 changes: 273 additions & 0 deletions root/week06/server/public/swagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
{
"openapi": "3.0.0",
"components": {
"examples": {},
"headers": {},
"parameters": {},
"requestBodies": {},
"responses": {},
"schemas": {
"SR": {
"properties": {
"isSuccess": {
"type": "boolean",
"enum": [
true
],
"nullable": false
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"result": {}
},
"required": [
"isSuccess",
"code",
"message",
"result"
],
"type": "object",
"additionalProperties": false
},
"ErrorResponse": {
"properties": {
"isSuccess": {
"type": "boolean",
"enum": [
false
],
"nullable": false
},
"code": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"isSuccess",
"code",
"message"
],
"type": "object",
"additionalProperties": false
}
},
"securitySchemes": {}
},
"info": {
"title": "server",
"version": "1.0.0",
"license": {
"name": "ISC"
},
"contact": {}
},
"paths": {
"/api/users/{userId}/reviews": {
"get": {
"operationId": "GetMyReviews",
"responses": {
"200": {
"description": "성공",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SR"
}
}
}
},
"404": {
"description": "존재하지 않는 유저입니다.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"description": "내가 작성한 리뷰 목록을 조회합니다.",
"tags": [
"User"
],
"security": [],
"parameters": [
{
"in": "path",
"name": "userId",
"required": true,
"schema": {
"format": "double",
"type": "number"
}
}
]
}
},
"/api/users/{userId}/missions": {
"get": {
"operationId": "GetMyMissions",
"responses": {
"200": {
"description": "성공",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SR"
}
}
}
},
"404": {
"description": "존재하지 않는 유저입니다.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"description": "내가 진행 중인 미션 목록을 조회합니다.",
"tags": [
"User"
],
"security": [],
"parameters": [
{
"in": "path",
"name": "userId",
"required": true,
"schema": {
"format": "double",
"type": "number"
}
},
{
"in": "query",
"name": "status",
"required": false,
"schema": {
"default": "IN_PROGRESS",
"type": "string",
"enum": [
"IN_PROGRESS",
"COMPLETED"
]
}
}
]
}
},
"/api/stores/{storeId}/missions": {
"get": {
"operationId": "GetStoreMissions",
"responses": {
"200": {
"description": "성공",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SR"
}
}
}
},
"404": {
"description": "존재하지 않는 가게입니다.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"description": "특정 가게의 미션 목록을 조회합니다.",
"tags": [
"Store"
],
"security": [],
"parameters": [
{
"in": "path",
"name": "storeId",
"required": true,
"schema": {
"format": "double",
"type": "number"
}
}
]
}
},
"/api/user-missions/{userMissionId}/complete": {
"patch": {
"operationId": "CompleteMission",
"responses": {
"200": {
"description": "성공",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SR"
}
}
}
},
"404": {
"description": "존재하지 않는 미션입니다.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"409": {
"description": "이미 완료된 미션입니다.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"description": "진행 중인 미션을 완료 처리합니다.",
"tags": [
"Mission"
],
"security": [],
"parameters": [
{
"in": "path",
"name": "userMissionId",
"required": true,
"schema": {
"format": "double",
"type": "number"
}
}
]
}
}
},
"servers": [
{
"url": "/"
}
]
}
22 changes: 22 additions & 0 deletions root/week06/server/src/controllers/MissionController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Controller, Patch, Path, Route, Response, SuccessResponse, Tags } from "tsoa";
import * as repo from "../repositories/mission.repository";
import { ErrorResponse, SuccessResponse as SR } from "../types";

@Route("api/user-missions")
@Tags("Mission")
export class MissionController extends Controller {

/**
* 진행 중인 미션을 완료 처리합니다.
*/
@Patch("{userMissionId}/complete")
@SuccessResponse("200", "성공")
@Response<ErrorResponse>(404, "존재하지 않는 미션입니다.")
@Response<ErrorResponse>(409, "이미 완료된 미션입니다.")
public async completeMission(
@Path() userMissionId: number
): Promise<SR> {
const result = await repo.completeMission(userMissionId);
return { isSuccess: true, code: "2000", message: "성공", result };
}
}
21 changes: 21 additions & 0 deletions root/week06/server/src/controllers/StoreController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Controller, Get, Path, Route, Response, SuccessResponse, Tags } from "tsoa";
import * as repo from "../repositories/store.repository";
import { ErrorResponse, SuccessResponse as SR } from "../types";

@Route("api/stores")
@Tags("Store")
export class StoreController extends Controller {

/**
* 특정 가게의 미션 목록을 조회합니다.
*/
@Get("{storeId}/missions")
@SuccessResponse("200", "성공")
@Response<ErrorResponse>(404, "존재하지 않는 가게입니다.")
public async getStoreMissions(
@Path() storeId: number
): Promise<SR> {
const result = await repo.getStoreMissions(storeId);
return { isSuccess: true, code: "2000", message: "성공", result };
}
}
35 changes: 35 additions & 0 deletions root/week06/server/src/controllers/UserController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Controller, Get, Path, Query, Route, Response, SuccessResponse, Tags } from "tsoa";
import * as repo from "../repositories/user.repository";
import { ErrorResponse, SuccessResponse as SR } from "../types";

@Route("api/users")
@Tags("User")
export class UserController extends Controller {

/**
* 내가 작성한 리뷰 목록을 조회합니다.
*/
@Get("{userId}/reviews")
@SuccessResponse("200", "성공")
@Response<ErrorResponse>(404, "존재하지 않는 유저입니다.")
public async getMyReviews(
@Path() userId: number
): Promise<SR> {
const result = await repo.getMyReviews(userId);
return { isSuccess: true, code: "2000", message: "성공", result };
}

/**
* 내가 진행 중인 미션 목록을 조회합니다.
*/
@Get("{userId}/missions")
@SuccessResponse("200", "성공")
@Response<ErrorResponse>(404, "존재하지 않는 유저입니다.")
public async getMyMissions(
@Path() userId: number,
@Query() status: "IN_PROGRESS" | "COMPLETED" = "IN_PROGRESS"
): Promise<SR> {
const result = await repo.getMyMissions(userId, status);
return { isSuccess: true, code: "2000", message: "성공", result };
}
}
Loading