From 9ac6106bc9fb7ca3ebdcfb190670e1b06d611c0a Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 10:40:16 +0800 Subject: [PATCH 01/20] feat: setup password encryption --- package.json | 1 + yarn.lock | 212 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 202 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 877ae95..998d113 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^6.1.4", "@nestjs/typeorm": "^9.0.1", + "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.0.3", diff --git a/yarn.lock b/yarn.lock index 877caae..6339f50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -819,6 +819,21 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@mapbox/node-pre-gyp@^1.0.10": + version "1.0.10" + resolved "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@nestjs/cli@^9.0.0": version "9.1.5" resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-9.1.5.tgz#aa150991b209dc2b459c974cc1c43ea10a276ff8" @@ -1469,6 +1484,11 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" +abbrev@1: + version "1.1.1" + resolved "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -1497,6 +1517,13 @@ acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== +agent-base@6: + version "6.0.2" + resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -1616,6 +1643,19 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -1759,6 +1799,14 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bcrypt@^5.1.0: + version "5.1.0" + resolved "https://registry.npmmirror.com/bcrypt/-/bcrypt-5.1.0.tgz#bbb27665dbc400480a524d8991ac7434e8529e17" + integrity sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.10" + node-addon-api "^5.0.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -1950,6 +1998,11 @@ chokidar@3.5.3, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -2095,6 +2148,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colorette@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" @@ -2155,6 +2213,11 @@ consola@^2.15.0: resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -2276,6 +2339,13 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2283,13 +2353,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -2338,6 +2401,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + denque@^2.0.1: version "2.1.0" resolved "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" @@ -2353,6 +2421,11 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3011,6 +3084,13 @@ fs-extra@10.1.0, fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs-monkey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" @@ -3046,6 +3126,21 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.npmmirror.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + generate-function@^2.3.1: version "2.3.1" resolved "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" @@ -3226,6 +3321,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3271,6 +3371,14 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -4305,7 +4413,7 @@ magic-string@0.26.2: dependencies: sourcemap-codec "^1.4.8" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -4449,6 +4557,26 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^4.0.0: + version "4.2.5" + resolved "https://registry.npmmirror.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" + integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp@^0.5.4: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -4456,7 +4584,7 @@ mkdirp@^0.5.4: dependencies: minimist "^1.2.6" -mkdirp@^1.0.4: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -4549,6 +4677,11 @@ node-abort-controller@^3.0.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.0.1.tgz#f91fa50b1dee3f909afabb7e261b1e1d6b0cb74e" integrity sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw== +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + node-emoji@1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" @@ -4563,6 +4696,13 @@ node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.7: + version "2.6.9" + resolved "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4573,6 +4713,13 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -4612,6 +4759,16 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5021,6 +5178,15 @@ readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -5264,6 +5430,11 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -5307,7 +5478,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -5452,7 +5623,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5628,6 +5799,18 @@ tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +tar@^6.1.11: + version "6.1.13" + resolved "https://registry.npmmirror.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^4.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -6111,6 +6294,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + windows-release@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-4.0.0.tgz#4725ec70217d1bf6e02c7772413b29cdde9ec377" From 12f6c743c2ddefc27a1fab7d4560fa22c5b53747 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 10:45:25 +0800 Subject: [PATCH 02/20] feat: user init --- src/app.module.ts | 2 ++ src/users/dto/create-user.dto.ts | 1 + src/users/entities/user.entity.ts | 1 + src/users/users.controller.spec.ts | 21 +++++++++++++++++++++ src/users/users.controller.ts | 8 ++++++++ src/users/users.module.ts | 10 ++++++++++ src/users/users.service.spec.ts | 19 +++++++++++++++++++ src/users/users.service.ts | 4 ++++ 8 files changed, 66 insertions(+) create mode 100644 src/users/dto/create-user.dto.ts create mode 100644 src/users/entities/user.entity.ts create mode 100644 src/users/users.controller.spec.ts create mode 100644 src/users/users.controller.ts create mode 100644 src/users/users.module.ts create mode 100644 src/users/users.service.spec.ts create mode 100644 src/users/users.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 51696d8..9f982c9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { dataSourceOptions } from "./config/data-source"; import { validate } from "./config/env.validation"; +import { UsersModule } from "./users/users.module"; @Module({ imports: [ @@ -13,6 +14,7 @@ import { validate } from "./config/env.validation"; validate, }), TypeOrmModule.forRoot(dataSourceOptions), + UsersModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts new file mode 100644 index 0000000..0311be1 --- /dev/null +++ b/src/users/dto/create-user.dto.ts @@ -0,0 +1 @@ +export class CreateUserDto {} diff --git a/src/users/entities/user.entity.ts b/src/users/entities/user.entity.ts new file mode 100644 index 0000000..4f82c14 --- /dev/null +++ b/src/users/entities/user.entity.ts @@ -0,0 +1 @@ +export class User {} diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts new file mode 100644 index 0000000..92b88f7 --- /dev/null +++ b/src/users/users.controller.spec.ts @@ -0,0 +1,21 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { UsersController } from "./users.controller"; +import { UsersService } from "./users.service"; + +describe("UsersController", () => { + let controller: UsersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [UsersService], + }).compile(); + + controller = module.get(UsersController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts new file mode 100644 index 0000000..4d338a4 --- /dev/null +++ b/src/users/users.controller.ts @@ -0,0 +1,8 @@ +import { Controller } from "@nestjs/common"; + +import { UsersService } from "./users.service"; + +@Controller("users") +export class UsersController { + constructor(private readonly usersService: UsersService) {} +} diff --git a/src/users/users.module.ts b/src/users/users.module.ts new file mode 100644 index 0000000..c6d188c --- /dev/null +++ b/src/users/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; + +import { UsersController } from "./users.controller"; +import { UsersService } from "./users.service"; + +@Module({ + controllers: [UsersController], + providers: [UsersService], +}) +export class UsersModule {} diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts new file mode 100644 index 0000000..995e5cb --- /dev/null +++ b/src/users/users.service.spec.ts @@ -0,0 +1,19 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { UsersService } from "./users.service"; + +describe("UsersService", () => { + let service: UsersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); + + service = module.get(UsersService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/users/users.service.ts b/src/users/users.service.ts new file mode 100644 index 0000000..5fa025a --- /dev/null +++ b/src/users/users.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from "@nestjs/common"; + +@Injectable() +export class UsersService {} From b229c7388dd6eb112f7425bdc02537d7fd6eb6ee Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 10:53:55 +0800 Subject: [PATCH 03/20] feat: setup&apply user entity --- .../1679539757893-UserInitMigration.ts | 35 +++++++++++++++++++ src/users/entities/user.entity.ts | 33 ++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/database/migrations/1679539757893-UserInitMigration.ts diff --git a/src/database/migrations/1679539757893-UserInitMigration.ts b/src/database/migrations/1679539757893-UserInitMigration.ts new file mode 100644 index 0000000..6a3c84c --- /dev/null +++ b/src/database/migrations/1679539757893-UserInitMigration.ts @@ -0,0 +1,35 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UserInitMigration1679539757893 implements MigrationInterface { + name = "UserInitMigration1679539757893"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`users\` + (\`id\` int NOT NULL AUTO_INCREMENT, + \`email\` varchar(255) NOT NULL, + \`name\` varchar(255) NOT NULL, + \`account\` varchar(255) NOT NULL, + \`password\` varchar(255) NOT NULL, + \`createAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),\`updateAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + UNIQUE INDEX \`IDX_97672ac88f789774dd47f7c8be\` (\`email\`), + UNIQUE INDEX \`IDX_51b8b26ac168fbe7d6f5653e6c\` (\`name\`), + UNIQUE INDEX \`IDX_dd44b05034165835d6dcc18d68\` (\`account\`), + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX \`IDX_dd44b05034165835d6dcc18d68\` ON \`users\``, + ); + await queryRunner.query( + `DROP INDEX \`IDX_51b8b26ac168fbe7d6f5653e6c\` ON \`users\``, + ); + await queryRunner.query( + `DROP INDEX \`IDX_97672ac88f789774dd47f7c8be\` ON \`users\``, + ); + await queryRunner.query(`DROP TABLE \`users\``); + } +} diff --git a/src/users/entities/user.entity.ts b/src/users/entities/user.entity.ts index 4f82c14..b96dbb6 100644 --- a/src/users/entities/user.entity.ts +++ b/src/users/entities/user.entity.ts @@ -1 +1,32 @@ -export class User {} +import { + BaseEntity, + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity({ name: "users" }) +export class UserEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ unique: true }) + email: string; + + @Column({ unique: true }) + name: string; + + @Column({ unique: true }) + account: string; + + @Column() + password: string; + + @CreateDateColumn() + createAt: Date; + + @UpdateDateColumn() + updateAt: Date; +} From 230866a0fc0609397a8aea661cc7454657a4fc2b Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 11:08:55 +0800 Subject: [PATCH 04/20] feat: setup user create api --- src/users/dto/create-user.dto.ts | 53 +++++++++++++++++++++++++++++++- src/users/users.controller.ts | 13 +++++++- src/users/users.service.ts | 17 +++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index 0311be1..69765cd 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -1 +1,52 @@ -export class CreateUserDto {} +import { ApiProperty } from "@nestjs/swagger"; +import { IsEmail, IsNotEmpty, Length } from "class-validator"; + +export class CreateUserDto { + @ApiProperty({ + description: "使用者信箱", + example: "jhon@gmail.com", + }) + @IsEmail({}, { message: "email 必須是信箱格式。" }) + @IsNotEmpty({ + message: "email 為必填欄位。", + }) + public readonly email: string; + + @ApiProperty({ + description: "顯示名", + example: "showname", + }) + @IsNotEmpty({ + message: "name 為必填欄位。", + }) + public readonly name: string; + + @ApiProperty({ + description: "登入用帳號名", + example: "account", + }) + @IsNotEmpty({ + message: "account 為必填欄位。", + }) + public readonly account: string; + + @ApiProperty({ + description: "使用者密碼", + example: "Password@123", + }) + @IsNotEmpty({ + message: "password 為必填欄位。", + }) + @Length(8, 24, { message: "password 長度必須於8-24個字元之間。" }) + public readonly password: string; + + @ApiProperty({ + description: "再次確認密碼", + example: "Password@123", + }) + @IsNotEmpty({ + message: "confirm 為必填欄位。", + }) + @Length(8, 24, { message: "confirm 必須長度大於等於8個字。" }) + public readonly confirm: string; +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 4d338a4..4d766fd 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,8 +1,19 @@ -import { Controller } from "@nestjs/common"; +import { BadRequestException, Body, Controller, Post } from "@nestjs/common"; +import { CreateUserDto } from "./dto/create-user.dto"; import { UsersService } from "./users.service"; @Controller("users") export class UsersController { constructor(private readonly usersService: UsersService) {} + @Post() + create(@Body() userDto: CreateUserDto) { + const user = this.usersService.create(userDto); + if (!user) { + throw new BadRequestException("Have some error"); + } + return { + message: "created OK", + }; + } } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 5fa025a..f87ac01 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,4 +1,19 @@ import { Injectable } from "@nestjs/common"; +import * as bcrypt from "bcrypt"; + +import { CreateUserDto } from "./dto/create-user.dto"; +import { UserEntity } from "./entities/user.entity"; @Injectable() -export class UsersService {} +export class UsersService { + async create(userDto: CreateUserDto): Promise { + const salt = await bcrypt.genSalt(); + const hash = await bcrypt.hash(userDto.password, salt); + const user = new UserEntity(); + user.name = userDto.name; + user.account = userDto.account; + user.email = userDto.email; + user.password = hash; + return await user.save(); + } +} From 4f6798e5176495d97c0f72b4d4e98dc4e2eb0e96 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 11:17:46 +0800 Subject: [PATCH 05/20] feat: setup input data format validation --- src/users/users.controller.ts | 10 +++++++++- src/users/users.utils.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/users/users.utils.ts diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 4d766fd..340ae50 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,12 +1,20 @@ -import { BadRequestException, Body, Controller, Post } from "@nestjs/common"; +import { + BadRequestException, + Body, + Controller, + Post, + UsePipes, +} from "@nestjs/common"; import { CreateUserDto } from "./dto/create-user.dto"; import { UsersService } from "./users.service"; +import { SETTINGS } from "./users.utils"; @Controller("users") export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() + @UsePipes(SETTINGS.VALIDATION_PIPE) create(@Body() userDto: CreateUserDto) { const user = this.usersService.create(userDto); if (!user) { diff --git a/src/users/users.utils.ts b/src/users/users.utils.ts new file mode 100644 index 0000000..08f0cf1 --- /dev/null +++ b/src/users/users.utils.ts @@ -0,0 +1,26 @@ +import { HttpStatus, ValidationPipe } from "@nestjs/common"; + +const PASSWORD_RULE = + /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/; + +const PASSWORD_RULE_MESSAGE = + " 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。"; +//Password should have 1 upper case, lowcase letter along with a number and special character. +const VALIDATION_PIPE = new ValidationPipe({ + errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + stopAtFirstError: false, + disableErrorMessages: false, + whitelist: true, +}); + +export const REGEX = { + PASSWORD_RULE, +}; + +export const MESSAGES = { + PASSWORD_RULE_MESSAGE, +}; + +export const SETTINGS = { + VALIDATION_PIPE, +}; From 0f9b94e4bea7b596e4985b0d227e1442dd8cdfe8 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 11:21:22 +0800 Subject: [PATCH 06/20] feat: setup validation swagger info --- .../exceptions/create-error.exception.ts | 48 +++++++++++++++++++ src/users/users.controller.ts | 12 +++++ 2 files changed, 60 insertions(+) create mode 100644 src/users/exceptions/create-error.exception.ts diff --git a/src/users/exceptions/create-error.exception.ts b/src/users/exceptions/create-error.exception.ts new file mode 100644 index 0000000..093c582 --- /dev/null +++ b/src/users/exceptions/create-error.exception.ts @@ -0,0 +1,48 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateEntityUserError { + @ApiProperty({ + type: "number", + description: "HTTP 回應代碼", + example: "422", + }) + public readonly StatusCode: number; + + @ApiProperty({ + type: "array", + description: "錯誤訊息", + items: { + properties: { + email: { + description: "email 為必填欄位。 \n" + "email 必須是信箱格式。 \n", + type: "string", + }, + name: { + description: "name 為必填欄位。 \n", + type: "string", + }, + account: { + description: "account 為必填欄位。 \n", + type: "string", + }, + password: { + description: + "password 為必填欄位。 \n" + + "password 必須長度大於等於8個字。 \n" + + "password 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。 \n", + + type: "string", + }, + confirm: { + description: + "confirm 為必填欄位。 \n" + + "confirm 必須長度大於等於8個字。 \n" + + "confirm 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。 \n", + type: "string", + }, + }, + }, + example: "email 為必填欄位。", + }) + public readonly error: string[]; +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 340ae50..b11154d 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -5,8 +5,13 @@ import { Post, UsePipes, } from "@nestjs/common"; +import { + ApiCreatedResponse, + ApiUnprocessableEntityResponse, +} from "@nestjs/swagger"; import { CreateUserDto } from "./dto/create-user.dto"; +import { CreateEntityUserError } from "./exceptions/create-error.exception"; import { UsersService } from "./users.service"; import { SETTINGS } from "./users.utils"; @@ -15,6 +20,13 @@ export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() @UsePipes(SETTINGS.VALIDATION_PIPE) + @ApiCreatedResponse({ + description: "使用者創建成功", + }) + @ApiUnprocessableEntityResponse({ + description: "使用者格式不符", + type: CreateEntityUserError, + }) create(@Body() userDto: CreateUserDto) { const user = this.usersService.create(userDto); if (!user) { From 3a58c3da6719b562eab10d1ca922de169588df61 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Thu, 23 Mar 2023 20:43:10 +0800 Subject: [PATCH 07/20] feat: setup input data conflict --- src/users/dto/create-user.dto.ts | 8 +++- .../create-conflict-error.exception.ts | 39 +++++++++++++++++++ src/users/users.controller.ts | 22 ++++------- src/users/users.service.ts | 30 ++++++++++++-- 4 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 src/users/exceptions/create-conflict-error.exception.ts diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index 69765cd..0a436e2 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty } from "@nestjs/swagger"; +import { ApiProperty, PickType } from "@nestjs/swagger"; import { IsEmail, IsNotEmpty, Length } from "class-validator"; export class CreateUserDto { @@ -50,3 +50,9 @@ export class CreateUserDto { @Length(8, 24, { message: "confirm 必須長度大於等於8個字。" }) public readonly confirm: string; } +export class CreateUserParam extends PickType(CreateUserDto, [ + "name", + "email", + "account", + "password", +] as const) {} diff --git a/src/users/exceptions/create-conflict-error.exception.ts b/src/users/exceptions/create-conflict-error.exception.ts new file mode 100644 index 0000000..1b47216 --- /dev/null +++ b/src/users/exceptions/create-conflict-error.exception.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateConflictUserError { + @ApiProperty({ + type: "number", + description: "HTTP StatusCode", + example: "409", + }) + public readonly StatusCode: number; + + @ApiProperty({ + type: "array", + description: "Error Message", + items: { + properties: { + email: { + description: "email has been registered. \n", + type: "string", + }, + name: { + description: "name has been registered. \n", + type: "string", + }, + account: { + description: "account has been registered. \n", + type: "string", + }, + password: { + description: + "Confirm and password do not match, please try again. \n", + + type: "string", + }, + }, + }, + example: "email has been registered.", + }) + public readonly error: string[]; +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index b11154d..b9d125c 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,16 +1,12 @@ +import { Body, Controller, Post, UsePipes } from "@nestjs/common"; import { - BadRequestException, - Body, - Controller, - Post, - UsePipes, -} from "@nestjs/common"; -import { + ApiConflictResponse, ApiCreatedResponse, ApiUnprocessableEntityResponse, } from "@nestjs/swagger"; import { CreateUserDto } from "./dto/create-user.dto"; +import { CreateConflictUserError } from "./exceptions/create-conflict-error.exception"; import { CreateEntityUserError } from "./exceptions/create-error.exception"; import { UsersService } from "./users.service"; import { SETTINGS } from "./users.utils"; @@ -23,17 +19,15 @@ export class UsersController { @ApiCreatedResponse({ description: "使用者創建成功", }) + @ApiConflictResponse({ + description: "Data Conflict", + type: CreateConflictUserError, + }) @ApiUnprocessableEntityResponse({ description: "使用者格式不符", type: CreateEntityUserError, }) create(@Body() userDto: CreateUserDto) { - const user = this.usersService.create(userDto); - if (!user) { - throw new BadRequestException("Have some error"); - } - return { - message: "created OK", - }; + return this.usersService.register(userDto); } } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index f87ac01..3e74237 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,12 +1,36 @@ -import { Injectable } from "@nestjs/common"; +import { ConflictException, Injectable } from "@nestjs/common"; import * as bcrypt from "bcrypt"; -import { CreateUserDto } from "./dto/create-user.dto"; +import { CreateUserDto, CreateUserParam } from "./dto/create-user.dto"; import { UserEntity } from "./entities/user.entity"; @Injectable() export class UsersService { - async create(userDto: CreateUserDto): Promise { + async register(userDto: CreateUserDto): Promise { + const existingUser = await UserEntity.findOne({ + where: [ + { email: userDto.email }, + { name: userDto.name }, + { account: userDto.account }, + ], + }); + if (existingUser) { + Object.keys(existingUser).forEach(key => { + if (existingUser[key] === userDto[key]) { + throw new ConflictException({ + message: key + " has been registered.", + }); + } + }); + } + if (userDto.confirm !== userDto.password) { + throw new ConflictException({ + message: "Confirm and password do not match, please try again.", + }); + } + return this.create(userDto); + } + async create(userDto: CreateUserParam): Promise { const salt = await bcrypt.genSalt(); const hash = await bcrypt.hash(userDto.password, salt); const user = new UserEntity(); From b4dd35beb05422f604c62ac9019038d5585f378f Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 02:02:56 +0800 Subject: [PATCH 08/20] feat: change lang zh-tw to en --- src/users/dto/create-user.dto.ts | 30 ++++++----- .../create-entity-error.exception.ts | 50 +++++++++++++++++++ .../exceptions/create-error.exception.ts | 48 ------------------ src/users/users.controller.ts | 6 +-- 4 files changed, 70 insertions(+), 64 deletions(-) create mode 100644 src/users/exceptions/create-entity-error.exception.ts delete mode 100644 src/users/exceptions/create-error.exception.ts diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index 0a436e2..3e2f299 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -3,51 +3,55 @@ import { IsEmail, IsNotEmpty, Length } from "class-validator"; export class CreateUserDto { @ApiProperty({ - description: "使用者信箱", + description: "User email", example: "jhon@gmail.com", }) - @IsEmail({}, { message: "email 必須是信箱格式。" }) + @IsEmail({}, { message: "email must be in mailbox format." }) @IsNotEmpty({ - message: "email 為必填欄位。", + message: "email is required field.", }) public readonly email: string; @ApiProperty({ - description: "顯示名", + description: "User showname", example: "showname", }) @IsNotEmpty({ - message: "name 為必填欄位。", + message: "name is required field.", }) public readonly name: string; @ApiProperty({ - description: "登入用帳號名", + description: "User account", example: "account", }) @IsNotEmpty({ - message: "account 為必填欄位。", + message: "account is required field.", }) public readonly account: string; @ApiProperty({ - description: "使用者密碼", + description: "User Password", example: "Password@123", }) @IsNotEmpty({ - message: "password 為必填欄位。", + message: "password is required field.", + }) + @Length(8, 24, { + message: "password's length must be between 8-24 characters.", }) - @Length(8, 24, { message: "password 長度必須於8-24個字元之間。" }) public readonly password: string; @ApiProperty({ - description: "再次確認密碼", + description: "check password again", example: "Password@123", }) @IsNotEmpty({ - message: "confirm 為必填欄位。", + message: "confirm is required field.", + }) + @Length(8, 24, { + message: "confirm's length must be between 8-24 characters.", }) - @Length(8, 24, { message: "confirm 必須長度大於等於8個字。" }) public readonly confirm: string; } export class CreateUserParam extends PickType(CreateUserDto, [ diff --git a/src/users/exceptions/create-entity-error.exception.ts b/src/users/exceptions/create-entity-error.exception.ts new file mode 100644 index 0000000..0bd627f --- /dev/null +++ b/src/users/exceptions/create-entity-error.exception.ts @@ -0,0 +1,50 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateEntityUserError { + @ApiProperty({ + type: "number", + description: "HTTP StatusCode", + example: "422", + }) + public readonly StatusCode: number; + + @ApiProperty({ + type: "array", + description: "Error Message", + items: { + properties: { + email: { + description: + "email is required field. \n" + + "email must be in mailbox format. \n", + type: "string", + }, + name: { + description: "name is required field. \n", + type: "string", + }, + account: { + description: "account is required field. \n", + type: "string", + }, + password: { + description: + "password is required field. \n" + + "password's length must be between 8-24 characters. \n" + + "password Should contain 1 uppercase letter, 1 lowercase letter plus numbers and special characters. \n", + + type: "string", + }, + confirm: { + description: + "confirm is required field. \n" + + "confirm 's length must be between 8-24 characters. \n" + + "confirm Should contain 1 uppercase letter, 1 lowercase letter plus numbers and special characters. \n", + type: "string", + }, + }, + }, + example: "email is required field.", + }) + public readonly error: string[]; +} diff --git a/src/users/exceptions/create-error.exception.ts b/src/users/exceptions/create-error.exception.ts deleted file mode 100644 index 093c582..0000000 --- a/src/users/exceptions/create-error.exception.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; - -export class CreateEntityUserError { - @ApiProperty({ - type: "number", - description: "HTTP 回應代碼", - example: "422", - }) - public readonly StatusCode: number; - - @ApiProperty({ - type: "array", - description: "錯誤訊息", - items: { - properties: { - email: { - description: "email 為必填欄位。 \n" + "email 必須是信箱格式。 \n", - type: "string", - }, - name: { - description: "name 為必填欄位。 \n", - type: "string", - }, - account: { - description: "account 為必填欄位。 \n", - type: "string", - }, - password: { - description: - "password 為必填欄位。 \n" + - "password 必須長度大於等於8個字。 \n" + - "password 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。 \n", - - type: "string", - }, - confirm: { - description: - "confirm 為必填欄位。 \n" + - "confirm 必須長度大於等於8個字。 \n" + - "confirm 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。 \n", - type: "string", - }, - }, - }, - example: "email 為必填欄位。", - }) - public readonly error: string[]; -} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index b9d125c..cecbe48 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -7,7 +7,7 @@ import { import { CreateUserDto } from "./dto/create-user.dto"; import { CreateConflictUserError } from "./exceptions/create-conflict-error.exception"; -import { CreateEntityUserError } from "./exceptions/create-error.exception"; +import { CreateEntityUserError } from "./exceptions/create-entity-error.exception"; import { UsersService } from "./users.service"; import { SETTINGS } from "./users.utils"; @@ -17,14 +17,14 @@ export class UsersController { @Post() @UsePipes(SETTINGS.VALIDATION_PIPE) @ApiCreatedResponse({ - description: "使用者創建成功", + description: "Created Successfully", }) @ApiConflictResponse({ description: "Data Conflict", type: CreateConflictUserError, }) @ApiUnprocessableEntityResponse({ - description: "使用者格式不符", + description: "Data Duplication", type: CreateEntityUserError, }) create(@Body() userDto: CreateUserDto) { From e37eae5a27a84fee05a24f001c82534cae3e8945 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 03:12:12 +0800 Subject: [PATCH 09/20] feat: change return message --- src/users/users.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 3e74237..1e6f73b 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -38,6 +38,7 @@ export class UsersService { user.account = userDto.account; user.email = userDto.email; user.password = hash; - return await user.save(); + const data = await user.save(); + return { statusCode: 201, message: data.name + " created successfully." }; } } From c9b3be92c776b71e3166b40314ccb5108895e37c Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 03:21:10 +0800 Subject: [PATCH 10/20] style: delete extra spaces --- src/users/exceptions/create-entity-error.exception.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/users/exceptions/create-entity-error.exception.ts b/src/users/exceptions/create-entity-error.exception.ts index 0bd627f..261c11e 100644 --- a/src/users/exceptions/create-entity-error.exception.ts +++ b/src/users/exceptions/create-entity-error.exception.ts @@ -38,7 +38,7 @@ export class CreateEntityUserError { confirm: { description: "confirm is required field. \n" + - "confirm 's length must be between 8-24 characters. \n" + + "confirm's length must be between 8-24 characters. \n" + "confirm Should contain 1 uppercase letter, 1 lowercase letter plus numbers and special characters. \n", type: "string", }, From d24f5b4898fcbdb803448a56dcdcf43a05a985b9 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 03:21:59 +0800 Subject: [PATCH 11/20] feat: setup logger --- package.json | 1 + yarn.lock | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 998d113..7cff222 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.0.3", + "morgan": "^1.10.0", "mysql2": "^2.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index 6339f50..7f7b216 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1799,6 +1799,13 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + bcrypt@^5.1.0: version "5.1.0" resolved "https://registry.npmmirror.com/bcrypt/-/bcrypt-5.1.0.tgz#bbb27665dbc400480a524d8991ac7434e8529e17" @@ -2411,7 +2418,7 @@ denque@^2.0.1: resolved "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -4589,6 +4596,17 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +morgan@^1.10.0: + version "1.10.0" + resolved "https://registry.npmmirror.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -4815,6 +4833,18 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5327,16 +5357,16 @@ rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.5.7: dependencies: tslib "^2.1.0" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" From e8425f059cb4fe204a9573f8d85d8f183b483de7 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 03:23:28 +0800 Subject: [PATCH 12/20] feat: apply logger and setup handle exception error --- src/app.module.ts | 10 +- src/core/all-exceptions.filter.ts | 92 +++++++++++++++++++ .../http-exception-response.interface.ts | 7 ++ src/main.ts | 7 ++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/core/all-exceptions.filter.ts create mode 100644 src/core/models/http-exception-response.interface.ts diff --git a/src/app.module.ts b/src/app.module.ts index 9f982c9..5a2ef86 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,13 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; +import { APP_FILTER } from "@nestjs/core"; import { TypeOrmModule } from "@nestjs/typeorm"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { dataSourceOptions } from "./config/data-source"; import { validate } from "./config/env.validation"; +import { AllExceptionsFilter } from "./core/all-exceptions.filter"; import { UsersModule } from "./users/users.module"; @Module({ @@ -17,6 +19,12 @@ import { UsersModule } from "./users/users.module"; UsersModule, ], controllers: [AppController], - providers: [AppService], + providers: [ + AppService, + { + provide: APP_FILTER, + useClass: AllExceptionsFilter, + }, + ], }) export class AppModule {} diff --git a/src/core/all-exceptions.filter.ts b/src/core/all-exceptions.filter.ts new file mode 100644 index 0000000..e20805b --- /dev/null +++ b/src/core/all-exceptions.filter.ts @@ -0,0 +1,92 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, +} from "@nestjs/common"; +import { Request, Response } from "express"; +import * as fs from "fs"; +import { QueryFailedError } from "typeorm"; + +import { CustomHttpExceptionResponse } from "./models/http-exception-response.interface"; + +@Catch() +export class AllExceptionsFilter implements ExceptionFilter { + catch(exception: any, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + let status: HttpStatus; + let tinyErrorMessage: string; + let fullErrorMessage: string; + let errorMessage: string; + if (exception instanceof HttpException) { + status = exception.getStatus(); + const errorResponse = exception.getResponse(); + errorMessage = errorResponse["message"]; + } else if (exception instanceof TypeError) { + status = HttpStatus.BAD_REQUEST; + errorMessage = exception.message + .substring(exception.message.indexOf("\n\n\n") + 1) + .trim(); + } else if (exception instanceof QueryFailedError) { + status = HttpStatus.INTERNAL_SERVER_ERROR; + tinyErrorMessage = exception.message + .substring(exception.message.indexOf("\n\n\n") + 1) + .trim(); + fullErrorMessage = exception["sql"]; + errorMessage = "Critical internal server error occurred!"; + } else { + status = HttpStatus.INTERNAL_SERVER_ERROR; + errorMessage = "Critical internal server error occurred!"; + fullErrorMessage = exception; + } + const errorResponse = this.getErrorResponse(status, errorMessage, request); + const errorLog = this.getErrorLog( + tinyErrorMessage, + fullErrorMessage, + errorResponse, + request, + exception, + ); + this.writeErrorLogToFile(errorLog); + response.status(status).json(errorResponse); + } + + private getErrorResponse = ( + status: HttpStatus, + errorMessage: string, + request: Request, + ): CustomHttpExceptionResponse => ({ + statusCode: status, + error: errorMessage, + path: request.url, + method: request.method, + timeStamp: new Date(), + }); + + private getErrorLog = ( + tinyErrorMessage: string, + fullErrorMessage: string, + errorResponse: CustomHttpExceptionResponse, + request: Request, + exception: unknown, + ): string => { + const { statusCode, error } = errorResponse; + const { method, url } = request; + const errorLog = `Response Code: ${statusCode} - Method: ${method} - URL: ${url} + ${JSON.stringify(errorResponse)} + ${tinyErrorMessage != undefined ? tinyErrorMessage : ""} + ${fullErrorMessage != undefined ? fullErrorMessage : ""} + ${exception instanceof HttpException ? exception.stack : error}\n\n`; + return errorLog; + }; + + private writeErrorLogToFile = (errorLog: string): void => { + fs.appendFile("error.log", errorLog, "utf8", err => { + if (err) throw err; + }); + }; +} diff --git a/src/core/models/http-exception-response.interface.ts b/src/core/models/http-exception-response.interface.ts new file mode 100644 index 0000000..8653f2b --- /dev/null +++ b/src/core/models/http-exception-response.interface.ts @@ -0,0 +1,7 @@ +export interface CustomHttpExceptionResponse { + statusCode: number; + error: string; + path: string; + method: string; + timeStamp: Date; +} diff --git a/src/main.ts b/src/main.ts index caba4c9..814cd44 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,22 @@ import { INestApplication } from "@nestjs/common"; import { NestFactory } from "@nestjs/core"; import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import * as fs from "fs"; +import * as morgan from "morgan"; import { AppModule } from "./app.module"; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.use(morgan("default", { stream: logStream })); setupSwagger(app); await app.listen(3000); } +const logStream = fs.createWriteStream("api.log", { + flags: "a", // append +}); + function setupSwagger(app: INestApplication) { const builder = new DocumentBuilder(); const config = builder From e9dc0e9844a84bdb5c074729a4faeb7e125cb82d Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 21:17:48 +0800 Subject: [PATCH 13/20] fix: change type from any to unkown --- src/core/all-exceptions.filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/all-exceptions.filter.ts b/src/core/all-exceptions.filter.ts index e20805b..fc920d0 100644 --- a/src/core/all-exceptions.filter.ts +++ b/src/core/all-exceptions.filter.ts @@ -13,7 +13,7 @@ import { CustomHttpExceptionResponse } from "./models/http-exception-response.in @Catch() export class AllExceptionsFilter implements ExceptionFilter { - catch(exception: any, host: ArgumentsHost) { + catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); @@ -41,7 +41,7 @@ export class AllExceptionsFilter implements ExceptionFilter { } else { status = HttpStatus.INTERNAL_SERVER_ERROR; errorMessage = "Critical internal server error occurred!"; - fullErrorMessage = exception; + fullErrorMessage = JSON.stringify(exception); } const errorResponse = this.getErrorResponse(status, errorMessage, request); const errorLog = this.getErrorLog( From e438e0bd96f28140dab45ff260293e2b46cd7baf Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 21:32:44 +0800 Subject: [PATCH 14/20] fix: change unmodified lang zh-tw to en --- src/users/users.utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/users/users.utils.ts b/src/users/users.utils.ts index 08f0cf1..876fa21 100644 --- a/src/users/users.utils.ts +++ b/src/users/users.utils.ts @@ -4,8 +4,7 @@ const PASSWORD_RULE = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/; const PASSWORD_RULE_MESSAGE = - " 應包含 1 個大寫字母、1 個小寫字母以及數字和特殊字符。"; -//Password should have 1 upper case, lowcase letter along with a number and special character. + "Password should have 1 upper case, lowcase letter along with a number and special character."; const VALIDATION_PIPE = new ValidationPipe({ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, stopAtFirstError: false, From 27758eda280fc04f89e99232b82d7652dac88934 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Fri, 24 Mar 2023 21:35:34 +0800 Subject: [PATCH 15/20] fix: fix database table layout --- src/database/migrations/1679539757893-UserInitMigration.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/database/migrations/1679539757893-UserInitMigration.ts b/src/database/migrations/1679539757893-UserInitMigration.ts index 6a3c84c..eb5a78d 100644 --- a/src/database/migrations/1679539757893-UserInitMigration.ts +++ b/src/database/migrations/1679539757893-UserInitMigration.ts @@ -6,12 +6,14 @@ export class UserInitMigration1679539757893 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( `CREATE TABLE \`users\` - (\`id\` int NOT NULL AUTO_INCREMENT, + ( + \`id\` int NOT NULL AUTO_INCREMENT, \`email\` varchar(255) NOT NULL, \`name\` varchar(255) NOT NULL, \`account\` varchar(255) NOT NULL, \`password\` varchar(255) NOT NULL, - \`createAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),\`updateAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + \`createAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + \`updateAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX \`IDX_97672ac88f789774dd47f7c8be\` (\`email\`), UNIQUE INDEX \`IDX_51b8b26ac168fbe7d6f5653e6c\` (\`name\`), UNIQUE INDEX \`IDX_dd44b05034165835d6dcc18d68\` (\`account\`), From 40bd4659a072926d5724d824ece2e867ff966dee Mon Sep 17 00:00:00 2001 From: a20688392 Date: Sat, 25 Mar 2023 19:19:18 +0800 Subject: [PATCH 16/20] refactor: example should be array --- src/users/exceptions/create-entity-error.exception.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/users/exceptions/create-entity-error.exception.ts b/src/users/exceptions/create-entity-error.exception.ts index 261c11e..d90547a 100644 --- a/src/users/exceptions/create-entity-error.exception.ts +++ b/src/users/exceptions/create-entity-error.exception.ts @@ -44,7 +44,7 @@ export class CreateEntityUserError { }, }, }, - example: "email is required field.", + example: ["email is required field.", "email must be in mailbox format."], }) public readonly error: string[]; } From 874d07ca194c33cd95f7657309f15c952f915d05 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Sun, 2 Apr 2023 22:02:56 +0800 Subject: [PATCH 17/20] refactor: change folder name from core to filters --- src/app.module.ts | 2 +- .../all-exception.filter.ts} | 0 .../models/http-exception-response.interface.ts | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/{core/all-exceptions.filter.ts => filters/all-exception.filter.ts} (100%) rename src/{core => filters}/models/http-exception-response.interface.ts (100%) diff --git a/src/app.module.ts b/src/app.module.ts index 5a2ef86..5d8a3c9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,7 +7,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { dataSourceOptions } from "./config/data-source"; import { validate } from "./config/env.validation"; -import { AllExceptionsFilter } from "./core/all-exceptions.filter"; +import { AllExceptionsFilter } from "./filters/all-exception.filter"; import { UsersModule } from "./users/users.module"; @Module({ diff --git a/src/core/all-exceptions.filter.ts b/src/filters/all-exception.filter.ts similarity index 100% rename from src/core/all-exceptions.filter.ts rename to src/filters/all-exception.filter.ts diff --git a/src/core/models/http-exception-response.interface.ts b/src/filters/models/http-exception-response.interface.ts similarity index 100% rename from src/core/models/http-exception-response.interface.ts rename to src/filters/models/http-exception-response.interface.ts From 42d00404893e53b88431790e8ff1283181003511 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Sun, 16 Apr 2023 18:17:44 +0800 Subject: [PATCH 18/20] refactor: change log archive location --- src/filters/all-exception.filter.ts | 2 +- src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filters/all-exception.filter.ts b/src/filters/all-exception.filter.ts index fc920d0..20f5074 100644 --- a/src/filters/all-exception.filter.ts +++ b/src/filters/all-exception.filter.ts @@ -85,7 +85,7 @@ export class AllExceptionsFilter implements ExceptionFilter { }; private writeErrorLogToFile = (errorLog: string): void => { - fs.appendFile("error.log", errorLog, "utf8", err => { + fs.appendFile("./log/error.log", errorLog, "utf8", err => { if (err) throw err; }); }; diff --git a/src/main.ts b/src/main.ts index 814cd44..1dc33cd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,7 +13,7 @@ async function bootstrap() { await app.listen(3000); } -const logStream = fs.createWriteStream("api.log", { +const logStream = fs.createWriteStream("./log/access.log", { flags: "a", // append }); From c19d7d34dc011a81104ef0e7c4c840131685e297 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Sun, 16 Apr 2023 18:20:46 +0800 Subject: [PATCH 19/20] refactor: add log folder --- log/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 log/.gitkeep diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 0000000..e69de29 From 0f4a61dd1baefbb14416ebdc180b4f0614ea9039 Mon Sep 17 00:00:00 2001 From: a20688392 Date: Sun, 16 Apr 2023 18:47:17 +0800 Subject: [PATCH 20/20] refactor: adjust user field --- .../1681641687260-AdjustUserFieldMigration.ts | 29 +++++++++++++++++++ src/users/entities/user.entity.ts | 10 +++---- 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/database/migrations/1681641687260-AdjustUserFieldMigration.ts diff --git a/src/database/migrations/1681641687260-AdjustUserFieldMigration.ts b/src/database/migrations/1681641687260-AdjustUserFieldMigration.ts new file mode 100644 index 0000000..217886b --- /dev/null +++ b/src/database/migrations/1681641687260-AdjustUserFieldMigration.ts @@ -0,0 +1,29 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AdjustUserFieldMigration1681641687260 + implements MigrationInterface +{ + name = "AdjustUserFieldMigration1681641687260"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX \`IDX_51b8b26ac168fbe7d6f5653e6c\` ON \`users\``, + ); + await queryRunner.query( + `ALTER TABLE \`users\` MODIFY \`password\` varchar(60) NOT NULL`, + ); + await queryRunner.query(`ALTER TABLE \`users\` DROP COLUMN \`updateAt\``); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`users\` ADD \`updateAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)`, + ); + await queryRunner.query( + `ALTER TABLE \`users\` MODIFY \`password\` varchar(255) NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX \`IDX_51b8b26ac168fbe7d6f5653e6c\` ON \`users\` (\`name\`)`, + ); + } +} diff --git a/src/users/entities/user.entity.ts b/src/users/entities/user.entity.ts index b96dbb6..0903861 100644 --- a/src/users/entities/user.entity.ts +++ b/src/users/entities/user.entity.ts @@ -4,7 +4,6 @@ import { CreateDateColumn, Entity, PrimaryGeneratedColumn, - UpdateDateColumn, } from "typeorm"; @Entity({ name: "users" }) @@ -15,18 +14,17 @@ export class UserEntity extends BaseEntity { @Column({ unique: true }) email: string; - @Column({ unique: true }) + @Column() name: string; @Column({ unique: true }) account: string; - @Column() + @Column({ + length: 60, + }) password: string; @CreateDateColumn() createAt: Date; - - @UpdateDateColumn() - updateAt: Date; }