diff --git a/.env.example b/.env.example index 4a211c9..ce91c63 100644 --- a/.env.example +++ b/.env.example @@ -75,7 +75,7 @@ DB_LOCATION__COUNTRY=country # The name of the column that contains the postal code of each location point # Comment or leave empty if the postal code is not available. -DB_LOCATION__POSTAL_CODE=postal_code +DB_LOCATION__POSTAL_CODE=postalCode ### ### Frontend configuration diff --git a/frontend/src/models/User.ts b/frontend/src/models/User.ts index 897a791..87b1439 100644 --- a/frontend/src/models/User.ts +++ b/frontend/src/models/User.ts @@ -7,7 +7,6 @@ class User { public firstName: Optional<string>; public lastName: Optional<string>; public createdAt: Optional<Date>; - public updatedAt: Optional<Date>; constructor(user: { id: number; @@ -16,7 +15,6 @@ class User { firstName?: string; lastName?: string; createdAt?: Date; - updatedAt?: Date; }) { this.id = user.id; this.username = user.username; @@ -24,7 +22,6 @@ class User { this.firstName = user.firstName || null; this.lastName = user.lastName || null; this.createdAt = user.createdAt || null; - this.updatedAt = user.updatedAt || null; } } diff --git a/frontend/src/models/UserSession.ts b/frontend/src/models/UserSession.ts index b40f1cc..7079a77 100644 --- a/frontend/src/models/UserSession.ts +++ b/frontend/src/models/UserSession.ts @@ -4,20 +4,17 @@ class UserSession { public id: string; public userId: number; public createdAt: Date; - public updatedAt: Date; public expiresAt: Optional<Date>; constructor(userSession: { id: string; userId: number; createdAt: Date; - updatedAt: Date; expiresAt?: Date; }) { this.id = userSession.id; this.userId = userSession.userId; this.createdAt = userSession.createdAt; - this.updatedAt = userSession.updatedAt; this.expiresAt = userSession.expiresAt || null; } } diff --git a/package-lock.json b/package-lock.json index e8e29ab..43fb65c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "pg": "^8.13.3", "sequelize": "^6.37.5", "sqlite3": "^5.1.7", + "umzug": "^3.8.2", "uuid": "^11.1.0" }, "devDependencies": { @@ -36,6 +37,41 @@ "license": "MIT", "optional": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@npmcli/fs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", @@ -62,6 +98,99 @@ "node": ">=10" } }, + "node_modules/@rushstack/node-core-library": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.11.0.tgz", + "integrity": "sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==", + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.0.tgz", + "integrity": "sha512-vXQPRQ+vJJn4GVqxkwRe+UGgzNxdV8xuJZY2zem46Y0p3tlahucH9/hPmLGj2i9dQnUBFiRnoM9/KW7PYw8F4Q==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.11.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.23.5", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.5.tgz", + "integrity": "sha512-jg70HfoK44KfSP3MTiL5rxsZH7X1ktX3cZs9Sl8eDu1/LxJSbPsh0MOFRC710lIuYYSgxWjI5AjbCBAl7u3RxA==", + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.15.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -72,6 +201,12 @@ "node": ">= 6" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -306,6 +441,53 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -352,6 +534,21 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -466,7 +663,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -805,6 +1001,18 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -970,6 +1178,37 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -980,7 +1219,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -1031,6 +1269,20 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1164,7 +1416,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -1189,8 +1440,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/has-flag": { "version": "3.0.0", @@ -1384,6 +1634,15 @@ "dev": true, "license": "ISC" }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1480,11 +1739,25 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1504,7 +1777,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -1524,7 +1796,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -1537,6 +1808,12 @@ "license": "ISC", "optional": true }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "license": "MIT" + }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", @@ -1544,6 +1821,24 @@ "license": "MIT", "optional": true }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -1646,7 +1941,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", - "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -1709,6 +2003,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1718,6 +2021,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2157,6 +2473,12 @@ "node": ">=0.10.0" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -2256,7 +2578,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -2265,6 +2586,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -2381,6 +2711,15 @@ "once": "^1.3.1" } }, + "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", @@ -2396,6 +2735,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2462,6 +2821,35 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -2478,6 +2866,16 @@ "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==", "license": "MIT" }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2495,6 +2893,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2977,6 +3398,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3027,6 +3457,18 @@ "node": ">=4" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -3091,7 +3533,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -3137,6 +3578,18 @@ "node": "*" } }, + "node_modules/type-fest": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.37.0.tgz", + "integrity": "sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3164,6 +3617,22 @@ "node": ">=14.17" } }, + "node_modules/umzug": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", + "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "license": "MIT", + "dependencies": { + "@rushstack/ts-command-line": "^4.12.2", + "emittery": "^0.13.0", + "fast-glob": "^3.3.2", + "pony-cause": "^2.1.4", + "type-fest": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -3197,6 +3666,15 @@ "imurmurhash": "^0.1.4" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3206,6 +3684,15 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index de19dc0..dd3a04a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "pg": "^8.13.3", "sequelize": "^6.37.5", "sqlite3": "^5.1.7", + "umzug": "^3.8.2", "uuid": "^11.1.0" }, "devDependencies": { diff --git a/src/App.ts b/src/App.ts index 0c7ecd6..b2ec986 100644 --- a/src/App.ts +++ b/src/App.ts @@ -18,8 +18,11 @@ class App { }: any) { useGlobals(); $db.sync().then(() => { - $repos.userRoles.init(); - $repos.users.syncAdminUser(); + $repos.userRoles.init().then(() => { + $repos.users.syncAdminUser().then(() => { + console.log(' The database is ready'); + }) + }) }) this.app = app; diff --git a/src/db/Db.ts b/src/db/Db.ts index 55f3aeb..bfd8f06 100644 --- a/src/db/Db.ts +++ b/src/db/Db.ts @@ -1,21 +1,25 @@ import { Sequelize, Dialect } from 'sequelize'; +import Migrations from './Migrations'; + import GPSData from './types/GPSData'; import Role from './types/Role'; import User from './types/User'; +import UserDevice from './types/UserDevice'; import UserRole from './types/UserRole'; import UserSession from './types/UserSession'; class Db { - private readonly url: string; - private readonly locationUrl: string; - private readonly locationTable: string; + public readonly url: string; + public readonly locationUrl: string; + public readonly locationTable: string; public readonly locationTableColumns: Record<string, string>; private readonly dialect: Dialect; private readonly locationDialect: Dialect; private readonly tablePrefix: string; private readonly appDb: Sequelize; private readonly locationDb: Sequelize; + private readonly migrations: Migrations; private static readonly envColumnPrefix = 'DB_LOCATION__'; @@ -51,6 +55,8 @@ class Db { logging: process.env.DEBUG === 'true' ? console.log : false }); } + + this.migrations = new Migrations(this.appDb); } public static fromEnv(): Db { @@ -87,7 +93,7 @@ class Db { 'address', 'locality', 'country', - 'postal_code' + 'postalCode' ].reduce((acc: any, name: string) => { acc[name] = process.env[this.prefixedEnv(name)]; if (!acc[name]?.length && requiredColumns[name]) { @@ -111,51 +117,10 @@ class Db { public async sync() { console.log('Syncing databases'); - - for (const modelName of ['GPSData', 'Role', 'User', 'UserRole', 'UserSession']) { - const model = (this as any)[modelName](); - process.stdout.write(` [⌛] Syncing ${model.name}`); - await model.sync(); - await this.appDb.sync(); - process.stdout.write(`\r [✅] Synced ${model.name} \n`); - } - - this.initConstraints(); + await this.migrations.up(); console.log('Database sync completed'); } - private initConstraints() { - this.appDb.models.UserSession.belongsTo(this.appDb.models.User, { - foreignKey: 'userId', - targetKey: 'id', - as: 'user', - onDelete: 'CASCADE' - }); - - this.appDb.models.User.hasMany(this.appDb.models.UserSession, { - foreignKey: 'userId', - sourceKey: 'id', - as: 'sessions' - }); - - this.appDb.models.User.belongsToMany(this.appDb.models.Role, { - through: this.appDb.models.UserRole, - foreignKey: 'userId', - otherKey: 'roleId', - as: 'roles' - }); - - this.appDb.models.Role.belongsToMany(this.appDb.models.User, { - through: this.appDb.models.UserRole, - foreignKey: 'roleId', - otherKey: 'userId', - as: 'users' - }); - - this.appDb.models.UserSession.sync(); - this.appDb.sync(); - } - /** * Tables */ @@ -176,31 +141,27 @@ class Db { public User() { return this.appDb.define('User', User(), { - indexes: [ - { - unique: true, - fields: ['username', 'email'], - }, - ], tableName: this.tableName('users'), timestamps: false, }); } + public UserDevice() { + return this.appDb.define('UserDevice', UserDevice(), { + tableName: this.tableName('user_devices'), + timestamps: false, + }); + } + public UserRole() { return this.appDb.define('UserRole', UserRole(), { - tableName: this.tableName('user_roles'), + tableName: this.tableName('users_roles'), timestamps: false, }); } public UserSession() { const ret = this.appDb.define('UserSession', UserSession(), { - indexes: [ - { - fields: ['userId'], - }, - ], tableName: this.tableName('user_sessions'), timestamps: false, }); diff --git a/src/db/Migrations.ts b/src/db/Migrations.ts new file mode 100644 index 0000000..55de727 --- /dev/null +++ b/src/db/Migrations.ts @@ -0,0 +1,23 @@ +import { Sequelize, } from 'sequelize'; +import { Umzug, SequelizeStorage, } from 'umzug'; + +class Migrations { + private readonly migrations: Umzug; + + constructor(db: Sequelize) { + this.migrations = new Umzug({ + storage: new SequelizeStorage({ sequelize: db }), + context: db.getQueryInterface(), + migrations: { + glob: 'src/db/migrations/*.*s', + }, + logger: console, + }) as Umzug; + } + + public async up(): Promise<void> { + await this.migrations.up(); + } +} + +export default Migrations; diff --git a/src/db/migrations/000_initial.ts b/src/db/migrations/000_initial.ts new file mode 100644 index 0000000..8e241e4 --- /dev/null +++ b/src/db/migrations/000_initial.ts @@ -0,0 +1,243 @@ +const { DataTypes } = require('sequelize'); +const { v4: uuidv4 } = require('uuid'); + +async function createLocationHistoryTable(query: { context: any }) { + // Only create the table if locationUrl == appUrl (i.e. the location is stored + // on the app managed database and not on an external one) + if ($db.locationUrl !== $db.url) { + console.log('The location history table is stored on an external database, skipping creation'); + return; + } + + const typeDef: Record<string, any> = { + [$db.locationTableColumns['id']]: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + [$db.locationTableColumns['latitude']]: { + type: DataTypes.FLOAT, + allowNull: false + }, + [$db.locationTableColumns['longitude']]: { + type: DataTypes.FLOAT, + allowNull: false + }, + [$db.locationTableColumns['timestamp']]: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + allowNull: false + }, + } + + if ($db.locationTableColumns['altitude']) { + typeDef[$db.locationTableColumns['altitude']] = { + type: DataTypes.FLOAT, + allowNull: true + } + } + + if ($db.locationTableColumns['address']) { + typeDef[$db.locationTableColumns['address']] = { + type: DataTypes.STRING, + allowNull: true + } + } + + if ($db.locationTableColumns['locality']) { + typeDef[$db.locationTableColumns['locality']] = { + type: DataTypes.STRING, + allowNull: true + } + } + + if ($db.locationTableColumns['country']) { + typeDef[$db.locationTableColumns['country']] = { + type: DataTypes.STRING, + allowNull: true + } + } + + if ($db.locationTableColumns['postalCode']) { + typeDef[$db.locationTableColumns['postalCode']] = { + type: DataTypes.STRING, + allowNull: true + } + } + + await query.context.createTable($db.locationTable, typeDef); +} + +async function createUsersTable(query: { context: any }) { + await query.context.createTable($db.tableName('users'), { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + username: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + password: { + type: DataTypes.STRING, + allowNull: false + }, + firstName: { + type: DataTypes.STRING, + }, + lastName: { + type: DataTypes.STRING, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }); +} + +async function createRolesTable(query: { context: any }) { + await query.context.createTable($db.tableName('roles'), { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }); +} + +async function createUsersRolesTable(query: { context: any }) { + await query.context.createTable($db.tableName('users_roles'), { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: $db.tableName('users'), + key: 'id' + } + }, + roleId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: $db.tableName('roles'), + key: 'id' + } + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }); +} + +async function createUserSessionsTable(query: { context: any }) { + await query.context.createTable($db.tableName('user_sessions'), { + id: { + type: DataTypes.UUID, + primaryKey: true, + allowNull: false, + defaultValue: () => uuidv4(), + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: $db.tableName('users'), + key: 'id' + } + }, + name: { + type: DataTypes.STRING, + allowNull: true, + }, + expiresAt: { + type: DataTypes.DATE, + allowNull: true, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }); +} + +async function createUserDevicesTable(query: { context: any }) { + await query.context.createTable($db.tableName('user_devices'), { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: $db.tableName('users'), + key: 'id' + } + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }); +} + +async function up(query: { context: any }) { + await createRolesTable({ context: query.context }); + await createUsersTable({ context: query.context }); + await createUserDevicesTable({ context: query.context }); + await createUsersRolesTable({ context: query.context }); + await createUserSessionsTable({ context: query.context }); + await createLocationHistoryTable({ context: query.context }); +} + +async function down(query: { context: any }) { + await query.context.dropTable($db.tableName('users')); + await query.context.dropTable($db.tableName('roles')); + await query.context.dropTable($db.tableName('user_devices')); + await query.context.dropTable($db.tableName('users_roles')); + await query.context.dropTable($db.tableName('user_sessions')); + + if ($db.locationUrl !== $db.url) { + console.log('The location history table is stored on an external database, skipping deletion'); + return; + } + + await query.context.dropTable($db.locationTable); +} + +module.exports = { + up, + down +} diff --git a/src/db/types/GPSData.ts b/src/db/types/GPSData.ts index e208849..60f7f8d 100644 --- a/src/db/types/GPSData.ts +++ b/src/db/types/GPSData.ts @@ -51,7 +51,7 @@ function GPSData(locationTableColumns: Record<string, string>): Record<string, a }; } - const postalCodeCol: string = locationTableColumns['postal_code']; + const postalCodeCol: string = locationTableColumns['postalCode']; if (postalCodeCol?.length) { typeDef[postalCodeCol] = { type: DataTypes.STRING, diff --git a/src/db/types/User.ts b/src/db/types/User.ts index 4650164..29cf6fb 100644 --- a/src/db/types/User.ts +++ b/src/db/types/User.ts @@ -1,4 +1,4 @@ -import { DataTypes, } from 'sequelize'; +import { DataTypes } from 'sequelize'; function User(): Record<string, any> { return { @@ -34,12 +34,6 @@ function User(): Record<string, any> { allowNull: false, defaultValue: () => new Date(), }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: () => new Date(), - onUpdate: () => new Date(), - }, }; } diff --git a/src/db/types/UserDevice.ts b/src/db/types/UserDevice.ts new file mode 100644 index 0000000..678396b --- /dev/null +++ b/src/db/types/UserDevice.ts @@ -0,0 +1,26 @@ +import { DataTypes, } from 'sequelize'; + +function UserDevice(): Record<string, any> { + return { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: () => new Date(), + }, + }; +} + +export default UserDevice; diff --git a/src/db/types/UserRole.ts b/src/db/types/UserRole.ts index 246f22d..0e0fb33 100644 --- a/src/db/types/UserRole.ts +++ b/src/db/types/UserRole.ts @@ -20,12 +20,6 @@ function UserRole(): Record<string, any> { allowNull: false, defaultValue: () => new Date(), }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: () => new Date(), - onUpdate: () => new Date(), - }, }; } diff --git a/src/db/types/UserSession.ts b/src/db/types/UserSession.ts index 2e9ec0d..b972043 100644 --- a/src/db/types/UserSession.ts +++ b/src/db/types/UserSession.ts @@ -22,12 +22,6 @@ function UserSession(): Record<string, any> { allowNull: false, defaultValue: () => new Date(), }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: () => new Date(), - onUpdate: () => new Date(), - }, } } diff --git a/src/models/User.ts b/src/models/User.ts index d13bfec..72f92dd 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -13,7 +13,6 @@ class User { public firstName: Optional<string>; public lastName: Optional<string>; public createdAt: Optional<Date>; - public updatedAt: Optional<Date>; constructor({ id, @@ -23,7 +22,6 @@ class User { firstName = null, lastName = null, createdAt = null, - updatedAt = null, }: User) { this.id = id; this.username = username; @@ -32,7 +30,6 @@ class User { this.firstName = firstName; this.lastName = lastName; this.createdAt = createdAt; - this.updatedAt = updatedAt; } public static hashPassword(password: string): string { diff --git a/src/models/UserSession.ts b/src/models/UserSession.ts index 912e12d..5513bff 100644 --- a/src/models/UserSession.ts +++ b/src/models/UserSession.ts @@ -7,22 +7,19 @@ class UserSession { public userId: number; public expiresAt: Optional<Date>; public createdAt: Optional<Date>; - public updatedAt: Optional<Date>; constructor({ id, userId, expiresAt = null, createdAt = null, - updatedAt = null, }: any) { this.id = id; this.userId = userId; this.expiresAt = expiresAt; this.createdAt = createdAt; - this.updatedAt = updatedAt; - ['expiresAt', 'createdAt', 'updatedAt'].forEach((key) => { + ['expiresAt', 'createdAt'].forEach((key) => { if ((this as any)[key] && !((this as any)[key] instanceof Date)) { (this as any)[key] = new Date((this as any)[key]); } diff --git a/src/repos/Location.ts b/src/repos/Location.ts index 9decd44..4f3a32a 100644 --- a/src/repos/Location.ts +++ b/src/repos/Location.ts @@ -24,7 +24,7 @@ class Location { address: data[mappings.address], locality: data[mappings.locality], country: data[mappings.country], - postalCode: data[mappings.postal_code], + postalCode: data[mappings.postalCode], timestamp: data[mappings.timestamp], }); }); diff --git a/src/repos/UserSessions.ts b/src/repos/UserSessions.ts index 42c11d4..9e08a55 100644 --- a/src/repos/UserSessions.ts +++ b/src/repos/UserSessions.ts @@ -20,10 +20,14 @@ class UserSessions { return session; } - public async create(userId: number, expiresAt: Optional<Date> = null): Promise<UserSession> { + public async create(userId: number, args: { + expiresAt?: Optional<Date>, + name?: Optional<string>, + }): Promise<UserSession> { const session = await $db.UserSession().create({ userId, - expiresAt: expiresAt ? new Date(expiresAt).toISOString() : null, + name: args.name, + expiresAt: args.expiresAt ? new Date(args.expiresAt).toISOString() : null, }); return new UserSession(session.dataValues); diff --git a/src/requests/LocationRequest.ts b/src/requests/LocationRequest.ts index a1701a6..d73a326 100644 --- a/src/requests/LocationRequest.ts +++ b/src/requests/LocationRequest.ts @@ -92,7 +92,7 @@ class LocationRequest { } if (this.postalCode != null) { - where[colMapping.postal_code || 'postal_code'] = this.postalCode; + where[colMapping.postalCode || 'postalCode'] = this.postalCode; } queryMap.where = where; diff --git a/src/routes/api/v1/Auth.ts b/src/routes/api/v1/Auth.ts index 66c2b07..97196f2 100644 --- a/src/routes/api/v1/Auth.ts +++ b/src/routes/api/v1/Auth.ts @@ -47,7 +47,11 @@ class Auth extends ApiV1Route { } } - session = await $repos.userSessions.create(user.id, expiresAtDate); + session = await $repos.userSessions.create(user.id, { + name: req.body?.name, + expiresAt: expiresAtDate, + }) + setCookie(res, { name: 'session', value: session.getToken(),