From a3f61b35c1a00bc89184656b7e84be52e432759e Mon Sep 17 00:00:00 2001 From: Thanu Poptiphueng Date: Fri, 19 Apr 2024 13:01:58 +0700 Subject: [PATCH] added playground and userroute --- .gitignore | 1 + ...iring_paibok.sql => 0000_hot_gressill.sql} | 21 +- drizzle/meta/0000_snapshot.json | 89 ++- drizzle/meta/_journal.json | 4 +- package.json | 11 +- pnpm-lock.yaml | 721 +++++++++++++++++- src/app.ts | 31 +- src/config.ts | 6 + src/db.ts | 8 + src/playgroud.ts | 23 + src/schema.ts | 46 +- src/trpc.ts | 59 ++ src/userRoute.ts | 170 +++++ 13 files changed, 1110 insertions(+), 80 deletions(-) rename drizzle/{0000_aspiring_paibok.sql => 0000_hot_gressill.sql} (67%) create mode 100644 src/config.ts create mode 100644 src/db.ts create mode 100644 src/playgroud.ts create mode 100644 src/trpc.ts create mode 100644 src/userRoute.ts diff --git a/.gitignore b/.gitignore index 8e0b6d7..f7c0b6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules sqlite.db +.DS_Store diff --git a/drizzle/0000_aspiring_paibok.sql b/drizzle/0000_hot_gressill.sql similarity index 67% rename from drizzle/0000_aspiring_paibok.sql rename to drizzle/0000_hot_gressill.sql index d10901e..a8b9460 100644 --- a/drizzle/0000_aspiring_paibok.sql +++ b/drizzle/0000_hot_gressill.sql @@ -9,6 +9,12 @@ CREATE TABLE `opinions` ( `type` text ); --> statement-breakpoint +CREATE TABLE `phone_tokens` ( + `phone` text PRIMARY KEY NOT NULL, + `token` text NOT NULL, + `created_on` integer DEFAULT CURRENT_TIMESTAMP +); +--> statement-breakpoint CREATE TABLE `provinces` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` text NOT NULL @@ -16,21 +22,26 @@ CREATE TABLE `provinces` ( --> statement-breakpoint CREATE TABLE `users` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text, + `firstName` text NOT NULL, + `lastName` text NOT NULL, + `title` text NOT NULL, `phone` text NOT NULL, `email` text, - `job` text, - `education` text, + `job` text NOT NULL, + `education` text NOT NULL, `vision` text, `reason` text, - `group_id` integer, - FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE no action + `group_id` integer NOT NULL, + `zone_id` integer NOT NULL, + FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`zone_id`) REFERENCES `zones`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint CREATE TABLE `user_opinions` ( `user_id` integer, `opinion_id` integer, `choice` text DEFAULT 'ignore', + PRIMARY KEY(`opinion_id`, `user_id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, FOREIGN KEY (`opinion_id`) REFERENCES `opinions`(`id`) ON UPDATE no action ON DELETE no action ); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index e7faa1a..9293ca3 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,7 +1,7 @@ { "version": "5", "dialect": "sqlite", - "id": "71187b23-6b9a-4f78-9f3c-0af6fb035f1b", + "id": "86633c77-c16c-46e0-92a5-f0e696d3276c", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { "groups": { @@ -57,6 +57,37 @@ "compositePrimaryKeys": {}, "uniqueConstraints": {} }, + "phone_tokens": { + "name": "phone_tokens", + "columns": { + "phone": { + "name": "phone", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_on": { + "name": "created_on", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, "provinces": { "name": "provinces", "columns": { @@ -90,11 +121,25 @@ "notNull": true, "autoincrement": true }, - "name": { - "name": "name", + "firstName": { + "name": "firstName", "type": "text", "primaryKey": false, - "notNull": false, + "notNull": true, + "autoincrement": false + }, + "lastName": { + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, "autoincrement": false }, "phone": { @@ -115,14 +160,14 @@ "name": "job", "type": "text", "primaryKey": false, - "notNull": false, + "notNull": true, "autoincrement": false }, "education": { "name": "education", "type": "text", "primaryKey": false, - "notNull": false, + "notNull": true, "autoincrement": false }, "vision": { @@ -143,7 +188,14 @@ "name": "group_id", "type": "integer", "primaryKey": false, - "notNull": false, + "notNull": true, + "autoincrement": false + }, + "zone_id": { + "name": "zone_id", + "type": "integer", + "primaryKey": false, + "notNull": true, "autoincrement": false } }, @@ -169,6 +221,19 @@ ], "onDelete": "no action", "onUpdate": "no action" + }, + "users_zone_id_zones_id_fk": { + "name": "users_zone_id_zones_id_fk", + "tableFrom": "users", + "tableTo": "zones", + "columnsFrom": [ + "zone_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -229,7 +294,15 @@ "onUpdate": "no action" } }, - "compositePrimaryKeys": {}, + "compositePrimaryKeys": { + "user_opinions_user_id_opinion_id_pk": { + "columns": [ + "opinion_id", + "user_id" + ], + "name": "user_opinions_user_id_opinion_id_pk" + } + }, "uniqueConstraints": {} }, "zones": { diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index ee0b7cf..dc83c81 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "5", - "when": 1713465576653, - "tag": "0000_aspiring_paibok", + "when": 1713506374595, + "tag": "0000_hot_gressill", "breakpoints": true } ] diff --git a/package.json b/package.json index 285a6fc..e702fe3 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,15 @@ "author": "", "license": "ISC", "dependencies": { - "@trpc/client": "11.0.0-rc.340", - "@trpc/server": "11.0.0-rc.340", + "@trpc/client": "^10.45.2", + "@trpc/server": "^10.45.2", "better-sqlite3": "^9.5.0", - "drizzle-orm": "^0.30.8" + "cors": "^2.8.5", + "drizzle-orm": "^0.30.8", + "drizzle-zod": "^0.5.1", + "express": "^4.19.2", + "trpc-playground": "^1.0.4", + "zod": "^3.22.4" }, "devDependencies": { "@swc/core": "^1.4.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcac090..2bbd9fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,17 +6,32 @@ settings: dependencies: '@trpc/client': - specifier: 11.0.0-rc.340 - version: 11.0.0-rc.340(@trpc/server@11.0.0-rc.340) + specifier: ^10.45.2 + version: 10.45.2(@trpc/server@10.45.2) '@trpc/server': - specifier: 11.0.0-rc.340 - version: 11.0.0-rc.340 + specifier: ^10.45.2 + version: 10.45.2 better-sqlite3: specifier: ^9.5.0 version: 9.5.0 + cors: + specifier: ^2.8.5 + version: 2.8.5 drizzle-orm: specifier: ^0.30.8 version: 0.30.8(@types/better-sqlite3@7.6.9)(better-sqlite3@9.5.0) + drizzle-zod: + specifier: ^0.5.1 + version: 0.5.1(drizzle-orm@0.30.8)(zod@3.22.4) + express: + specifier: ^4.19.2 + version: 4.19.2 + trpc-playground: + specifier: ^1.0.4 + version: 1.0.4(@trpc/server@10.45.2)(@types/node@20.12.7)(express@4.19.2)(typescript@5.4.5)(zod@3.22.4) + zod: + specifier: ^3.22.4 + version: 3.22.4 devDependencies: '@swc/core': @@ -82,7 +97,6 @@ packages: cpu: [arm64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-arm64@0.19.12: @@ -100,7 +114,6 @@ packages: cpu: [arm] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-arm@0.19.12: @@ -118,7 +131,6 @@ packages: cpu: [x64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-x64@0.19.12: @@ -136,7 +148,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/darwin-arm64@0.19.12: @@ -154,7 +165,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/darwin-x64@0.19.12: @@ -172,7 +182,6 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-arm64@0.19.12: @@ -190,7 +199,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-x64@0.19.12: @@ -208,7 +216,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm64@0.19.12: @@ -226,7 +233,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm@0.19.12: @@ -244,7 +250,6 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ia32@0.19.12: @@ -262,7 +267,6 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-loong64@0.19.12: @@ -280,7 +284,6 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-mips64el@0.19.12: @@ -298,7 +301,6 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ppc64@0.19.12: @@ -316,7 +318,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-riscv64@0.19.12: @@ -334,7 +335,6 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-s390x@0.19.12: @@ -352,7 +352,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-x64@0.19.12: @@ -370,7 +369,6 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true - dev: true optional: true /@esbuild/netbsd-x64@0.19.12: @@ -388,7 +386,6 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true - dev: true optional: true /@esbuild/openbsd-x64@0.19.12: @@ -406,7 +403,6 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true - dev: true optional: true /@esbuild/sunos-x64@0.19.12: @@ -424,7 +420,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-arm64@0.19.12: @@ -442,7 +437,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-ia32@0.19.12: @@ -460,7 +454,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-x64@0.19.12: @@ -613,16 +606,42 @@ packages: '@swc/counter': 0.1.3 dev: true - /@trpc/client@11.0.0-rc.340(@trpc/server@11.0.0-rc.340): - resolution: {integrity: sha512-q2ecZW58LM9pKpzRt+um9Jh33Nmjt7qDZu3xsjMFD61QLE6M/Tply7lC02iqZzrXzTGxR0w2+dLdkkrSlITOKQ==} - peerDependencies: - '@trpc/server': 11.0.0-rc.340+d1652d1bc + /@trpc-playground/html@1.0.4(@types/node@20.12.7): + resolution: {integrity: sha512-HVnqaUXxEJJcrAHvVgivwtZFqTE6NNxXxrXEnd+rCxun97xnZknwdiTxq69wQEsZYxPrgtwPmO/nAEZL8AReWg==} dependencies: - '@trpc/server': 11.0.0-rc.340 + vite: 4.5.3(@types/node@20.12.7) + xss: 1.0.15 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - terser dev: false - /@trpc/server@11.0.0-rc.340: - resolution: {integrity: sha512-fKzFeAzFbsezC8Jx6gIy4AjlzNvQy4aFc2LT0qIq7hNWYCkuv3gQ7ROgcIF4dUI0rfPC3Gt/y8PQjE+GayAhvA==} + /@trpc-playground/types@1.0.0(@trpc/server@10.45.2)(typescript@5.4.5): + resolution: {integrity: sha512-Ap5aNaVlglfM9WLkPLKF2rHDZ8CwHcqK32A8YaqTyHUuoEFTSOh6oSgVU6Cv8toxClKiTXFvbWmE4aZxIoh4Zg==} + peerDependencies: + '@trpc/server': ^10 + dependencies: + '@trpc/server': 10.45.2 + ts-essentials: 9.4.2(typescript@5.4.5) + transitivePeerDependencies: + - typescript + dev: false + + /@trpc/client@10.45.2(@trpc/server@10.45.2): + resolution: {integrity: sha512-ykALM5kYWTLn1zYuUOZ2cPWlVfrXhc18HzBDyRhoPYN0jey4iQHEFSEowfnhg1RvYnrAVjNBgHNeSAXjrDbGwg==} + peerDependencies: + '@trpc/server': 10.45.2 + dependencies: + '@trpc/server': 10.45.2 + dev: false + + /@trpc/server@10.45.2: + resolution: {integrity: sha512-wOrSThNNE4HUnuhJG6PfDRp4L2009KDVxsd+2VYH8ro6o/7/jwYZ8Uu5j+VaW+mOmc8EHerHzGcdbGNQSAUPgg==} dev: false /@tsconfig/node10@1.0.11: @@ -655,6 +674,14 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: true + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -678,6 +705,10 @@ packages: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -713,6 +744,26 @@ packages: readable-stream: 3.6.2 dev: false + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -744,6 +795,22 @@ packages: ieee754: 1.2.1 dev: false + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + dev: false + /camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} @@ -784,6 +851,10 @@ packages: timers-ext: 0.1.7 dev: true + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: false + /commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -793,6 +864,27 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: false + /copy-anything@3.0.5: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} @@ -800,10 +892,22 @@ packages: is-what: 4.1.16 dev: true + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /cssfilter@0.0.10: + resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} + dev: false + /d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -812,6 +916,17 @@ packages: type: 2.7.2 dev: true + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -837,6 +952,25 @@ packages: engines: {node: '>=4.0.0'} dev: false + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: false + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} @@ -966,6 +1100,25 @@ packages: better-sqlite3: 9.5.0 dev: false + /drizzle-zod@0.5.1(drizzle-orm@0.30.8)(zod@3.22.4): + resolution: {integrity: sha512-C/8bvzUH/zSnVfwdSibOgFjLhtDtbKYmkbPbUCq46QZyZCH6kODIMSOgZ8R7rVjoI+tCj3k06MRJMDqsIeoS4A==} + peerDependencies: + drizzle-orm: '>=0.23.13' + zod: '*' + dependencies: + drizzle-orm: 0.30.8(@types/better-sqlite3@7.6.9)(better-sqlite3@9.5.0) + zod: 3.22.4 + dev: false + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: @@ -977,6 +1130,18 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: false + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: false + /es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} engines: {node: '>=0.10'} @@ -1052,7 +1217,6 @@ packages: '@esbuild/win32-arm64': 0.18.20 '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 - dev: true /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} @@ -1085,6 +1249,10 @@ packages: '@esbuild/win32-x64': 0.19.12 dev: true + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /esniff@2.0.1: resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} engines: {node: '>=0.10'} @@ -1095,6 +1263,11 @@ packages: type: 2.7.2 dev: true + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + /event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} dependencies: @@ -1107,6 +1280,45 @@ packages: engines: {node: '>=6'} dev: false + /express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /ext@1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} dependencies: @@ -1124,6 +1336,31 @@ packages: to-regex-range: 5.0.1 dev: true + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: false @@ -1137,9 +1374,23 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + dev: false + /get-tsconfig@4.7.3: resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} dependencies: @@ -1168,6 +1419,12 @@ packages: once: 1.4.0 dev: true + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: false + /hanji@0.0.5: resolution: {integrity: sha512-Abxw1Lq+TnYiL4BueXqMau222fPSPMFtya8HdpWsz/xVAhifXou71mPh/kY2+08RgFcVccjG3uZHs6K5HAe3zw==} dependencies: @@ -1180,10 +1437,51 @@ packages: engines: {node: '>=4'} dev: true + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: false + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + /heap@0.2.7: resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} dev: true + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: false @@ -1206,6 +1504,11 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: false + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1252,6 +1555,10 @@ packages: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1268,6 +1575,11 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + /memoizee@0.4.15: resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} dependencies: @@ -1281,6 +1593,33 @@ packages: timers-ext: 0.1.7 dev: true + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + /mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -1314,14 +1653,33 @@ packages: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: false + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: false + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -1362,16 +1720,54 @@ packages: engines: {node: '>=0.10.0'} dev: true + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: false + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: false + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: false + /prebuild-install@7.1.2: resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} @@ -1391,6 +1787,14 @@ packages: tunnel-agent: 0.6.0 dev: false + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + /pstree.remy@1.1.8: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} dev: true @@ -1402,6 +1806,28 @@ packages: once: 1.4.0 dev: false + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.6 + dev: false + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -1432,10 +1858,22 @@ packages: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: false + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + /semver@7.6.0: resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} @@ -1443,6 +1881,65 @@ packages: dependencies: lru-cache: 6.0.0 + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: false + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + dev: false + /simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} dev: false @@ -1466,6 +1963,11 @@ packages: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: false + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -1478,6 +1980,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: @@ -1537,6 +2044,11 @@ packages: is-number: 7.0.0 dev: true + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + /touch@3.1.0: resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} hasBin: true @@ -1544,6 +2056,45 @@ packages: nopt: 1.0.10 dev: true + /trpc-playground@1.0.4(@trpc/server@10.45.2)(@types/node@20.12.7)(express@4.19.2)(typescript@5.4.5)(zod@3.22.4): + resolution: {integrity: sha512-6nVf1o2LNoo1g6oTS24yajoH7xV4CFjxGAbAH7qLXqa3yo9qB5QHsWLbrm1hNt51SyCG++P0zHJaCdVfgvEVDg==} + peerDependencies: + '@trpc/server': ^10 + zod: ^3 + dependencies: + '@trpc-playground/html': 1.0.4(@types/node@20.12.7) + '@trpc-playground/types': 1.0.0(@trpc/server@10.45.2)(typescript@5.4.5) + '@trpc/server': 10.45.2 + lodash: 4.17.21 + uttp: 0.1.3(express@4.19.2) + zod: 3.22.4 + zod-to-ts: 1.2.0(typescript@5.4.5)(zod@3.22.4) + transitivePeerDependencies: + - '@types/node' + - express + - fastify + - h3 + - koa + - less + - lightningcss + - sass + - stylus + - sugarss + - terser + - typescript + dev: false + + /ts-essentials@9.4.2(typescript@5.4.5): + resolution: {integrity: sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==} + peerDependencies: + typescript: '>=4.1.0' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.4.5 + dev: false + /ts-node@10.9.2(@swc/core@1.4.16)(@types/node@20.12.7)(typescript@5.4.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -1582,6 +2133,14 @@ packages: safe-buffer: 5.2.1 dev: false + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + /type@2.7.2: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true @@ -1590,7 +2149,6 @@ packages: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true - dev: true /undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -1599,14 +2157,85 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /uttp@0.1.3(express@4.19.2): + resolution: {integrity: sha512-LWRHPNmivLr/4rTt3CXVtQfWdhzoN7OQwBPHfcsYGffeFo9a8/CtDxtl3xcVR965paRjLr3Ocxs/shwyjPzE2Q==} + peerDependencies: + express: ^4 + fastify: ^3 || ^4 + h3: '*' + koa: ^2 + peerDependenciesMeta: + express: + optional: true + fastify: + optional: true + h3: + optional: true + koa: + optional: true + dependencies: + express: 4.19.2 + dev: false + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /vite@4.5.3(@types/node@20.12.7): + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.12.7 + esbuild: 0.18.20 + postcss: 8.4.38 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: false + /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true @@ -1614,6 +2243,15 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + /xss@1.0.15: + resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} + engines: {node: '>= 0.10.0'} + hasBin: true + dependencies: + commander: 2.20.3 + cssfilter: 0.0.10 + dev: false + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -1622,6 +2260,15 @@ packages: engines: {node: '>=6'} dev: true + /zod-to-ts@1.2.0(typescript@5.4.5)(zod@3.22.4): + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + dependencies: + typescript: 5.4.5 + zod: 3.22.4 + dev: false + /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: true diff --git a/src/app.ts b/src/app.ts index c936bb6..021506a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,21 +1,24 @@ -import { drizzle } from "drizzle-orm/better-sqlite3"; -import { migrate } from "drizzle-orm/better-sqlite3/migrator"; -import Database from "better-sqlite3"; -import { user } from "./schema.ts"; +import { createContext, router } from "./trpc"; +import { createHTTPServer } from "@trpc/server/adapters/standalone"; +import { db } from "./db"; +import { userRoute } from "./userRoute"; +import { runPlayground } from "./playgroud"; +import cors from "cors"; -const sqlite = new Database("sqlite.db"); -const db = drizzle(sqlite); -migrate(db, { migrationsFolder: "drizzle" }); +export const appRouter = router({ + user: userRoute, +}); +export type AppRouter = typeof appRouter; async function main() { - await db.insert(user).values({ - job: "Software Engineer", - phone: "1234567890", - group: 1, - name: "John Doe", - education: "Bachelor", + const server = createHTTPServer({ + createContext: createContext, + router: appRouter, + middleware: cors(), }); - console.log("User inserted"); + + server.listen(3000); + runPlayground(appRouter); } (async () => { diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..0adafea --- /dev/null +++ b/src/config.ts @@ -0,0 +1,6 @@ +export const Config = { + sms_api_key: "1796570121771765", + sms_api_secret: "0957b611d575febff1ae0fc51070c8b7", + sms_api_request_endpoint: "https://otp.thaibulksms.com/v2/otp/request", + sms_api_verify_endpoint: "https://otp.thaibulksms.com/v2/otp/verify", +}; diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..2c9bd85 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,8 @@ +import { drizzle } from "drizzle-orm/better-sqlite3"; +import { migrate } from "drizzle-orm/better-sqlite3/migrator"; +import Database from "better-sqlite3"; +import * as schema from "./schema.ts"; + +const sqlite = new Database("sqlite.db"); +export const db = drizzle(sqlite, { schema }); +migrate(db, { migrationsFolder: "drizzle" }); diff --git a/src/playgroud.ts b/src/playgroud.ts new file mode 100644 index 0000000..4a54524 --- /dev/null +++ b/src/playgroud.ts @@ -0,0 +1,23 @@ +import express from "express"; +import { expressHandler } from "trpc-playground/handlers/express"; +import { AppRouter } from "./app"; + +export const runPlayground = async (appRouter: AppRouter) => { + const app = express(); + + const trpcApiEndpoint = "http://localhost:3000"; + const playgroundEndpoint = "/"; + + app.use( + playgroundEndpoint, + await expressHandler({ + trpcApiEndpoint, + playgroundEndpoint, + router: appRouter, + }) + ); + + app.listen(3001, () => { + console.log("listening at http://localhost:3001"); + }); +}; diff --git a/src/schema.ts b/src/schema.ts index 9a9dd9f..33b0bec 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -2,14 +2,16 @@ import { sqliteTable, text, integer, - uniqueIndex, + primaryKey, } from "drizzle-orm/sqlite-core"; -import { relations } from "drizzle-orm"; +import { relations, sql } from "drizzle-orm"; //----------------User export const user = sqliteTable("users", { id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), - name: text("name").notNull(), + firstName: text("firstName").notNull(), + lastName: text("lastName").notNull(), + title: text("title").notNull(), phone: text("phone").unique().notNull(), email: text("email"), job: text("job").notNull(), @@ -19,6 +21,9 @@ export const user = sqliteTable("users", { group: integer("group_id") .references(() => group.id) .notNull(), + zone: integer("zone_id") + .notNull() + .references(() => zone.id), }); export const userRelation = relations(user, ({ many, one }) => ({ @@ -27,6 +32,10 @@ export const userRelation = relations(user, ({ many, one }) => ({ fields: [user.group], references: [group.id], }), + zone: one(zone, { + fields: [user.zone], + references: [zone.id], + }), })); //----------------Group @@ -47,13 +56,19 @@ export const opinion = sqliteTable("opinions", { }); //----------------UserOpinion -export const userOpinion = sqliteTable("user_opinions", { - userId: integer("user_id").references(() => user.id), - opinionId: integer("opinion_id").references(() => opinion.id), - choice: text("choice", { - enum: ["agree", "disagree", "deciding", "ignore"], - }).default("ignore"), -}); +export const userOpinion = sqliteTable( + "user_opinions", + { + userId: integer("user_id").references(() => user.id), + opinionId: integer("opinion_id").references(() => opinion.id), + choice: text("choice", { + enum: ["agree", "disagree", "deciding", "ignore"], + }).default("ignore"), + }, + (t) => ({ + pk: primaryKey({ columns: [t.userId, t.opinionId] }), + }) +); export const userOpinionRelation = relations(userOpinion, ({ one }) => ({ user: one(user, { @@ -84,5 +99,14 @@ export const province = sqliteTable("provinces", { }); export const provinceRelation = relations(province, ({ many }) => ({ - province: many(zone), + zones: many(zone), })); + +//----------------PhoneToken +export const phoneToken = sqliteTable("phone_tokens", { + phone: text("phone").primaryKey(), + token: text("token").notNull(), + createdOn: integer("created_on", { mode: "timestamp" }).default( + sql`CURRENT_TIMESTAMP` + ), +}); diff --git a/src/trpc.ts b/src/trpc.ts new file mode 100644 index 0000000..d0c3cda --- /dev/null +++ b/src/trpc.ts @@ -0,0 +1,59 @@ +import { initTRPC } from "@trpc/server"; +import type { CreateHTTPContextOptions } from "@trpc/server/adapters/standalone"; +import { db } from "./db"; +const t = initTRPC.context().create(); + +export const router = t.router; +export const publicProcedure = t.procedure; +export const protectedProcedure = t.procedure.use(async (opts) => { + const { ctx } = opts; + if (ctx.user === undefined) { + throw new Error("Unauthorized"); + } else { + return opts.next({ + ctx: { + ...ctx, + user: ctx.user, + }, + }); + } +}); +export const verifiedPhone = t.procedure.use(async (opts) => { + const { ctx } = opts; + if (ctx.phone === undefined) { + throw new Error("Unauthorized"); + } else { + return opts.next({ + ctx: { + phone: ctx.phone, + }, + }); + } +}); + +type Context = Awaited>; + +export const createContext = async (opts: CreateHTTPContextOptions) => { + const authorizationHeader = opts.req.headers.authorization || ""; + const bearerToken = authorizationHeader.split(" ")[1]; + const phone = verifyToken(bearerToken); + if (phone !== null) { + let user = await db.query.user.findFirst({ + where: (user, { eq }) => eq(user.phone, phone), + }); + return { + phone, + user: user, + }; + } else { + return { + phone: undefined, + user: undefined, + }; + } +}; + +function verifyToken(token: string): string | null { + //TODO: Implement token verification + return "08999"; +} diff --git a/src/userRoute.ts b/src/userRoute.ts new file mode 100644 index 0000000..fbd08e3 --- /dev/null +++ b/src/userRoute.ts @@ -0,0 +1,170 @@ +import { + router, + verifiedPhone, + publicProcedure, + protectedProcedure, +} from "./trpc"; +import { db } from "./db"; +import { phoneToken, user } from "./schema"; +import { createInsertSchema } from "drizzle-zod"; +import { z } from "zod"; +import { SQL, eq } from "drizzle-orm"; +import { Config } from "./config"; +import { TRPCError } from "@trpc/server"; + +const userInsertSchema = createInsertSchema(user); +type UserInsertSchema = z.infer; + +export const userRoute = router({ + getAllUser: publicProcedure + .input( + z.object({ + offset: z.number().default(0), + limit: z.number().max(50).default(10), + group: z.number().optional(), + zone: z.number().optional(), + }) + ) + .query( + async ({ input }) => + await getAllUser(input.offset, input.limit, input.group, input.zone) + ), + createUser: verifiedPhone + .input(userInsertSchema.omit({ id: true, phone: true })) + .mutation( + async ({ input, ctx }) => await createUser({ ...input, phone: ctx.phone }) + ), + requestOtp: publicProcedure + .input(z.object({ phone: z.string().trim().min(5) })) + .mutation(async ({ input }) => await requestOtp(input.phone)), + verifyOtp: publicProcedure + .input( + z.object({ + pin: z.string().trim(), + token: z.string(), + }) + ) + .mutation(async ({ input }) => await verifyOtp(input.token, input.pin)), +}); + +async function getAllUser( + offset: number, + limit: number, + group?: number, + zone?: number +) { + let users = await db.query.user.findMany({ + with: { + group: true, + opinions: true, + zone: { + with: { province: true }, + }, + }, + limit, + offset, + where: (user, { eq, and }) => { + let conditions: SQL[] = []; + if (group) { + conditions.push(eq(user.group, group)); + } + if (zone) { + conditions.push(eq(user.zone, zone)); + } + return and(...conditions); + }, + }); + return users; +} +async function createUser(newUser: UserInsertSchema) { + try { + let result = await db.insert(user).values(newUser); + return result.lastInsertRowid; + } catch (e) { + console.error(e); + throw new Error(`Unable to create new user:\n${e}`); + } +} + +async function requestOtp(phone: string) { + const _phone = phone.trim(); + try { + let rs = await fetch(Config.sms_api_request_endpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + key: Config.sms_api_key, + secret: Config.sms_api_secret, + msisdn: _phone, + }), + }).then((res) => res.json()); + + if (rs.errors) { + console.error(rs); + throw new TRPCError({ + message: `Unable to request OTP`, + code: "INTERNAL_SERVER_ERROR", + }); + } + await db.insert(phoneToken).values({ phone: _phone, token: rs.token }); + return { + status: rs.status as string, + token: rs.token as string, + refno: rs.refno as string, + }; + } catch (e) { + console.error(e); + throw new TRPCError({ + message: `Unable to request OTP:\n${e}`, + code: "INTERNAL_SERVER_ERROR", + }); + } +} + +async function verifyOtp(token: string, pin: string) { + try { + console.log(token, pin); + let pt = await db.query.phoneToken.findFirst({ + where: (pt, { eq }) => eq(pt.token, token), + orderBy: (pt, { desc }) => desc(pt.createdOn), + }); + if (pt === undefined) { + throw new TRPCError({ + message: `Invalid token`, + code: "BAD_REQUEST", + }); + } + pt; + let rs = await fetch(Config.sms_api_verify_endpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + key: Config.sms_api_key, + secret: Config.sms_api_secret, + token, + pin, + }), + }).then((res) => res.json()); + if (rs.errors) { + console.error(rs); + throw new TRPCError({ + message: `Unable to verify OTP`, + code: "BAD_REQUEST", + }); + } else { + await db.delete(phoneToken).where(eq(phoneToken.phone, pt.phone)); + console.log(rs, pt.phone); + return rs; + } + } catch (e) { + console.error(e); + throw new TRPCError({ + message: `Unable to verify OTP:\n${e}`, + code: "BAD_REQUEST", + }); + } +}