diff --git a/d-script/restart.sh b/d-script/restart.sh new file mode 100644 index 0000000..5e072a9 --- /dev/null +++ b/d-script/restart.sh @@ -0,0 +1,4 @@ +docker stop bh-mongo; +docker volume rm bh-mongo-volume; +docker volume create bh-mongo-volume; +sh d-script/up-mongo.sh; diff --git a/d-script/up-mongo.sh b/d-script/up-mongo.sh index 15ab53f..d1d1f80 100644 --- a/d-script/up-mongo.sh +++ b/d-script/up-mongo.sh @@ -5,6 +5,6 @@ docker \ --name bh-mongo \ -e MONGO_INITDB_ROOT_USERNAME=qqq \ -e MONGO_INITDB_ROOT_PASSWORD=qqq \ - -p 27017:27017 \ + -p 8888:27017 \ -v bh-mongo-volume:/data/db \ mongo:8.0.3; diff --git a/package-lock.json b/package-lock.json index 0f88b18..404b9b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "express-json-validator-middleware": "^3.0.1", "install": "^0.13.0", "jsonwebtoken": "^9.0.2", + "mongoose": "^8.8.3", "npm": "^10.9.1", "pbkdf2-password": "^1.2.1", "ts-node": "^10.9.2" @@ -62,6 +63,15 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -205,6 +215,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -288,6 +313,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/binary-extensions": { @@ -386,6 +412,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -405,6 +432,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -475,6 +511,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, "node_modules/content-disposition": { @@ -849,6 +886,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -899,6 +937,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -944,6 +983,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -1037,6 +1077,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -1184,6 +1225,15 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1241,6 +1291,12 @@ "node": ">= 0.8" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -1287,6 +1343,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1318,6 +1375,111 @@ "node": ">=10" } }, + "node_modules/mongodb": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.8.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.3.tgz", + "integrity": "sha512-/I4n/DcXqXyIiLRfAmUIiTjj3vXfeISke8dt4U4Y8Wfm074Wa6sXnQrXN49NFOFf2mM1kUdOXryoBvkuCnr+Qw==", + "license": "MIT", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "~6.10.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4089,6 +4251,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4152,6 +4315,15 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -4438,6 +4610,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -4472,6 +4650,15 @@ "source-map": "^0.6.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -4505,6 +4692,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -4558,6 +4746,18 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -4732,6 +4932,28 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index b229d78..50004da 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "express-json-validator-middleware": "^3.0.1", "install": "^0.13.0", "jsonwebtoken": "^9.0.2", + "mongoose": "^8.8.3", "npm": "^10.9.1", "pbkdf2-password": "^1.2.1", "ts-node": "^10.9.2" diff --git a/src/connect.ts b/src/connect.ts new file mode 100644 index 0000000..6ad46ed --- /dev/null +++ b/src/connect.ts @@ -0,0 +1,29 @@ +import mongoose from "mongoose"; + +import ItemListModel from "./model/todo-list"; +import ItemModel from "./model/todo-item"; + +export const connect = async () => { + await mongoose.connect(process.env.MONGO_CONNECT_URL!); + console.log("Connected to database"); + + const lists = await ItemListModel.find(); + + console.log(JSON.stringify(lists, null, 4)) + + if (lists.length === 0) { + await ItemListModel.create({ + name: "Test List", + }); + + const item = await ItemModel.create({ + title: "Test Item", + }); + + lists.forEach(async (list) => { + await (list as unknown as any).addItem(item._id); + }) + } + + console.log("Database initiated"); +}; diff --git a/src/main.ts b/src/main.ts index 352df53..cf70bc2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,9 @@ import express, { json } from 'express'; -import { handleError } from './utils/errorHandler' import 'dotenv/config' import { router } from './routes' +import { handleError } from './utils/errorHandler' +import { connect } from './connect'; const app = express(); @@ -13,6 +14,14 @@ app.use(json({ limit: '100kb' })) app.use(router) app.use(handleError) -app.listen(port, () => { - console.log(`App is running on port http://localhost:${port}`); -}) + +const start = async () => { + console.log('starting...') + await connect() + + app.listen(port, () => { + console.log(`App is running on port http://localhost:${port}`); + }) +} + +start() diff --git a/src/model/todo-item.ts b/src/model/todo-item.ts new file mode 100644 index 0000000..150ed8d --- /dev/null +++ b/src/model/todo-item.ts @@ -0,0 +1,24 @@ +import { Schema, model } from 'mongoose' + +const schema = new Schema({ + title: {type: String, required: true }, + description: String, + created: { type: Date, default: () => new Date().toISOString() }, + done: { type: Boolean, default: false }, + // createdBy: { type: Schema.Types.ObjectId, required: true, ref: 'User' } + deleted: { type: Boolean, default: false } +}) + +schema.virtual('id').get(function () { + return this._id.toHexString() +}) + +schema.set('toJSON', { + virtuals: true, + versionKey: false, + transform: (doc, ret) => { + delete ret._id + } +}) + +export default model('Item', schema) diff --git a/src/model/todo-list.ts b/src/model/todo-list.ts new file mode 100644 index 0000000..a572d28 --- /dev/null +++ b/src/model/todo-list.ts @@ -0,0 +1,34 @@ +import { Schema, model } from 'mongoose' + +const schema = new Schema({ + name: String, + items: [{ type: Schema.Types.ObjectId, required: true, ref: 'Item' }], + created: { type: Date, default: () => new Date().toISOString() }, + // createdBy: { type: Schema.Types.ObjectId, required: true, ref: 'User' } + deleted: { type: Boolean, default: false } +}) + +schema.virtual('id').get(function () { + return this._id.toHexString() +}) + +schema.set('toJSON', { + virtuals: true, + versionKey: false, + transform: (doc, ret) => { + delete ret._id + } +}) + +schema.method('addItem', async function(item) { + this.items.push(item._id) + return await this.save() +}) + +schema.method('removeItem', async function(item) { + this.items = this.items.filter(i => i !== item._id) + return await this.save() +}) + + +export default model('TodoList', schema) diff --git a/src/routes/index.ts b/src/routes/index.ts index f5c87ad..b38fede 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -3,9 +3,11 @@ import { Router } from "express"; import pkg from '../../package.json' import { router as usersRouter } from './users' +import { router as todoRouter } from './todo' export const router = Router(); router.get('/healthcheck', (req, res) => void res.send({ ok: true, version: pkg.version })); -router.use('/users', usersRouter) \ No newline at end of file +router.use('/users', usersRouter) +router.use('/todo', todoRouter) diff --git a/src/routes/todo/index.ts b/src/routes/todo/index.ts new file mode 100644 index 0000000..2066bd4 --- /dev/null +++ b/src/routes/todo/index.ts @@ -0,0 +1,66 @@ +import { Router } from "express"; +import ListsModel from '../../model/todo-list'; +import ItemModel from '../../model/todo-item'; + +export const router = Router(); + +router.get('/lists', async (req, res) => { + const lists = await ListsModel.find({}); + + res.json(lists); +}) + +router.post('/list', async (req, res) => { + const { name } = req.body + const list = await ListsModel.create({ + name + }); + + res.json(list); +}) + +router.delete('/item/:itemId', async (req, res) => { + const { itemId } = req.params; + + const item = await ItemModel.findById(itemId); + + if (!item) throw new Error('Item not found'); + + await ItemModel.findByIdAndDelete(itemId); + + res.send(item); +}) + +router.get('/list/:listId', async (req, res) => { + const { listId } = req.params; + + const list = await ListsModel + .findById(listId) + .populate('items') + .exec(); + + if (!list) throw new Error('List not found'); + + res.json(list); +}) + +router.post('/:listId/item', async (req, res) => { + const { listId } = req.params + const { title, description = '' } = req.body + + const list = await ListsModel.findById(listId); + + if (!list) { + throw new Error('List not found'); + } + + const item = await ItemModel.create({ + title, + description + }); + + await (list as any).addItem(item._id); + + res.send(item) +}) + diff --git a/src/routes/users/index.ts b/src/routes/users/index.ts index 8fc487e..5bc43ed 100644 --- a/src/routes/users/index.ts +++ b/src/routes/users/index.ts @@ -1,7 +1,6 @@ import { Router } from "express"; import { Validator } from "express-json-validator-middleware"; import bkfd2Password from "pbkdf2-password"; -import { promisify } from 'node:util' import jwt from 'jsonwebtoken'