diff --git a/.env.example b/.env.example
index 769e0fa8..ea107da9 100644
--- a/.env.example
+++ b/.env.example
@@ -16,3 +16,10 @@ CORS_WHITELIST=[CORS 정책에서 허용하는 도메인의 목록(e.g. ["http:/
GOOGLE_APPLICATION_CREDENTIALS=[GOOGLE_APPLICATION_CREDENTIALS JSON]
TEST_ACCOUNTS=[스팍스SSO로 로그인시 무조건 테스트로 로그인이 가능한 허용 아이디 목록]
SLACK_REPORT_WEBHOOK_URL=[Slack 웹훅 URL들이 담긴 JSON]
+
+# optional environment variables for taxiSampleGenerator
+SAMPLE_NUM_OF_ROOMS=[방의 개수]
+SAMPLE_NUM_OF_CHATS=[각 방의 채팅 개수]
+SAMPLE_MAXIMUM_INTERVAL_BETWEEN_CHATS=[채팅 간 최대 시간 간격(단위: 초, 실수도 가능)]
+SAMPLE_OCCURENCE_OF_JOIN=[새로운 채팅이 입장 메세지일 확률(0 ~ 1 사이의 값)]
+SAMPLE_OCCURENCE_OF_ABORT=[새로운 채팅이 퇴장 메세지일 확률(0 ~ 1 사이의 값)]
\ No newline at end of file
diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml
index b08563d1..1f19bb94 100644
--- a/.github/workflows/test_ci.yml
+++ b/.github/workflows/test_ci.yml
@@ -31,20 +31,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- - id: submodule-local
- name: Save local version of submodule
- run: echo "ver=`cd sampleGenerator && git log --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT
- - id: submodule-origin
- name: Save origin version of submodule
- run: echo "ver=`cd sampleGenerator && git log origin --pretty="%h" -1 && cd ..`" >> $GITHUB_OUTPUT
- - name: Check submodule version
- if: ${{ steps.submodule-local.outputs.ver != steps.submodule-origin.outputs.ver }}
- uses: actions/github-script@v3
- with:
- script: |
- core.setFailed('Please update submodule to the latest version by using \"git submodule update --remote\"')
- - name: Install sampleGenerator dependencies from package-lock.json
- run: cd sampleGenerator && pnpm i --force --frozen-lockfile && cd ..
- name: Install taxi-back dependencies from package-lock.json
run: pnpm i --force --frozen-lockfile
- name: Run unit tests
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index f15db4d8..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,4 +0,0 @@
-[submodule "sampleGenerator"]
- path = sampleGenerator
- url = https://github.com/sparcs-kaist/taxiSampleGenerator
- branch = main
diff --git a/README.md b/README.md
index 26cda418..9a335131 100644
--- a/README.md
+++ b/README.md
@@ -49,4 +49,3 @@ See [contributors](https://github.com/sparcs-kaist/taxi-front/graphs/contributor
- app : https://github.com/sparcs-kaist/taxi-app
- docker : https://github.com/sparcs-kaist/taxi-docker
- figma : https://www.figma.com/file/li34hP1oStJAzLNjcG5KjN/SPARCS-Taxi?node-id=0%3A1
- - taxiSampleGenerator : https://github.com/sparcs-kaist/taxiSampleGenerator
diff --git a/package.json b/package.json
index d0a3d172..e9d3ff5e 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,8 @@
"clean": "rimraf dist/",
"serve": "cross-env TZ='Asia/Seoul' NODE_ENV=production node dist/index.js",
"lint": "pnpm eslint .",
- "sample": "cd sampleGenerator && npm start && cd .."
+ "runscript": "cross-env TZ='Asia/Seoul' NODE_ENV=production node",
+ "sample": "cd src/sampleGenerator && npm start && cd .."
},
"engines": {
"node": ">=18.0.0",
@@ -75,6 +76,7 @@
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-mocha": "^10.1.0",
"mocha": "^10.2.0",
+ "mongodb": "^4.1.0",
"nodemon": "^3.0.1",
"rimraf": "^5.0.5",
"supertest": "^6.2.4",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 72a2f835..469bd3d7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -163,6 +163,9 @@ devDependencies:
mocha:
specifier: ^10.2.0
version: 10.2.0
+ mongodb:
+ specifier: ^4.1.0
+ version: 4.17.1
nodemon:
specifier: ^3.0.1
version: 3.0.1
@@ -277,7 +280,6 @@ packages:
'@aws-crypto/util': 3.0.0
'@aws-sdk/types': 3.425.0
tslib: 1.14.1
- dev: false
optional: true
/@aws-crypto/ie11-detection@3.0.0:
@@ -285,7 +287,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 1.14.1
- dev: false
optional: true
/@aws-crypto/sha256-browser@3.0.0:
@@ -300,7 +301,6 @@ packages:
'@aws-sdk/util-locate-window': 3.310.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
- dev: false
optional: true
/@aws-crypto/sha256-js@3.0.0:
@@ -310,7 +310,6 @@ packages:
'@aws-crypto/util': 3.0.0
'@aws-sdk/types': 3.425.0
tslib: 1.14.1
- dev: false
optional: true
/@aws-crypto/supports-web-crypto@3.0.0:
@@ -318,7 +317,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 1.14.1
- dev: false
optional: true
/@aws-crypto/util@3.0.0:
@@ -328,7 +326,6 @@ packages:
'@aws-sdk/types': 3.425.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
- dev: false
optional: true
/@aws-sdk/client-cognito-identity@3.427.0:
@@ -375,7 +372,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/client-sso@3.427.0:
@@ -419,7 +415,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/client-sts@3.427.0:
@@ -467,7 +462,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/credential-provider-cognito-identity@3.427.0:
@@ -482,7 +476,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/credential-provider-env@3.425.0:
@@ -494,7 +487,6 @@ packages:
'@smithy/property-provider': 2.0.12
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/credential-provider-http@3.425.0:
@@ -509,7 +501,6 @@ packages:
'@smithy/protocol-http': 3.0.7
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/credential-provider-ini@3.427.0:
@@ -529,7 +520,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/credential-provider-node@3.427.0:
@@ -550,7 +540,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/credential-provider-process@3.425.0:
@@ -563,7 +552,6 @@ packages:
'@smithy/shared-ini-file-loader': 2.2.0
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/credential-provider-sso@3.427.0:
@@ -580,7 +568,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/credential-provider-web-identity@3.425.0:
@@ -592,7 +579,6 @@ packages:
'@smithy/property-provider': 2.0.12
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/credential-providers@3.427.0:
@@ -618,7 +604,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/middleware-host-header@3.425.0:
@@ -630,7 +615,6 @@ packages:
'@smithy/protocol-http': 3.0.7
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/middleware-logger@3.425.0:
@@ -641,7 +625,6 @@ packages:
'@aws-sdk/types': 3.425.0
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/middleware-recursion-detection@3.425.0:
@@ -653,7 +636,6 @@ packages:
'@smithy/protocol-http': 3.0.7
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/middleware-sdk-sts@3.425.0:
@@ -665,7 +647,6 @@ packages:
'@aws-sdk/types': 3.425.0
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/middleware-signing@3.425.0:
@@ -680,7 +661,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/util-middleware': 2.0.4
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/middleware-user-agent@3.427.0:
@@ -693,7 +673,6 @@ packages:
'@smithy/protocol-http': 3.0.7
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/region-config-resolver@3.425.0:
@@ -706,7 +685,6 @@ packages:
'@smithy/util-config-provider': 2.0.0
'@smithy/util-middleware': 2.0.4
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/token-providers@3.427.0:
@@ -751,7 +729,6 @@ packages:
tslib: 2.6.2
transitivePeerDependencies:
- aws-crt
- dev: false
optional: true
/@aws-sdk/types@3.425.0:
@@ -761,7 +738,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/util-endpoints@3.427.0:
@@ -772,7 +748,6 @@ packages:
'@aws-sdk/types': 3.425.0
'@smithy/node-config-provider': 2.1.1
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/util-locate-window@3.310.0:
@@ -781,7 +756,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/util-user-agent-browser@3.425.0:
@@ -792,7 +766,6 @@ packages:
'@smithy/types': 2.3.5
bowser: 2.11.0
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/util-user-agent-node@3.425.0:
@@ -809,7 +782,6 @@ packages:
'@smithy/node-config-provider': 2.1.1
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@aws-sdk/util-utf8-browser@3.259.0:
@@ -817,7 +789,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@babel/code-frame@7.22.5:
@@ -2334,7 +2305,7 @@ packages:
'@firebase/database-types': 0.10.4
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.3
- tslib: 2.6.1
+ tslib: 2.6.2
dev: false
/@firebase/database-types@0.10.4:
@@ -2595,7 +2566,6 @@ packages:
requiresBuild: true
dependencies:
sparse-bitfield: 3.0.3
- dev: false
optional: true
/@nodelib/fs.scandir@2.1.5:
@@ -2861,7 +2831,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/config-resolver@2.0.14:
@@ -2874,7 +2843,6 @@ packages:
'@smithy/util-config-provider': 2.0.0
'@smithy/util-middleware': 2.0.4
tslib: 2.6.2
- dev: false
optional: true
/@smithy/credential-provider-imds@2.0.16:
@@ -2887,7 +2855,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/url-parser': 2.0.11
tslib: 2.6.2
- dev: false
optional: true
/@smithy/eventstream-codec@2.0.11:
@@ -2898,7 +2865,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/util-hex-encoding': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/fetch-http-handler@2.2.2:
@@ -2910,7 +2876,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/util-base64': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/hash-node@2.0.11:
@@ -2922,7 +2887,6 @@ packages:
'@smithy/util-buffer-from': 2.0.0
'@smithy/util-utf8': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/invalid-dependency@2.0.11:
@@ -2931,7 +2895,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/is-array-buffer@2.0.0:
@@ -2940,7 +2903,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/middleware-content-length@2.0.13:
@@ -2951,7 +2913,6 @@ packages:
'@smithy/protocol-http': 3.0.7
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/middleware-endpoint@2.0.11:
@@ -2964,7 +2925,6 @@ packages:
'@smithy/url-parser': 2.0.11
'@smithy/util-middleware': 2.0.4
tslib: 2.6.2
- dev: false
optional: true
/@smithy/middleware-retry@2.0.16:
@@ -2980,7 +2940,6 @@ packages:
'@smithy/util-retry': 2.0.4
tslib: 2.6.2
uuid: 8.3.2
- dev: false
optional: true
/@smithy/middleware-serde@2.0.11:
@@ -2990,7 +2949,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/middleware-stack@2.0.5:
@@ -3000,7 +2958,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/node-config-provider@2.1.1:
@@ -3012,7 +2969,6 @@ packages:
'@smithy/shared-ini-file-loader': 2.2.0
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/node-http-handler@2.1.7:
@@ -3025,7 +2981,6 @@ packages:
'@smithy/querystring-builder': 2.0.11
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/property-provider@2.0.12:
@@ -3035,7 +2990,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/protocol-http@3.0.7:
@@ -3045,7 +2999,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/querystring-builder@2.0.11:
@@ -3056,7 +3009,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/util-uri-escape': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/querystring-parser@2.0.11:
@@ -3066,7 +3018,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/service-error-classification@2.0.4:
@@ -3075,7 +3026,6 @@ packages:
requiresBuild: true
dependencies:
'@smithy/types': 2.3.5
- dev: false
optional: true
/@smithy/shared-ini-file-loader@2.2.0:
@@ -3085,7 +3035,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/signature-v4@2.0.11:
@@ -3101,7 +3050,6 @@ packages:
'@smithy/util-uri-escape': 2.0.0
'@smithy/util-utf8': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/smithy-client@2.1.10:
@@ -3113,7 +3061,6 @@ packages:
'@smithy/types': 2.3.5
'@smithy/util-stream': 2.0.15
tslib: 2.6.2
- dev: false
optional: true
/@smithy/types@2.3.5:
@@ -3122,7 +3069,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/url-parser@2.0.11:
@@ -3132,7 +3078,6 @@ packages:
'@smithy/querystring-parser': 2.0.11
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-base64@2.0.0:
@@ -3142,7 +3087,6 @@ packages:
dependencies:
'@smithy/util-buffer-from': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-body-length-browser@2.0.0:
@@ -3150,7 +3094,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-body-length-node@2.1.0:
@@ -3159,7 +3102,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-buffer-from@2.0.0:
@@ -3169,7 +3111,6 @@ packages:
dependencies:
'@smithy/is-array-buffer': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-config-provider@2.0.0:
@@ -3178,7 +3119,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-defaults-mode-browser@2.0.14:
@@ -3191,7 +3131,6 @@ packages:
'@smithy/types': 2.3.5
bowser: 2.11.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-defaults-mode-node@2.0.18:
@@ -3206,7 +3145,6 @@ packages:
'@smithy/smithy-client': 2.1.10
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-hex-encoding@2.0.0:
@@ -3215,7 +3153,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-middleware@2.0.4:
@@ -3225,7 +3162,6 @@ packages:
dependencies:
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-retry@2.0.4:
@@ -3236,7 +3172,6 @@ packages:
'@smithy/service-error-classification': 2.0.4
'@smithy/types': 2.3.5
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-stream@2.0.15:
@@ -3252,7 +3187,6 @@ packages:
'@smithy/util-hex-encoding': 2.0.0
'@smithy/util-utf8': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-uri-escape@2.0.0:
@@ -3261,7 +3195,6 @@ packages:
requiresBuild: true
dependencies:
tslib: 2.6.2
- dev: false
optional: true
/@smithy/util-utf8@2.0.0:
@@ -3271,7 +3204,6 @@ packages:
dependencies:
'@smithy/util-buffer-from': 2.0.0
tslib: 2.6.2
- dev: false
optional: true
/@socket.io/component-emitter@3.1.0:
@@ -3995,14 +3927,12 @@ packages:
/@types/webidl-conversions@7.0.0:
resolution: {integrity: sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==}
- dev: false
/@types/whatwg-url@8.2.2:
resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==}
dependencies:
'@types/node': 20.9.0
'@types/webidl-conversions': 7.0.0
- dev: false
/@typescript-eslint/eslint-plugin@6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.22.0)(typescript@5.2.2):
resolution: {integrity: sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==}
@@ -4516,7 +4446,6 @@ packages:
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- dev: false
/base64id@2.0.0:
resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
@@ -4575,7 +4504,6 @@ packages:
/bowser@2.11.0:
resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
requiresBuild: true
- dev: false
optional: true
/brace-expansion@1.1.11:
@@ -4616,7 +4544,6 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
buffer: 5.7.1
- dev: false
/buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
@@ -4639,7 +4566,6 @@ packages:
dependencies:
base64-js: 1.5.1
ieee754: 1.1.13
- dev: false
/builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
@@ -5840,7 +5766,6 @@ packages:
requiresBuild: true
dependencies:
strnum: 1.0.5
- dev: false
optional: true
/fast-xml-parser@4.2.7:
@@ -6439,7 +6364,6 @@ packages:
/ieee754@1.1.13:
resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==}
- dev: false
/ignore-by-default@1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
@@ -6482,7 +6406,6 @@ packages:
/ip@2.0.0:
resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
- dev: false
/ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
@@ -7198,7 +7121,6 @@ packages:
/memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
requiresBuild: true
- dev: false
optional: true
/merge-descriptors@1.0.1:
@@ -7347,7 +7269,6 @@ packages:
dependencies:
'@types/whatwg-url': 8.2.2
whatwg-url: 11.0.0
- dev: false
/mongodb@4.17.1:
resolution: {integrity: sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==}
@@ -7361,7 +7282,6 @@ packages:
'@mongodb-js/saslprep': 1.1.0
transitivePeerDependencies:
- aws-crt
- dev: false
/mongoose@6.12.0:
resolution: {integrity: sha512-sd/q83C6TBRPBrrD2A/POSbA/exbCFM2WOuY7Lf2JuIJFlHFG39zYSDTTAEiYlzIfahNOLmXPxBGFxdAch41Mw==}
@@ -8723,7 +8643,6 @@ packages:
/smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
- dev: false
/socket.io-adapter@2.5.2:
resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==}
@@ -8767,7 +8686,6 @@ packages:
dependencies:
ip: 2.0.0
smart-buffer: 4.2.0
- dev: false
/source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
@@ -8796,7 +8714,6 @@ packages:
requiresBuild: true
dependencies:
memory-pager: 1.5.0
- dev: false
optional: true
/stack-trace@0.0.10:
@@ -8895,7 +8812,6 @@ packages:
/strnum@1.0.5:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
requiresBuild: true
- dev: false
optional: true
/stubs@3.0.0:
@@ -9121,7 +9037,6 @@ packages:
engines: {node: '>=12'}
dependencies:
punycode: 2.3.0
- dev: false
/triple-beam@1.4.1:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
@@ -9161,16 +9076,10 @@ packages:
/tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
requiresBuild: true
- dev: false
optional: true
- /tslib@2.6.1:
- resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
- dev: false
-
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- dev: false
/type-check@0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
@@ -9403,7 +9312,6 @@ packages:
/uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
- dev: false
/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
@@ -9454,7 +9362,6 @@ packages:
/webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
- dev: false
/websocket-driver@0.7.4:
resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==}
@@ -9476,7 +9383,6 @@ packages:
dependencies:
tr46: 3.0.0
webidl-conversions: 7.0.0
- dev: false
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
diff --git a/sampleGenerator b/sampleGenerator
deleted file mode 160000
index bd4329c1..00000000
--- a/sampleGenerator
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit bd4329c15405a09c94e7b78e19ff296b4c2d0fb3
diff --git a/scripts/profileImageUrlUpdater.js b/scripts/profileImageUrlUpdater.js
new file mode 100644
index 00000000..78ebe778
--- /dev/null
+++ b/scripts/profileImageUrlUpdater.js
@@ -0,0 +1,36 @@
+// Issue #173을 해결하기 위한 DB 마이그레이션 스크립트입니다.
+// https://github.com/sparcs-kaist/taxi-back/issues/173
+
+const { MongoClient } = require("mongodb");
+const { mongo: mongoUrl, aws: awsEnv } = require("../loadenv");
+
+const time = Date.now();
+
+const client = new MongoClient(mongoUrl);
+const db = client.db("taxi");
+const users = db.collection("users");
+
+async function run() {
+ try {
+ for await (const doc of users.find()) {
+ // 이미 변환이 완료된 경우에는 Pass합니다.
+ if (doc.profileImageUrl.startsWith(awsEnv.s3Url)) continue;
+
+ await users.findOneAndUpdate(
+ { _id: doc._id },
+ {
+ $set: {
+ profileImageUrl: `${awsEnv.s3Url}/profile-img/${doc.profileImageUrl}?token=${time}`,
+ },
+ }
+ );
+ }
+ } catch (err) {
+ console.log(err);
+ } finally {
+ await client.close();
+ }
+}
+run().then(() => {
+ console.log("Done!");
+});
diff --git a/src/index.ts b/src/index.ts
index ef692ea5..1b3d00f9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -3,7 +3,7 @@ import express from "express";
import cookieParser from "cookie-parser";
import http from "http";
-import { nodeEnv, port as httpPort } from "@/loadenv";
+import { nodeEnv, mongo as mongoUrl, port as httpPort } from "@/loadenv";
import {
corsMiddleware,
sessionMiddleware,
@@ -38,7 +38,7 @@ initializeApp();
const app = express();
// 데이터베이스 연결
-connectDatabase();
+connectDatabase(mongoUrl);
// [Middleware] request body 파싱
app.use(express.urlencoded({ extended: false }));
diff --git a/src/modules/modifyProfile.js b/src/modules/modifyProfile.js
index 0d15bd58..e8702f98 100755
--- a/src/modules/modifyProfile.js
+++ b/src/modules/modifyProfile.js
@@ -1,4 +1,5 @@
const crypto = require("crypto");
+const aws = require("./stores/aws");
const nouns = [
"재료역학",
@@ -81,7 +82,7 @@ const generateNickname = (id) => {
// 기존 프로필 사진의 URI 중 하나를 무작위로 선택해 반환합니다.
const generateProfileImageUrl = () => {
const ridx = crypto.randomInt(defaultProfile.length);
- return `default/${defaultProfile[ridx]}`;
+ return aws.getS3Url(`/profile-img/default/${defaultProfile[ridx]}`);
};
// 사용자의 이름과 성을 받아, 한글인지 영어인지에 따라 전체 이름을 반환합니다.
diff --git a/src/modules/socket.js b/src/modules/socket.js
index eabbbe62..cd1d7d95 100644
--- a/src/modules/socket.js
+++ b/src/modules/socket.js
@@ -219,9 +219,11 @@ const emitUpdateEvent = async (io, roomId) => {
throw new IllegalArgumentsException();
}
- part.forEach(({ user }) => io.in(`user-${user}`).emit("chat_update"), {
- roomId,
- });
+ part.forEach(({ user }) =>
+ io.in(`user-${user}`).emit("chat_update", {
+ roomId,
+ })
+ );
return true;
} catch (err) {
diff --git a/src/modules/stores/mongo.js b/src/modules/stores/mongo.js
index f4741652..7aaebb27 100755
--- a/src/modules/stores/mongo.js
+++ b/src/modules/stores/mongo.js
@@ -1,7 +1,6 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
-const { mongo: mongoUrl } = require("@/loadenv");
const logger = require("@/modules/logger");
const userSchema = Schema({
@@ -184,23 +183,27 @@ database.on("error", function (err) {
logger.error("데이터베이스 연결 에러 발생: " + err);
mongoose.disconnect();
});
-database.on("disconnected", function () {
- // 데이터베이스 연결이 끊어지면 5초 후 재연결을 시도합니다.
- logger.error("데이터베이스와 연결이 끊어졌습니다!");
- setTimeout(() => {
- mongoose.connect(mongoUrl, {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- });
- }, 5000);
-});
-const connectDatabase = () =>
+const connectDatabase = (mongoUrl) => {
+ database.on("disconnected", function () {
+ // 데이터베이스 연결이 끊어지면 5초 후 재연결을 시도합니다.
+ logger.error("데이터베이스와 연결이 끊어졌습니다!");
+ setTimeout(() => {
+ mongoose.connect(mongoUrl, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+ });
+ }, 5000);
+ });
+
mongoose.connect(mongoUrl, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
+ return database;
+};
+
module.exports = {
connectDatabase,
userModel: mongoose.model("User", userSchema),
diff --git a/src/routes/docs/chats.js b/src/routes/docs/chats.js
new file mode 100644
index 00000000..97fc1352
--- /dev/null
+++ b/src/routes/docs/chats.js
@@ -0,0 +1,513 @@
+const { objectIdPattern } = require("./utils");
+
+const tag = "chats";
+const apiPrefix = "/chats";
+
+const chatsDocs = {};
+chatsDocs[`${apiPrefix}`] = {
+ post: {
+ tags: [tag],
+ summary: "가장 최근 채팅 가져오기",
+ description: "가장 최근에 도착한 60개의 채팅을 가져옵니다.",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅을 보내는 방의 id",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ examples: {
+ "소켓 연결 오류": { value: "Chat/ : socket did not connected" },
+ "유저가 방에 참여하지 않음": {
+ value: "Chat/ : user did not participated in the room",
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/ : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/load/before`] = {
+ post: {
+ tags: [tag],
+ summary: "특정 시점 이전의 채팅 가져오기",
+ description: "lastMsgDate 이전에 도착한 60개의 채팅을 가져옵니다.",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅을 보내는 방의 id",
+ },
+ lastMsgDate: {
+ type: "string",
+ format: "date-time",
+ description: "이전 채팅을 가져올 특정 시점",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ examples: {
+ "소켓 연결 오류": {
+ value: "Chat/load/before : socket did not connected",
+ },
+ "유저가 방에 참여하지 않음": {
+ value:
+ "Chat/load/before : user did not participated in the room",
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/load/before : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/load/after`] = {
+ post: {
+ tags: [tag],
+ summary: "특정 시점 이후 채팅 가져오기",
+ description: "lastMsgDate 이후에 도착한 60개의 채팅을 가져옵니다.",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅을 보내는 방의 id",
+ },
+ lastMsgDate: {
+ type: "string",
+ format: "date-time",
+ description: "이전 채팅을 가져올 특정 시점",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ examples: {
+ "소켓 연결 오류": {
+ value: "Chat/load/after : socket did not connected",
+ },
+ "유저가 방에 참여하지 않음": {
+ value:
+ "Chat/load/after : user did not participated in the room",
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/load/after : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/send`] = {
+ post: {
+ tags: [tag],
+ summary: "채팅 요청 처리",
+ description: `채팅 요청을 처리합니다.
+ socker 통신을 통하여 같은 방에 있는 user들에게 이 채팅을 전송합니다.
+
+ 채팅 기록은 아래와 같이 구성됩니다.
+
+ Chat {
+ roomId: ObjectId, //방의 objectId
+ type: String, // 메시지 종류 ("text": 일반 메시지, "s3img": S3에 업로드된 이미지, "in": 입장 메시지, "out": 퇴장 메시지, "payment": 결제 메시지, "settlement": 정산 완료 메시지, "account": 계좌 전송 메시지)
+ authorId: ObejctId, //작성자의 objectId
+ content: String, // 메시지 내용 (메시지 종류에 따라 포맷이 상이함)
+ time: String(ISO 8601), // ex) 2024-01-08T01:52:00.000Z
+ isValid: Boolean, // 클라이언트가 보낸 메시지가 유효한 지 여부. 클라이언트가 이미지를 업로드했을 때, 해당 이미지가 제대로 업로드됐는지 확인하기 전까지 이미지를 보여주지 않기 위해 사용됨.
+ }
+ `,
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅을 보내는 방의 id",
+ },
+ type: {
+ type: "string",
+ enum: [
+ "text",
+ "s3img",
+ "in",
+ "out",
+ "payment",
+ "settlement",
+ "account",
+ "departure",
+ "arrival",
+ ],
+ description: `채팅 메시지의 유형
+ 일반 text의 경우 *text*, 사진의 경우 *s3img*
+ 기타 종류의 채팅의 경우(입장, 퇴장 메시지 등) 정해진 type의 채팅을 사용`,
+ },
+ content: {
+ type: "string",
+ example: "안녕하세요~!",
+ description: "채팅 메세지의 본문",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ examples: {
+ "소켓 연결 오류": {
+ value: "Chat/send : socket did not connected",
+ },
+ "유저가 방에 참여하지 않음": {
+ value: "Chat/send : user did not participated in the room",
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/send : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/read`] = {
+ post: {
+ tags: [tag],
+ summary: "채팅 읽은 시각 업데이트 요청",
+ description: `채팅 읽은 시각의 업데이트 요청을 처리합니다.
+ socket 통신을 통하여 같은 방에 있는 user들에게 업데이트를 요청합니다.`,
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅을 보내는 방의 id",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ example: "Chat/read : socket did not connected",
+ },
+ },
+ },
+ 404: {
+ content: {
+ "text/html": {
+ example: "Chat/read : cannot find room info",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ examples: {
+ "소켓 이벤트 전송 오류": {
+ value: "Chat/read : failed to emit socket events",
+ },
+ "기타 서버 오류": {
+ value: "Chat/read : internal server error",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/uploadChatImg/getPUrl`] = {
+ post: {
+ tags: [tag],
+ summary: "채팅 이미지를 업로드할 수 있는 Presigned-url을 발급",
+ description: `채팅 이미지를 업로드 하기 위한 Presigned-url을 발급합니다.
+ 이미지 전송을 위해 \`s3img\` 형식의 chat document를 생성 후 저장하며,
+ presigned-url은 aws S3 api를 통해 생성됩니다.`,
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅 이미지를 보내는 방의 id",
+ },
+ type: {
+ type: "string",
+ enum: ["image/png", "image/jpg", "image/jpeg"],
+ description: "채팅 이미지의 파일 형식",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ id: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "생성된 chat Document의 object id",
+ },
+ url: {
+ type: "string",
+ example: "https://s3.{region}.amazonaws.com/{bucket-name}",
+ description: "taxi s3 url 주소",
+ },
+ fields: {
+ type: "object",
+ properties: {
+ bucket: {
+ type: "string",
+ example: "bucket-name",
+ },
+ "Content-Type": {
+ type: "string",
+ enum: ["image/png", "image/jpg", "image/jpeg"],
+ },
+ key: {
+ type: "string",
+ pattern: `^chat-img/[a-fA-F\d]{24}$`,
+ },
+ },
+ description: "image의 key, type, bucket와 같은 정보",
+ },
+ },
+ },
+ },
+ },
+ },
+ 403: {
+ content: {
+ "text/html": {
+ example: "Chat/uploadChatImg/getPUrl : did not joined the chatting",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/uploadChatImg/getPUrl : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+chatsDocs[`${apiPrefix}/uploadChatImg/done`] = {
+ post: {
+ tags: [tag],
+ summary: "채팅 이미지 업로드 완료 여부 확인",
+ description: `채팅 이미지가 제대로 업로드 되었는지 확인합니다.
+ 이미지가 제대로 업로드 되었다면, socket 통신을 통해 채팅 이미지를 전송합니다.
+ 이때 채팅의 \`content\`에는 s3에 저장된 url을 나타내는 채팅의 object id를 넣어줍니다.`,
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ roomId: {
+ type: "string",
+ pattern: objectIdPattern,
+ description: "채팅 이미지를 보내는 방의 id",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 404: {
+ content: {
+ "text/html": {
+ example: "Chat/uploadChatImg/done : no corresponding chat",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Chat/uploadChatImg/getPUrl : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+module.exports = chatsDocs;
diff --git a/src/routes/docs/chats.md b/src/routes/docs/chats.md
deleted file mode 100644
index 307d2def..00000000
--- a/src/routes/docs/chats.md
+++ /dev/null
@@ -1,99 +0,0 @@
-## `chats`: 채팅 시 발생하는 이벤트 정리
-
-Taxi의 채팅 기능은 Socket.IO 라이브러리를 이용해 구현되어 있습니다.
-클라이언트에서의 일반적인 Socket.IO 사용법은 [공식 문서](https://socket.io/docs/v4/client-socket-instance/)를 참조해주세요.
-아래와 같은 채팅 이벤트들이 구현되어 있습니다.
-
-- `chats-join`
-- `chats-receive`
-- `chats-send`
-- `chats-load`
-- `chats-disconnected` (삭제해야 함)
-
-서버로부터 받은 모든 채팅 기록은 아래와 같은 자료형으로 구성되어 있습니다.
-
-```javascript
-Chat {
- roomId: ObjectId, //방의 objectId
- type: String, // 메시지 종류("text": 일반 메시지, "in": 입장 메시지, "out": 퇴장 메시지, "s3img": S3에 업로드된 이미지, "payment": 결제 메시지, "settlement": 정산 완료 메시지)
- authorId: ObejctId, //작성자의 objectId
- content: String, // 메시지 내용(메시지 종류에 따라 포맷이 상이하며, 하단 참조)
- time: String(ISO 8601), // ex) '2022-01-12T13:58:20.180Z'
- isValid: Boolean, // 클라이언트가 보낸 메시지가 유효한 지 여부. 클라이언트가 이미지를 업로드했을 때, 해당 이미지가 제대로 업로드됐는지 확인하기 전까지 이미지를 보여주지 않기 위해 사용됨.
-}
-```
-
-### 1. `chats-join`
-
-방에 새 사용자가 참여할 때 이 이벤트를 발생시키세요.
-필요한 인자는 `roomId`입니다.
-
-- `roomId`: 방의 ObjectID (`String`), Socket.IO 서버와 연결을 시도할 때 사용자는 로그인이 되어 있어야 하며, 들어가려는 채팅방에 참여자로 참여하고 있어야 합니다.
-
-```javascript
-const socket = io(server_address, { withCredentials: true });
-socket.emit("chats-join", roomId);
-```
-
-채팅방 접속이 정상적으로 완료되면, Socket.io 서버는 최근 30개의 메시지들(`Chat` 배열)을 전송합니다.
-
-```javascript
-socket.on("chats-join", (chats) => {
- // 최근 30개의 채팅 메시지 출력
- console.log(chats);
-});
-```
-
-### 2. `chats-send`
-
-채팅 메시지를 보낼 때 이 이벤트를 발생시키세요.
-필요한 인자는 `roomId`와 `content`입니다.
-
-- `roomId`: 참여중인 방의 ObjectID(`String`)
-- `content`: 보낼 텍스트(`String`)
-
-```javascript
-socket.emit("chats-send", { roomId, content });
-```
-
-메시지 전송이 성공/실패하면, Socket.IO 서버도 `chats-send` 이벤트를 발생시킵니다.
-
-```javascript
-socket.on("chats-send", (response) => {
- // 최근 30개의 채팅 메시지 출력
- console.log(response);
-});
-```
-
-`response`는 전송이 성공했을 경우 `{done: true}`, 실패했을 경우 `{err: true}`입니다.
-
-### 3. `chats-receive`
-
-이 이벤트는 서버나 다른 사용자가 채팅 메시지를 전송했을 때 발생합니다. 아래와 같이 `chat`에 접근하여 해당 메시지의 내용을 확인할 수 있습니다.
-
-```javascript
-socket.on("chats-receive", (chat) => {
- // 새로운 메시지 출력
- console.log(chat);
-});
-```
-
-### 4. `chats-load`
-
-과거 대화 목록을 더 불러오려면 이 이벤트를 발생시키세요. 필요한 인자는 `lastDate`와 `amount`(선택 사항) 입니다.
-
-- `lastDate`: 현재 클라이언트에서 불러온 채팅들 중 가장 오래된 것의 생성 시각. 서버는 이보다 먼저 생성된 메시지들을 반환합니다. ISO8601을 만족하는 `String`이어야 합니다. e.g.) `"2022-03-15T13:57:04.732Z"`
-- `amount` (선택 사항): 불러올 과거 메시지의 수. 1~50의 자연수여야 하며, 입력하지 않은 경우 30개의 메시지를 가져옵니다.
-
-```javascript
-socket.emit("chats-load", { lastDate: "2022-03-15T13:57:04.732Z", amount: 30 });
-```
-
-`chats-load` 이벤트가 발생하면 서버는 클라이언트에 다시 `chats-load` 이벤트를 발생시켜 과거 채팅들(`Chat` 배열)을 보냅니다.
-
-```javascript
-socket.on("chats-load", (chats) => {
- // 과거 메시지들 출력
- console.log(chats);
-});
-```
diff --git a/src/routes/docs/locations.js b/src/routes/docs/locations.js
index 1fb445b9..34773482 100644
--- a/src/routes/docs/locations.js
+++ b/src/routes/docs/locations.js
@@ -6,8 +6,8 @@ locationsDocs[`${apiPrefix}`] = {
get: {
tags: [tag],
summary: "출발지/도착지 정보 반환",
- description:
- "출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
\n (로그인된 상태에서만 접근 가능)",
+ description: `출발지/도착지로 사용 가능한 장소 목록 조회 및 요청 처리 당시 서버 시각 반환
+ (로그인된 상태에서만 접근 가능)`,
responses: {
200: {
description: "서버에 저장된 location이 없을 경우, locations은 빈 배열",
diff --git a/src/routes/docs/logininfo.js b/src/routes/docs/logininfo.js
index e752d3b0..59b5e0a8 100644
--- a/src/routes/docs/logininfo.js
+++ b/src/routes/docs/logininfo.js
@@ -1,3 +1,5 @@
+const { objectIdPattern } = require("./utils");
+
const tag = "logininfo";
const apiPrefix = "/logininfo";
@@ -18,6 +20,7 @@ logininfoDocs[`${apiPrefix}`] = {
properties: {
oid: {
type: "string",
+ type: objectIdPattern,
},
id: {
type: "string",
diff --git a/src/routes/docs/reports.js b/src/routes/docs/reports.js
index 0f5b8e76..b3976e7b 100644
--- a/src/routes/docs/reports.js
+++ b/src/routes/docs/reports.js
@@ -1,3 +1,5 @@
+const { objectIdPattern } = require("./utils");
+
const tag = "reports";
const apiPrefix = "/reports";
@@ -57,9 +59,17 @@ reportsDocs[`${apiPrefix}/searchByUser`] = {
properties: {
reporting: {
type: "array",
+ items: {
+ type: "string",
+ pattern: objectIdPattern,
+ },
},
reported: {
type: "array",
+ items: {
+ type: "string",
+ pattern: objectIdPattern,
+ },
},
},
},
diff --git a/src/routes/docs/reportsSchema.js b/src/routes/docs/reportsSchema.js
index 841918cf..654e1178 100644
--- a/src/routes/docs/reportsSchema.js
+++ b/src/routes/docs/reportsSchema.js
@@ -1,3 +1,5 @@
+const { objectIdPattern } = require("./utils");
+
const reportsSchema = {
createHandler: {
type: "object",
@@ -5,7 +7,7 @@ const reportsSchema = {
properties: {
reportedId: {
type: "string",
- pattern: "^[a-fA-F\\d]{24}$",
+ pattern: objectIdPattern,
},
type: {
type: "string",
diff --git a/src/routes/docs/swaggerDocs.js b/src/routes/docs/swaggerDocs.js
index 84f4e040..c0b474b4 100644
--- a/src/routes/docs/swaggerDocs.js
+++ b/src/routes/docs/swaggerDocs.js
@@ -4,6 +4,7 @@ const logininfoDocs = require("./logininfo");
const locationsDocs = require("./locations");
const authDocs = require("./auth");
const usersDocs = require("./users");
+const chatsDocs = require("./chats");
const { port, nodeEnv } = require("../../../loadenv");
const serverList = [
@@ -56,6 +57,10 @@ const swaggerDocs = {
name: "users",
description: "유저 계정 정보 수정 및 조회",
},
+ {
+ name: "chats",
+ description: "채팅 시 발생하는 이벤트 정리",
+ },
],
consumes: ["application/json"],
produces: ["application/json"],
@@ -65,6 +70,7 @@ const swaggerDocs = {
...locationsDocs,
...usersDocs,
...authDocs,
+ ...chatsDocs,
},
components: {
schemas: {
diff --git a/src/routes/docs/users.js b/src/routes/docs/users.js
index e76cd2b4..3cd8aa96 100644
--- a/src/routes/docs/users.js
+++ b/src/routes/docs/users.js
@@ -2,6 +2,120 @@ const tag = "users";
const apiPrefix = "/users";
const usersDocs = {};
+usersDocs[`${apiPrefix}/agreeOnTermsOfService`] = {
+ post: {
+ tags: [tag],
+ summary: "이용 약관에 동의",
+ description:
+ "요청을 보낸 유저의 약관 동의 여부를 동의함으로 변경합니다. 철회는 불가능합니다.",
+ responses: {
+ 200: {
+ content: {
+ "text/html": {
+ example:
+ "Users/agreeOnTermsOfService : agree on Terms of Service successful",
+ },
+ },
+ },
+ 400: {
+ content: {
+ "text/html": {
+ example: "Users/agreeOnTermsOfService : already agreed",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Users/agreeOnTermsOfService : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+usersDocs[`${apiPrefix}/getAgreeOnTermsOfService`] = {
+ get: {
+ tags: [tag],
+ summary: "이용 약관 동의 여부 전송",
+ description:
+ "요청을 보낸 유저의 이용 약관 동의 여부를 json 형태로 전송합니다.",
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ agreeOnTermsOfService: {
+ type: "boolean",
+ description: "유저의 이용 약관 동의 여부",
+ example: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Users/getAgreeOnTermsOfService : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+usersDocs[`${apiPrefix}/editNickname`] = {
+ post: {
+ tags: [tag],
+ summary: "유저의 닉네임 변경",
+ description: "유저의 닉네임을 요청한 닉네임으로 변경합니다.",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ nickname: {
+ type: "string",
+ example: "끈질긴 열과 분자의 이동",
+ description: "유저의 새 닉네임",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "text/html": {
+ example: "Users/editNickname : edit user nickname successful",
+ },
+ },
+ },
+ 400: {
+ content: {
+ "text/html": {
+ example: "Users/editNickname : such user id does not exist",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Users/editNickname : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
usersDocs[`${apiPrefix}/resetNickname`] = {
get: {
tags: [tag],
@@ -11,21 +125,171 @@ usersDocs[`${apiPrefix}/resetNickname`] = {
200: {
content: {
"text/html": {
- example: "User/resetNickname : reset user nickname successful",
+ example: "Users/resetNickname : reset user nickname successful",
+ },
+ },
+ },
+ 400: {
+ content: {
+ "text/html": {
+ example: "Users/resetNickname : such user does not exist",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Users/resetNickname : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+usersDocs[`${apiPrefix}/editAccount`] = {
+ post: {
+ tags: [tag],
+ summary: "유저의 계좌 번호 변경",
+ description: "유저의 계좌 번호를 요청한 계좌 번호로 변경합니다.",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ account: {
+ type: "string",
+ description: "유저의 새 계좌 번호",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "text/html": {
+ example: "Users/editAccount : edit user account successful",
},
},
},
400: {
content: {
"text/html": {
- example: "User/resetNickname : such user does not exist",
+ example: "Users/editAccount : such user id does not exist",
+ },
+ },
+ },
+ 500: {
+ content: {
+ "text/html": {
+ example: "Users/editAccount : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+usersDocs[`${apiPrefix}/editProfileImg/getPUrl`] = {
+ post: {
+ tags: [tag],
+ summary: "프로필 이미지 업로드를 위한 presigned-url 발급",
+ description: `유저의 프로필 이미지는 AWS S3에서 관리됩니다. 변경할 프로필을 업로드 하기 위한 주소인 presigned-url을 발급합니다.
+
+ **프로필 사진은 아래 규칙을 만족해야 합니다:**
+ 1. 파일 형식은 image/png, image/jpg, image/jpeg 중 하나
+ 2. 파일 크기는 최대 50 MB`,
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ type: {
+ type: "string",
+ description: "업로드할 이미지 type",
+ example: "image/png",
+ },
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ url: {
+ type: "string",
+ description: "이미지 업로드를 위한 presigned-url",
+ },
+ fields: {
+ type: "object",
+ properties: {
+ "Content-Type": {
+ type: "string",
+ description: "이미지의 type",
+ },
+ key: {
+ type: "string",
+ description: "이미지의 S3 파일 경로",
+ },
+ },
+ description: "업로드 파일의 type 및 key 정보",
+ },
+ },
+ },
},
},
},
500: {
+ contnet: {
+ "text/html": {
+ example: "Users/editProfileImg/getPUrl : internal server error",
+ },
+ },
+ },
+ },
+ },
+};
+
+usersDocs[`${apiPrefix}/editProfileImg/done`] = {
+ get: {
+ tags: [tag],
+ summary: "프로필 이미지 정상 업로드 여부 확인",
+ description: `프로필 이미지가 S3에 정상적으로 업로드 되었는지 확인합니다.
+ 정상적으로 확인 되었다면, 유저의 \`profileImageUrl\` 정보를 새 프로필 이미지 파일명으로 업데이트 합니다.`,
+ responses: {
+ 200: {
content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ result: {
+ type: "boolean",
+ description: "정상적인 업로드 성공 여부",
+ },
+ profileImageUrl: {
+ type: "string",
+ description:
+ "새 프로필 이미지 파일명 (업로드 실패 시 `undefined`)",
+ },
+ },
+ },
+ },
+ },
+ },
+ 500: {
+ contnet: {
"text/html": {
- example: "User/resetNickname : internal server error",
+ example: "Users/editProfileImg/done : internal server error",
},
},
},
@@ -43,21 +307,21 @@ usersDocs[`${apiPrefix}/resetProfileImg`] = {
content: {
"text/html": {
example:
- "User/resetProfileImg : reset user profile image successful",
+ "Users/resetProfileImg : reset user profile image successful",
},
},
},
400: {
content: {
"text/html": {
- example: "User/resetProfileImg : such user does not exist",
+ example: "Users/resetProfileImg : such user does not exist",
},
},
},
500: {
content: {
"text/html": {
- example: "User/resetProfileImg : internal server error",
+ example: "Users/resetProfileImg : internal server error",
},
},
},
diff --git a/src/routes/docs/users.md b/src/routes/docs/users.md
deleted file mode 100755
index 6b46963b..00000000
--- a/src/routes/docs/users.md
+++ /dev/null
@@ -1,305 +0,0 @@
-## `/users`
-
-- 사용자 정보 조회 및 수정 기능을 지원하는 API.
-- 로그인된 상태에서만 접근 가능
-- 사용자를 반환할 경우 그 type은 다음과 같다.
-
-```javascript
-User {
- name: String,
- nickname: String, // 3글자 이상 25글자 이하로 구성되며 영어 대소문자, 한글, " ", 0~9, "-", "_" 으로만 이루어져야 함.
- id: String,
- withdraw: Boolean,
- ban: Boolean,
- joinat: Date,
- agreeOnTermsOfService: { type: Boolean, default: false }, //이용약관 동의 여부
- room: [Room],
- subinfo: {
- kaist: String,
- sparcs: String,
- facebook: String,
- twitter: String,
- },
- email: String,
- __v: Number,
-}
-```
-
-### `/agreeOnTermsOfService` **(POST)**
-
-- 이용 약관에 동의함 (철회 불가)
-
-#### URL Parameters, Request JSON form
-
-- 없음
-
-#### Response
-
-- 200 "agree on Terms of Service successful"
-- 400 "already agreed"
-- 500 "internal server error"
-
-### `/getAgreeOnTermsOfService` **(GET)**
-
-- 이용 약관 동의 여부를 가져옴
-
-#### URL Parameters, Request JSON form
-
-- 없음
-
-#### Response
-
-```javascript
-{
- agreeOnTermsOfService: Boolean
-},
-```
-
-### `/editNickname` **(POST)**
-
-- 해당 사용자의 닉네임을 새로 설정함.
-- 새로운 닉네임은 상술한 규칙을 만족해야 함.
-
-#### URL Parameters
-
-- user_id : 사용자의 SPARCS SSO ID
-
-#### request JSON form
-
-```javascript
-{
- nickname: String, // 새 닉네임
-}
-```
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: "edit user nickname successful",
-}
-```
-
-#### Errors
-
-- 400 "wrong nickname"
-- 400 "such user id does not exist"
-- 403 "not logged in"
-- 500 "internal server error"
-
-### `/editProfileImg/getPUrl` **(POST)**
-
-- 프로필 이미지를 업로드할 수 있는 Presigned-url을 발급합니다.
-- 프로필 사진은 아래 규칙을 만족해야 함.
- 1. 파일 형식은 image/png, image/jpg, image/jpeg 중 하나
- 2. 파일 크기는 최대 50 MB
-
-#### URL Parameters
-
-- type : 업로드할 이미지 type
-
-#### request JSON form
-
-```javascript
-{
- url: String, // pre-signed url
- fields: Object, // post fields
-}
-```
-
-#### Errors
-
-- 500 "internal server error"
-
-### `/editProfileImg/done` **(GET)**
-
-- 프로필 이미지가 S3에 정상적으로 업로드가 되었는지 확인합니다.
-
-#### URL Parameters
-
-- 없음
-
-#### request JSON form
-
-```javascript
-{
- result: Boolean, // 정상적으로 업로드 되었으면 true
- profileImageUrl?: user._id, // 정상적으로 업로드 되었으면 새 프로필 이미지 파일명, 그렇지 않은 경우 undefined
-}
-```
-
-#### Errors
-
-- 500 "internal server error"
-
-### `/` **(GET)** (for dev)
-
-- 사용자 전체 리스트를 반환함.
-
-#### URL Parameters
-
-- 없음
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: User[], // 전체 사용자 리스트
-}
-```
-
-#### Errors
-
-- 없음
-
-### `/rooms` **(GET)** (for dev)
-
-- 사용자의 방 리스트를 반환함.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### Response
-
-```javascript
-{
- id: String, // 요청된 id
- rooms: Room[], // 방 리스트
-}
-```
-
-#### Errors
-
-- 404 "user/rooms : such id does not exist"
-- 500 "user/rooms : internal server error"
-
-### `/:id` **(GET)** (for dev)
-
-- 사용자 정보를 반환함.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: User, //id에 대응되는 사용자 정보
-}
-```
-
-#### Errors
-
-- 404 "user/:id : such id does not exist"
-- 500 "user/:id : internal server error"
-
-### `/:id/edit` **(POST)** (for dev)
-
-- 새 사용자 정보를 받아 업데이트함.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### request JSON form
-
-```javascript
-User; //수정할 사용자 정보
-```
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: "edit user successful",
-}
-```
-
-#### Errors
-
-- 400 "such id does not exist"
-
-### `/:id/ban` **(GET)** (for dev)
-
-- 해당 사용자를 밴함.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: "The user banned successfully",
-}
-```
-
-#### Errors
-
-- 400 "The user does not exist"
-- 409 "The user is already banned"
-- 500 "User/ban : Error 500"
-
-### `/:id/unban` **(GET)** (for dev)
-
-- 해당 사용자를 밴 해제함.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: "The user unbanned successfully",
-}
-```
-
-#### Errors
-
-- 400 "The user does not exist"
-- 409 "The user is already banned"
-- 500 "User/unban : Error 500"
-
-### `/:id/participate` **(POST)** (for dev)
-
-- 해당 사용자를 특정 방에 참여시킴.
-
-#### URL Parameters
-
-- id : User document의 id
-
-#### request JSON form
-
-```javascript
-{
- room: String, // Room document의 id
-}
-```
-
-#### Response
-
-```javascript
-{
- status: 200,
- data: "User/participate : Successful",
-}
-```
-
-#### Errors
-
-- 400 "User/participate : Bad request"
-- 400 "User/participate : No corresponding room"
-- 400 "The user does not exist"
-- 409 "The user already entered the room"
-- 500 "User/participate : Error 500"
diff --git a/src/routes/docs/utils.js b/src/routes/docs/utils.js
new file mode 100644
index 00000000..b435476b
--- /dev/null
+++ b/src/routes/docs/utils.js
@@ -0,0 +1,3 @@
+const objectIdPattern = `^[a-fA-F\d]{24}$`;
+
+module.exports = { objectIdPattern };
diff --git a/src/sampleGenerator/.gitignore b/src/sampleGenerator/.gitignore
new file mode 100644
index 00000000..2909449b
--- /dev/null
+++ b/src/sampleGenerator/.gitignore
@@ -0,0 +1,107 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# MongoDB Dump
+dump/
diff --git a/src/sampleGenerator/README.md b/src/sampleGenerator/README.md
new file mode 100644
index 00000000..5afd5960
--- /dev/null
+++ b/src/sampleGenerator/README.md
@@ -0,0 +1,28 @@
+# taxiSampleGenerator
+
+이 node 프로그램은 SPARCS-Taxi 프로젝트를 위한 샘플 사용자, 방, 채팅 목록을 생성합니다.
+현재 이 프로그램으로 생성된 샘플 채팅 데이터는 입, 퇴장 메시지들과 일반 채팅 메시지들로만 구성되어 있습니다.
+
+**WARNING**
+스크립트 실행 시 기존에 MongoDB에 저장된 사용자, 방, 채팅 정보는 **삭제**됩니다!
+
+**SETUP**
+
+1. *(optional)* Root directory의 `.env.test` 파일에 다음 내용을 추가합니다.
+ ```
+ #방과 각각의 방의 채팅 개수
+ SAMPLE_NUM_OF_ROOMS=2
+ SAMPLE_NUM_OF_CHATS=200
+ #채팅 간 최대 시간 간격(단위: 초, 소수도 가능)
+ SAMPLE_MAXIMUM_INTERVAL_BETWEEN_CHATS=20
+ #새로운 채팅이 각각 입/퇴장 메시지일 확률(각각 10%)
+ SAMPLE_OCCURENCE_OF_JOIN=0.1
+ SAMPLE_OCCURENCE_OF_ABORT=0.1
+ ```
+1. sampleData.json에 장소, 유저, 방 데이터를 입력합니다.
+ javascript `User { "id": "sampleId", 사용자 id }`
+
+1. `pnpm start`로 샘플 채팅 데이터를 만들 수 있습니다.
+
+1. `pnpm run dumpDB`으로 현재 DB를 덤프할 수 있습니다.
+1. `pnpm run restoreDB`로 과거 DB를 덤프 파일로부터 복원할 수 있습니다.
diff --git a/src/sampleGenerator/index.js b/src/sampleGenerator/index.js
new file mode 100644
index 00000000..e83a9335
--- /dev/null
+++ b/src/sampleGenerator/index.js
@@ -0,0 +1,47 @@
+const {
+ generateUser,
+ generateRoom,
+ generateSampleLocations,
+ generateChats,
+} = require("./src/testData");
+const { connectDatabase } = require("../modules/stores/mongo");
+const { mongo: mongoUrl, numberOfChats, numberOfRooms } = require("./loadenv");
+
+const database = connectDatabase(mongoUrl);
+
+const fs = require("fs");
+const sampleData = JSON.parse(fs.readFileSync("./sampleData.json"));
+
+const main = async () => {
+ await database.db.dropDatabase();
+
+ const { users, locations } = sampleData;
+
+ const userOids = [];
+ const roomOids = [];
+
+ for (const [index, user] of users.entries()) {
+ const userOid = await generateUser(user.id, index + 1, user.isAdmin);
+ userOids.push(userOid);
+ }
+
+ const sampleLocationOids = await generateSampleLocations(locations);
+
+ for (const index of Array(numberOfRooms).keys()) {
+ const roomOid = await generateRoom(
+ sampleLocationOids,
+ index + 1,
+ 7,
+ userOids[0]
+ ); //하드코딩: 일주일 뒤에 출발하는 방(들)을 만듭니다.
+ roomOids.push(roomOid);
+ }
+
+ for (const roomOid of roomOids) {
+ await generateChats(roomOid, userOids, numberOfChats);
+ }
+ console.log("끝! 스크립트 실행을 중단하셔도 됩니다.");
+ process.exit(0);
+};
+
+database.on("open", main);
diff --git a/src/sampleGenerator/loadenv.js b/src/sampleGenerator/loadenv.js
new file mode 100644
index 00000000..0843789b
--- /dev/null
+++ b/src/sampleGenerator/loadenv.js
@@ -0,0 +1,13 @@
+// Root directory에 있는 .env.test 파일을 읽어옴
+require("dotenv").config({ path: "../../.env.test" });
+
+module.exports = {
+ mongo: process.env.DB_PATH, // required
+ numberOfRooms: parseInt(process.env.SAMPLE_NUM_OF_ROOMS ?? 2), // optional
+ numberOfChats: parseInt(process.env.SAMPLE_NUM_OF_CHATS ?? 200), // optional
+ maximumIntervalBtwChats: parseFloat(
+ process.env.SAMPLE_MAXIMUM_INTERVAL_BETWEEN_CHATS ?? 20
+ ), // optional
+ occurenceOfJoin: parseFloat(process.env.SAMPLE_OCCURENCE_OF_JOIN ?? 0.1), // optional
+ occurenceOfAbort: parseFloat(process.env.SAMPLE_OCCURENCE_OF_ABORT ?? 0.1), // optional
+};
diff --git a/src/sampleGenerator/package.json b/src/sampleGenerator/package.json
new file mode 100644
index 00000000..3c7473bc
--- /dev/null
+++ b/src/sampleGenerator/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "taxisamplegenerator",
+ "version": "1.0.0",
+ "description": "sample generator",
+ "main": "index.js",
+ "scripts": {
+ "preinstall": "npx only-allow pnpm",
+ "start": "node index.js",
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "dumpDB": "node tools/dump.js",
+ "restoreDB": "node tools/restore.js"
+ },
+ "keywords": [
+ "test"
+ ],
+ "license": "ISC"
+}
diff --git a/src/sampleGenerator/sampleData.json b/src/sampleGenerator/sampleData.json
new file mode 100644
index 00000000..546812cd
--- /dev/null
+++ b/src/sampleGenerator/sampleData.json
@@ -0,0 +1,112 @@
+{
+ "users": [
+ {
+ "id": "sunday",
+ "isAdmin": true
+ },
+ {
+ "id": "monday",
+ "isAdmin": true
+ },
+ {
+ "id": "tuesday",
+ "isAdmin": true
+ },
+ {
+ "id": "wednesday",
+ "isAdmin": true
+ }
+ ],
+ "locations": [
+ {
+ "koName": "택시승강장",
+ "enName": "Taxi Stand",
+ "longitude": 127.359507,
+ "latitude": 36.373199
+ },
+ {
+ "koName": "대전역",
+ "enName": "Daejeon Station",
+ "longitude": 127.434522,
+ "latitude": 36.331894
+ },
+ {
+ "koName": "갤러리아 타임월드",
+ "enName": "Galleria Timeworld",
+ "longitude": 127.378188,
+ "latitude": 36.351938
+ },
+ {
+ "koName": "궁동 로데오거리",
+ "enName": "Gung-dong Rodeo Street",
+ "longitude": 127.350161,
+ "latitude": 36.362785
+ },
+ {
+ "koName": "대전복합터미널",
+ "enName": "Daejeon Terminal Complex",
+ "longitude": 127.350161,
+ "latitude": 36.362785
+ },
+ {
+ "koName": "만년중학교",
+ "enName": "Mannyon Middle School",
+ "longitude": 127.375993,
+ "latitude": 36.366990
+ },
+ {
+ "koName": "서대전역",
+ "enName": "Seodaejeon Station",
+ "longitude": 127.403933,
+ "latitude": 36.322517
+ },
+ {
+ "koName": "신세계백화점",
+ "enName": "Shinsegae Department Store",
+ "longitude": 127.381905,
+ "latitude": 36.375168
+ },
+ {
+ "koName": "오리연못",
+ "enName": "Duck Pond",
+ "longitude": 127.362371,
+ "latitude": 36.367715
+ },
+ {
+ "koName": "월평역",
+ "enName": "Wolpyeong Station",
+ "longitude": 127.364352,
+ "latitude": 36.358271
+ },
+ {
+ "koName": "유성구청",
+ "enName": "Yuseong-gu Office",
+ "longitude": 127.356384,
+ "latitude": 36.362084
+ },
+ {
+ "koName": "유성 고속버스터미널",
+ "enName": "Yuseong Express Bus Terminal",
+ "longitude": 127.336467,
+ "latitude": 36.358279
+ },
+ {
+ "koName": "유성 시외버스터미널",
+ "enName": "Yuseong Intercity Bus Terminal",
+ "longitude": 127.335971,
+ "latitude": 36.355604
+ },
+ {
+ "koName": "대전청사 고속버스터미널",
+ "enName": "Government Complex Express Bus Terminal",
+ "longitude": 127.390504,
+ "latitude": 36.361462
+ },
+ {
+ "koName": "대전청사 시외버스터미널",
+ "enName": "Government Complex Intercity Bus Terminal",
+ "longitude": 127.379759,
+ "latitude": 36.361512
+ }
+ ]
+}
diff --git a/src/sampleGenerator/src/testData.js b/src/sampleGenerator/src/testData.js
new file mode 100644
index 00000000..1209c52a
--- /dev/null
+++ b/src/sampleGenerator/src/testData.js
@@ -0,0 +1,199 @@
+const {
+ userModel,
+ roomModel,
+ locationModel,
+ chatModel,
+} = require("../../modules/stores/mongo");
+const { generateProfileImageUrl } = require("../../modules/modifyProfile");
+
+const {
+ maximumIntervalBtwChats,
+ occurenceOfJoin,
+ occurenceOfAbort,
+} = require("../loadenv");
+
+const generateUser = async (id, num, isAdmin) => {
+ const newUser = new userModel({
+ id: id,
+ name: `${id}-name`,
+ nickname: `${id}-nickname`,
+ profileImageUrl: generateProfileImageUrl(),
+ joinat: Date.now(),
+ subinfo: {
+ kaist: new String(20230000 + num), // ^-^
+ sparcs: "",
+ facebook: "",
+ twitter: "",
+ },
+ email: `${id}@kaist.ac.kr`,
+ isAdmin: isAdmin,
+ });
+ await newUser.save();
+ return newUser._id;
+};
+
+const generateSampleLocations = async (locations) => {
+ if (locations.length === 0) {
+ console.log("Please provide location(s)!");
+ }
+
+ for (const location of locations) {
+ const locationDocument = new locationModel({
+ koName: location.koName,
+ enName: location.enName,
+ longitude: location.longitude,
+ latitude: location.latitude,
+ });
+ await locationDocument.save();
+ }
+
+ const locationDocuments = await locationModel.find().lean();
+ return locationDocuments.map((locationDocument) => locationDocument._id);
+};
+
+const generateRoom = async (sampleLocationOids, num, daysAfter, creatorId) => {
+ const date = new Date();
+ date.setDate(date.getDate() + daysAfter);
+
+ let fromIdx = 0;
+ let toIdx = 0;
+
+ while (fromIdx === toIdx) {
+ fromIdx = Math.floor(Math.random() * sampleLocationOids.length);
+ toIdx = Math.floor(Math.random() * sampleLocationOids.length);
+ }
+
+ const newRoom = new roomModel({
+ name: `test-${num}`,
+ from: sampleLocationOids[fromIdx],
+ to: sampleLocationOids[toIdx],
+ time: date,
+ part: [{ user: creatorId }],
+ madeat: Date.now(),
+ maxPartLength: 4,
+ });
+ await newRoom.save();
+ return newRoom._id;
+};
+
+const joinUserToRoom = async (userIdsInRoom, userIdsOutRoom, roomId) => {
+ // 들어올 사용자를 무작위로 선택
+ const authorIdx = Math.floor(Math.random() * userIdsOutRoom.length);
+ const userOid = userIdsOutRoom[authorIdx];
+
+ // 방, 유저 상태 갱신
+ userIdsInRoom.push(userOid);
+ userIdsOutRoom.splice(authorIdx, 1);
+ const user = await userModel.findById(userOid, "ongoingRoom");
+ user.ongoingRoom.push(roomId);
+ await user.save();
+
+ return { userIdsInRoom, userIdsOutRoom, userOid };
+};
+
+const abortUserfromRoom = async (userIdsInRoom, userIdsOutRoom, roomId) => {
+ // 나갈 사용자를 무작위로 선택
+ const authorIdx = Math.floor(Math.random() * userIdsInRoom.length);
+ const userOid = userIdsInRoom[authorIdx];
+
+ // 방, 유저 상태 갱신
+ userIdsOutRoom.push(userOid);
+ userIdsInRoom.splice(authorIdx, 1);
+ const user = await userModel.findById(userOid, "ongoingRoom");
+ user.ongoingRoom.splice(user.ongoingRoom.indexOf(roomId), 1);
+ await user.save();
+
+ return { userIdsInRoom, userIdsOutRoom, userOid };
+};
+
+const generateNormalChat = async (i, roomId, userOid, time) => {
+ const user = await userModel.findById(userOid);
+ const newChat = new chatModel({
+ roomId: roomId,
+ type: "text",
+ authorId: user._id,
+ content: `안녕하세요! (${i}번째 메시지)`,
+ time: time,
+ inValid: false,
+ });
+ await newChat.save();
+};
+
+const generateJoinAbortChat = async (roomId, userOid, isJoining, time) => {
+ const user = await userModel.findById(userOid);
+ const newChat = new chatModel({
+ roomId: roomId,
+ type: isJoining ? "in" : "out",
+ authorId: user._id,
+ content: user.id,
+ time: time,
+ isValid: false,
+ });
+ await newChat.save();
+};
+
+const generateChats = async (roomId, userOids, numOfChats) => {
+ const roomPopulateQuery = [{ path: "part", select: "id name nickname -_id" }];
+ const room = await roomModel.findById(roomId).populate(roomPopulateQuery);
+
+ let userIdsInRoom = [];
+ let userIdsOutRoom = userOids.map((userOid) => userOid);
+ let lastTime = Date.now();
+ const maximumIntervalBtwChatsMilliseconds = 1000 * maximumIntervalBtwChats;
+
+ for (const i of Array(numOfChats).keys()) {
+ lastTime += Math.floor(Math.random() * maximumIntervalBtwChatsMilliseconds);
+ const event = Math.random();
+
+ if (
+ userIdsInRoom.length === 0 ||
+ (event < occurenceOfJoin && userIdsOutRoom.length !== 0)
+ ) {
+ // 더 들어올 사용자가 있을 경우, 더 들어옴
+ // 방, 유저 상태 갱신
+ let userOid;
+ ({ userIdsInRoom, userIdsOutRoom, userOid } = await joinUserToRoom(
+ userIdsInRoom,
+ userIdsOutRoom,
+ roomId
+ ));
+ // 입장 메시지 생성
+ await generateJoinAbortChat(roomId, userOid, true, lastTime);
+ } else if (
+ occurenceOfJoin <= event &&
+ event < occurenceOfJoin + occurenceOfAbort &&
+ userIdsInRoom.length > 1
+ ) {
+ // 나갈 사용자가 있을 경우, 나감
+ // 방, 유저 상태 갱신
+ let userOid;
+ ({ userIdsInRoom, userIdsOutRoom, userOid } = await abortUserfromRoom(
+ userIdsInRoom,
+ userIdsOutRoom,
+ roomId
+ ));
+ // 퇴장 메시지 생성
+ await generateJoinAbortChat(roomId, userOid, false, lastTime);
+ } else {
+ // 방이 비어있지 않을 경우, 일반 채팅 메시지를 만듦
+ if (userIdsInRoom.length !== 0) {
+ const authorIdx = Math.floor(Math.random() * userIdsInRoom.length);
+ const user = userIdsInRoom[authorIdx];
+ await generateNormalChat(i, roomId, user, lastTime);
+ }
+ }
+ }
+ // 현재 참여중인 사용자 기준으로 방의 part 리스트를 업데이트함
+ room.part = userIdsInRoom.map((userOid) => {
+ return { user: userOid };
+ });
+ await room.save();
+ return;
+};
+
+module.exports = {
+ generateUser,
+ generateRoom,
+ generateSampleLocations,
+ generateChats,
+};
diff --git a/src/sampleGenerator/tools/dump.js b/src/sampleGenerator/tools/dump.js
new file mode 100644
index 00000000..7d26d802
--- /dev/null
+++ b/src/sampleGenerator/tools/dump.js
@@ -0,0 +1,20 @@
+const util = require("util");
+const path = require("path");
+const exec = util.promisify(require("child_process").exec);
+const { mongo: mongoUrl } = require("../loadenv");
+
+const main = async () => {
+ const { stdout, stderr } = await exec(
+ `mongodump ${mongoUrl} --out ${path.resolve("dump")}`
+ );
+ console.log("dump 디렉토리에 데이터베이스 데이터를 덤프했습니다.");
+ process.exit(0);
+};
+
+try {
+ main();
+} catch {
+ console.log(
+ "DB 연결 주소가 올바르지 않습니다. DB 연결 주소를 다시 한 번 확인해주세요."
+ );
+}
diff --git a/src/sampleGenerator/tools/restore.js b/src/sampleGenerator/tools/restore.js
new file mode 100644
index 00000000..5c14c98b
--- /dev/null
+++ b/src/sampleGenerator/tools/restore.js
@@ -0,0 +1,23 @@
+const util = require("util");
+const path = require("path");
+const exec = util.promisify(require("child_process").exec);
+const { mongo: mongoUrl } = require("../loadenv");
+
+const main = async () => {
+ const dbName = mongoUrl.split("/").pop();
+ const { stdout, stderr } = await exec(
+ `mongorestore ${mongoUrl} ${path.resolve("dump", dbName)}`
+ );
+ console.log(
+ "dump 디렉토리로부터 데이터베이스 정보를 성공적으로 복원했습니다."
+ );
+ process.exit(0);
+};
+
+try {
+ main();
+} catch {
+ console.log(
+ "DB를 덤프해올 디렉토리가 존재하지 않습니다. 경로를 다시 한 번 확인해주세요."
+ );
+}
diff --git a/src/services/chats.js b/src/services/chats.js
index 921c4946..c21f4b75 100644
--- a/src/services/chats.js
+++ b/src/services/chats.js
@@ -273,7 +273,7 @@ const uploadChatImgDoneHandler = async (req, res) => {
if (!user) {
return res
.status(500)
- .send("Chat/uploadChatImg/getPUrl : internal server error");
+ .send("Chat/uploadChatImg/done : internal server error");
}
if (!chat) {
return res.status(404).json({
@@ -294,7 +294,7 @@ const uploadChatImgDoneHandler = async (req, res) => {
if (err) {
return res
.status(500)
- .send("Chat/uploadChatImg/getPUrl : internal server error");
+ .send("Chat/uploadChatImg/done : internal server error");
}
chat.content = chat._id;
diff --git a/src/services/users.js b/src/services/users.js
index 73f79dde..d3adde6f 100644
--- a/src/services/users.js
+++ b/src/services/users.js
@@ -18,13 +18,13 @@ const agreeOnTermsOfServiceHandler = async (req, res) => {
res
.status(200)
.send(
- "User/agreeOnTermsOfService : agree on Terms of Service successful"
+ "Users/agreeOnTermsOfService : agree on Terms of Service successful"
);
} else {
- res.status(400).send("User/agreeOnTermsOfService : already agreed");
+ res.status(400).send("Users/agreeOnTermsOfService : already agreed");
}
} catch {
- res.status(500).send("User/agreeOnTermsOfService : internal server error");
+ res.status(500).send("Users/agreeOnTermsOfService : internal server error");
}
};
@@ -36,7 +36,9 @@ const getAgreeOnTermsOfServiceHandler = async (req, res) => {
const agreeOnTermsOfService = user.agreeOnTermsOfService === true;
res.json({ agreeOnTermsOfService });
} catch {
- res.status(500).send("/getAgreeOnTermsOfService : internal server error");
+ res
+ .status(500)
+ .send("Users/getAgreeOnTermsOfService : internal server error");
}
};
@@ -55,13 +57,15 @@ const editNicknameHandler = async (req, res) => {
req.timestamp
);
- res.status(200).send("User/editNickname : edit user nickname successful");
+ res
+ .status(200)
+ .send("Users/editNickname : edit user nickname successful");
} else {
- res.status(400).send("User/editNickname : such user id does not exist");
+ res.status(400).send("Users/editNickname : such user id does not exist");
}
} catch (err) {
logger.error(err);
- res.status(500).send("User/editNickname : internal server error");
+ res.status(500).send("Users/editNickname : internal server error");
}
};
@@ -81,13 +85,13 @@ const editAccountHandler = async (req, res) => {
newAccount
);
- res.status(200).send("User/editAccount : edit user account successful");
+ res.status(200).send("Users/editAccount : edit user account successful");
} else {
- res.status(400).send("User/editAccount : such user id does not exist");
+ res.status(400).send("Users/editAccount : such user id does not exist");
}
} catch (err) {
logger.error(err);
- res.status(500).send("User/editAccount : internal server error");
+ res.status(500).send("Users/editAccount : internal server error");
}
};
@@ -98,14 +102,14 @@ const editProfileImgGetPUrlHandler = async (req, res) => {
if (!user) {
return res
.status(500)
- .send("User/editProfileImg/getPUrl : internal server error");
+ .send("Users/editProfileImg/getPUrl : internal server error");
}
const key = `profile-img/${user._id}`;
aws.getUploadPUrlPost(key, type, (err, data) => {
if (err) {
return res
.status(500)
- .send("User/editProfileImg/getPUrl : internal server error");
+ .send("Users/editProfileImg/getPUrl : internal server error");
}
data.fields["Content-Type"] = type;
data.fields["key"] = key;
@@ -115,7 +119,9 @@ const editProfileImgGetPUrlHandler = async (req, res) => {
});
});
} catch (e) {
- res.status(500).send("User/editProfileImg/getPUrl : internal server error");
+ res
+ .status(500)
+ .send("Users/editProfileImg/getPUrl : internal server error");
}
};
@@ -125,7 +131,7 @@ const editProfileImgDoneHandler = async (req, res) => {
if (!user) {
return res
.status(500)
- .send("User/editProfileImg/done : internal server error");
+ .send("Users/editProfileImg/done : internal server error");
}
const key = `profile-img/${user._id}`;
aws.foundObject(key, async (err) => {
@@ -133,25 +139,25 @@ const editProfileImgDoneHandler = async (req, res) => {
logger.error(err);
return res
.status(500)
- .send("User/editProfileImg/done : internal server error");
+ .send("Users/editProfileImg/done : internal server error");
}
const userAfter = await userModel.findOneAndUpdate(
{ id: req.userId },
- { profileImageUrl: user._id },
+ { profileImageUrl: aws.getS3Url(`/${key}?token=${req.timestamp}`) },
{ new: true }
);
if (!userAfter) {
return res
.status(500)
- .send("User/editProfileImg/done : internal server error");
+ .send("Users/editProfileImg/done : internal server error");
}
res.json({
result: true,
- profileImageUrl: userAfter._id,
+ profileImageUrl: userAfter.profileImageUrl,
});
});
} catch (e) {
- res.status(500).send("User/editProfileImg/done : internal server error");
+ res.status(500).send("Users/editProfileImg/done : internal server error");
}
};
@@ -165,11 +171,13 @@ const resetNicknameHandler = async (req, res) => {
if (!result)
return res
.status(400)
- .send("User/resetNickname : such user does not exist");
- res.status(200).send("User/resetNickname : reset user nickname successful");
+ .send("Users/resetNickname : such user does not exist");
+ res
+ .status(200)
+ .send("Users/resetNickname : reset user nickname successful");
} catch (err) {
logger.error(err);
- res.status(500).send("User/resetNickname : internal server error");
+ res.status(500).send("Users/resetNickname : internal server error");
}
};
@@ -183,12 +191,12 @@ const resetProfileImgHandler = async (req, res) => {
if (!result)
return res
.status(400)
- .send("User/resetProfileImg : such user does not exist");
+ .send("Users/resetProfileImg : such user does not exist");
res
.status(200)
- .send("User/resetProfileImg : reset user profile image successful");
+ .send("Users/resetProfileImg : reset user profile image successful");
} catch (err) {
- res.status(500).send("User/resetProfileImg : internal server error");
+ res.status(500).send("Users/resetProfileImg : internal server error");
}
};
diff --git a/test/services/users.js b/test/services/users.js
index 1bb2f586..4f2195da 100644
--- a/test/services/users.js
+++ b/test/services/users.js
@@ -15,7 +15,7 @@ describe("[users] 1.agreeOnTermsOfServiceHandler", () => {
it("should return correct response from handler", async () => {
const testUser1 = await userGenerator("test1", testData);
const msg =
- "User/agreeOnTermsOfService : agree on Terms of Service successful";
+ "Users/agreeOnTermsOfService : agree on Terms of Service successful";
let req = httpMocks.createRequest({
userId: testUser1.id,
});
@@ -50,7 +50,7 @@ describe("[users] 3.editNicknameHandler", () => {
it("should return correct response from handler", async () => {
const testUser1 = await userModel.findOne({ id: "test1" });
- const msg = "User/editNickname : edit user nickname successful";
+ const msg = "Users/editNickname : edit user nickname successful";
let req = httpMocks.createRequest({
userId: testUser1.id,
body: {
@@ -77,7 +77,7 @@ describe("[users] 4.editAccountHandler", () => {
it("should return correct response from handler", async () => {
const testUser1 = await userModel.findOne({ id: "test1" });
- const msg = "User/editAccount : edit user account successful";
+ const msg = "Users/editAccount : edit user account successful";
let req = httpMocks.createRequest({
userId: testUser1.id,
body: {
diff --git a/test/utils.js b/test/utils.js
index b3920e83..e537913b 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -7,8 +7,9 @@ const {
connectDatabase,
} = require("../src/modules/stores/mongo");
const { generateProfileImageUrl } = require("../src/modules/modifyProfile");
+const { mongo: mongoUrl } = require("../loadenv");
-connectDatabase();
+connectDatabase(mongoUrl);
// 테스트를 위한 유저 생성 함수
const userGenerator = async (username, testData) => {