From 121bd1d15767b03eb1a8cae7838a0bb79aee80bc Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Thu, 4 Jan 2024 17:08:46 +0900 Subject: [PATCH 01/12] =?UTF-8?q?gitignore=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 39 +++++++++++++++++++++++++++++++++++++++ BE/musicspot/.gitignore | 37 ------------------------------------- 2 files changed, 39 insertions(+), 37 deletions(-) delete mode 100644 BE/musicspot/.gitignore diff --git a/.gitignore b/.gitignore index 1f603ff..4656b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,42 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/macos,windows,xcode,swift,swiftpackagemanager node_modules + + +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +.env \ No newline at end of file diff --git a/BE/musicspot/.gitignore b/BE/musicspot/.gitignore deleted file mode 100644 index ea48d65..0000000 --- a/BE/musicspot/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -# compiled output -/dist -/node_modules - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -.env \ No newline at end of file From 1fa33f70b3a0950a3bafd1949af0433e0dab7a69 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 5 Jan 2024 21:31:09 +0900 Subject: [PATCH 02/12] =?UTF-8?q?:memo:=20Gitignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배포 관련 dist 파일 gitignore 추가 --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4656b1e..651fa0a 100644 --- a/.gitignore +++ b/.gitignore @@ -168,8 +168,8 @@ node_modules # compiled output -/dist -/node_modules +dist +node_modules # Logs logs From cf9dbc6f96df8e81b14f37313d0fbe9e18924c74 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 5 Jan 2024 21:39:28 +0900 Subject: [PATCH 03/12] =?UTF-8?q?:recycle:=20Spot=20coordinate=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spot 저장 시 coordinate를 해당 journey에 추가 --- BE/musicspot/src/spot/service/spot.service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BE/musicspot/src/spot/service/spot.service.ts b/BE/musicspot/src/spot/service/spot.service.ts index d5cf240..78c8bae 100644 --- a/BE/musicspot/src/spot/service/spot.service.ts +++ b/BE/musicspot/src/spot/service/spot.service.ts @@ -38,11 +38,11 @@ export class SpotService { async insertToSpot(spotData) { const data = { ...spotData, coordinate: JSON.parse(spotData.coordinate) }; const createdSpotData = await new this.spotModel(data).save(); - const spotId = createdSpotData._id; + const {_id, coordinate} = createdSpotData; await this.journeyModel .findOneAndUpdate( { _id: spotData.journeyId }, - { $push: { spots: spotId } }, + { $push: { spots: _id ,coordinates: coordinate} }, { new: true }, ) .lean(); @@ -59,7 +59,6 @@ export class SpotService { ...recordSpotDto, photoKey, }); - const { journeyId, coordinate, timestamp } = createdSpotData; const returnData: RecordSpotResDTO = { journeyId, From b7199bf1c42051ac3c65d0e82211de19d30bb272 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 5 Jan 2024 22:47:12 +0900 Subject: [PATCH 04/12] =?UTF-8?q?:recycle:=20Coordinate=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20decorator=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존에 coordinate에 기존 문자열(ex "asd") 를 입력해도 정상 작동하는 문제 해결 --- BE/musicspot/src/common/util/coordinate.util.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BE/musicspot/src/common/util/coordinate.util.ts b/BE/musicspot/src/common/util/coordinate.util.ts index c1a103e..97b7d9e 100644 --- a/BE/musicspot/src/common/util/coordinate.util.ts +++ b/BE/musicspot/src/common/util/coordinate.util.ts @@ -6,6 +6,8 @@ export const is1DArray = (arr) => { if (!isInCoordinateRange(arr)) { return false; } + } else { + return false; } return true; @@ -33,4 +35,4 @@ export const isInCoordinateRange = (pos) => { return false; } return true; -}; +}; \ No newline at end of file From 04fc5fe5c5b34443a4bb5ea6f328af57c6a9e629 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 5 Jan 2024 23:39:30 +0900 Subject: [PATCH 05/12] =?UTF-8?q?:recycle:=20Journey=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=20=EC=8B=9C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit journey record 시 coordinate의 유효성을 먼저 검사해서 나머지 로직을 수행하지 않도록 개선 --- BE/musicspot/src/spot/service/spot.service.spec.ts | 2 +- BE/musicspot/src/spot/service/spot.service.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/BE/musicspot/src/spot/service/spot.service.spec.ts b/BE/musicspot/src/spot/service/spot.service.spec.ts index dc16ef0..6cfe8ec 100644 --- a/BE/musicspot/src/spot/service/spot.service.spec.ts +++ b/BE/musicspot/src/spot/service/spot.service.spec.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { Test, TestingModule } from '@nestjs/testing'; import { SpotService } from './spot.service'; import mongoose from 'mongoose'; -import { Spot, SpotSchema } from '../schema/spot.schemaㄴ'; +import { Spot, SpotSchema } from '../schema/spot.schema'; import { getModelToken } from '@nestjs/mongoose'; import { Journey, JourneySchema } from '../../journey/schema/journey.schema'; diff --git a/BE/musicspot/src/spot/service/spot.service.ts b/BE/musicspot/src/spot/service/spot.service.ts index 78c8bae..1ce1972 100644 --- a/BE/musicspot/src/spot/service/spot.service.ts +++ b/BE/musicspot/src/spot/service/spot.service.ts @@ -14,6 +14,8 @@ import { bucketName, makePresignedUrl, } from '../../common/s3/objectStorage'; +import { coordinateNotCorrectException } from 'src/filters/journey.exception'; +import { is1DArray } from 'src/common/util/coordinate.util'; @Injectable() export class SpotService { constructor( @@ -36,7 +38,7 @@ export class SpotService { } async insertToSpot(spotData) { - const data = { ...spotData, coordinate: JSON.parse(spotData.coordinate) }; + const data = { ...spotData }; const createdSpotData = await new this.spotModel(data).save(); const {_id, coordinate} = createdSpotData; await this.journeyModel @@ -50,6 +52,15 @@ export class SpotService { } async create(file, recordSpotDto) { + let parsedCoordinate; + try { + parsedCoordinate = JSON.parse(recordSpotDto.coordinate); + } catch (err) { + throw new coordinateNotCorrectException(); + } + if (!is1DArray(parsedCoordinate)) { + throw new coordinateNotCorrectException(); + } const photoKey = await this.uploadPhotoToStorage( recordSpotDto.journeyId, file, @@ -58,6 +69,7 @@ export class SpotService { const createdSpotData = await this.insertToSpot({ ...recordSpotDto, photoKey, + coordinate: parsedCoordinate }); const { journeyId, coordinate, timestamp } = createdSpotData; const returnData: RecordSpotResDTO = { From dafa121ebc274575fe320b07e6b16ff320bbc217 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 6 Jan 2024 00:15:11 +0900 Subject: [PATCH 06/12] =?UTF-8?q?:recycle:=20=EB=A7=88=EC=A7=80=EB=A7=89?= =?UTF-8?q?=20=EC=97=AC=EC=A0=95=20=EA=B2=80=EC=83=89=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 마지막 여정 검색 시 모든 여정을 탐색하지 않고 마지막 여정만 확인하도록 수정, api 경로 수정(loadLastData -> :userId/last) --- .../journey/controller/journey.controller.ts | 4 +- .../src/journey/service/journey.service.ts | 52 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/BE/musicspot/src/journey/controller/journey.controller.ts b/BE/musicspot/src/journey/controller/journey.controller.ts index 0cd7ee9..912a025 100644 --- a/BE/musicspot/src/journey/controller/journey.controller.ts +++ b/BE/musicspot/src/journey/controller/journey.controller.ts @@ -135,8 +135,8 @@ export class JourneyController { description: '사용자가 진행중이었던 여정 정보', type: Journey, }) - @Get('loadLastData') - async loadLastData(@Query('userId') userId: string) { + @Get(':userId/last') + async loadLastData(@Param('userId') userId: string) { return await this.journeyService.loadLastJourney(userId); } diff --git a/BE/musicspot/src/journey/service/journey.service.ts b/BE/musicspot/src/journey/service/journey.service.ts index d2d4b9f..1082e49 100644 --- a/BE/musicspot/src/journey/service/journey.service.ts +++ b/BE/musicspot/src/journey/service/journey.service.ts @@ -193,30 +193,44 @@ export class JourneyService { } async loadLastJourney(userId) { - const user = await this.userModel.findById(userId).lean(); - - if (!user) { + const user = await this.userModel.findOne({userId}).lean(); + if(!user){ throw new UserNotFoundException(); } + const journeys = user.journeys; - const journey = await this.findLastJourney(journeys); - if (journey) { - return journey; - } - return '진행중이었던 여정이 없습니다.'; - } - async findLastJourney(journeys) { - for (let i = 0; i < journeys.length; i++) { - let journey = await this.journeyModel.findById(journeys[i]).lean(); - if (!journey) { - throw new JourneyNotFoundException(); - } - if (journey.title === '') { - return journey; + const len = journeys.length; + const lastJourneyId = journeys[len-1]; + + const lastJourney = await this.journeyModel + .findById(lastJourneyId) + .populate({ + path: 'spots', + model: 'Spot', + options: { + transform: (spot) => { + return { + coordinate: spot.coordinate, + timestamp: spot.timestamp, + photoUrl: makePresignedUrl(spot.photoKey), + }; + }, + }, + }) + .lean(); + + if (!lastJourney) { + return { + journey: null, + isRecording: false, + }; } - } - return false; + + return { + journey: lastJourney.title ? null : lastJourney, + isRecording: lastJourney.title ? false : true, + }; } async getJourneyById(journeyId) { From 2fd063ce95ecd9a838ca8a742a16e44005685ffe Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 02:16:49 +0900 Subject: [PATCH 07/12] =?UTF-8?q?:memo:=20mysql=20=EC=84=A4=EC=B9=98?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20package=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/musicspot/package-lock.json | 585 +++++++++++++++++++++++++++++---- BE/musicspot/package.json | 6 +- 2 files changed, 522 insertions(+), 69 deletions(-) diff --git a/BE/musicspot/package-lock.json b/BE/musicspot/package-lock.json index 66388d5..b7b47ec 100644 --- a/BE/musicspot/package-lock.json +++ b/BE/musicspot/package-lock.json @@ -14,6 +14,7 @@ "@nestjs/mongoose": "^10.0.2", "@nestjs/platform-express": "^10.2.10", "@nestjs/swagger": "^7.1.15", + "@nestjs/typeorm": "^10.0.1", "@turf/turf": "^6.5.0", "@types/dotenv": "^8.2.0", "@types/multer": "^1.4.10", @@ -24,12 +25,15 @@ "gitmoji-cli": "^9.0.0", "handlebars": "^4.7.8", "mongoose": "^8.0.0", + "mysql2": "^3.7.0", "nest-winston": "^1.9.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "typeorm": "^0.3.19", "uuid": "^9.0.1", "winston": "^3.11.0", - "winston-daily-rotate-file": "^4.7.1" + "winston-daily-rotate-file": "^4.7.1", + "wkx": "^0.5.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -850,7 +854,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -862,7 +866,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -993,7 +997,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1010,7 +1013,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1022,7 +1024,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -1033,14 +1034,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1057,7 +1056,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1072,7 +1070,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1509,7 +1506,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -1537,7 +1534,7 @@ "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", @@ -1815,6 +1812,21 @@ } } }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1871,7 +1883,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -1969,6 +1980,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -1989,25 +2005,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@turf/along": { "version": "6.5.0", @@ -4247,7 +4263,7 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -4277,7 +4293,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -4388,6 +4404,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4401,6 +4422,14 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -4410,7 +4439,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", @@ -4655,8 +4684,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -5245,6 +5273,77 @@ "node": ">=8" } }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/cli-spinners": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", @@ -5284,7 +5383,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -5298,7 +5396,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5679,7 +5776,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -5745,6 +5842,11 @@ "node": ">= 14" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debounce-fn": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz", @@ -6099,6 +6201,14 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/density-clustering": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz", @@ -6144,7 +6254,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -6321,7 +6431,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -7053,7 +7162,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -7221,6 +7329,14 @@ "node": ">=10" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7267,7 +7383,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -7873,7 +7988,6 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -7913,7 +8027,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -7922,7 +8035,6 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8140,6 +8252,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -8600,6 +8720,11 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -8780,7 +8905,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -9659,6 +9783,11 @@ "node": ">=0.1.90" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -9722,7 +9851,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -9882,7 +10011,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -9907,25 +10035,28 @@ } }, "node_modules/mongodb": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", - "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", + "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", + "optional": true, + "peer": true, "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", - "mongodb-connection-string-url": "^2.6.0" + "bson": "^5.5.0", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" }, "engines": { - "node": ">=16.20.1" + "node": ">=14.20.1" + }, + "optionalDependencies": { + "@mongodb-js/saslprep": "^1.1.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" + "@mongodb-js/zstd": "^1.0.0", + "kerberos": "^1.0.0 || ^2.0.0", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -9934,9 +10065,6 @@ "@mongodb-js/zstd": { "optional": true }, - "gcp-metadata": { - "optional": true - }, "kerberos": { "optional": true }, @@ -9945,9 +10073,6 @@ }, "snappy": { "optional": true - }, - "socks": { - "optional": true } } }, @@ -9991,6 +10116,16 @@ "node": ">=12" } }, + "node_modules/mongodb/node_modules/bson": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.20.1" + } + }, "node_modules/mongoose": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.0.tgz", @@ -10012,6 +10147,51 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", + "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^2.6.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, "node_modules/mongoose/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -10064,6 +10244,72 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mysql2": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz", + "integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10495,6 +10741,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -10539,7 +10803,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -10555,7 +10818,6 @@ "version": "10.0.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", - "dev": true, "dependencies": { "semver": "^7.3.5" }, @@ -11098,7 +11360,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -11512,6 +11773,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -11567,6 +11833,18 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11772,6 +12050,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -11921,7 +12207,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -11947,7 +12232,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12230,6 +12514,25 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -12434,7 +12737,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12578,11 +12881,153 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typeorm": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.19.tgz", + "integrity": "sha512-OGelrY5qEoAU80mR1iyvmUHiKCPUydL6xp6bebXzS7jyv/X70Gp/jBWRAfF4qGOfy2A7orMiGRfwsBUNbEL65g==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.1.13", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">= 12.9.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12799,7 +13244,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.1.3", @@ -13220,6 +13665,14 @@ "node": ">= 6" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -13243,7 +13696,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -13324,7 +13776,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -13339,7 +13790,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -13357,7 +13807,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } @@ -13366,7 +13815,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/BE/musicspot/package.json b/BE/musicspot/package.json index 07b66bb..7b34646 100644 --- a/BE/musicspot/package.json +++ b/BE/musicspot/package.json @@ -25,6 +25,7 @@ "@nestjs/mongoose": "^10.0.2", "@nestjs/platform-express": "^10.2.10", "@nestjs/swagger": "^7.1.15", + "@nestjs/typeorm": "^10.0.1", "@turf/turf": "^6.5.0", "@types/dotenv": "^8.2.0", "@types/multer": "^1.4.10", @@ -35,12 +36,15 @@ "gitmoji-cli": "^9.0.0", "handlebars": "^4.7.8", "mongoose": "^8.0.0", + "mysql2": "^3.7.0", "nest-winston": "^1.9.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "typeorm": "^0.3.19", "uuid": "^9.0.1", "winston": "^3.11.0", - "winston-daily-rotate-file": "^4.7.1" + "winston-daily-rotate-file": "^4.7.1", + "wkx": "^0.5.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", From ba98f1b2827fd437b55ffcbf6b44c69426a89384 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 02:38:20 +0900 Subject: [PATCH 08/12] =?UTF-8?q?:sparkles:=20mysql=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=A4=EC=8A=A4=ED=85=80=20repository=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/musicspot/src/app.module.ts | 26 +++++++++++++-- .../decorator/customRepository.decorator.ts | 10 ++++++ BE/musicspot/src/dynamic.module.ts | 32 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 BE/musicspot/src/common/decorator/customRepository.decorator.ts create mode 100644 BE/musicspot/src/dynamic.module.ts diff --git a/BE/musicspot/src/app.module.ts b/BE/musicspot/src/app.module.ts index 74829b5..3c1f494 100644 --- a/BE/musicspot/src/app.module.ts +++ b/BE/musicspot/src/app.module.ts @@ -8,13 +8,33 @@ import { UserModule } from './user/module/user.module'; import { ReleaseController } from './releasePage/release.controller'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; +import { TypeOrmModule } from '@nestjs/typeorm'; + import * as dotenv from 'dotenv'; +import { User } from './user/entities/user.entity'; +import { Journey } from './journey/entities/journey.entity'; +import {Spot} from './spot/entities/spot.entity' dotenv.config(); + @Module({ imports: [ - MongooseModule.forRoot( - `mongodb://${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`, - ), + TypeOrmModule.forRoot({ + type: 'mysql', + host: process.env.DB_HOST, + port: Number(process.env.DB_PORT), + username: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + entities: [ + User, Journey, Spot, + ], + synchronize: false, + legacySpatialSupport: false, + }), + // MongooseModule.forRoot( + // `mongodb://${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`, + // ), + JourneyModule, UserModule, SpotModule, diff --git a/BE/musicspot/src/common/decorator/customRepository.decorator.ts b/BE/musicspot/src/common/decorator/customRepository.decorator.ts new file mode 100644 index 0000000..8455cb8 --- /dev/null +++ b/BE/musicspot/src/common/decorator/customRepository.decorator.ts @@ -0,0 +1,10 @@ +import { SetMetadata } from "@nestjs/common"; + +export const TYPEORM_EX_CUSTOM_REPOSITORY = "TYPEORM_EX_CUSTOM_REPOSITORY"; + +export function CustomRepository(entity : Function): ClassDecorator{ + return SetMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, entity); +} + +// SetMetadata는 key, value 형태의 값을 취함 +// 즉 TYPEORM_EX_CUSTOM_REPOSITORY : entity \ No newline at end of file diff --git a/BE/musicspot/src/dynamic.module.ts b/BE/musicspot/src/dynamic.module.ts new file mode 100644 index 0000000..535609e --- /dev/null +++ b/BE/musicspot/src/dynamic.module.ts @@ -0,0 +1,32 @@ +import { DynamicModule, Provider } from "@nestjs/common"; +import { TYPEORM_EX_CUSTOM_REPOSITORY } from "./common/decorator/customRepository.decorator"; +import { getDataSourceToken } from "@nestjs/typeorm"; +import { DataSource } from "typeorm"; + +export class TypeOrmExModule{ + public static forFeature any>(repositories: T[]): DynamicModule { + const providers: Provider[] = []; + for(const repository of repositories){ + + const entity = Reflect.getMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, repository); + + if(!entity){ + continue; + } + + providers.push({ + inject: [getDataSourceToken()], + provide : repository, + useFactory : (dataSource:DataSource) : typeof repository =>{ + const baseRepository = dataSource.getRepository(entity); + return new repository(baseRepository.target, baseRepository.manager, baseRepository) + } + }) + } + return { + exports : providers, + module : TypeOrmExModule, + providers + } + } +} \ No newline at end of file From e774762bbad535bf6bf0298884ef986121b9fb5b Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 03:04:33 +0900 Subject: [PATCH 09/12] =?UTF-8?q?:recycle:=20user=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/user/controller/user.controller.ts | 3 +- BE/musicspot/src/user/entities/user.entity.ts | 11 ++++ BE/musicspot/src/user/module/user.module.ts | 9 ++- .../src/user/repository/user.repository.ts | 8 +++ BE/musicspot/src/user/schema/user.schema.ts | 56 ++++++++-------- BE/musicspot/src/user/serivce/user.service.ts | 66 ++++++++++++------- 6 files changed, 97 insertions(+), 56 deletions(-) create mode 100644 BE/musicspot/src/user/entities/user.entity.ts create mode 100644 BE/musicspot/src/user/repository/user.repository.ts diff --git a/BE/musicspot/src/user/controller/user.controller.ts b/BE/musicspot/src/user/controller/user.controller.ts index 2afef3d..8fd5744 100644 --- a/BE/musicspot/src/user/controller/user.controller.ts +++ b/BE/musicspot/src/user/controller/user.controller.ts @@ -1,8 +1,9 @@ import { Controller, Body, Post } from '@nestjs/common'; import { UserService } from '../serivce/user.service'; import { CreateUserDTO } from '../dto/createUser.dto'; -import { User } from '../schema/user.schema'; import { ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { User } from '../entities/user.entity'; + @Controller('user') @ApiTags('user 관련 API') export class UserController { diff --git a/BE/musicspot/src/user/entities/user.entity.ts b/BE/musicspot/src/user/entities/user.entity.ts new file mode 100644 index 0000000..3b8dc4d --- /dev/null +++ b/BE/musicspot/src/user/entities/user.entity.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { UUID } from "crypto"; +import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"; + +@Entity() +export class User{ + @ApiProperty({example: "0f8fad5b-d9cb-469f-a165-70867728950e"}) + @PrimaryGeneratedColumn() + userId: UUID; + +} \ No newline at end of file diff --git a/BE/musicspot/src/user/module/user.module.ts b/BE/musicspot/src/user/module/user.module.ts index 27c737a..11a9401 100644 --- a/BE/musicspot/src/user/module/user.module.ts +++ b/BE/musicspot/src/user/module/user.module.ts @@ -1,11 +1,16 @@ import { Module } from '@nestjs/common'; import { UserController } from '../controller/user.controller'; import { UserService } from '../serivce/user.service'; -import { User, UserSchema } from '../schema/user.schema'; +// import { User, UserSchema } from '../schema/user.schema'; import { MongooseModule } from '@nestjs/mongoose'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserRepository } from '../repository/user.repository'; +import { TypeOrmExModule } from 'src/dynamic.module'; + @Module({ imports: [ - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + TypeOrmExModule.forFeature([UserRepository]) + // MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), ], controllers: [UserController], providers: [UserService], diff --git a/BE/musicspot/src/user/repository/user.repository.ts b/BE/musicspot/src/user/repository/user.repository.ts new file mode 100644 index 0000000..ccfacfc --- /dev/null +++ b/BE/musicspot/src/user/repository/user.repository.ts @@ -0,0 +1,8 @@ +import { CustomRepository } from "src/common/decorator/customRepository.decorator"; +import { User } from "../entities/user.entity"; +import { Repository } from "typeorm"; +import { CreateUserDTO } from "../dto/createUser.dto"; + +@CustomRepository(User) +export class UserRepository extends Repository{ +}; \ No newline at end of file diff --git a/BE/musicspot/src/user/schema/user.schema.ts b/BE/musicspot/src/user/schema/user.schema.ts index 321c7f2..1d64691 100644 --- a/BE/musicspot/src/user/schema/user.schema.ts +++ b/BE/musicspot/src/user/schema/user.schema.ts @@ -1,33 +1,33 @@ -import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; -import { UUID } from 'crypto'; -import { HydratedDocument } from 'mongoose'; -import { ApiProperty } from '@nestjs/swagger'; -export type UserDocument = HydratedDocument; +// import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +// import { UUID } from 'crypto'; +// import { HydratedDocument } from 'mongoose'; +// import { ApiProperty } from '@nestjs/swagger'; +// export type UserDocument = HydratedDocument; -@Schema({ collection: 'user' }) -export class User { - @ApiProperty({ - example: 'ab4068ef-95ed-40c3-be6d-3db35df866b9', - description: '유저 id(UUID)', - uniqueItems: true, - required: true, - }) - @Prop() - userId: UUID; +// @Schema({ collection: 'user' }) +// export class User { +// @ApiProperty({ +// example: 'ab4068ef-95ed-40c3-be6d-3db35df866b9', +// description: '유저 id(UUID)', +// uniqueItems: true, +// required: true, +// }) +// @Prop() +// userId: UUID; - @ApiProperty({ - example: [], - description: 'journey id 리스트(생성 시에는 빈 배열입니다.)', - required: true, - }) - @Prop() - journeys: string[]; +// @ApiProperty({ +// example: [], +// description: 'journey id 리스트(생성 시에는 빈 배열입니다.)', +// required: true, +// }) +// @Prop() +// journeys: string[]; - // @Prop() - // email: string; +// // @Prop() +// // email: string; - // @Prop() - // nickname: string; -} +// // @Prop() +// // nickname: string; +// } -export const UserSchema = SchemaFactory.createForClass(User); +// export const UserSchema = SchemaFactory.createForClass(User); diff --git a/BE/musicspot/src/user/serivce/user.service.ts b/BE/musicspot/src/user/serivce/user.service.ts index 5066218..74f3ef2 100644 --- a/BE/musicspot/src/user/serivce/user.service.ts +++ b/BE/musicspot/src/user/serivce/user.service.ts @@ -1,37 +1,53 @@ import { Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; import { InjectModel } from '@nestjs/mongoose'; -import { User } from '../schema/user.schema'; +// import { User } from '../schema/user.schema'; import { UUID } from 'crypto'; import { CreateUserDTO } from '../dto/createUser.dto'; import { UserAlreadyExistException } from '../../filters/user.exception'; +import { UserRepository } from '../repository/user.repository'; +// @Injectable() +// export class UserService { +// constructor(@InjectModel(User.name) private userModel: Model) {} +// async create(createUserDto: CreateUserDTO): Promise { +// const userData = { +// ...createUserDto, +// journeys: [], +// }; + +// // 유저 id 중복 시 예외 처리 +// if (await this.isExist(createUserDto.userId)) { +// throw new UserAlreadyExistException(); +// } +// const createdUser = new this.userModel(userData); +// return await createdUser.save(); +// } + +// async isExist(userId: UUID) { +// return this.userModel.exists({ userId }); +// } +// // async appendJourneyIdToUser( +// // userEmail: string, +// // journeyId: object, +// // ): Promise { +// // return this.userModel.updateOne( +// // { email: userEmail }, +// // { $push: { journeys: journeyId } }, +// // ); +// // } +// } + + @Injectable() -export class UserService { - constructor(@InjectModel(User.name) private userModel: Model) {} - async create(createUserDto: CreateUserDTO): Promise { - const userData = { - ...createUserDto, - journeys: [], - }; +export class UserService{ + constructor(private userRepository: UserRepository){} - // 유저 id 중복 시 예외 처리 - if (await this.isExist(createUserDto.userId)) { + async create(createUserDto: CreateUserDTO):Promise{ + const {userId} = createUserDto + if(await this.userRepository.findOne({where:{userId}})){ throw new UserAlreadyExistException(); } - const createdUser = new this.userModel(userData); - return await createdUser.save(); + return await this.userRepository.save(createUserDto); } - async isExist(userId: UUID) { - return this.userModel.exists({ userId }); - } - // async appendJourneyIdToUser( - // userEmail: string, - // journeyId: object, - // ): Promise { - // return this.userModel.updateOne( - // { email: userEmail }, - // { $push: { journeys: journeyId } }, - // ); - // } -} +} \ No newline at end of file From a835c100e242e7ac060a8be281936eda9bacbaf9 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 03:05:08 +0900 Subject: [PATCH 10/12] =?UTF-8?q?:recycle:=20db=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20spot=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/spot/controller/spot.controller.ts | 2 +- BE/musicspot/src/spot/dto/recordSpot.dto.ts | 18 +- BE/musicspot/src/spot/entities/spot.entity.ts | 29 ++++ BE/musicspot/src/spot/module/spot.module.ts | 15 +- .../src/spot/repository/spot.repository.ts | 6 + BE/musicspot/src/spot/service/spot.service.ts | 160 ++++++++++-------- 6 files changed, 139 insertions(+), 91 deletions(-) create mode 100644 BE/musicspot/src/spot/entities/spot.entity.ts create mode 100644 BE/musicspot/src/spot/repository/spot.repository.ts diff --git a/BE/musicspot/src/spot/controller/spot.controller.ts b/BE/musicspot/src/spot/controller/spot.controller.ts index f34fee8..8c13db5 100644 --- a/BE/musicspot/src/spot/controller/spot.controller.ts +++ b/BE/musicspot/src/spot/controller/spot.controller.ts @@ -44,7 +44,7 @@ export class SpotController { type: SpotDTO, }) @Get('find') - async findSpotImage(@Query('spotId') spotId: string) { + async findSpotImage(@Query('spotId') spotId: number) { try { return await this.spotService.getSpotImage(spotId); } catch (err) { diff --git a/BE/musicspot/src/spot/dto/recordSpot.dto.ts b/BE/musicspot/src/spot/dto/recordSpot.dto.ts index e400447..2044b79 100644 --- a/BE/musicspot/src/spot/dto/recordSpot.dto.ts +++ b/BE/musicspot/src/spot/dto/recordSpot.dto.ts @@ -1,7 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, IsDateString, IsString, IsUrl } from 'class-validator'; +import { IsArray, IsDateString, IsString, IsUrl, IsNumber } from 'class-validator'; import { IsCoordinate } from '../../common/decorator/coordinate.decorator'; -import { IsObjectId } from 'src/common/decorator/objectId.decorator'; export class RecordSpotReqDTO { @ApiProperty({ @@ -9,8 +8,8 @@ export class RecordSpotReqDTO { description: '여정 id', required: true, }) - @IsObjectId({ message: 'ObjectId 형식만 유효합니다.' }) - readonly journeyId: string; + @IsNumber() + readonly journeyId: number; @ApiProperty({ example: [37.555946, 126.972384], @@ -31,22 +30,15 @@ export class RecordSpotReqDTO { @IsDateString() readonly timestamp: string; - // @ApiProperty({ - // example: 'base64-encoded-binary-image-data', // Update this with a valid base64-encoded image data - // description: 'Buffer', - // required: true, - // }) - // readonly photoData: Buffer; } export class RecordSpotResDTO { @ApiProperty({ - example: '655efda2fdc81cae36d20650', + example: 20, description: '여정 id', required: true, }) - @IsString() - readonly journeyId: string; + readonly journeyId: number; @ApiProperty({ example: [37.555946, 126.972384], diff --git a/BE/musicspot/src/spot/entities/spot.entity.ts b/BE/musicspot/src/spot/entities/spot.entity.ts new file mode 100644 index 0000000..6d77308 --- /dev/null +++ b/BE/musicspot/src/spot/entities/spot.entity.ts @@ -0,0 +1,29 @@ + +import { Journey } from "src/journey/entities/journey.entity"; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn } from "typeorm"; + +@Entity() +export class Spot{ + @PrimaryGeneratedColumn() + spotId: number; + + @Column() + journeyId : number; + + @Column("geometry") + coordinate: string; + + @Column() + timestamp: string; + + @Column() + photoKey : string; + + + + @ManyToOne(()=>Journey, (journey) => journey.spots) + @JoinColumn({name: "journeyId"}) + journey : Journey + +} + diff --git a/BE/musicspot/src/spot/module/spot.module.ts b/BE/musicspot/src/spot/module/spot.module.ts index b51d29a..a2f5daa 100644 --- a/BE/musicspot/src/spot/module/spot.module.ts +++ b/BE/musicspot/src/spot/module/spot.module.ts @@ -3,13 +3,18 @@ import { SpotController } from '../controller/spot.controller'; import { SpotService } from '../service/spot.service'; import { MongooseModule } from '@nestjs/mongoose'; import { Spot, SpotSchema } from '../schema/spot.schema'; -import { Journey, JourneySchema } from 'src/journey/schema/journey.schema'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SpotRepository } from '../repository/spot.repository'; +import { TypeOrmExModule } from 'src/dynamic.module'; +import { JourneyRepository } from 'src/journey/repository/journey.repository'; +// import { Journey, JourneySchema } from 'src/journey/schema/journey.schema'; @Module({ imports: [ - MongooseModule.forFeature([ - { name: Spot.name, schema: SpotSchema }, - { name: Journey.name, schema: JourneySchema }, - ]), + TypeOrmExModule.forFeature([SpotRepository, JourneyRepository]) + // MongooseModule.forFeature([ + // { name: Spot.name, schema: SpotSchema }, + // { name: Journey.name, schema: JourneySchema }, + // ]), ], controllers: [SpotController], providers: [SpotService], diff --git a/BE/musicspot/src/spot/repository/spot.repository.ts b/BE/musicspot/src/spot/repository/spot.repository.ts new file mode 100644 index 0000000..7375f31 --- /dev/null +++ b/BE/musicspot/src/spot/repository/spot.repository.ts @@ -0,0 +1,6 @@ +import { CustomRepository } from "src/common/decorator/customRepository.decorator"; +import { Repository } from "typeorm"; +import { Spot } from "../entities/spot.entity"; + +@CustomRepository(Spot) +export class SpotRepository extends Repository{}; \ No newline at end of file diff --git a/BE/musicspot/src/spot/service/spot.service.ts b/BE/musicspot/src/spot/service/spot.service.ts index 1ce1972..8fa8206 100644 --- a/BE/musicspot/src/spot/service/spot.service.ts +++ b/BE/musicspot/src/spot/service/spot.service.ts @@ -1,91 +1,107 @@ -import { Model } from 'mongoose'; import { Injectable } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { Spot } from '../schema/spot.schema'; -import { RecordSpotReqDTO, RecordSpotResDTO } from '../dto/recordSpot.dto'; -import { Journey } from '../../journey/schema/journey.schema'; +import {Spot} from '../entities/spot.entity' import { SpotNotFoundException, SpotRecordFail, } from 'src/filters/spot.exception'; import { S3, - endpoint, bucketName, makePresignedUrl, } from '../../common/s3/objectStorage'; -import { coordinateNotCorrectException } from 'src/filters/journey.exception'; -import { is1DArray } from 'src/common/util/coordinate.util'; +import { JourneyNotFoundException, coordinateNotCorrectException } from 'src/filters/journey.exception'; +import { is1DArray, parseCoordinateFromDtoToGeo } from 'src/common/util/coordinate.util'; +import { SpotRepository } from '../repository/spot.repository'; +import { RecordSpotResDTO } from '../dto/recordSpot.dto'; +import { JourneyRepository } from 'src/journey/repository/journey.repository'; + + + @Injectable() export class SpotService { constructor( - @InjectModel(Spot.name) private spotModel: Model, - @InjectModel(Journey.name) private journeyModel: Model, - ) {} - async uploadPhotoToStorage(journeyId, file) { - try { - const key = `${journeyId}/${Date.now()}`; - const result = await S3.putObject({ - Bucket: bucketName, - Key: key, - Body: file.buffer, - }).promise(); + private spotRepository: SpotRepository, private journeyRepository: JourneyRepository) {} + - return key; - } catch (err) { - throw new SpotRecordFail(); + async uploadPhotoToStorage(journeyId, file) { + try{ + const key = `${journeyId}/${Date.now()}`; + const result = await S3.putObject({ + Bucket: bucketName, + Key: key, + Body: file.buffer, + }).promise(); + + return key; + } catch(err){ + throw new SpotRecordFail(); + } } - } - - async insertToSpot(spotData) { - const data = { ...spotData }; - const createdSpotData = await new this.spotModel(data).save(); - const {_id, coordinate} = createdSpotData; - await this.journeyModel - .findOneAndUpdate( - { _id: spotData.journeyId }, - { $push: { spots: _id ,coordinates: coordinate} }, - { new: true }, - ) - .lean(); - return createdSpotData.toObject(); - } - - async create(file, recordSpotDto) { - let parsedCoordinate; - try { - parsedCoordinate = JSON.parse(recordSpotDto.coordinate); - } catch (err) { - throw new coordinateNotCorrectException(); + + async insertToSpot(spotData){ + const point = `POINT(${parseCoordinateFromDtoToGeo(spotData.coordinate)})`; + const data = {...spotData, journeyId :Number(spotData.journeyId), coordinate: point } + + return await this.spotRepository.save(data); } - if (!is1DArray(parsedCoordinate)) { - throw new coordinateNotCorrectException(); + + async updateCoordinatesToJourney(journeyId, coordinate){ + const parsedCoordinate = parseCoordinateFromDtoToGeo(coordinate); + const originalJourney = await this.journeyRepository.findOne({where:{journeyId}}) + const lineStringLen = 'LINESTRING('.length; + + if(!originalJourney){ + throw new JourneyNotFoundException(); + } + originalJourney.coordinates = `LINESTRING(${originalJourney.coordinates.slice(lineStringLen, -1)}, ${parsedCoordinate})` + + + return await this.journeyRepository.save(originalJourney); } - const photoKey = await this.uploadPhotoToStorage( - recordSpotDto.journeyId, - file, - ); - const presignedUrl = makePresignedUrl(photoKey); - const createdSpotData = await this.insertToSpot({ - ...recordSpotDto, - photoKey, - coordinate: parsedCoordinate - }); - const { journeyId, coordinate, timestamp } = createdSpotData; - const returnData: RecordSpotResDTO = { - journeyId, - coordinate, - timestamp, - photoUrl: presignedUrl, - }; - return returnData; - } - async getSpotImage(spotId: string) { - const spot = await this.spotModel.findById(spotId).lean(); - if (!spot) { - throw new SpotNotFoundException(); + + async create(file, recordSpotDto){ + let parsedCoordinate; + try { + parsedCoordinate = JSON.parse(recordSpotDto.coordinate); + } catch (err) { + throw new coordinateNotCorrectException(); + } + if (!is1DArray(parsedCoordinate)) { + throw new coordinateNotCorrectException(); + } + + const photoKey = await this.uploadPhotoToStorage( + recordSpotDto.journeyId, + file, + ); + const presignedUrl = makePresignedUrl(photoKey); + + const createdSpotData = await this.insertToSpot({ + ...recordSpotDto, + photoKey, + coordinate: parsedCoordinate + }); + const updatedJourneyData = await this.updateCoordinatesToJourney(recordSpotDto.journeyId, parsedCoordinate) + + const returnData:RecordSpotResDTO = { + journeyId : createdSpotData.journeyId, + coordinate : parsedCoordinate, + timestamp : createdSpotData.timestamp, + photoUrl : presignedUrl + + } + + return returnData + } + + async getSpotImage(spotId: number) { + const spot = await this.spotRepository.findOne({where: {spotId}}); + if (!spot) { + throw new SpotNotFoundException(); + } + + return spot.photoKey; } - - return spot.photoKey; - } } + + From f38fc8a29aafb657643aa4949828954224e373cc Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 03:05:35 +0900 Subject: [PATCH 11/12] =?UTF-8?q?:recycle:=20db=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20journey=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../journey/controller/journey.controller.ts | 29 +- .../src/journey/dto/journeyDelete.dto.ts | 14 +- .../journey/dto/journeyEnd/journeyEnd.dto.ts | 29 +- .../dto/journeyRecord/journeyRecord.dto.ts | 38 +- .../dto/journeyStart/journeyStart.dto.ts | 52 +-- .../src/journey/entities/journey.entity.ts | 30 ++ .../src/journey/module/journey.module.ts | 15 +- .../journey/repository/journey.repository.ts | 6 + .../src/journey/schema/artwork.schema.ts | 64 +-- .../src/journey/schema/journey.schema.ts | 90 ++-- .../journey/schema/journeyMetadata.schema.ts | 36 +- .../src/journey/schema/song.schema.ts | 66 +-- .../src/journey/service/journey.service.ts | 395 +++++++----------- 13 files changed, 388 insertions(+), 476 deletions(-) create mode 100644 BE/musicspot/src/journey/entities/journey.entity.ts create mode 100644 BE/musicspot/src/journey/repository/journey.repository.ts diff --git a/BE/musicspot/src/journey/controller/journey.controller.ts b/BE/musicspot/src/journey/controller/journey.controller.ts index 912a025..add9ad3 100644 --- a/BE/musicspot/src/journey/controller/journey.controller.ts +++ b/BE/musicspot/src/journey/controller/journey.controller.ts @@ -10,16 +10,13 @@ import { Delete, } from '@nestjs/common'; import { JourneyService } from '../service/journey.service'; - import { StartJourneyReqDTO } from '../dto/journeyStart/journeyStart.dto'; - import { ApiCreatedResponse, ApiOperation, ApiQuery, ApiTags, } from '@nestjs/swagger'; -import { Journey } from '../schema/journey.schema'; import { CheckJourneyResDTO, CheckJourneyReqDTO, @@ -35,6 +32,7 @@ import { } from '../dto/journeyRecord/journeyRecord.dto'; import { StartJourneyResDTO } from '../dto/journeyStart/journeyStart.dto'; import { DeleteJourneyReqDTO } from '../dto/journeyDelete.dto'; +import { Journey } from '../entities/journey.entity'; @Controller('journey') @ApiTags('journey 관련 API') @@ -51,7 +49,7 @@ export class JourneyController { }) @Post('start') async create(@Body() startJourneyDTO: StartJourneyReqDTO) { - return await this.journeyService.create(startJourneyDTO); + return await this.journeyService.insertJourneyData(startJourneyDTO); } @ApiOperation({ @@ -81,6 +79,8 @@ export class JourneyController { await this.journeyService.pushCoordianteToJourney(recordJourneyDTO); return returnData; } + + @ApiOperation({ summary: '여정 조회 API', description: '해당 범위 내의 여정들을 반환합니다.', @@ -124,7 +124,7 @@ export class JourneyController { minCoordinate, maxCoordinate, }; - return await this.journeyService.checkJourney(checkJourneyDTO); + return await this.journeyService.getJourneyByCoordinationRange(checkJourneyDTO); } @ApiOperation({ @@ -135,9 +135,9 @@ export class JourneyController { description: '사용자가 진행중이었던 여정 정보', type: Journey, }) - @Get(':userId/last') - async loadLastData(@Param('userId') userId: string) { - return await this.journeyService.loadLastJourney(userId); + @Get('last') + async loadLastData(@Body('userId') userId) { + return await this.journeyService.getLastJourneyByUserId(userId); } @ApiOperation({ @@ -167,16 +167,3 @@ export class JourneyController { } } -// @ApiOperation({ -// summary: '여정 조회 API', -// description: '해당 범위 내의 여정들을 반환합니다.', -// }) -// @ApiCreatedResponse({ -// description: '범위에 있는 여정의 기록들을 반환', -// type: CheckJourneyResDTO, -// }) -// @Post('check') -// @UsePipes(ValidationPipe) //유효성 체크 -// async checkPost(@Body() checkJourneyDTO: CheckJourneyReqDTO) { -// return await this.journeyService.checkJourney(checkJourneyDTO); -// } diff --git a/BE/musicspot/src/journey/dto/journeyDelete.dto.ts b/BE/musicspot/src/journey/dto/journeyDelete.dto.ts index 54a1a4e..8162919 100644 --- a/BE/musicspot/src/journey/dto/journeyDelete.dto.ts +++ b/BE/musicspot/src/journey/dto/journeyDelete.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsDateString } from 'class-validator'; +import { IsString, IsDateString, IsNumber } from 'class-validator'; import { UUID } from 'crypto'; export class DeleteJourneyReqDTO { @ApiProperty({ @@ -15,8 +15,8 @@ export class DeleteJourneyReqDTO { description: '여정 id', required: true, }) - @IsString() - readonly journeyId: string; + @IsNumber() + readonly journeyId: number; } export class DeleteJourneyResDTO { @@ -42,12 +42,4 @@ export class DeleteJourneyResDTO { }) @IsString() readonly journeyId: string; - - // @ApiProperty({ - // example: 'hello@gmail.com', - // description: '이메일', - // required: true, - // }) - // @IsString() - // readonly email: string; } diff --git a/BE/musicspot/src/journey/dto/journeyEnd/journeyEnd.dto.ts b/BE/musicspot/src/journey/dto/journeyEnd/journeyEnd.dto.ts index bb660e6..bfc39a7 100644 --- a/BE/musicspot/src/journey/dto/journeyEnd/journeyEnd.dto.ts +++ b/BE/musicspot/src/journey/dto/journeyEnd/journeyEnd.dto.ts @@ -6,6 +6,7 @@ import { IsDefined, IsNumber, IsArray, + IsObject, } from 'class-validator'; import { IsCoordinate, @@ -13,16 +14,18 @@ import { } from '../../../common/decorator/coordinate.decorator'; import { Type } from 'class-transformer'; import { SongDTO } from '../song/song.dto'; -import { IsObjectId } from 'src/common/decorator/objectId.decorator'; + + export class EndJourneyReqDTO { @ApiProperty({ - example: '655efda2fdc81cae36d20650', + example: 20, description: '여정 id', required: true, }) - @IsObjectId({ message: 'ObjectId 형식만 유효합니다.' }) - readonly journeyId: string; + // @IsObjectId({ message: 'ObjectId 형식만 유효합니다.' }) + @IsNumber() + readonly journeyId: number; @ApiProperty({ example: [ @@ -31,6 +34,7 @@ export class EndJourneyReqDTO { ], description: '위치 좌표', required: true, + }) @IsCoordinates({ message: @@ -54,25 +58,22 @@ export class EndJourneyReqDTO { @IsString() readonly title: string; + @IsObject() @ApiProperty({ - type: SongDTO, description: '노래 정보', required: true, }) - @ValidateNested() - @Type(() => SongDTO) - @IsDefined() - readonly song: SongDTO; + readonly song: object; } export class EndJourneyResDTO { @ApiProperty({ - example: '655efda2fdc81cae36d20650', + example: 20, description: '여정 id', required: true, }) @IsString() - readonly journeyId: string; + readonly journeyId: number; @ApiProperty({ example: [ @@ -102,12 +103,8 @@ export class EndJourneyResDTO { readonly numberOfCoordinates: number; @ApiProperty({ - type: SongDTO, description: '노래 정보', required: true, }) - @ValidateNested({ each: true }) - @Type(() => SongDTO) - @IsDefined() - readonly song: SongDTO; + readonly song: Object; } diff --git a/BE/musicspot/src/journey/dto/journeyRecord/journeyRecord.dto.ts b/BE/musicspot/src/journey/dto/journeyRecord/journeyRecord.dto.ts index f172679..4eca333 100644 --- a/BE/musicspot/src/journey/dto/journeyRecord/journeyRecord.dto.ts +++ b/BE/musicspot/src/journey/dto/journeyRecord/journeyRecord.dto.ts @@ -1,31 +1,17 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, IsString } from 'class-validator'; +import { IsNumber} from 'class-validator'; import { - IsCoordinate, IsCoordinates, } from '../../../common/decorator/coordinate.decorator'; -import { IsObjectId } from 'src/common/decorator/objectId.decorator'; -export class RecordJourneyResDTO { - @ApiProperty({ - example: [ - [37.555946, 126.972384], - [37.555946, 126.972384], - ], - description: '저장된 위치 좌표', - required: true, - }) - @IsCoordinates() - readonly coordinates: number[][]; -} export class RecordJourneyReqDTO { @ApiProperty({ - example: '655efda2fdc81cae36d20650', + example: 20, description: '여정 id', required: true, }) - @IsObjectId({ message: 'ObjectId 형식만 유효합니다.' }) - readonly journeyId: string; + @IsNumber() + readonly journeyId: number; @ApiProperty({ example: [ @@ -41,3 +27,19 @@ export class RecordJourneyReqDTO { }) readonly coordinates: number[][]; } + + +export class RecordJourneyResDTO { + @ApiProperty({ + example: [ + [37.555946, 126.972384], + [37.555946, 126.972384], + ], + description: '저장된 위치 좌표', + required: true, + }) + @IsCoordinates() + readonly coordinates: number[][]; +} + + diff --git a/BE/musicspot/src/journey/dto/journeyStart/journeyStart.dto.ts b/BE/musicspot/src/journey/dto/journeyStart/journeyStart.dto.ts index 73185e4..0a7f28d 100644 --- a/BE/musicspot/src/journey/dto/journeyStart/journeyStart.dto.ts +++ b/BE/musicspot/src/journey/dto/journeyStart/journeyStart.dto.ts @@ -2,7 +2,16 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsDateString, IsArray, IsUUID } from 'class-validator'; import { IsCoordinate } from '../../../common/decorator/coordinate.decorator'; import { UUID } from 'crypto'; + export class StartJourneyReqDTO { + @ApiProperty({ + example: 'ab4068ef-95ed-40c3-be6d-3db35df866b9', + description: '사용자 id', + required: true, + }) + @IsUUID() + readonly userId: UUID; + @ApiProperty({ example: [37.555946, 126.972384], description: '위치 좌표', @@ -22,52 +31,27 @@ export class StartJourneyReqDTO { @IsDateString() readonly startTimestamp: string; - @ApiProperty({ - example: 'ab4068ef-95ed-40c3-be6d-3db35df866b9', - description: '사용자 id', - required: true, - }) - @IsUUID() - readonly userId: UUID; - - // @ApiProperty({ - // example: 'hello@gmail.com', - // description: '이메일', - // required: true, - // }) - // @IsString() - // readonly email: string; + } export class StartJourneyResDTO { + @ApiProperty({ + example: 20, + description: '저장한 journey id', + }) + readonly journeyId: number; + @ApiProperty({ example: [37.555946, 126.972384], description: '위치 좌표', - required: true, }) - readonly coordinate: number[]; + readonly coordinate:number[]; @ApiProperty({ example: '2023-11-22T12:00:00Z', description: 'timestamp', - required: true, }) - @IsDateString() readonly startTimestamp: string; - @ApiProperty({ - example: '656f4b55b11c27334d1fd347', - description: '저장한 journey id', - required: true, - }) - @IsString() - readonly journeyId: string; - - // @ApiProperty({ - // example: 'hello@gmail.com', - // description: '이메일', - // required: true, - // }) - // @IsString() - // readonly email: string; } + diff --git a/BE/musicspot/src/journey/entities/journey.entity.ts b/BE/musicspot/src/journey/entities/journey.entity.ts new file mode 100644 index 0000000..cdf3987 --- /dev/null +++ b/BE/musicspot/src/journey/entities/journey.entity.ts @@ -0,0 +1,30 @@ +import { UUID } from "crypto"; +import { Spot } from "../../spot/entities/spot.entity"; +import {Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm"; +@Entity() +export class Journey{ + @PrimaryGeneratedColumn() + journeyId: number; + + @Column({length : 36}) + userId : UUID; + + @Column({length : 30}) + title : string; + + @Column() + startTimestamp : string; + + @Column() + endTimestamp : string; + + @Column() + song : string; + + @Column("geometry") + coordinates:string; + + // @OneToMany(()=> Spot, spot => spot.journeyId) + @OneToMany(()=> Spot, (spot)=>spot.journey) + spots: Spot[]; +} \ No newline at end of file diff --git a/BE/musicspot/src/journey/module/journey.module.ts b/BE/musicspot/src/journey/module/journey.module.ts index 9fa9e6f..8030d43 100644 --- a/BE/musicspot/src/journey/module/journey.module.ts +++ b/BE/musicspot/src/journey/module/journey.module.ts @@ -2,15 +2,22 @@ import { Module } from '@nestjs/common'; import { JourneyController } from '../controller/journey.controller'; import { JourneyService } from '../service/journey.service'; import { MongooseModule } from '@nestjs/mongoose'; -import { JourneySchema, Journey } from '../schema/journey.schema'; +// import { JourneySchema, Journey } from '../schema/journey.schema'; import { UserService } from '../../user/serivce/user.service'; -import { User, UserSchema } from 'src/user/schema/user.schema'; +// import { User, UserSchema } from 'src/user/schema/user.schema'; import { UserModule } from '../../user/module/user.module'; +import { JourneyRepository } from '../repository/journey.repository'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserRepository } from 'src/user/repository/user.repository'; +import { TypeOrmExModule } from 'src/dynamic.module'; +import { SpotRepository } from 'src/spot/repository/spot.repository'; +import { Spot } from '../../spot/entities/spot.entity'; @Module({ imports: [ - MongooseModule.forFeature([{ name: Journey.name, schema: JourneySchema }]), - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + TypeOrmExModule.forFeature([JourneyRepository, UserRepository, SpotRepository, Spot]), + // MongooseModule.forFeature([{ name: Journey.name, schema: JourneySchema }]), + // MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), UserModule, ], controllers: [JourneyController], diff --git a/BE/musicspot/src/journey/repository/journey.repository.ts b/BE/musicspot/src/journey/repository/journey.repository.ts new file mode 100644 index 0000000..1116bac --- /dev/null +++ b/BE/musicspot/src/journey/repository/journey.repository.ts @@ -0,0 +1,6 @@ +import { CustomRepository } from "src/common/decorator/customRepository.decorator"; +import { Repository } from "typeorm"; +import { Journey } from "../entities/journey.entity"; + +@CustomRepository(Journey) +export class JourneyRepository extends Repository{}; \ No newline at end of file diff --git a/BE/musicspot/src/journey/schema/artwork.schema.ts b/BE/musicspot/src/journey/schema/artwork.schema.ts index 08eb5f5..7f29c43 100644 --- a/BE/musicspot/src/journey/schema/artwork.schema.ts +++ b/BE/musicspot/src/journey/schema/artwork.schema.ts @@ -1,36 +1,36 @@ -import { Prop } from '@nestjs/mongoose'; -import { ApiProperty } from '@nestjs/swagger'; +// import { Prop } from '@nestjs/mongoose'; +// import { ApiProperty } from '@nestjs/swagger'; -export class Artwork { - @ApiProperty({ - example: 3000, - description: '앨범 커버 이미지 넓이', - required: true, - }) - @Prop() - width: number; +// export class Artwork { +// @ApiProperty({ +// example: 3000, +// description: '앨범 커버 이미지 넓이', +// required: true, +// }) +// @Prop() +// width: number; - @ApiProperty({ - example: 3000, - description: '앨범 커버 이미지 높이', - required: true, - }) - @Prop() - height: number; +// @ApiProperty({ +// example: 3000, +// description: '앨범 커버 이미지 높이', +// required: true, +// }) +// @Prop() +// height: number; - @ApiProperty({ - example: - 'https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/0b/b2/52/0bb2524d-ecfc-1bae-9c1e-218c978d7072/Honeymoon_3K.jpg/{w}x{h}bb.jpg', - description: '앨범 커버 이미지 URL', - }) - @Prop() - url?: URL; +// @ApiProperty({ +// example: +// 'https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/0b/b2/52/0bb2524d-ecfc-1bae-9c1e-218c978d7072/Honeymoon_3K.jpg/{w}x{h}bb.jpg', +// description: '앨범 커버 이미지 URL', +// }) +// @Prop() +// url?: URL; - @ApiProperty({ - example: 3000, - description: '배경 컬러', - required: true, - }) - @Prop() - bgColor: string; -} +// @ApiProperty({ +// example: 3000, +// description: '배경 컬러', +// required: true, +// }) +// @Prop() +// bgColor: string; +// } diff --git a/BE/musicspot/src/journey/schema/journey.schema.ts b/BE/musicspot/src/journey/schema/journey.schema.ts index 7da63a6..84f43fe 100644 --- a/BE/musicspot/src/journey/schema/journey.schema.ts +++ b/BE/musicspot/src/journey/schema/journey.schema.ts @@ -1,52 +1,52 @@ -import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; -import { HydratedDocument } from 'mongoose'; -import { ApiProperty } from '@nestjs/swagger'; -import { Song } from './song.schema'; -import { JourneyMetadata } from './journeyMetadata.schema'; -import { IsDefined, ValidateNested } from 'class-validator'; +// import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +// import { HydratedDocument } from 'mongoose'; +// import { ApiProperty } from '@nestjs/swagger'; +// import { Song } from './song.schema'; +// import { JourneyMetadata } from './journeyMetadata.schema'; +// import { IsDefined, ValidateNested } from 'class-validator'; -export type JourneyDocument = HydratedDocument; +// export type JourneyDocument = HydratedDocument; -@Schema({ collection: 'journey' }) -export class Journey { - @ApiProperty({ - example: 'test title', - description: '여정 제목', - required: true, - }) - @Prop() - title?: string; +// @Schema({ collection: 'journey' }) +// export class Journey { +// @ApiProperty({ +// example: 'test title', +// description: '여정 제목', +// required: true, +// }) +// @Prop() +// title?: string; - @ApiProperty({ - example: [ - '655efda2fdc81cae36d20650', - '655efd902908e4514ae5ff9b', - '655efd27a08c7995defc8e91', - ], - description: 'spot id 배열', - required: true, - }) - @Prop() - spots?: string[]; +// @ApiProperty({ +// example: [ +// '655efda2fdc81cae36d20650', +// '655efd902908e4514ae5ff9b', +// '655efd27a08c7995defc8e91', +// ], +// description: 'spot id 배열', +// required: true, +// }) +// @Prop() +// spots?: string[]; - @ApiProperty({ - example: [ - [37.674986, 126.776032], - [37.555946, 126.972384], - ], - description: '위치 좌표 배열', - required: true, - }) - @Prop({ type: [[Number]] }) - coordinates?: number[][]; +// @ApiProperty({ +// example: [ +// [37.674986, 126.776032], +// [37.555946, 126.972384], +// ], +// description: '위치 좌표 배열', +// required: true, +// }) +// @Prop({ type: [[Number]] }) +// coordinates?: number[][]; - @ApiProperty({ description: '메타데이터', type: JourneyMetadata }) - @Prop({ type: JourneyMetadata }) - journeyMetadata?: JourneyMetadata; +// @ApiProperty({ description: '메타데이터', type: JourneyMetadata }) +// @Prop({ type: JourneyMetadata }) +// journeyMetadata?: JourneyMetadata; - @ApiProperty({ description: '음악 정보', type: Song }) - @Prop({ type: Song }) - song?: Song; -} +// @ApiProperty({ description: '음악 정보', type: Song }) +// @Prop({ type: Song }) +// song?: Song; +// } -export const JourneySchema = SchemaFactory.createForClass(Journey); +// export const JourneySchema = SchemaFactory.createForClass(Journey); diff --git a/BE/musicspot/src/journey/schema/journeyMetadata.schema.ts b/BE/musicspot/src/journey/schema/journeyMetadata.schema.ts index e6add17..2000ac0 100644 --- a/BE/musicspot/src/journey/schema/journeyMetadata.schema.ts +++ b/BE/musicspot/src/journey/schema/journeyMetadata.schema.ts @@ -1,19 +1,19 @@ -import { Prop } from '@nestjs/mongoose'; -import { ApiProperty } from '@nestjs/swagger'; -export class JourneyMetadata { - @ApiProperty({ - example: '2023-11-22T15:30:00.000+09:00', - description: '여정 시작 시간', - required: true, - }) - @Prop({ type: String }) - startTimestamp: string; +// import { Prop } from '@nestjs/mongoose'; +// import { ApiProperty } from '@nestjs/swagger'; +// export class JourneyMetadata { +// @ApiProperty({ +// example: '2023-11-22T15:30:00.000+09:00', +// description: '여정 시작 시간', +// required: true, +// }) +// @Prop({ type: String }) +// startTimestamp: string; - @ApiProperty({ - example: '2023-11-22T15:30:00.000+09:00', - description: '여정 종료 시간', - required: true, - }) - @Prop({ type: String }) - endTimestamp: string; -} +// @ApiProperty({ +// example: '2023-11-22T15:30:00.000+09:00', +// description: '여정 종료 시간', +// required: true, +// }) +// @Prop({ type: String }) +// endTimestamp: string; +// } diff --git a/BE/musicspot/src/journey/schema/song.schema.ts b/BE/musicspot/src/journey/schema/song.schema.ts index a9001db..236f077 100644 --- a/BE/musicspot/src/journey/schema/song.schema.ts +++ b/BE/musicspot/src/journey/schema/song.schema.ts @@ -1,36 +1,36 @@ -import { Prop } from '@nestjs/mongoose'; -import { ApiProperty } from '@nestjs/swagger'; -import { Artwork } from './artwork.schema'; -export class Song { - @ApiProperty({ - example: '655efda2fdc81cae36d20650', - description: '노래 ID', - required: true, - }) - @Prop({ type: String }) - id?: string; +// import { Prop } from '@nestjs/mongoose'; +// import { ApiProperty } from '@nestjs/swagger'; +// import { Artwork } from './artwork.schema'; +// export class Song { +// @ApiProperty({ +// example: '655efda2fdc81cae36d20650', +// description: '노래 ID', +// required: true, +// }) +// @Prop({ type: String }) +// id?: string; - @ApiProperty({ - example: '655efda2fdc81cae36d20650', - description: '노래 ID', - required: true, - }) - @ApiProperty({ - example: 'super shy', - description: '노래 제목', - required: true, - }) - @Prop({ type: String }) - name?: string; +// @ApiProperty({ +// example: '655efda2fdc81cae36d20650', +// description: '노래 ID', +// required: true, +// }) +// @ApiProperty({ +// example: 'super shy', +// description: '노래 제목', +// required: true, +// }) +// @Prop({ type: String }) +// name?: string; - @ApiProperty({ - example: 'newjeans', - description: '가수', - required: true, - }) - @Prop({ type: String }) - artistName?: string; +// @ApiProperty({ +// example: 'newjeans', +// description: '가수', +// required: true, +// }) +// @Prop({ type: String }) +// artistName?: string; - @Prop({ type: Artwork }) - artwork?: Artwork; -} +// @Prop({ type: Artwork }) +// artwork?: Artwork; +// } diff --git a/BE/musicspot/src/journey/service/journey.service.ts b/BE/musicspot/src/journey/service/journey.service.ts index 1082e49..74a06bb 100644 --- a/BE/musicspot/src/journey/service/journey.service.ts +++ b/BE/musicspot/src/journey/service/journey.service.ts @@ -1,283 +1,190 @@ -import mongoose, { Model } from 'mongoose'; -import { InjectModel } from '@nestjs/mongoose'; -import { Injectable } from '@nestjs/common'; - -import { StartJourneyReqDTO } from '../dto/journeyStart/journeyStart.dto'; -import { Journey } from '../schema/journey.schema'; - -import { User } from '../../user/schema/user.schema'; +import { Injectable } from "@nestjs/common"; +import { JourneyRepository } from "../repository/journey.repository"; +import { StartJourneyReqDTO, StartJourneyResDTO } from '../dto/journeyStart/journeyStart.dto'; import { JourneyNotFoundException, coordinateNotCorrectException, } from '../../filters/journey.exception'; -import { UserNotFoundException } from '../../filters/user.exception'; -import * as turf from '@turf/turf'; -import { LoadJourneyDTO } from '../dto/journeyLoad.dto'; -import { - S3, - bucketName, - makePresignedUrl, -} from '../../common/s3/objectStorage'; -import { EndJourneyReqDTO } from '../dto/journeyEnd/journeyEnd.dto'; -import { CheckJourneyReqDTO } from '../dto/journeyCheck/journeyCheck.dto'; -import { RecordJourneyReqDTO } from '../dto/journeyRecord/journeyRecord.dto'; -import { is1DArray } from 'src/common/util/coordinate.util'; +import { EndJourneyReqDTO, EndJourneyResDTO } from '../dto/journeyEnd/journeyEnd.dto'; +import { RecordJourneyReqDTO, RecordJourneyResDTO } from '../dto/journeyRecord/journeyRecord.dto'; +import { is1DArray, parseCoordinateFromGeoToDto, parseCoordinatesFromGeoToDto } from 'src/common/util/coordinate.util'; import { DeleteJourneyReqDTO } from '../dto/journeyDelete.dto'; -import { checkPrimeSync } from 'crypto'; +import { UserRepository } from 'src/user/repository/user.repository'; + +import { Journey } from '../entities/journey.entity'; +import { makePresignedUrl } from "src/common/s3/objectStorage"; +import { parse } from "path"; @Injectable() -export class JourneyService { - constructor( - @InjectModel(Journey.name) private journeyModel: Model, - @InjectModel(User.name) private userModel: Model, - ) {} - async insertJourneyData(startJourneyDTO: StartJourneyReqDTO) { - const journeyData: Journey = { - ...startJourneyDTO, - coordinates: [startJourneyDTO.coordinate], - spots: [], - journeyMetadata: { - startTimestamp: startJourneyDTO.startTimestamp, - endTimestamp: '', - }, - }; - const createdJourneyData = new this.journeyModel(journeyData); - return await createdJourneyData.save(); - } - async pushJourneyIdToUser(journeyId, userId) { - const result = await this.userModel - .findOneAndUpdate( - { userId }, - { $push: { journeys: journeyId } }, - { new: true }, - ) - .lean(); - if (!result) { - new UserNotFoundException(); - } - return result; - } +export class JourneyService{ + constructor(private journeyRepository: JourneyRepository, private userRepository: UserRepository){} + + async insertJourneyData(startJourneyDTO:StartJourneyReqDTO){ + const startPoint = startJourneyDTO.coordinate.join(' '); - async create(startJourneyDTO: StartJourneyReqDTO) { - const createdJourneyData = await this.insertJourneyData(startJourneyDTO); - const updateUserInfo = await this.pushJourneyIdToUser( - createdJourneyData._id, - startJourneyDTO.userId, - ); - const { coordinates, journeyMetadata, _id } = createdJourneyData; - const [coordinate] = coordinates; - const { startTimestamp } = journeyMetadata; - return { coordinate, startTimestamp, journeyId: _id }; - } + const lineStringOfCoordinates = `LINESTRING(${startPoint}, ${startPoint})` - async end(endJourneyDTO: EndJourneyReqDTO) { - const { journeyId, title, coordinates, endTimestamp, song } = endJourneyDTO; - // const coordinateToAdd = Array.isArray(coordinate[0]) - // ? coordinate - // : [coordinate]; - const coordinatesLen = coordinates.length; + const returnedData = await this.journeyRepository.save({...startJourneyDTO, coordinates: lineStringOfCoordinates}) - const updatedJourney = await this.journeyModel - .findOneAndUpdate( - { _id: journeyId }, - { - $set: { title, song, 'journeyMetadata.endTimestamp': endTimestamp }, - $push: { coordinates: { $each: coordinates } }, - }, - { new: true }, - ) - .lean(); + const [parsedCoordinate] = parseCoordinatesFromGeoToDto(returnedData.coordinates) + + const returnData:StartJourneyResDTO = { + journeyId : returnedData.journeyId, + coordinate : parsedCoordinate, + startTimestamp : returnedData.startTimestamp, + } - if (!updatedJourney) { - throw new JourneyNotFoundException(); + return returnData; } - const updatedCoordinates = updatedJourney.coordinates; - const updatedEndTimestamp = updatedJourney.journeyMetadata.endTimestamp; - const updatedId = updatedJourney._id; - const updatedSong = updatedJourney.song; - const updatedCoordinatesLen = coordinates.length; - const totalCoordinatesLen = updatedCoordinates.length; - const slicedCoordinates = updatedCoordinates.slice(-updatedCoordinatesLen); - return { - id: updatedId, - coordinates: slicedCoordinates, - endTimestamp: updatedEndTimestamp, - song: updatedSong, - numberOfCoordinates: totalCoordinatesLen, - }; - } + async end(endJourneyDTO: EndJourneyReqDTO){ + const {coordinates, journeyId, song, title, endTimestamp} = endJourneyDTO; + const coordinatesLen = coordinates.length; + const originData = await this.journeyRepository.findOne({where:{journeyId}}) + if(!originData){ + throw new JourneyNotFoundException(); + } - async pushCoordianteToJourney(recordJourneyDTO: RecordJourneyReqDTO) { - const { journeyId, coordinates } = recordJourneyDTO; - const coordinatesLen = coordinates.length; - // coordinate가 단일 배열인 경우 이를 이중 배열로 감싸서 처리 + const originCoordinates =originData.coordinates; + const newCoordinates = originData.coordinates = originCoordinates.slice(0, -1) + ',' +endJourneyDTO.coordinates.map((item)=> `${item[0]} ${item[1]}`).join(',') + ')' + const newJourneyData = {...originData, ...endJourneyDTO, song : JSON.stringify(song), coordinates: newCoordinates}; + + const returnedDate = await this.journeyRepository.save(newJourneyData); + + const parsedCoordinates = parseCoordinatesFromGeoToDto(returnedDate.coordinates) + const returnData:EndJourneyResDTO = { + journeyId : returnedDate.journeyId, + coordinates : parsedCoordinates.slice(parsedCoordinates.length-coordinatesLen), + endTimestamp : returnedDate.endTimestamp, + numberOfCoordinates : parsedCoordinates.length, + song : JSON.parse(returnedDate.song) + } - // const coordinateToAdd = Array.isArray(coordinate[0]) - // ? coordinate - // : [coordinate]; - const updatedJourney = await this.journeyModel - .findOneAndUpdate( - { _id: journeyId }, - { $push: { coordinates: { $each: coordinates } } }, - { new: true }, - ) - .lean(); - if (!updatedJourney) { - throw new JourneyNotFoundException(); + return returnData } - const updatedCoordinates = updatedJourney.coordinates; - return { coordinates: updatedCoordinates.slice(-coordinatesLen) }; - // const { coordinates } = updatedJourney; - // const len = coordinates.length; - // return { coordinate: coordinates[len - 1] }; - } - async checkJourney(checkJourneyDTO) { - let { userId, minCoordinate, maxCoordinate } = checkJourneyDTO; - if (!(Array.isArray(minCoordinate) && Array.isArray(maxCoordinate))) { - throw new coordinateNotCorrectException(); - } - minCoordinate = minCoordinate.map((item) => Number(item)); - maxCoordinate = maxCoordinate.map((item) => Number(item)); - if (!(is1DArray(minCoordinate) && is1DArray(maxCoordinate))) { - throw new coordinateNotCorrectException(); - } - const user = await this.userModel.findOne({ userId }).lean(); - if (!user) { - throw new UserNotFoundException(); + async pushCoordianteToJourney(recordJourneyDTO: RecordJourneyReqDTO) { + + const {journeyId, coordinates} = recordJourneyDTO; + const coordinateLen = coordinates.length; + const originData = await this.journeyRepository.findOne({where:{journeyId}}); + if(!originData){ + throw new JourneyNotFoundException(); + } + const originCoordinates =originData.coordinates; + + + originData.coordinates = originCoordinates.slice(0, -1) + ',' +recordJourneyDTO.coordinates.map((item)=> `${item[0]} ${item[1]}`).join(',') + ')' + const returnedData = await this.journeyRepository.save(originData) + + const updatedCoordinate = parseCoordinatesFromGeoToDto(returnedData.coordinates); + const len = updatedCoordinate.length; + + + return {coordinates:updatedCoordinate.slice(len-coordinateLen)} + } - const journeys = user.journeys; - const boundingBox = turf.bboxPolygon([ - minCoordinate[0], - minCoordinate[1], - maxCoordinate[0], - maxCoordinate[1], - ]); - // console.log(boundingBox); - const journeyList = await this.findMinMaxCoordinates(boundingBox, journeys); - return journeyList; - } - async findMinMaxCoordinates(boundingBox, journeys) { - let journeyList = []; - for (let i = 0; i < journeys.length; i++) { - let journey = await this.findByIdWithPopulate( - journeys[i], - 'spots', - 'Spot', - { - transform: (spot) => { - return { - coordinate: spot.coordinate, - timestamp: spot.timestamp, - photoUrl: makePresignedUrl(spot.photoKey), - }; - }, - }, - ); - if (!journey) { - throw new JourneyNotFoundException(); + async getJourneyByCoordinationRange(checkJourneyDTO) { + let { userId, minCoordinate, maxCoordinate } = checkJourneyDTO; + if (!(Array.isArray(minCoordinate) && Array.isArray(maxCoordinate))) { + throw new coordinateNotCorrectException(); } - if (journey.coordinates.length < 2) { - continue; + + minCoordinate = minCoordinate.map((item) => Number(item)); + maxCoordinate = maxCoordinate.map((item) => Number(item)); + + if (!(is1DArray(minCoordinate) && is1DArray(maxCoordinate))) { + throw new coordinateNotCorrectException(); } - let journeyLine = turf.lineString(journey.coordinates); - if (!turf.booleanDisjoint(journeyLine, boundingBox)) { - journeyList.push(journey); + const coordinatesRange = { + xMinCoordinate : minCoordinate[0], + yMinCoordinate : minCoordinate[1], + xMaxCoordinate : maxCoordinate[0], + yMaxCoordinate : maxCoordinate[1] } - } - return journeyList; - } - - async loadLastJourney(userId) { - const user = await this.userModel.findOne({userId}).lean(); - if(!user){ - throw new UserNotFoundException(); + const returnedData = await this.journeyRepository.manager + .createQueryBuilder(Journey, "journey") + .leftJoinAndSelect("journey.spots", "spot") + .where(`st_within(coordinates, ST_PolygonFromText('POLYGON((:xMinCoordinate :yMinCoordinate, :xMaxCoordinate :yMinCoordinate, :xMaxCoordinate :yMaxCoordinate, :xMinCoordinate :yMaxCoordinate, :xMinCoordinate :yMinCoordinate))'))`, coordinatesRange) + .where('userId = :userId', {userId}) + .getMany(); + + return returnedData.map(data =>{ + return this.parseJourneyFromEntityToDto(data) + }) } - const journeys = user.journeys; + async getLastJourneyByUserId(userId){ + const journeys = await this.journeyRepository.manager + .createQueryBuilder(Journey, "journey") + .where({userId}) + .leftJoinAndSelect("journey.spots", "spot") + .getMany() - const len = journeys.length; - const lastJourneyId = journeys[len-1]; - - const lastJourney = await this.journeyModel - .findById(lastJourneyId) - .populate({ - path: 'spots', - model: 'Spot', - options: { - transform: (spot) => { - return { - coordinate: spot.coordinate, - timestamp: spot.timestamp, - photoUrl: makePresignedUrl(spot.photoKey), - }; - }, - }, - }) - .lean(); - - if (!lastJourney) { + if(!journeys){ return { - journey: null, - isRecording: false, + journey : null, + isRecording: false }; } + const journeyLen = journeys.length; + const lastJourneyData = journeys[journeyLen-1]; + + if(lastJourneyData.title){ + return {journey:null, isRecording : false} + } + + return { - journey: lastJourney.title ? null : lastJourney, - isRecording: lastJourney.title ? false : true, - }; - } + journey : this.parseJourneyFromEntityToDto(lastJourneyData), + isRecording : true + + + } + + + } + + async getJourneyById(journeyId) { + const returnedData = await this.journeyRepository.manager + .createQueryBuilder(Journey, "journey") + .where({journeyId}) + .leftJoinAndSelect("journey.spots", "spot") + .getOne() + return this.parseJourneyFromEntityToDto(returnedData); + + + } - async getJourneyById(journeyId) { - return await this.findByIdWithPopulate(journeyId, 'spots', 'Spot', { - transform: (spot) => { + parseJourneyFromEntityToDto(journey){ + const {journeyId, coordinates, startTimestamp, endTimestamp, song, title, spots} = journey; return { - coordinate: spot.coordinate, - timestamp: spot.timestamp, - photoUrl: makePresignedUrl(spot.photoKey), - }; - }, - }); - } - - async findByIdWithPopulate(id, path, model, options?) { - return await this.journeyModel - .findById(id) - .populate({ - path, - model, - options, - }) - .lean(); - } + journeyId, + coordinates : parseCoordinatesFromGeoToDto(coordinates), + title, + journeyMetadata : {startTimestamp : journey.startTimestamp, endTimestamp : journey.endTimestamp}, + song : JSON.parse(song), + spots : journey.spots.map(spot =>{ + return { + ...spot, + coordinate : parseCoordinateFromGeoToDto(spot.coordinate), + photoUrl : makePresignedUrl(spot.photoKey) + } + }) - async deleteJourneyById(deletedJourneyDto: DeleteJourneyReqDTO) { - const { userId, journeyId } = deletedJourneyDto; + } + } - const deletedJourney = await this.journeyModel - .findOneAndDelete({ - _id: journeyId, - }) - .lean(); - if (!deletedJourney) { - throw new JourneyNotFoundException(); - } + async deleteJourneyById(deletedJourneyDto: DeleteJourneyReqDTO){ + const {journeyId} = deletedJourneyDto; + return await this.journeyRepository.delete({journeyId}) + } - const deletedUserData = await this.userModel.findOneAndUpdate( - { userId }, - { $pull: { journeys: new mongoose.Types.ObjectId(journeyId) } }, - { new: true }, - ); +} - if (!deletedUserData) { - throw new UserNotFoundException(); - } + - return deletedJourney; - } -} From 13935e1d6daba102de70b458e2d514776ca9720b Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Sat, 27 Jan 2024 03:14:38 +0900 Subject: [PATCH 12/12] =?UTF-8?q?:sparkles:=20rdb=20special=20data=20type?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20dto=EB=A1=9C=20=EB=B3=80=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/common/util/coordinate.util.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/BE/musicspot/src/common/util/coordinate.util.ts b/BE/musicspot/src/common/util/coordinate.util.ts index 97b7d9e..07c1fd1 100644 --- a/BE/musicspot/src/common/util/coordinate.util.ts +++ b/BE/musicspot/src/common/util/coordinate.util.ts @@ -35,4 +35,32 @@ export const isInCoordinateRange = (pos) => { return false; } return true; -}; \ No newline at end of file +}; + + + +export const parseCoordinateFromDtoToGeo = (coordinate: [number, number])=>{ + // coordinate = [1, 2] + return `${coordinate.join(' ')}` + +} + +export const parseCoordinateFromGeoToDto = (coordinate: string) =>{ + // coordinate = 'POINT(1 2)' + + const pointLen = 'POINT('.length; + const numberOfCoordinate = coordinate.slice(pointLen, -1); + return numberOfCoordinate.split(' ').map(pos=>Number(pos)); + +} + +export const parseCoordinatesFromDtoToGeo = (coordinates: number[][]) =>{ + // coordinates = [[1,2], [3,4]] + return `${coordinates.map(coordinate=>`${coordinate[0]} ${coordinate[1]}`).join(',')}` +} + +export const parseCoordinatesFromGeoToDto = (coordinates: string) :number[][]=>{ + const lineStringLen = 'LINESTRING('.length; + const numberOfCoordinates = coordinates.slice(lineStringLen, -1); + return numberOfCoordinates.split(',').map(coordinate=>coordinate.split(' ').map(num=>Number(num.trim()))) +} \ No newline at end of file