add features
Browse files- package-lock.json +278 -6
- package.json +5 -2
- src/app.ts +13 -0
- src/config/database.ts +2 -1
- src/controllers/adminController.ts +49 -0
- src/controllers/creatorAuthController.ts +142 -0
- src/controllers/creatorController.ts +86 -0
- src/controllers/userController.ts +70 -0
- src/controllers/walletController.ts +22 -12
- src/docs/swagger.ts +26 -0
- src/entities/CreatorProfile.ts +101 -0
- src/entities/User.ts +3 -0
- src/routes/adminRoutes.ts +10 -0
- src/routes/authRoutes.ts +30 -2
- src/routes/creatorAuthRoutes.ts +10 -0
- src/routes/creatorRoutes.ts +11 -0
- src/routes/userRoutes.ts +10 -0
package-lock.json
CHANGED
|
@@ -22,6 +22,8 @@
|
|
| 22 |
"pg": "*",
|
| 23 |
"pinata": "*",
|
| 24 |
"reflect-metadata": "*",
|
|
|
|
|
|
|
| 25 |
"typeorm": "*",
|
| 26 |
"uuid": "*"
|
| 27 |
},
|
|
@@ -34,6 +36,7 @@
|
|
| 34 |
"@types/multer": "*",
|
| 35 |
"@types/node": "*",
|
| 36 |
"@types/supertest": "*",
|
|
|
|
| 37 |
"@types/uuid": "*",
|
| 38 |
"jest": "*",
|
| 39 |
"supertest": "*",
|
|
@@ -42,6 +45,62 @@
|
|
| 42 |
"typescript": "*"
|
| 43 |
}
|
| 44 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
"node_modules/@babel/code-frame": {
|
| 46 |
"version": "7.27.1",
|
| 47 |
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
|
@@ -966,6 +1025,11 @@
|
|
| 966 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 967 |
}
|
| 968 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
"node_modules/@napi-rs/wasm-runtime": {
|
| 970 |
"version": "0.2.12",
|
| 971 |
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
|
@@ -1020,6 +1084,12 @@
|
|
| 1020 |
"url": "https://opencollective.com/pkgr"
|
| 1021 |
}
|
| 1022 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1023 |
"node_modules/@sinclair/typebox": {
|
| 1024 |
"version": "0.34.41",
|
| 1025 |
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
|
@@ -1231,6 +1301,11 @@
|
|
| 1231 |
"pretty-format": "^30.0.0"
|
| 1232 |
}
|
| 1233 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1234 |
"node_modules/@types/jsonwebtoken": {
|
| 1235 |
"version": "9.0.10",
|
| 1236 |
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
|
@@ -1359,6 +1434,16 @@
|
|
| 1359 |
"@types/superagent": "^8.1.0"
|
| 1360 |
}
|
| 1361 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1362 |
"node_modules/@types/uuid": {
|
| 1363 |
"version": "11.0.0",
|
| 1364 |
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz",
|
|
@@ -2133,6 +2218,11 @@
|
|
| 2133 |
"url": "https://github.com/sponsors/ljharb"
|
| 2134 |
}
|
| 2135 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2136 |
"node_modules/callsites": {
|
| 2137 |
"version": "3.1.0",
|
| 2138 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
@@ -2350,6 +2440,14 @@
|
|
| 2350 |
"node": ">= 0.8"
|
| 2351 |
}
|
| 2352 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2353 |
"node_modules/component-emitter": {
|
| 2354 |
"version": "1.3.1",
|
| 2355 |
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
|
@@ -2362,8 +2460,7 @@
|
|
| 2362 |
"node_modules/concat-map": {
|
| 2363 |
"version": "0.0.1",
|
| 2364 |
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
| 2365 |
-
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
| 2366 |
-
"dev": true
|
| 2367 |
},
|
| 2368 |
"node_modules/concat-stream": {
|
| 2369 |
"version": "2.0.0",
|
|
@@ -2560,6 +2657,17 @@
|
|
| 2560 |
"node": ">=0.3.1"
|
| 2561 |
}
|
| 2562 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2563 |
"node_modules/dotenv": {
|
| 2564 |
"version": "17.2.3",
|
| 2565 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
|
@@ -2727,6 +2835,14 @@
|
|
| 2727 |
"node": ">=4"
|
| 2728 |
}
|
| 2729 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2730 |
"node_modules/etag": {
|
| 2731 |
"version": "1.8.1",
|
| 2732 |
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
@@ -3028,8 +3144,7 @@
|
|
| 3028 |
"node_modules/fs.realpath": {
|
| 3029 |
"version": "1.0.0",
|
| 3030 |
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
| 3031 |
-
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
| 3032 |
-
"dev": true
|
| 3033 |
},
|
| 3034 |
"node_modules/fsevents": {
|
| 3035 |
"version": "2.3.3",
|
|
@@ -3360,7 +3475,6 @@
|
|
| 3360 |
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
| 3361 |
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
| 3362 |
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
| 3363 |
-
"dev": true,
|
| 3364 |
"dependencies": {
|
| 3365 |
"once": "^1.3.0",
|
| 3366 |
"wrappy": "1"
|
|
@@ -4315,6 +4429,12 @@
|
|
| 4315 |
"node": ">=8"
|
| 4316 |
}
|
| 4317 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4318 |
"node_modules/lodash.includes": {
|
| 4319 |
"version": "4.3.0",
|
| 4320 |
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
|
@@ -4325,6 +4445,12 @@
|
|
| 4325 |
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
| 4326 |
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
| 4327 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4328 |
"node_modules/lodash.isinteger": {
|
| 4329 |
"version": "4.0.4",
|
| 4330 |
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
|
@@ -4351,6 +4477,11 @@
|
|
| 4351 |
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
| 4352 |
"dev": true
|
| 4353 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4354 |
"node_modules/lodash.once": {
|
| 4355 |
"version": "4.1.1",
|
| 4356 |
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
|
@@ -4737,6 +4868,12 @@
|
|
| 4737 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4738 |
}
|
| 4739 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4740 |
"node_modules/p-limit": {
|
| 4741 |
"version": "3.1.0",
|
| 4742 |
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
|
@@ -4832,7 +4969,6 @@
|
|
| 4832 |
"version": "1.0.1",
|
| 4833 |
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
| 4834 |
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
| 4835 |
-
"dev": true,
|
| 4836 |
"engines": {
|
| 4837 |
"node": ">=0.10.0"
|
| 4838 |
}
|
|
@@ -5837,6 +5973,98 @@
|
|
| 5837 |
"url": "https://github.com/sponsors/ljharb"
|
| 5838 |
}
|
| 5839 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5840 |
"node_modules/synckit": {
|
| 5841 |
"version": "0.11.11",
|
| 5842 |
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
|
@@ -6462,6 +6690,14 @@
|
|
| 6462 |
"node": ">=10.12.0"
|
| 6463 |
}
|
| 6464 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6465 |
"node_modules/vary": {
|
| 6466 |
"version": "1.1.2",
|
| 6467 |
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
@@ -6640,6 +6876,14 @@
|
|
| 6640 |
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 6641 |
"dev": true
|
| 6642 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6643 |
"node_modules/yargs": {
|
| 6644 |
"version": "17.7.2",
|
| 6645 |
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
|
@@ -6722,6 +6966,34 @@
|
|
| 6722 |
"funding": {
|
| 6723 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 6724 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6725 |
}
|
| 6726 |
}
|
| 6727 |
}
|
|
|
|
| 22 |
"pg": "*",
|
| 23 |
"pinata": "*",
|
| 24 |
"reflect-metadata": "*",
|
| 25 |
+
"swagger-jsdoc": "*",
|
| 26 |
+
"swagger-ui-express": "*",
|
| 27 |
"typeorm": "*",
|
| 28 |
"uuid": "*"
|
| 29 |
},
|
|
|
|
| 36 |
"@types/multer": "*",
|
| 37 |
"@types/node": "*",
|
| 38 |
"@types/supertest": "*",
|
| 39 |
+
"@types/swagger-ui-express": "*",
|
| 40 |
"@types/uuid": "*",
|
| 41 |
"jest": "*",
|
| 42 |
"supertest": "*",
|
|
|
|
| 45 |
"typescript": "*"
|
| 46 |
}
|
| 47 |
},
|
| 48 |
+
"node_modules/@apidevtools/json-schema-ref-parser": {
|
| 49 |
+
"version": "9.1.2",
|
| 50 |
+
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
|
| 51 |
+
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
|
| 52 |
+
"dependencies": {
|
| 53 |
+
"@jsdevtools/ono": "^7.1.3",
|
| 54 |
+
"@types/json-schema": "^7.0.6",
|
| 55 |
+
"call-me-maybe": "^1.0.1",
|
| 56 |
+
"js-yaml": "^4.1.0"
|
| 57 |
+
}
|
| 58 |
+
},
|
| 59 |
+
"node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": {
|
| 60 |
+
"version": "2.0.1",
|
| 61 |
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
| 62 |
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
| 63 |
+
},
|
| 64 |
+
"node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": {
|
| 65 |
+
"version": "4.1.0",
|
| 66 |
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
| 67 |
+
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
| 68 |
+
"dependencies": {
|
| 69 |
+
"argparse": "^2.0.1"
|
| 70 |
+
},
|
| 71 |
+
"bin": {
|
| 72 |
+
"js-yaml": "bin/js-yaml.js"
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"node_modules/@apidevtools/openapi-schemas": {
|
| 76 |
+
"version": "2.1.0",
|
| 77 |
+
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
|
| 78 |
+
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
|
| 79 |
+
"engines": {
|
| 80 |
+
"node": ">=10"
|
| 81 |
+
}
|
| 82 |
+
},
|
| 83 |
+
"node_modules/@apidevtools/swagger-methods": {
|
| 84 |
+
"version": "3.0.2",
|
| 85 |
+
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
|
| 86 |
+
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg=="
|
| 87 |
+
},
|
| 88 |
+
"node_modules/@apidevtools/swagger-parser": {
|
| 89 |
+
"version": "10.0.3",
|
| 90 |
+
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
| 91 |
+
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
|
| 92 |
+
"dependencies": {
|
| 93 |
+
"@apidevtools/json-schema-ref-parser": "^9.0.6",
|
| 94 |
+
"@apidevtools/openapi-schemas": "^2.0.4",
|
| 95 |
+
"@apidevtools/swagger-methods": "^3.0.2",
|
| 96 |
+
"@jsdevtools/ono": "^7.1.3",
|
| 97 |
+
"call-me-maybe": "^1.0.1",
|
| 98 |
+
"z-schema": "^5.0.1"
|
| 99 |
+
},
|
| 100 |
+
"peerDependencies": {
|
| 101 |
+
"openapi-types": ">=7"
|
| 102 |
+
}
|
| 103 |
+
},
|
| 104 |
"node_modules/@babel/code-frame": {
|
| 105 |
"version": "7.27.1",
|
| 106 |
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
|
|
|
| 1025 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 1026 |
}
|
| 1027 |
},
|
| 1028 |
+
"node_modules/@jsdevtools/ono": {
|
| 1029 |
+
"version": "7.1.3",
|
| 1030 |
+
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
| 1031 |
+
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="
|
| 1032 |
+
},
|
| 1033 |
"node_modules/@napi-rs/wasm-runtime": {
|
| 1034 |
"version": "0.2.12",
|
| 1035 |
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
|
|
|
| 1084 |
"url": "https://opencollective.com/pkgr"
|
| 1085 |
}
|
| 1086 |
},
|
| 1087 |
+
"node_modules/@scarf/scarf": {
|
| 1088 |
+
"version": "1.4.0",
|
| 1089 |
+
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
| 1090 |
+
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
|
| 1091 |
+
"hasInstallScript": true
|
| 1092 |
+
},
|
| 1093 |
"node_modules/@sinclair/typebox": {
|
| 1094 |
"version": "0.34.41",
|
| 1095 |
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
|
|
|
| 1301 |
"pretty-format": "^30.0.0"
|
| 1302 |
}
|
| 1303 |
},
|
| 1304 |
+
"node_modules/@types/json-schema": {
|
| 1305 |
+
"version": "7.0.15",
|
| 1306 |
+
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
| 1307 |
+
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
| 1308 |
+
},
|
| 1309 |
"node_modules/@types/jsonwebtoken": {
|
| 1310 |
"version": "9.0.10",
|
| 1311 |
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
|
|
|
| 1434 |
"@types/superagent": "^8.1.0"
|
| 1435 |
}
|
| 1436 |
},
|
| 1437 |
+
"node_modules/@types/swagger-ui-express": {
|
| 1438 |
+
"version": "4.1.8",
|
| 1439 |
+
"resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz",
|
| 1440 |
+
"integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==",
|
| 1441 |
+
"dev": true,
|
| 1442 |
+
"dependencies": {
|
| 1443 |
+
"@types/express": "*",
|
| 1444 |
+
"@types/serve-static": "*"
|
| 1445 |
+
}
|
| 1446 |
+
},
|
| 1447 |
"node_modules/@types/uuid": {
|
| 1448 |
"version": "11.0.0",
|
| 1449 |
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz",
|
|
|
|
| 2218 |
"url": "https://github.com/sponsors/ljharb"
|
| 2219 |
}
|
| 2220 |
},
|
| 2221 |
+
"node_modules/call-me-maybe": {
|
| 2222 |
+
"version": "1.0.2",
|
| 2223 |
+
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
| 2224 |
+
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="
|
| 2225 |
+
},
|
| 2226 |
"node_modules/callsites": {
|
| 2227 |
"version": "3.1.0",
|
| 2228 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
|
|
| 2440 |
"node": ">= 0.8"
|
| 2441 |
}
|
| 2442 |
},
|
| 2443 |
+
"node_modules/commander": {
|
| 2444 |
+
"version": "6.2.0",
|
| 2445 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
|
| 2446 |
+
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
|
| 2447 |
+
"engines": {
|
| 2448 |
+
"node": ">= 6"
|
| 2449 |
+
}
|
| 2450 |
+
},
|
| 2451 |
"node_modules/component-emitter": {
|
| 2452 |
"version": "1.3.1",
|
| 2453 |
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
|
|
|
| 2460 |
"node_modules/concat-map": {
|
| 2461 |
"version": "0.0.1",
|
| 2462 |
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
| 2463 |
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
|
|
|
| 2464 |
},
|
| 2465 |
"node_modules/concat-stream": {
|
| 2466 |
"version": "2.0.0",
|
|
|
|
| 2657 |
"node": ">=0.3.1"
|
| 2658 |
}
|
| 2659 |
},
|
| 2660 |
+
"node_modules/doctrine": {
|
| 2661 |
+
"version": "3.0.0",
|
| 2662 |
+
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
| 2663 |
+
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
|
| 2664 |
+
"dependencies": {
|
| 2665 |
+
"esutils": "^2.0.2"
|
| 2666 |
+
},
|
| 2667 |
+
"engines": {
|
| 2668 |
+
"node": ">=6.0.0"
|
| 2669 |
+
}
|
| 2670 |
+
},
|
| 2671 |
"node_modules/dotenv": {
|
| 2672 |
"version": "17.2.3",
|
| 2673 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
|
|
|
| 2835 |
"node": ">=4"
|
| 2836 |
}
|
| 2837 |
},
|
| 2838 |
+
"node_modules/esutils": {
|
| 2839 |
+
"version": "2.0.3",
|
| 2840 |
+
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
| 2841 |
+
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
| 2842 |
+
"engines": {
|
| 2843 |
+
"node": ">=0.10.0"
|
| 2844 |
+
}
|
| 2845 |
+
},
|
| 2846 |
"node_modules/etag": {
|
| 2847 |
"version": "1.8.1",
|
| 2848 |
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
|
|
| 3144 |
"node_modules/fs.realpath": {
|
| 3145 |
"version": "1.0.0",
|
| 3146 |
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
| 3147 |
+
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
|
|
|
| 3148 |
},
|
| 3149 |
"node_modules/fsevents": {
|
| 3150 |
"version": "2.3.3",
|
|
|
|
| 3475 |
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
| 3476 |
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
| 3477 |
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
|
|
|
| 3478 |
"dependencies": {
|
| 3479 |
"once": "^1.3.0",
|
| 3480 |
"wrappy": "1"
|
|
|
|
| 4429 |
"node": ">=8"
|
| 4430 |
}
|
| 4431 |
},
|
| 4432 |
+
"node_modules/lodash.get": {
|
| 4433 |
+
"version": "4.4.2",
|
| 4434 |
+
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
| 4435 |
+
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
| 4436 |
+
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead."
|
| 4437 |
+
},
|
| 4438 |
"node_modules/lodash.includes": {
|
| 4439 |
"version": "4.3.0",
|
| 4440 |
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
|
|
|
| 4445 |
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
| 4446 |
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
| 4447 |
},
|
| 4448 |
+
"node_modules/lodash.isequal": {
|
| 4449 |
+
"version": "4.5.0",
|
| 4450 |
+
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
| 4451 |
+
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
| 4452 |
+
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead."
|
| 4453 |
+
},
|
| 4454 |
"node_modules/lodash.isinteger": {
|
| 4455 |
"version": "4.0.4",
|
| 4456 |
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
|
|
|
| 4477 |
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
| 4478 |
"dev": true
|
| 4479 |
},
|
| 4480 |
+
"node_modules/lodash.mergewith": {
|
| 4481 |
+
"version": "4.6.2",
|
| 4482 |
+
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
| 4483 |
+
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
|
| 4484 |
+
},
|
| 4485 |
"node_modules/lodash.once": {
|
| 4486 |
"version": "4.1.1",
|
| 4487 |
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
|
|
|
| 4868 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4869 |
}
|
| 4870 |
},
|
| 4871 |
+
"node_modules/openapi-types": {
|
| 4872 |
+
"version": "12.1.3",
|
| 4873 |
+
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
|
| 4874 |
+
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
| 4875 |
+
"peer": true
|
| 4876 |
+
},
|
| 4877 |
"node_modules/p-limit": {
|
| 4878 |
"version": "3.1.0",
|
| 4879 |
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
|
|
|
| 4969 |
"version": "1.0.1",
|
| 4970 |
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
| 4971 |
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
|
|
|
| 4972 |
"engines": {
|
| 4973 |
"node": ">=0.10.0"
|
| 4974 |
}
|
|
|
|
| 5973 |
"url": "https://github.com/sponsors/ljharb"
|
| 5974 |
}
|
| 5975 |
},
|
| 5976 |
+
"node_modules/swagger-jsdoc": {
|
| 5977 |
+
"version": "6.2.8",
|
| 5978 |
+
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
|
| 5979 |
+
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
|
| 5980 |
+
"dependencies": {
|
| 5981 |
+
"commander": "6.2.0",
|
| 5982 |
+
"doctrine": "3.0.0",
|
| 5983 |
+
"glob": "7.1.6",
|
| 5984 |
+
"lodash.mergewith": "^4.6.2",
|
| 5985 |
+
"swagger-parser": "^10.0.3",
|
| 5986 |
+
"yaml": "2.0.0-1"
|
| 5987 |
+
},
|
| 5988 |
+
"bin": {
|
| 5989 |
+
"swagger-jsdoc": "bin/swagger-jsdoc.js"
|
| 5990 |
+
},
|
| 5991 |
+
"engines": {
|
| 5992 |
+
"node": ">=12.0.0"
|
| 5993 |
+
}
|
| 5994 |
+
},
|
| 5995 |
+
"node_modules/swagger-jsdoc/node_modules/brace-expansion": {
|
| 5996 |
+
"version": "1.1.12",
|
| 5997 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
| 5998 |
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
| 5999 |
+
"dependencies": {
|
| 6000 |
+
"balanced-match": "^1.0.0",
|
| 6001 |
+
"concat-map": "0.0.1"
|
| 6002 |
+
}
|
| 6003 |
+
},
|
| 6004 |
+
"node_modules/swagger-jsdoc/node_modules/glob": {
|
| 6005 |
+
"version": "7.1.6",
|
| 6006 |
+
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
| 6007 |
+
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
| 6008 |
+
"deprecated": "Glob versions prior to v9 are no longer supported",
|
| 6009 |
+
"dependencies": {
|
| 6010 |
+
"fs.realpath": "^1.0.0",
|
| 6011 |
+
"inflight": "^1.0.4",
|
| 6012 |
+
"inherits": "2",
|
| 6013 |
+
"minimatch": "^3.0.4",
|
| 6014 |
+
"once": "^1.3.0",
|
| 6015 |
+
"path-is-absolute": "^1.0.0"
|
| 6016 |
+
},
|
| 6017 |
+
"engines": {
|
| 6018 |
+
"node": "*"
|
| 6019 |
+
},
|
| 6020 |
+
"funding": {
|
| 6021 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 6022 |
+
}
|
| 6023 |
+
},
|
| 6024 |
+
"node_modules/swagger-jsdoc/node_modules/minimatch": {
|
| 6025 |
+
"version": "3.1.2",
|
| 6026 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
| 6027 |
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
| 6028 |
+
"dependencies": {
|
| 6029 |
+
"brace-expansion": "^1.1.7"
|
| 6030 |
+
},
|
| 6031 |
+
"engines": {
|
| 6032 |
+
"node": "*"
|
| 6033 |
+
}
|
| 6034 |
+
},
|
| 6035 |
+
"node_modules/swagger-parser": {
|
| 6036 |
+
"version": "10.0.3",
|
| 6037 |
+
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
| 6038 |
+
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
|
| 6039 |
+
"dependencies": {
|
| 6040 |
+
"@apidevtools/swagger-parser": "10.0.3"
|
| 6041 |
+
},
|
| 6042 |
+
"engines": {
|
| 6043 |
+
"node": ">=10"
|
| 6044 |
+
}
|
| 6045 |
+
},
|
| 6046 |
+
"node_modules/swagger-ui-dist": {
|
| 6047 |
+
"version": "5.30.1",
|
| 6048 |
+
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.1.tgz",
|
| 6049 |
+
"integrity": "sha512-4mNAUM31sr52K3JcK9qiGbfsFKNh/dm3PkEe+F9FAM31YY/NoRYUgsR/L6d7LLFn6PgZXtBG2ygp8+7UnpUIPg==",
|
| 6050 |
+
"dependencies": {
|
| 6051 |
+
"@scarf/scarf": "=1.4.0"
|
| 6052 |
+
}
|
| 6053 |
+
},
|
| 6054 |
+
"node_modules/swagger-ui-express": {
|
| 6055 |
+
"version": "5.0.1",
|
| 6056 |
+
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
|
| 6057 |
+
"integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
|
| 6058 |
+
"dependencies": {
|
| 6059 |
+
"swagger-ui-dist": ">=5.0.0"
|
| 6060 |
+
},
|
| 6061 |
+
"engines": {
|
| 6062 |
+
"node": ">= v0.10.32"
|
| 6063 |
+
},
|
| 6064 |
+
"peerDependencies": {
|
| 6065 |
+
"express": ">=4.0.0 || >=5.0.0-beta"
|
| 6066 |
+
}
|
| 6067 |
+
},
|
| 6068 |
"node_modules/synckit": {
|
| 6069 |
"version": "0.11.11",
|
| 6070 |
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
|
|
|
| 6690 |
"node": ">=10.12.0"
|
| 6691 |
}
|
| 6692 |
},
|
| 6693 |
+
"node_modules/validator": {
|
| 6694 |
+
"version": "13.15.20",
|
| 6695 |
+
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
| 6696 |
+
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
| 6697 |
+
"engines": {
|
| 6698 |
+
"node": ">= 0.10"
|
| 6699 |
+
}
|
| 6700 |
+
},
|
| 6701 |
"node_modules/vary": {
|
| 6702 |
"version": "1.1.2",
|
| 6703 |
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
|
|
| 6876 |
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 6877 |
"dev": true
|
| 6878 |
},
|
| 6879 |
+
"node_modules/yaml": {
|
| 6880 |
+
"version": "2.0.0-1",
|
| 6881 |
+
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
|
| 6882 |
+
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
|
| 6883 |
+
"engines": {
|
| 6884 |
+
"node": ">= 6"
|
| 6885 |
+
}
|
| 6886 |
+
},
|
| 6887 |
"node_modules/yargs": {
|
| 6888 |
"version": "17.7.2",
|
| 6889 |
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
|
|
|
| 6966 |
"funding": {
|
| 6967 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 6968 |
}
|
| 6969 |
+
},
|
| 6970 |
+
"node_modules/z-schema": {
|
| 6971 |
+
"version": "5.0.5",
|
| 6972 |
+
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
|
| 6973 |
+
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
|
| 6974 |
+
"dependencies": {
|
| 6975 |
+
"lodash.get": "^4.4.2",
|
| 6976 |
+
"lodash.isequal": "^4.5.0",
|
| 6977 |
+
"validator": "^13.7.0"
|
| 6978 |
+
},
|
| 6979 |
+
"bin": {
|
| 6980 |
+
"z-schema": "bin/z-schema"
|
| 6981 |
+
},
|
| 6982 |
+
"engines": {
|
| 6983 |
+
"node": ">=8.0.0"
|
| 6984 |
+
},
|
| 6985 |
+
"optionalDependencies": {
|
| 6986 |
+
"commander": "^9.4.1"
|
| 6987 |
+
}
|
| 6988 |
+
},
|
| 6989 |
+
"node_modules/z-schema/node_modules/commander": {
|
| 6990 |
+
"version": "9.5.0",
|
| 6991 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
|
| 6992 |
+
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
|
| 6993 |
+
"optional": true,
|
| 6994 |
+
"engines": {
|
| 6995 |
+
"node": "^12.20.0 || >=14"
|
| 6996 |
+
}
|
| 6997 |
}
|
| 6998 |
}
|
| 6999 |
}
|
package.json
CHANGED
|
@@ -32,7 +32,9 @@
|
|
| 32 |
"bcryptjs": "*",
|
| 33 |
"pinata": "*",
|
| 34 |
"multer": "*",
|
| 35 |
-
"form-data": "*"
|
|
|
|
|
|
|
| 36 |
},
|
| 37 |
"devDependencies": {
|
| 38 |
"@types/express": "*",
|
|
@@ -48,6 +50,7 @@
|
|
| 48 |
"jest": "*",
|
| 49 |
"ts-jest": "*",
|
| 50 |
"supertest": "*",
|
| 51 |
-
"@types/supertest": "*"
|
|
|
|
| 52 |
}
|
| 53 |
}
|
|
|
|
| 32 |
"bcryptjs": "*",
|
| 33 |
"pinata": "*",
|
| 34 |
"multer": "*",
|
| 35 |
+
"form-data": "*",
|
| 36 |
+
"swagger-ui-express": "*",
|
| 37 |
+
"swagger-jsdoc": "*"
|
| 38 |
},
|
| 39 |
"devDependencies": {
|
| 40 |
"@types/express": "*",
|
|
|
|
| 50 |
"jest": "*",
|
| 51 |
"ts-jest": "*",
|
| 52 |
"supertest": "*",
|
| 53 |
+
"@types/supertest": "*",
|
| 54 |
+
"@types/swagger-ui-express": "*"
|
| 55 |
}
|
| 56 |
}
|
src/app.ts
CHANGED
|
@@ -2,13 +2,19 @@ import express, { Application } from 'express';
|
|
| 2 |
import cors from 'cors';
|
| 3 |
import helmet from 'helmet';
|
| 4 |
import rateLimit from 'express-rate-limit';
|
|
|
|
|
|
|
| 5 |
|
| 6 |
// Routes
|
| 7 |
import authRoutes from './routes/authRoutes';
|
|
|
|
| 8 |
import agentRoutes from './routes/agentRoutes';
|
| 9 |
import chatRoutes from './routes/chatRoutes';
|
| 10 |
import subscriptionRoutes from './routes/subscriptionRoutes';
|
| 11 |
import walletRoutes from './routes/walletRoutes';
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
const app: Application = express();
|
| 14 |
|
|
@@ -35,8 +41,15 @@ app.get('/health', (req, res) => {
|
|
| 35 |
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
| 36 |
});
|
| 37 |
|
|
|
|
|
|
|
|
|
|
| 38 |
// API routes
|
| 39 |
app.use('/api/auth', authRoutes);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
app.use('/api/agents', agentRoutes);
|
| 41 |
app.use('/api/chat', chatRoutes);
|
| 42 |
app.use('/api/subscriptions', subscriptionRoutes);
|
|
|
|
| 2 |
import cors from 'cors';
|
| 3 |
import helmet from 'helmet';
|
| 4 |
import rateLimit from 'express-rate-limit';
|
| 5 |
+
import swaggerUi from 'swagger-ui-express';
|
| 6 |
+
import { swaggerSpec } from './docs/swagger';
|
| 7 |
|
| 8 |
// Routes
|
| 9 |
import authRoutes from './routes/authRoutes';
|
| 10 |
+
import creatorAuthRoutes from './routes/creatorAuthRoutes';
|
| 11 |
import agentRoutes from './routes/agentRoutes';
|
| 12 |
import chatRoutes from './routes/chatRoutes';
|
| 13 |
import subscriptionRoutes from './routes/subscriptionRoutes';
|
| 14 |
import walletRoutes from './routes/walletRoutes';
|
| 15 |
+
import userRoutes from './routes/userRoutes';
|
| 16 |
+
import creatorRoutes from './routes/creatorRoutes';
|
| 17 |
+
import adminRoutes from './routes/adminRoutes';
|
| 18 |
|
| 19 |
const app: Application = express();
|
| 20 |
|
|
|
|
| 41 |
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
| 42 |
});
|
| 43 |
|
| 44 |
+
// Docs
|
| 45 |
+
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
| 46 |
+
|
| 47 |
// API routes
|
| 48 |
app.use('/api/auth', authRoutes);
|
| 49 |
+
app.use('/api/creator-auth', creatorAuthRoutes);
|
| 50 |
+
app.use('/api/users', userRoutes);
|
| 51 |
+
app.use('/api/creators', creatorRoutes);
|
| 52 |
+
app.use('/api/admin', adminRoutes);
|
| 53 |
app.use('/api/agents', agentRoutes);
|
| 54 |
app.use('/api/chat', chatRoutes);
|
| 55 |
app.use('/api/subscriptions', subscriptionRoutes);
|
src/config/database.ts
CHANGED
|
@@ -6,11 +6,12 @@ import { ApiKey } from '../entities/ApiKey';
|
|
| 6 |
import { ChatMessage } from '../entities/ChatMessage';
|
| 7 |
import { Wallet } from '../entities/Wallet';
|
| 8 |
import { Transaction } from '../entities/Transaction';
|
|
|
|
| 9 |
|
| 10 |
export const AppDataSource = new DataSource({
|
| 11 |
type: 'postgres',
|
| 12 |
url: process.env.DATABASE_URL,
|
| 13 |
-
entities: [Agent, User, Subscription, ApiKey, ChatMessage, Wallet, Transaction],
|
| 14 |
synchronize: process.env.NODE_ENV !== 'production', // Auto-sync in dev only
|
| 15 |
logging: process.env.NODE_ENV === 'development',
|
| 16 |
});
|
|
|
|
| 6 |
import { ChatMessage } from '../entities/ChatMessage';
|
| 7 |
import { Wallet } from '../entities/Wallet';
|
| 8 |
import { Transaction } from '../entities/Transaction';
|
| 9 |
+
import { CreatorProfile } from '../entities/CreatorProfile';
|
| 10 |
|
| 11 |
export const AppDataSource = new DataSource({
|
| 12 |
type: 'postgres',
|
| 13 |
url: process.env.DATABASE_URL,
|
| 14 |
+
entities: [Agent, User, Subscription, ApiKey, ChatMessage, Wallet, Transaction, CreatorProfile],
|
| 15 |
synchronize: process.env.NODE_ENV !== 'production', // Auto-sync in dev only
|
| 16 |
logging: process.env.NODE_ENV === 'development',
|
| 17 |
});
|
src/controllers/adminController.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Request, Response } from 'express';
|
| 2 |
+
import { AppDataSource } from '../config/database';
|
| 3 |
+
import { User } from '../entities/User';
|
| 4 |
+
import { Agent, AgentStatus } from '../entities/Agent';
|
| 5 |
+
import { ChatMessage } from '../entities/ChatMessage';
|
| 6 |
+
import { Transaction } from '../entities/Transaction';
|
| 7 |
+
|
| 8 |
+
const userRepository = AppDataSource.getRepository(User);
|
| 9 |
+
const agentRepository = AppDataSource.getRepository(Agent);
|
| 10 |
+
const messageRepository = AppDataSource.getRepository(ChatMessage);
|
| 11 |
+
const txRepository = AppDataSource.getRepository(Transaction);
|
| 12 |
+
|
| 13 |
+
export class AdminController {
|
| 14 |
+
async overview(req: Request, res: Response) {
|
| 15 |
+
try {
|
| 16 |
+
const { from, to } = req.query as any;
|
| 17 |
+
|
| 18 |
+
const [users, agents, messages] = await Promise.all([
|
| 19 |
+
userRepository.count(),
|
| 20 |
+
agentRepository.count(),
|
| 21 |
+
messageRepository.count(),
|
| 22 |
+
]);
|
| 23 |
+
|
| 24 |
+
const pendingAgents = await agentRepository.count({ where: { status: AgentStatus.PENDING } });
|
| 25 |
+
|
| 26 |
+
let pvQb = txRepository
|
| 27 |
+
.createQueryBuilder('t')
|
| 28 |
+
.select('SUM(ABS(t.amount))', 'sum');
|
| 29 |
+
|
| 30 |
+
if (from) pvQb = pvQb.where('t.createdAt >= :from', { from });
|
| 31 |
+
if (to) pvQb = pvQb.andWhere('t.createdAt <= :to', { to });
|
| 32 |
+
|
| 33 |
+
const totalPointsVolumeRow = await pvQb.getRawOne();
|
| 34 |
+
|
| 35 |
+
res.json({
|
| 36 |
+
totals: {
|
| 37 |
+
users,
|
| 38 |
+
agents,
|
| 39 |
+
messages,
|
| 40 |
+
pendingAgents,
|
| 41 |
+
pointsVolume: Number(totalPointsVolumeRow?.sum || 0),
|
| 42 |
+
},
|
| 43 |
+
});
|
| 44 |
+
} catch (error) {
|
| 45 |
+
console.error('Admin overview error:', error);
|
| 46 |
+
res.status(500).json({ error: 'Failed to get overview' });
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
}
|
src/controllers/creatorAuthController.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Request, Response } from 'express';
|
| 2 |
+
import bcrypt from 'bcryptjs';
|
| 3 |
+
import jwt from 'jsonwebtoken';
|
| 4 |
+
import { AppDataSource } from '../config/database';
|
| 5 |
+
import { User } from '../entities/User';
|
| 6 |
+
import { CreatorProfile } from '../entities/CreatorProfile';
|
| 7 |
+
|
| 8 |
+
const userRepository = AppDataSource.getRepository(User);
|
| 9 |
+
const profileRepository = AppDataSource.getRepository(CreatorProfile);
|
| 10 |
+
|
| 11 |
+
export class CreatorAuthController {
|
| 12 |
+
async register(req: Request, res: Response) {
|
| 13 |
+
try {
|
| 14 |
+
const {
|
| 15 |
+
fullName,
|
| 16 |
+
username,
|
| 17 |
+
email,
|
| 18 |
+
password,
|
| 19 |
+
profileImage,
|
| 20 |
+
bio,
|
| 21 |
+
organization,
|
| 22 |
+
role,
|
| 23 |
+
website,
|
| 24 |
+
linkedinUrl,
|
| 25 |
+
githubUrl,
|
| 26 |
+
huggingfaceUrl,
|
| 27 |
+
primaryLanguages,
|
| 28 |
+
frameworks,
|
| 29 |
+
agentSpecialties,
|
| 30 |
+
preferredCompute,
|
| 31 |
+
endpointHosting,
|
| 32 |
+
walletAddress,
|
| 33 |
+
bankAccount,
|
| 34 |
+
preferredCurrency,
|
| 35 |
+
country,
|
| 36 |
+
taxId,
|
| 37 |
+
idDocumentUrl,
|
| 38 |
+
} = req.body;
|
| 39 |
+
|
| 40 |
+
if (!fullName || !username || !email || !password) {
|
| 41 |
+
return res.status(400).json({ error: 'fullName, username, email and password are required' });
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
// Username stored without leading @
|
| 45 |
+
const cleanUsername = String(username).replace(/^@+/, '').toLowerCase();
|
| 46 |
+
|
| 47 |
+
// Uniqueness checks
|
| 48 |
+
const existingUser = await userRepository.findOne({ where: { email } });
|
| 49 |
+
if (existingUser) return res.status(400).json({ error: 'Email already in use' });
|
| 50 |
+
const existingProfile = await profileRepository.findOne({ where: { username: cleanUsername } });
|
| 51 |
+
if (existingProfile) return res.status(400).json({ error: 'Username already in use' });
|
| 52 |
+
|
| 53 |
+
const hashedPassword = await bcrypt.hash(password, 10);
|
| 54 |
+
|
| 55 |
+
// Create user
|
| 56 |
+
const user = userRepository.create({ email, password: hashedPassword, name: fullName, isCreator: true, isAdmin: false });
|
| 57 |
+
const savedUser = await userRepository.save(user);
|
| 58 |
+
|
| 59 |
+
// Normalize arrays
|
| 60 |
+
const normArray = (v: any) => {
|
| 61 |
+
if (!v) return [] as string[];
|
| 62 |
+
if (Array.isArray(v)) return v;
|
| 63 |
+
try { return JSON.parse(v); } catch { return [String(v)]; }
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
// Create profile
|
| 67 |
+
const profile = profileRepository.create({
|
| 68 |
+
userId: savedUser.id,
|
| 69 |
+
fullName,
|
| 70 |
+
username: cleanUsername,
|
| 71 |
+
profileImage,
|
| 72 |
+
bio,
|
| 73 |
+
organization,
|
| 74 |
+
role,
|
| 75 |
+
website,
|
| 76 |
+
linkedinUrl,
|
| 77 |
+
githubUrl,
|
| 78 |
+
huggingfaceUrl,
|
| 79 |
+
primaryLanguages: normArray(primaryLanguages),
|
| 80 |
+
frameworks: normArray(frameworks),
|
| 81 |
+
agentSpecialties: normArray(agentSpecialties),
|
| 82 |
+
preferredCompute,
|
| 83 |
+
endpointHosting,
|
| 84 |
+
walletAddress,
|
| 85 |
+
bankAccount: bankAccount ? (typeof bankAccount === 'string' ? JSON.parse(bankAccount) : bankAccount) : undefined,
|
| 86 |
+
preferredCurrency,
|
| 87 |
+
country,
|
| 88 |
+
taxId,
|
| 89 |
+
idDocumentUrl,
|
| 90 |
+
});
|
| 91 |
+
await profileRepository.save(profile);
|
| 92 |
+
|
| 93 |
+
// Token
|
| 94 |
+
const token = jwt.sign(
|
| 95 |
+
{ id: savedUser.id, email: savedUser.email, isAdmin: savedUser.isAdmin },
|
| 96 |
+
process.env.JWT_SECRET!,
|
| 97 |
+
{ expiresIn: '7d' }
|
| 98 |
+
);
|
| 99 |
+
|
| 100 |
+
res.status(201).json({ token, user: { id: savedUser.id, email: savedUser.email, name: savedUser.name, isCreator: true } });
|
| 101 |
+
} catch (error: any) {
|
| 102 |
+
console.error('Creator register error:', error);
|
| 103 |
+
res.status(500).json({ error: error.message || 'Failed to register creator' });
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
async login(req: Request, res: Response) {
|
| 108 |
+
try {
|
| 109 |
+
const { email, password } = req.body;
|
| 110 |
+
if (!email || !password) return res.status(400).json({ error: 'Email and password are required' });
|
| 111 |
+
|
| 112 |
+
const user = await userRepository.findOne({ where: { email } });
|
| 113 |
+
if (!user || !user.isCreator) return res.status(401).json({ error: 'Invalid credentials' });
|
| 114 |
+
|
| 115 |
+
const isValid = await bcrypt.compare(password, user.password);
|
| 116 |
+
if (!isValid) return res.status(401).json({ error: 'Invalid credentials' });
|
| 117 |
+
|
| 118 |
+
const token = jwt.sign(
|
| 119 |
+
{ id: user.id, email: user.email, isAdmin: user.isAdmin },
|
| 120 |
+
process.env.JWT_SECRET!,
|
| 121 |
+
{ expiresIn: '7d' }
|
| 122 |
+
);
|
| 123 |
+
|
| 124 |
+
// Fetch profile brief
|
| 125 |
+
const profile = await profileRepository.findOne({ where: { userId: user.id } });
|
| 126 |
+
|
| 127 |
+
res.json({
|
| 128 |
+
token,
|
| 129 |
+
user: {
|
| 130 |
+
id: user.id,
|
| 131 |
+
email: user.email,
|
| 132 |
+
name: user.name,
|
| 133 |
+
isCreator: user.isCreator,
|
| 134 |
+
profile: profile ? { username: profile.username, fullName: profile.fullName } : null,
|
| 135 |
+
},
|
| 136 |
+
});
|
| 137 |
+
} catch (error: any) {
|
| 138 |
+
console.error('Creator login error:', error);
|
| 139 |
+
res.status(500).json({ error: error.message || 'Failed to login creator' });
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
}
|
src/controllers/creatorController.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Request, Response } from 'express';
|
| 2 |
+
import { AppDataSource } from '../config/database';
|
| 3 |
+
import { Agent } from '../entities/Agent';
|
| 4 |
+
import { Transaction, TransactionType } from '../entities/Transaction';
|
| 5 |
+
|
| 6 |
+
const agentRepository = AppDataSource.getRepository(Agent);
|
| 7 |
+
const txRepository = AppDataSource.getRepository(Transaction);
|
| 8 |
+
|
| 9 |
+
export class CreatorController {
|
| 10 |
+
async overview(req: Request, res: Response) {
|
| 11 |
+
try {
|
| 12 |
+
const userId = (req as any).user.id;
|
| 13 |
+
const { agentId, from, to } = req.query as any;
|
| 14 |
+
|
| 15 |
+
const agents = await agentRepository.find({ where: { creatorId: userId } });
|
| 16 |
+
const agentIds = agents.map((a) => a.id);
|
| 17 |
+
|
| 18 |
+
let countQb = txRepository
|
| 19 |
+
.createQueryBuilder('t')
|
| 20 |
+
.where('t.type = :type', { type: TransactionType.CHARGE })
|
| 21 |
+
.andWhere('t.agentId IN (:...agentIds)', { agentIds: agentIds.length ? agentIds : [''] });
|
| 22 |
+
|
| 23 |
+
if (agentId) countQb = countQb.andWhere('t.agentId = :agentId', { agentId });
|
| 24 |
+
if (from) countQb = countQb.andWhere('t.createdAt >= :from', { from });
|
| 25 |
+
if (to) countQb = countQb.andWhere('t.createdAt <= :to', { to });
|
| 26 |
+
|
| 27 |
+
const totalTasks = await countQb.getCount();
|
| 28 |
+
|
| 29 |
+
let sumQb = txRepository
|
| 30 |
+
.createQueryBuilder('t')
|
| 31 |
+
.where('t.type = :type', { type: TransactionType.CHARGE })
|
| 32 |
+
.andWhere('t.agentId IN (:...agentIds)', { agentIds: agentIds.length ? agentIds : [''] })
|
| 33 |
+
.select('SUM(ABS(t.amount))', 'sum');
|
| 34 |
+
|
| 35 |
+
if (agentId) sumQb = sumQb.andWhere('t.agentId = :agentId', { agentId });
|
| 36 |
+
if (from) sumQb = sumQb.andWhere('t.createdAt >= :from', { from });
|
| 37 |
+
if (to) sumQb = sumQb.andWhere('t.createdAt <= :to', { to });
|
| 38 |
+
|
| 39 |
+
const totalPoints = await sumQb.getRawOne();
|
| 40 |
+
|
| 41 |
+
res.json({
|
| 42 |
+
agents: agents.map((a) => ({ id: a.id, name: a.name, pointsPerTask: Number(a.pointsPerTask || 0) })),
|
| 43 |
+
totals: {
|
| 44 |
+
agents: agents.length,
|
| 45 |
+
tasks: totalTasks,
|
| 46 |
+
pointsEarned: Number(totalPoints?.sum || 0),
|
| 47 |
+
},
|
| 48 |
+
});
|
| 49 |
+
} catch (error) {
|
| 50 |
+
console.error('Creator overview error:', error);
|
| 51 |
+
res.status(500).json({ error: 'Failed to get overview' });
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
async earnings(req: Request, res: Response) {
|
| 56 |
+
try {
|
| 57 |
+
const userId = (req as any).user.id;
|
| 58 |
+
const { from, to, agentId } = req.query as any;
|
| 59 |
+
|
| 60 |
+
const agents = await agentRepository.find({ where: { creatorId: userId } });
|
| 61 |
+
const agentIds = agents.map((a) => a.id);
|
| 62 |
+
|
| 63 |
+
let qb = txRepository
|
| 64 |
+
.createQueryBuilder('t')
|
| 65 |
+
.where('t.agentId IN (:...agentIds)', { agentIds: agentIds.length ? agentIds : [''] })
|
| 66 |
+
.andWhere('t.type = :type', { type: TransactionType.CHARGE })
|
| 67 |
+
.select("DATE_TRUNC('day', t.createdAt)", 'day')
|
| 68 |
+
.addSelect('SUM(ABS(t.amount))', 'points')
|
| 69 |
+
.groupBy("DATE_TRUNC('day', t.createdAt)")
|
| 70 |
+
.orderBy('day', 'ASC');
|
| 71 |
+
|
| 72 |
+
if (agentId) qb = qb.andWhere('t.agentId = :agentId', { agentId });
|
| 73 |
+
if (from) qb = qb.andWhere('t.createdAt >= :from', { from });
|
| 74 |
+
if (to) qb = qb.andWhere('t.createdAt <= :to', { to });
|
| 75 |
+
|
| 76 |
+
const rows = await qb.getRawMany();
|
| 77 |
+
|
| 78 |
+
res.json({
|
| 79 |
+
series: rows.map((r) => ({ day: r.day, points: Number(r.points) })),
|
| 80 |
+
});
|
| 81 |
+
} catch (error) {
|
| 82 |
+
console.error('Creator earnings error:', error);
|
| 83 |
+
res.status(500).json({ error: 'Failed to get earnings' });
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
}
|
src/controllers/userController.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Request, Response } from 'express';
|
| 2 |
+
import { AppDataSource } from '../config/database';
|
| 3 |
+
import { ChatMessage } from '../entities/ChatMessage';
|
| 4 |
+
import { Agent } from '../entities/Agent';
|
| 5 |
+
|
| 6 |
+
const messageRepository = AppDataSource.getRepository(ChatMessage);
|
| 7 |
+
const agentRepository = AppDataSource.getRepository(Agent);
|
| 8 |
+
|
| 9 |
+
export class UserController {
|
| 10 |
+
/**
|
| 11 |
+
* Get aggregated chat history across agents for the current user
|
| 12 |
+
*/
|
| 13 |
+
async getMyHistory(req: Request, res: Response) {
|
| 14 |
+
try {
|
| 15 |
+
const userId = (req as any).user.id;
|
| 16 |
+
const { limit = 50, offset = 0, agentId, role, from, to, search } = req.query as any;
|
| 17 |
+
|
| 18 |
+
// Build query with filters
|
| 19 |
+
let qb = messageRepository
|
| 20 |
+
.createQueryBuilder('m')
|
| 21 |
+
.where('m.userId = :userId', { userId })
|
| 22 |
+
.orderBy('m.createdAt', 'DESC')
|
| 23 |
+
.limit(Number(limit))
|
| 24 |
+
.offset(Number(offset));
|
| 25 |
+
|
| 26 |
+
if (agentId) {
|
| 27 |
+
qb = qb.andWhere('m.agentId = :agentId', { agentId });
|
| 28 |
+
}
|
| 29 |
+
if (role) {
|
| 30 |
+
qb = qb.andWhere('m.role = :role', { role });
|
| 31 |
+
}
|
| 32 |
+
if (from) {
|
| 33 |
+
qb = qb.andWhere('m.createdAt >= :from', { from });
|
| 34 |
+
}
|
| 35 |
+
if (to) {
|
| 36 |
+
qb = qb.andWhere('m.createdAt <= :to', { to });
|
| 37 |
+
}
|
| 38 |
+
if (search) {
|
| 39 |
+
qb = qb.andWhere('m.content ILIKE :search', { search: `%${search}%` });
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
const messages = await qb.getMany();
|
| 43 |
+
|
| 44 |
+
// Attach minimal agent info
|
| 45 |
+
const agentIds = Array.from(new Set(messages.map((m) => m.agentId)));
|
| 46 |
+
const agents = agentIds.length ? await agentRepository.findByIds(agentIds) : [];
|
| 47 |
+
const agentMap = new Map(agents.map((a) => [a.id, { id: a.id, name: a.name, avatar: a.avatar, category: a.category }]));
|
| 48 |
+
|
| 49 |
+
res.json({
|
| 50 |
+
messages: messages.map((m) => ({
|
| 51 |
+
id: m.id,
|
| 52 |
+
agentId: m.agentId,
|
| 53 |
+
agent: agentMap.get(m.agentId) || null,
|
| 54 |
+
role: m.role,
|
| 55 |
+
content: m.content,
|
| 56 |
+
metadata: m.metadata,
|
| 57 |
+
createdAt: m.createdAt,
|
| 58 |
+
})),
|
| 59 |
+
pagination: {
|
| 60 |
+
limit: Number(limit),
|
| 61 |
+
offset: Number(offset),
|
| 62 |
+
count: messages.length,
|
| 63 |
+
},
|
| 64 |
+
});
|
| 65 |
+
} catch (error) {
|
| 66 |
+
console.error('Get user history error:', error);
|
| 67 |
+
res.status(500).json({ error: 'Failed to get history' });
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
}
|
src/controllers/walletController.ts
CHANGED
|
@@ -67,7 +67,7 @@ export class WalletController {
|
|
| 67 |
*/
|
| 68 |
async verifyPayment(req: Request, res: Response) {
|
| 69 |
try {
|
| 70 |
-
const secret = process.env.
|
| 71 |
if (!secret) {
|
| 72 |
throw new Error('Paystack secret not configured');
|
| 73 |
}
|
|
@@ -84,27 +84,37 @@ export class WalletController {
|
|
| 84 |
const event = req.body;
|
| 85 |
|
| 86 |
if (event.event === 'charge.success') {
|
| 87 |
-
const { reference, amount, metadata } = event.data;
|
| 88 |
|
| 89 |
-
//
|
| 90 |
-
const userId = metadata?.userId
|
|
|
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
const amountInNaira = amount / 100;
|
| 100 |
-
const
|
| 101 |
-
const
|
|
|
|
| 102 |
|
| 103 |
// Add points to wallet
|
| 104 |
-
await walletService.addPoints(
|
| 105 |
paystackEvent: event.event,
|
| 106 |
amountInNaira,
|
| 107 |
amountInDollars,
|
|
|
|
|
|
|
| 108 |
});
|
| 109 |
}
|
| 110 |
|
|
|
|
| 67 |
*/
|
| 68 |
async verifyPayment(req: Request, res: Response) {
|
| 69 |
try {
|
| 70 |
+
const secret = process.env.PAYSTACK_SECRET_KEY;
|
| 71 |
if (!secret) {
|
| 72 |
throw new Error('Paystack secret not configured');
|
| 73 |
}
|
|
|
|
| 84 |
const event = req.body;
|
| 85 |
|
| 86 |
if (event.event === 'charge.success') {
|
| 87 |
+
const { reference, amount, metadata, customer } = event.data;
|
| 88 |
|
| 89 |
+
// Prefer userId in metadata, else try by email
|
| 90 |
+
const userId = metadata?.userId;
|
| 91 |
+
const email = customer?.email;
|
| 92 |
|
| 93 |
+
let resolvedUserId = userId;
|
| 94 |
+
if (!resolvedUserId && email) {
|
| 95 |
+
// For now, require userId in metadata in production flows
|
| 96 |
+
console.warn('No userId in metadata; include userId in reference metadata for reliability');
|
| 97 |
}
|
| 98 |
|
| 99 |
+
if (!resolvedUserId) {
|
| 100 |
+
return res.status(400).json({ error: 'Missing userId in metadata' });
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// Calculate points from amount
|
| 104 |
+
// amount is typically in kobo (NGN) β naira = amount/100 β dollars via FX β points via POINT_VALUE_USD
|
| 105 |
+
const pointValueUsd = parseFloat(process.env.POINT_VALUE_USD || '0.05');
|
| 106 |
const amountInNaira = amount / 100;
|
| 107 |
+
const fx = parseFloat(process.env.NGN_PER_USD || '750');
|
| 108 |
+
const amountInDollars = amountInNaira / fx;
|
| 109 |
+
const points = Math.round(amountInDollars / pointValueUsd);
|
| 110 |
|
| 111 |
// Add points to wallet
|
| 112 |
+
await walletService.addPoints(resolvedUserId, points, reference, {
|
| 113 |
paystackEvent: event.event,
|
| 114 |
amountInNaira,
|
| 115 |
amountInDollars,
|
| 116 |
+
fx,
|
| 117 |
+
pointValueUsd,
|
| 118 |
});
|
| 119 |
}
|
| 120 |
|
src/docs/swagger.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import swaggerJSDoc from 'swagger-jsdoc';
|
| 2 |
+
|
| 3 |
+
export const swaggerSpec = swaggerJSDoc({
|
| 4 |
+
definition: {
|
| 5 |
+
openapi: '3.0.0',
|
| 6 |
+
info: {
|
| 7 |
+
title: 'Zurri API',
|
| 8 |
+
version: '1.0.0',
|
| 9 |
+
description: 'Zurri Agents Marketplace API with Wallet Point System',
|
| 10 |
+
},
|
| 11 |
+
servers: [{ url: '/api' }],
|
| 12 |
+
components: {
|
| 13 |
+
securitySchemes: {
|
| 14 |
+
bearerAuth: {
|
| 15 |
+
type: 'http',
|
| 16 |
+
scheme: 'bearer',
|
| 17 |
+
bearerFormat: 'JWT',
|
| 18 |
+
},
|
| 19 |
+
},
|
| 20 |
+
},
|
| 21 |
+
security: [{ bearerAuth: [] }],
|
| 22 |
+
},
|
| 23 |
+
apis: [
|
| 24 |
+
'./src/routes/*.ts',
|
| 25 |
+
],
|
| 26 |
+
});
|
src/entities/CreatorProfile.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm';
|
| 2 |
+
import { User } from './User';
|
| 3 |
+
|
| 4 |
+
export enum VerificationStatus {
|
| 5 |
+
UNVERIFIED = 'UNVERIFIED',
|
| 6 |
+
PENDING = 'PENDING',
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
@Entity('creator_profiles')
|
| 10 |
+
@Index(['username'], { unique: true })
|
| 11 |
+
export class CreatorProfile {
|
| 12 |
+
@PrimaryGeneratedColumn('uuid')
|
| 13 |
+
id: string;
|
| 14 |
+
|
| 15 |
+
@OneToOne(() => User)
|
| 16 |
+
@JoinColumn({ name: 'userId' })
|
| 17 |
+
user: User;
|
| 18 |
+
|
| 19 |
+
@Column({ unique: true })
|
| 20 |
+
userId: string;
|
| 21 |
+
|
| 22 |
+
// Basic Information
|
| 23 |
+
@Column()
|
| 24 |
+
fullName: string;
|
| 25 |
+
|
| 26 |
+
@Column({ unique: true })
|
| 27 |
+
username: string; // e.g. @neuralnex (store without @)
|
| 28 |
+
|
| 29 |
+
@Column({ nullable: true })
|
| 30 |
+
profileImage?: string; // URL/IPFS
|
| 31 |
+
|
| 32 |
+
@Column({ length: 200, nullable: true })
|
| 33 |
+
bio?: string;
|
| 34 |
+
|
| 35 |
+
// Professional Information
|
| 36 |
+
@Column({ nullable: true })
|
| 37 |
+
organization?: string;
|
| 38 |
+
|
| 39 |
+
@Column({ nullable: true })
|
| 40 |
+
role?: string; // e.g., AI Engineer
|
| 41 |
+
|
| 42 |
+
@Column({ nullable: true })
|
| 43 |
+
website?: string;
|
| 44 |
+
|
| 45 |
+
@Column({ nullable: true })
|
| 46 |
+
linkedinUrl?: string;
|
| 47 |
+
|
| 48 |
+
@Column({ nullable: true })
|
| 49 |
+
githubUrl?: string;
|
| 50 |
+
|
| 51 |
+
@Column({ nullable: true })
|
| 52 |
+
huggingfaceUrl?: string;
|
| 53 |
+
|
| 54 |
+
// Technical Stack & Focus
|
| 55 |
+
@Column('text', { array: true, default: '{}' })
|
| 56 |
+
primaryLanguages: string[];
|
| 57 |
+
|
| 58 |
+
@Column('text', { array: true, default: '{}' })
|
| 59 |
+
frameworks: string[];
|
| 60 |
+
|
| 61 |
+
@Column('text', { array: true, default: '{}' })
|
| 62 |
+
agentSpecialties: string[];
|
| 63 |
+
|
| 64 |
+
@Column({ nullable: true })
|
| 65 |
+
preferredCompute?: string;
|
| 66 |
+
|
| 67 |
+
@Column({ nullable: true })
|
| 68 |
+
endpointHosting?: string;
|
| 69 |
+
|
| 70 |
+
// Payout & Monetization
|
| 71 |
+
@Column({ nullable: true })
|
| 72 |
+
walletAddress?: string;
|
| 73 |
+
|
| 74 |
+
@Column('jsonb', { nullable: true })
|
| 75 |
+
bankAccount?: { accountName?: string; accountNumber?: string; bankCode?: string };
|
| 76 |
+
|
| 77 |
+
@Column({ nullable: true })
|
| 78 |
+
preferredCurrency?: string;
|
| 79 |
+
|
| 80 |
+
@Column({ nullable: true })
|
| 81 |
+
country?: string;
|
| 82 |
+
|
| 83 |
+
@Column({ nullable: true })
|
| 84 |
+
taxId?: string;
|
| 85 |
+
|
| 86 |
+
// Verification
|
| 87 |
+
@Column({ nullable: true })
|
| 88 |
+
idDocumentUrl?: string;
|
| 89 |
+
|
| 90 |
+
@Column({ type: 'enum', enum: VerificationStatus, default: VerificationStatus.UNVERIFIED })
|
| 91 |
+
verificationStatus: VerificationStatus;
|
| 92 |
+
|
| 93 |
+
@Column({ default: false })
|
| 94 |
+
portfolioApproved: boolean;
|
| 95 |
+
|
| 96 |
+
@CreateDateColumn()
|
| 97 |
+
createdAt: Date;
|
| 98 |
+
|
| 99 |
+
@UpdateDateColumn()
|
| 100 |
+
updatedAt: Date;
|
| 101 |
+
}
|
src/entities/User.ts
CHANGED
|
@@ -29,6 +29,9 @@ export class User {
|
|
| 29 |
@Column({ default: false })
|
| 30 |
isAdmin: boolean;
|
| 31 |
|
|
|
|
|
|
|
|
|
|
| 32 |
@Column({ default: true })
|
| 33 |
isActive: boolean;
|
| 34 |
|
|
|
|
| 29 |
@Column({ default: false })
|
| 30 |
isAdmin: boolean;
|
| 31 |
|
| 32 |
+
@Column({ default: false })
|
| 33 |
+
isCreator: boolean;
|
| 34 |
+
|
| 35 |
@Column({ default: true })
|
| 36 |
isActive: boolean;
|
| 37 |
|
src/routes/adminRoutes.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Router } from 'express';
|
| 2 |
+
import { authenticate, requireAdmin } from '../middlewares/auth';
|
| 3 |
+
import { AdminController } from '../controllers/adminController';
|
| 4 |
+
|
| 5 |
+
const router = Router();
|
| 6 |
+
const adminController = new AdminController();
|
| 7 |
+
|
| 8 |
+
router.get('/overview', authenticate, requireAdmin, adminController.overview.bind(adminController));
|
| 9 |
+
|
| 10 |
+
export default router;
|
src/routes/authRoutes.ts
CHANGED
|
@@ -4,9 +4,15 @@ import jwt from 'jsonwebtoken';
|
|
| 4 |
import { AppDataSource } from '../config/database';
|
| 5 |
import { User } from '../entities/User';
|
| 6 |
import { authenticate } from '../middlewares/auth';
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
const router = Router();
|
| 9 |
const userRepository = AppDataSource.getRepository(User);
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
/**
|
| 12 |
* Register new user
|
|
@@ -110,7 +116,7 @@ router.post('/login', async (req: Request, res: Response) => {
|
|
| 110 |
});
|
| 111 |
|
| 112 |
/**
|
| 113 |
-
* Get current user
|
| 114 |
*/
|
| 115 |
router.get('/me', authenticate, async (req: Request, res: Response) => {
|
| 116 |
try {
|
|
@@ -124,7 +130,29 @@ router.get('/me', authenticate, async (req: Request, res: Response) => {
|
|
| 124 |
return res.status(404).json({ error: 'User not found' });
|
| 125 |
}
|
| 126 |
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
} catch (error) {
|
| 129 |
console.error('Get me error:', error);
|
| 130 |
res.status(500).json({ error: 'Failed to get user' });
|
|
|
|
| 4 |
import { AppDataSource } from '../config/database';
|
| 5 |
import { User } from '../entities/User';
|
| 6 |
import { authenticate } from '../middlewares/auth';
|
| 7 |
+
import { WalletService } from '../services/walletService';
|
| 8 |
+
import { Agent } from '../entities/Agent';
|
| 9 |
+
import { ChatMessage } from '../entities/ChatMessage';
|
| 10 |
|
| 11 |
const router = Router();
|
| 12 |
const userRepository = AppDataSource.getRepository(User);
|
| 13 |
+
const agentRepository = AppDataSource.getRepository(Agent);
|
| 14 |
+
const messageRepository = AppDataSource.getRepository(ChatMessage);
|
| 15 |
+
const walletService = new WalletService();
|
| 16 |
|
| 17 |
/**
|
| 18 |
* Register new user
|
|
|
|
| 116 |
});
|
| 117 |
|
| 118 |
/**
|
| 119 |
+
* Get current user (profile + wallet + counts)
|
| 120 |
*/
|
| 121 |
router.get('/me', authenticate, async (req: Request, res: Response) => {
|
| 122 |
try {
|
|
|
|
| 130 |
return res.status(404).json({ error: 'User not found' });
|
| 131 |
}
|
| 132 |
|
| 133 |
+
// Wallet summary
|
| 134 |
+
const wallet = await walletService.getOrCreateWallet(userId);
|
| 135 |
+
|
| 136 |
+
// Counts
|
| 137 |
+
const agentsCreated = await agentRepository.count({ where: { creatorId: userId } });
|
| 138 |
+
const totalMessages = await messageRepository.count({ where: { userId } });
|
| 139 |
+
|
| 140 |
+
res.json({
|
| 141 |
+
id: user.id,
|
| 142 |
+
email: user.email,
|
| 143 |
+
name: user.name,
|
| 144 |
+
isAdmin: user.isAdmin,
|
| 145 |
+
createdAt: user.createdAt,
|
| 146 |
+
wallet: {
|
| 147 |
+
balance: Number(wallet.balance),
|
| 148 |
+
balanceInDollars: walletService.pointsToDollars(Number(wallet.balance)),
|
| 149 |
+
freeTasksRemaining: await walletService.getFreeTasksRemaining(userId),
|
| 150 |
+
},
|
| 151 |
+
counts: {
|
| 152 |
+
agentsCreated,
|
| 153 |
+
totalMessages,
|
| 154 |
+
},
|
| 155 |
+
});
|
| 156 |
} catch (error) {
|
| 157 |
console.error('Get me error:', error);
|
| 158 |
res.status(500).json({ error: 'Failed to get user' });
|
src/routes/creatorAuthRoutes.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Router } from 'express';
|
| 2 |
+
import { CreatorAuthController } from '../controllers/creatorAuthController';
|
| 3 |
+
|
| 4 |
+
const router = Router();
|
| 5 |
+
const controller = new CreatorAuthController();
|
| 6 |
+
|
| 7 |
+
router.post('/register', controller.register.bind(controller));
|
| 8 |
+
router.post('/login', controller.login.bind(controller));
|
| 9 |
+
|
| 10 |
+
export default router;
|
src/routes/creatorRoutes.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Router } from 'express';
|
| 2 |
+
import { authenticate } from '../middlewares/auth';
|
| 3 |
+
import { CreatorController } from '../controllers/creatorController';
|
| 4 |
+
|
| 5 |
+
const router = Router();
|
| 6 |
+
const creatorController = new CreatorController();
|
| 7 |
+
|
| 8 |
+
router.get('/me/overview', authenticate, creatorController.overview.bind(creatorController));
|
| 9 |
+
router.get('/me/earnings', authenticate, creatorController.earnings.bind(creatorController));
|
| 10 |
+
|
| 11 |
+
export default router;
|
src/routes/userRoutes.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Router } from 'express';
|
| 2 |
+
import { authenticate } from '../middlewares/auth';
|
| 3 |
+
import { UserController } from '../controllers/userController';
|
| 4 |
+
|
| 5 |
+
const router = Router();
|
| 6 |
+
const userController = new UserController();
|
| 7 |
+
|
| 8 |
+
router.get('/me/history', authenticate, userController.getMyHistory.bind(userController));
|
| 9 |
+
|
| 10 |
+
export default router;
|