From 94c1d602785bf4a04d55075f642082a65f3e5cb6 Mon Sep 17 00:00:00 2001 From: Sv443 Date: Mon, 21 Oct 2024 00:07:49 +0200 Subject: [PATCH] feat: mikroorm boilerplate and example models --- package.json | 3 + pnpm-lock.yaml | 501 ++++++++++++++++++++++++++ src/lib/env.ts | 58 +++ src/mikro-orm.config.ts | 32 ++ src/models/Billing.model.ts | 0 src/models/Connection.model.ts | 0 src/models/Invoice.model.ts | 0 src/models/Joke.model.ts | 0 src/models/Report.model.ts | 0 src/models/ReportNote.model.ts | 40 ++ src/models/Submission.model.ts | 0 src/models/User.model.ts | 53 +++ src/models/UserSettings.model.ts | 0 src/models/index.ts | 9 + src/server/index.ts | 2 +- src/{ => server}/routes/index.ts | 0 src/{ => server}/routes/joke.ts | 0 src/{ => server}/routes/submission.ts | 0 src/{ => server}/routes/user.ts | 0 src/types/index.ts | 6 +- src/types/jokeapi | 2 +- src/types/misc.ts | 2 + src/types/user.ts | 3 + tsconfig.json | 19 +- 24 files changed, 715 insertions(+), 15 deletions(-) create mode 100644 src/lib/env.ts create mode 100644 src/mikro-orm.config.ts create mode 100644 src/models/Billing.model.ts create mode 100644 src/models/Connection.model.ts create mode 100644 src/models/Invoice.model.ts create mode 100644 src/models/Joke.model.ts create mode 100644 src/models/Report.model.ts create mode 100644 src/models/ReportNote.model.ts create mode 100644 src/models/Submission.model.ts create mode 100644 src/models/User.model.ts create mode 100644 src/models/UserSettings.model.ts create mode 100644 src/models/index.ts rename src/{ => server}/routes/index.ts (100%) rename src/{ => server}/routes/joke.ts (100%) rename src/{ => server}/routes/submission.ts (100%) rename src/{ => server}/routes/user.ts (100%) create mode 100644 src/types/user.ts diff --git a/package.json b/package.json index 3f5751c8..c0e772c1 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "email": "contact@sv443.net" }, "dependencies": { + "@mikro-orm/core": "^6.3.13", + "@mikro-orm/postgresql": "^6.3.13", "@pm2/io": "^5.0.0", "compression": "^1.7.4", "cors": "^2.8.5", @@ -43,6 +45,7 @@ }, "devDependencies": { "@changesets/cli": "^2.27.1", + "@mikro-orm/cli": "^6.3.13", "@types/compression": "^1.7.2", "@types/cors": "^2.8.12", "@types/express": "^4.17.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0389bb03..2d7c4361 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@mikro-orm/core': + specifier: ^6.3.13 + version: 6.3.13 + '@mikro-orm/postgresql': + specifier: ^6.3.13 + version: 6.3.13(@mikro-orm/core@6.3.13) '@pm2/io': specifier: ^5.0.0 version: 5.0.2 @@ -48,6 +54,9 @@ importers: '@changesets/cli': specifier: ^2.27.1 version: 2.27.9 + '@mikro-orm/cli': + specifier: ^6.3.13 + version: 6.3.13(pg@8.13.0) '@types/compression': specifier: ^1.7.2 version: 1.7.5 @@ -184,6 +193,9 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@jercle/yargonaut@1.1.5': + resolution: {integrity: sha512-zBp2myVvBHp1UaJsNTyS6q4UDKT7eRiqTS4oNTS6VQMd6mpxYOdbeK4pY279cDCdakGy6hG0J3ejoXZVsPwHqw==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -200,6 +212,37 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@mikro-orm/cli@6.3.13': + resolution: {integrity: sha512-6ubnNi7waCyNUsed8nFGeJ9HTg1CfoFnd01Wj0c7Bcb0Ktq0+5lvbS7oXUOKhrTF2zrIOEdRKknzza9OGRWW8A==} + engines: {node: '>= 18.12.0'} + hasBin: true + + '@mikro-orm/core@6.3.13': + resolution: {integrity: sha512-gNvk/98Xe283+Yn4CFxNNJmx1iXT8gRfDn4agjFUa4pI9pVw+idkxOljXuLlvoW44ybiwji8qagaoY+tmD0BTQ==} + engines: {node: '>= 18.12.0'} + + '@mikro-orm/knex@6.3.13': + resolution: {integrity: sha512-+ws7bEFbgw2fLYry9KZJ2pQpGgzCsoke/sFn0Dn8vNznqdq5ktJMGj5kxBRsLhzGQ4zS1wjgn0JJELqXVtPX5g==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@mikro-orm/core': ^6.0.0 + better-sqlite3: '*' + libsql: '*' + mariadb: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + libsql: + optional: true + mariadb: + optional: true + + '@mikro-orm/postgresql@6.3.13': + resolution: {integrity: sha512-0U2/wW4ff/DNMTNRhe3TE/SHibrI7loPuBQffjHZCWWs8KPjI46EN4oTjvY7LNNOPWzfS+UpxafNHUR+zrfuZQ==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@mikro-orm/core': ^6.0.0 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -477,6 +520,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -484,6 +531,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -530,6 +584,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + dataloader@2.2.2: + resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -547,6 +604,15 @@ packages: supports-color: optional: true + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -600,6 +666,9 @@ packages: emitter-listener@1.1.2: resolution: {integrity: sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -620,6 +689,10 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -645,6 +718,10 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -708,6 +785,11 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + figlet@1.8.0: + resolution: {integrity: sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw==} + engines: {node: '>= 0.4.0'} + hasBin: true + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -766,10 +848,21 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + getopts@2.3.0: + resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -859,6 +952,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + interpret@2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + ip-regex@4.3.0: resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} engines: {node: '>=8'} @@ -879,6 +976,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -932,6 +1033,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -948,6 +1054,34 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + knex@3.1.0: + resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + pg-native: + optional: true + sqlite3: + optional: true + tedious: + optional: true + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1002,6 +1136,10 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mikro-orm@6.3.13: + resolution: {integrity: sha512-la2qeQjTMcpctz7hWs+sIe+qC3uNRe8e0Tv7aVw0ZTG3YcZe14lRWtaHM1HMc1Xdt/Fj1buxLx+yFCpiPZx5VQ==} + engines: {node: '>= 18.12.0'} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -1026,6 +1164,9 @@ packages: resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} engines: {node: '>=10'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + module-details-from-path@1.0.3: resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} @@ -1130,6 +1271,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parent-require@1.0.0: + resolution: {integrity: sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==} + engines: {node: '>= 0.4.0'} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -1156,6 +1301,43 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.13.0: + resolution: {integrity: sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1167,6 +1349,34 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@4.0.2: + resolution: {integrity: sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==} + engines: {node: '>=12'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1222,12 +1432,23 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} request-ip@3.3.0: resolution: {integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-in-the-middle@5.2.0: resolution: {integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==} engines: {node: '>=6'} @@ -1331,6 +1552,10 @@ packages: spawndamnit@2.0.0: resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -1338,10 +1563,18 @@ packages: resolution: {integrity: sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==} engines: {node: '>= 0.6'} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -1373,6 +1606,10 @@ packages: resolution: {integrity: sha512-EW8kUpRXfwwfH31Yu6fygIz8XsXeI9OIVU9fG8LFuwjreVCYLT6TJhxzeJEMvVRlF/pyTWi9FKkuCIF5C3H2FQ==} engines: {node: '>=14'} + tarn@3.0.2: + resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} + engines: {node: '>=8.0.0'} + tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} @@ -1383,6 +1620,10 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + tildify@2.0.0: + resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} + engines: {node: '>=8'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -1413,6 +1654,10 @@ packages: '@swc/wasm': optional: true + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -1495,18 +1740,38 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} xmlcreate@2.0.4: resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -1702,6 +1967,12 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@jercle/yargonaut@1.1.5': + dependencies: + chalk: 4.1.2 + figlet: 1.8.0 + parent-require: 1.0.0 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} @@ -1727,6 +1998,70 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@mikro-orm/cli@6.3.13(pg@8.13.0)': + dependencies: + '@jercle/yargonaut': 1.1.5 + '@mikro-orm/core': 6.3.13 + '@mikro-orm/knex': 6.3.13(@mikro-orm/core@6.3.13)(pg@8.13.0) + fs-extra: 11.2.0 + tsconfig-paths: 4.2.0 + yargs: 17.7.2 + transitivePeerDependencies: + - better-sqlite3 + - libsql + - mariadb + - mysql + - mysql2 + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + + '@mikro-orm/core@6.3.13': + dependencies: + dataloader: 2.2.2 + dotenv: 16.4.5 + esprima: 4.0.1 + fs-extra: 11.2.0 + globby: 11.1.0 + mikro-orm: 6.3.13 + reflect-metadata: 0.2.2 + + '@mikro-orm/knex@6.3.13(@mikro-orm/core@6.3.13)(pg@8.13.0)': + dependencies: + '@mikro-orm/core': 6.3.13 + fs-extra: 11.2.0 + knex: 3.1.0(pg@8.13.0) + sqlstring: 2.3.3 + transitivePeerDependencies: + - mysql + - mysql2 + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + + '@mikro-orm/postgresql@6.3.13(@mikro-orm/core@6.3.13)': + dependencies: + '@mikro-orm/core': 6.3.13 + '@mikro-orm/knex': 6.3.13(@mikro-orm/core@6.3.13)(pg@8.13.0) + pg: 8.13.0 + postgres-array: 3.0.2 + postgres-date: 2.1.0 + postgres-interval: 4.0.2 + transitivePeerDependencies: + - better-sqlite3 + - libsql + - mariadb + - mysql + - mysql2 + - pg-native + - sqlite3 + - supports-color + - tedious + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2071,12 +2406,22 @@ snapshots: ci-info@3.9.0: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + colorette@2.0.19: {} + + commander@10.0.1: {} + compressible@2.0.18: dependencies: mime-db: 1.53.0 @@ -2132,6 +2477,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + dataloader@2.2.2: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -2140,6 +2487,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.4: + dependencies: + ms: 2.1.2 + debug@4.3.7(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -2180,6 +2531,8 @@ snapshots: dependencies: shimmer: 1.2.1 + emoji-regex@8.0.0: {} + encodeurl@1.0.2: {} encodeurl@2.0.0: {} @@ -2195,6 +2548,8 @@ snapshots: es-errors@1.3.0: {} + escalade@3.2.0: {} + escape-html@1.0.3: {} escape-string-regexp@4.0.0: {} @@ -2254,6 +2609,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm@3.2.25: {} + espree@9.6.1: dependencies: acorn: 8.13.0 @@ -2342,6 +2699,8 @@ snapshots: dependencies: reusify: 1.0.4 + figlet@1.8.0: {} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -2409,6 +2768,8 @@ snapshots: function-bind@1.1.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -2417,6 +2778,10 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-package-type@0.1.0: {} + + getopts@2.3.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -2505,6 +2870,8 @@ snapshots: inherits@2.0.4: {} + interpret@2.2.0: {} + ip-regex@4.3.0: {} ipaddr.js@1.9.1: {} @@ -2519,6 +2886,8 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -2565,6 +2934,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -2583,6 +2954,27 @@ snapshots: kleur@4.1.5: {} + knex@3.1.0(pg@8.13.0): + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4 + escalade: 3.2.0 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.17.21 + pg-connection-string: 2.6.2 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + optionalDependencies: + pg: 8.13.0 + transitivePeerDependencies: + - supports-color + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -2628,6 +3020,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mikro-orm@6.3.13: {} + mime-db@1.52.0: {} mime-db@1.53.0: {} @@ -2646,6 +3040,8 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimist@1.2.8: {} + module-details-from-path@1.0.3: {} mri@1.2.0: {} @@ -2742,6 +3138,8 @@ snapshots: dependencies: callsites: 3.1.0 + parent-require@1.0.0: {} + parseurl@1.3.3: {} path-exists@4.0.0: {} @@ -2756,12 +3154,65 @@ snapshots: path-type@4.0.0: {} + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.6.2: {} + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.7.0(pg@8.13.0): + dependencies: + pg: 8.13.0 + + pg-protocol@1.7.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.13.0: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.0) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.1: {} pify@4.0.1: {} + postgres-array@2.0.0: {} + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres-interval@4.0.2: {} + prelude-ls@1.2.1: {} prettier@2.8.8: {} @@ -2819,10 +3270,18 @@ snapshots: dependencies: picomatch: 2.3.1 + rechoir@0.8.0: + dependencies: + resolve: 1.22.8 + + reflect-metadata@0.2.2: {} + regenerator-runtime@0.14.1: {} request-ip@3.3.0: {} + require-directory@2.1.1: {} + require-in-the-middle@5.2.0: dependencies: debug: 4.3.7(supports-color@5.5.0) @@ -2937,13 +3396,23 @@ snapshots: cross-spawn: 5.1.0 signal-exit: 3.0.7 + split2@4.2.0: {} + sprintf-js@1.0.3: {} sqlstring@2.3.1: optional: true + sqlstring@2.3.3: {} + statuses@2.0.1: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -2976,6 +3445,8 @@ snapshots: optionalDependencies: mysql: 2.18.1 + tarn@3.0.2: {} + tcp-port-used@1.0.2: dependencies: debug: 4.3.1 @@ -2987,6 +3458,8 @@ snapshots: text-table@0.2.0: {} + tildify@2.0.0: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -3017,6 +3490,12 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@1.14.1: {} tslib@1.9.3: {} @@ -3074,14 +3553,36 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrappy@1.0.2: {} xmlcreate@2.0.4: {} + xtend@4.0.2: {} + + y18n@5.0.8: {} + yallist@2.1.2: {} yallist@4.0.0: {} + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yn@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/src/lib/env.ts b/src/lib/env.ts new file mode 100644 index 00000000..8c93b80d --- /dev/null +++ b/src/lib/env.ts @@ -0,0 +1,58 @@ +import type { Stringifiable } from "@/types/index.js"; +import "dotenv/config"; + +/** + * Grabs an environment variable's value, and casts it to a `string` (or what's passed in the TRetVal generic). + * However if the string is empty (or unset), undefined is returned. + */ +export function getEnvVar(varName: string, asType?: "stringNoEmpty"): undefined | TRetVal +/** Grabs an environment variable's value, and casts it to a `string` (or what's passed in the TRetVal generic) */ +export function getEnvVar(varName: string, asType?: "string"): undefined | TRetVal +/** Grabs an environment variable's value, and casts it to a `number` (or what's passed in the TRetVal generic) */ +export function getEnvVar(varName: string, asType: "number"): undefined | TRetVal +/** Grabs an environment variable's value, and casts it to a `string[]` (or what's passed in the TRetVal generic) */ +export function getEnvVar(varName: string, asType: "stringArray"): undefined | TRetVal +/** Grabs an environment variable's value, and casts it to a `number[]` (or what's passed in the TRetVal generic) */ +export function getEnvVar(varName: string, asType: "numberArray"): undefined | TRetVal +/** Grabs an environment variable's value, and casts it to a specific type (stringNoEmpty by default) */ +export function getEnvVar< + T extends ("string" | "number" | "stringArray" | "numberArray" | "stringNoEmpty") +>( + varName: string, + asType: T = "stringNoEmpty" as T, +): undefined | (string | number | string[] | number[]) { + const val = process.env[varName]; + + if(!val) + return undefined; + + let transform: (value: string) => unknown = v => v.trim(); + + const commasRegex = /[,،,٫٬]/g; + + switch(asType) { + case "number": + transform = v => parseInt(v.trim()); + break; + case "stringArray": + transform = v => v.trim().split(commasRegex); + break; + case "numberArray": + transform = v => v.split(commasRegex).map(n => parseInt(n.trim())); + break; + case "stringNoEmpty": + transform = v => String(v).trim().length == 0 ? undefined : String(v).trim(); + } + + return transform(val) as string; // I'm lazy and ts is happy, so we can all be happy and pretend this doesn't exist +} + +/** + * Tests if the value of the environment variable {@linkcode varName} equals {@linkcode compareValue} casted to string. + * Set {@linkcode caseSensitive} to true to make the comparison case-sensitive. + */ +export function envVarEquals(varName: string, compareValue: Stringifiable, caseSensitive = false) { + const envVal = (caseSensitive ? getEnvVar(varName) : getEnvVar(varName)?.toLowerCase()); + const compVal = (caseSensitive ? String(compareValue) : String(compareValue).toLowerCase()); + return envVal === compVal; +} diff --git a/src/mikro-orm.config.ts b/src/mikro-orm.config.ts new file mode 100644 index 00000000..c377eafd --- /dev/null +++ b/src/mikro-orm.config.ts @@ -0,0 +1,32 @@ +import { defineConfig } from "@mikro-orm/core"; +import { MikroORM } from "@mikro-orm/postgresql"; +import { envVarEquals, getEnvVar } from "@lib/env.js"; + +const config = defineConfig({ + clientUrl: getEnvVar("DATABASE_URL", "stringNoEmpty"), + charset: "utf8", + entities: ["dist/**/*.model.js"], + entitiesTs: ["src/**/*.model.ts"], + debug: envVarEquals("DATABASE_DEBUG", true), +}); + +/** MikroORM instance */ +export let orm: Awaited>; +/** EntityManager instance */ +export let em: typeof orm.em; + +/** Load MikroORM instances */ +export async function initDatabase() { + orm = await MikroORM.init(config); + em = orm.em.fork(); + + // run migrations + try { + await orm.getSchemaGenerator().updateSchema(); + } + catch(e) { + console.error("Error running migrations:", e); + setImmediate(() => process.exit(1)); + } +} + diff --git a/src/models/Billing.model.ts b/src/models/Billing.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/Connection.model.ts b/src/models/Connection.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/Invoice.model.ts b/src/models/Invoice.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/Joke.model.ts b/src/models/Joke.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/Report.model.ts b/src/models/Report.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/ReportNote.model.ts b/src/models/ReportNote.model.ts new file mode 100644 index 00000000..62208be8 --- /dev/null +++ b/src/models/ReportNote.model.ts @@ -0,0 +1,40 @@ +import { em } from "@/mikro-orm.config.js"; +import { Entity, ManyToOne, PrimaryKey, Property } from "@mikro-orm/core"; +import { User } from "@models/User.model.js"; + +export type ReportNoteProps = { + text: string; + authorId: string; +}; + +@Entity() +export class ReportNote { + constructor(props: ReportNoteProps) { + this.text = props.text; + this.authorId = props.authorId; + this.createdAt = this.updatedAt = new Date(); + } + + @PrimaryKey({ type: "string", generated: "uuid" }) + id!: string; + + @Property({ type: "string", length: 255 }) + text: string; + + @Property({ type: "string" }) + authorId: string; + + @ManyToOne({ entity: () => User }) + get author(): User { + return em.getReference(User, this.authorId); + } + + @Property({ type: "date" }) + createdAt: Date; + + @Property({ type: "date", onUpdate: () => new Date() }) + updatedAt: Date; + + @Property({ type: "date", nullable: true }) + deletedAt?: Date; +} diff --git a/src/models/Submission.model.ts b/src/models/Submission.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/User.model.ts b/src/models/User.model.ts new file mode 100644 index 00000000..456045de --- /dev/null +++ b/src/models/User.model.ts @@ -0,0 +1,53 @@ +import { Entity, PrimaryKey, Property } from "@mikro-orm/core"; +import type { AccountTier, UserRole } from "@/types/user.js"; + +export type UserProps = { + username: string; + email: string; + tier: AccountTier; + roles: UserRole[]; +}; + +@Entity() +export class User { + constructor(props: UserProps) { + this.username = props.username; + this.email = props.email; + this.createdAt = this.updatedAt = new Date(); + } + + @PrimaryKey({ type: "string", generated: "uuid" }) + id!: string; + + @PrimaryKey({ type: "string", length: 24 }) + username: string; + + @Property({ type: "string", length: 320 }) + email: string; + + @Property({ type: "string", length: 16 }) + tier: AccountTier = "free"; + + @Property({ type: "array", length: 16, runtimeType: "string" }) + roles = ["user"]; + + // TODO: + + // @OneToOne({ entity: () => UserSettings, orphanRemoval: true }) + // settings: UserSettings; + + // @OneToOne({ entity: () => UserBilling, orphanRemoval: true }) + // billing: Billing; + + // @OneToMany({ entity: () => ReportNote, mappedBy: "author" }) + // connections: Connection[]; + + @Property({ type: "date" }) + createdAt: Date; + + @Property({ type: "date", onUpdate: () => new Date() }) + updatedAt: Date; + + @Property({ type: "date", nullable: true }) + deletedAt?: Date; +} diff --git a/src/models/UserSettings.model.ts b/src/models/UserSettings.model.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 00000000..6878b572 --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,9 @@ +export * from "./Joke.model.js"; +export * from "./Submission.model.js"; +export * from "./User.model.js"; +export * from "./UserSettings.model.js"; +export * from "./Billing.model.js"; +export * from "./Invoice.model.js"; +export * from "./Connection.model.js"; +export * from "./Report.model.js"; +export * from "./ReportNote.model.js"; diff --git a/src/server/index.ts b/src/server/index.ts index 13a181e7..7a7d8d9e 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -10,7 +10,7 @@ import { createHash } from "node:crypto"; import type { JSONCompatible } from "svcorelib"; import { settings } from "../settings.js"; -import { initFuncs as routeInitFuncs } from "../routes/index.js"; +import { initFuncs as routeInitFuncs } from "./routes/index.js"; import { error } from "../error.js"; import { isValidToken } from "../auth.js"; import { genericRateLimit } from "../rateLimiters.js"; diff --git a/src/routes/index.ts b/src/server/routes/index.ts similarity index 100% rename from src/routes/index.ts rename to src/server/routes/index.ts diff --git a/src/routes/joke.ts b/src/server/routes/joke.ts similarity index 100% rename from src/routes/joke.ts rename to src/server/routes/joke.ts diff --git a/src/routes/submission.ts b/src/server/routes/submission.ts similarity index 100% rename from src/routes/submission.ts rename to src/server/routes/submission.ts diff --git a/src/routes/user.ts b/src/server/routes/user.ts similarity index 100% rename from src/routes/user.ts rename to src/server/routes/user.ts diff --git a/src/types/index.ts b/src/types/index.ts index 6da5b7c0..a6fbbe8f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,3 @@ -export * from "./misc"; - -export * from "./jokeapi/index"; +export * from "./jokeapi/index.js"; +export * from "./misc.js"; +export * from "./user.js"; diff --git a/src/types/jokeapi b/src/types/jokeapi index 43c6c6b5..5b9e09a2 160000 --- a/src/types/jokeapi +++ b/src/types/jokeapi @@ -1 +1 @@ -Subproject commit 43c6c6b5a3834787560fbceafbb22be22bc44264 +Subproject commit 5b9e09a2a3d4c9fd2b0c06809093aae740c42b9a diff --git a/src/types/misc.ts b/src/types/misc.ts index 7dc87107..70f3651d 100644 --- a/src/types/misc.ts +++ b/src/types/misc.ts @@ -1,3 +1,5 @@ +export type Stringifiable = string | number | boolean | null | undefined | { toString(): string }; + export type ResponseFormat = "json" | "xml" | "text"; export enum LogLevel { diff --git a/src/types/user.ts b/src/types/user.ts new file mode 100644 index 00000000..ef6702a5 --- /dev/null +++ b/src/types/user.ts @@ -0,0 +1,3 @@ +export type UserRole = "user" | "moderator" | "admin"; + +export type AccountTier = "free" | "premium" | "enterprise"; diff --git a/tsconfig.json b/tsconfig.json index 61266324..f1a25457 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,8 @@ "baseUrl": ".", "target": "ESNext", "lib": ["ESNext"], - "module": "ESNext", - "moduleResolution": "node", + "module": "NodeNext", + "moduleResolution": "NodeNext", "rootDir": "./src/", "outDir": "./out/", "strict": true, @@ -20,19 +20,18 @@ "useDefineForClassFields": true, "noImplicitThis": false, "paths": { - "src/*": ["src/*"], - "@src/*": ["src/*"], - "server/*": ["src/server/*"], + "@db/*": ["src/db/*"], + "@lib/*": ["src/lib/*"], + "@models/*": ["src/models/*"], + "@routes/*": ["src/server/routes/*"], "@server/*": ["src/server/*"], - "db/*": ["src/database/*"], - "@db/*": ["src/database/*"], - "types/*": ["src/types/*"], - "@types/*": ["src/types/*"], + "@/*": ["src/*"], }, }, "ts-node": { "esm": true, - "preferTsExts": true + "preferTsExts": true, + "transpileOnly": true }, "files": ["src/index.ts"], "include": ["src/**/*.ts", "src/types/**.*"],