Dwayne Harris 5 years ago
parent
commit
28a6aef9b9
  1. 422
      package-lock.json
  2. 3
      package.json
  3. 54
      src/lib/collections.ts
  4. 6
      src/lib/crypto.ts
  5. 27
      src/plugins/api/authentication.ts
  6. 8
      src/plugins/api/groups.ts
  7. 114
      src/plugins/api/posts.ts
  8. 53
      src/plugins/api/users.ts
  9. 7
      src/schemas.ts
  10. 15
      src/types/collections.ts

422
package-lock.json

@ -54,13 +54,11 @@
"integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==",
"dev": true
},
"@phc/format": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@phc/format/-/format-0.5.0.tgz",
"integrity": "sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg==",
"requires": {
"safe-buffer": "^5.1.2"
}
"@types/bcryptjs": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
"integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==",
"dev": true
},
"@types/debug": {
"version": "4.1.5",
@ -124,7 +122,8 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"abort-controller": {
"version": "3.0.0",
@ -162,7 +161,8 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
@ -194,59 +194,11 @@
}
}
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"archy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
},
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"argon2": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.25.0.tgz",
"integrity": "sha512-Kvj6nb4qrvLi1yen9UZ69hbpRiA47qm6eZNhaQwTtfhahjq3S0GV/K78yrvIWnJOoTQTPxrDglbGGBitdbvM3w==",
"requires": {
"@phc/format": "^0.5.0",
"node-addon-api": "^1.7.1",
"node-pre-gyp": "^0.13.0"
}
},
"args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
@ -335,7 +287,8 @@
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"base": {
"version": "0.11.2",
@ -392,6 +345,11 @@
}
}
},
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"binary-extensions": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
@ -422,6 +380,7 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -526,11 +485,6 @@
"upath": "^1.1.1"
}
},
"chownr": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
},
"ci-info": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
@ -566,11 +520,6 @@
"integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
"dev": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@ -648,7 +597,8 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"configstore": {
"version": "3.1.2",
@ -664,11 +614,6 @@
"xdg-basedir": "^3.0.0"
}
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"content-security-policy-builder": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz",
@ -740,7 +685,8 @@
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true
},
"deepmerge": {
"version": "4.2.1",
@ -801,21 +747,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"diagnostics": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
@ -1288,19 +1224,6 @@
"resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz",
"integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g=="
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"requires": {
"minipass": "^2.6.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
@ -1854,54 +1777,6 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
}
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@ -1916,19 +1791,6 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true
},
"glob": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@ -2011,11 +1873,6 @@
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"has-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@ -2088,14 +1945,6 @@
"depd": "2.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ienoopen": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz",
@ -2107,14 +1956,6 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true
},
"ignore-walk": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.2.tgz",
"integrity": "sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw==",
"requires": {
"minimatch": "^3.0.4"
}
},
"import-lazy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
@ -2127,15 +1968,6 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@ -2144,7 +1976,8 @@
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"ip-regex": {
"version": "2.1.0",
@ -2269,7 +2102,8 @@
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-glob": {
"version": "4.0.1",
@ -2648,6 +2482,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -2655,31 +2490,8 @@
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
},
"dependencies": {
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
},
"minizlib": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"requires": {
"minipass": "^2.9.0"
}
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"mixin-deep": {
"version": "1.3.2",
@ -2702,21 +2514,6 @@
}
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
@ -2759,26 +2556,6 @@
"to-regex": "^3.0.1"
}
},
"needle": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
"integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
}
}
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@ -2794,44 +2571,11 @@
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-1.0.4.tgz",
"integrity": "sha512-7cNtLKTAg0LrW3ViS2C7UfIzbL3rZd8L0++5MidbKqQVJ8yrH6+1VRSHl33P0ZjBTbOJd37d9EYekvHyKkB0QQ=="
},
"node-addon-api": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz",
"integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"node-pre-gyp": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz",
"integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
},
"dependencies": {
"nopt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
}
}
},
"nodemon": {
"version": "1.19.4",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz",
@ -2894,20 +2638,6 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
"npm-bundled": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
},
"npm-packlist": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz",
"integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
}
},
"npm-run-all": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
@ -2933,27 +2663,6 @@
"path-key": "^2.0.0"
}
},
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@ -3030,11 +2739,6 @@
"resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz",
"integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4="
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-name": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
@ -3044,20 +2748,6 @@
"windows-release": "^3.1.0"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@ -3100,7 +2790,8 @@
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
@ -3266,6 +2957,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@ -3413,14 +3105,6 @@
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"requires": {
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -3451,11 +3135,6 @@
"ret": "~0.2.0"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -3490,11 +3169,6 @@
"resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz",
"integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@ -3785,6 +3459,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
@ -3813,6 +3488,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@ -3831,7 +3507,8 @@
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
@ -3842,27 +3519,6 @@
"has-flag": "^3.0.0"
}
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
},
"term-size": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
@ -4200,14 +3856,6 @@
"isexe": "^2.0.0"
}
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"widest-line": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",

3
package.json

@ -11,6 +11,7 @@
"watch": "run-p watch-typescript watch-server"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/dotenv": "^6.1.1",
"@types/jsonwebtoken": "^8.3.5",
"@types/lodash": "^4.14.144",
@ -23,7 +24,7 @@
"dependencies": {
"@azure/cosmos": "^3.3.6",
"@azure/storage-blob": "^10.5.0",
"argon2": "^0.25.0",
"bcryptjs": "^2.4.3",
"dotenv": "^8.2.0",
"fastify": "^2.10.0",
"fastify-cors": "^2.1.3",

54
src/lib/collections.ts

@ -1,22 +1,58 @@
import { CosmosClient } from '@azure/cosmos'
import { Logger } from 'fastify'
import compact from 'lodash/compact'
import uniq from 'lodash/uniq'
import { DatabaseItem } from '../types'
import { containerFor, createQuerySpec, queryItems, getItem } from './database'
import { User, UserSubscription, UserBlock, GroupMembership, UserItemType, GroupItemType } from '../types/collections'
import { User, UserSubscription, UserBlock, Group, GroupMembership, UserItemType, GroupItemType } from '../types/collections'
export async function getUser(client: CosmosClient, id: string): Promise<User | undefined> {
const user = await getItem<User>({
container: containerFor(client, 'Users'),
id,
})
if (!user) return
const getGroup = async (id: string) => {
return await getItem<Group>({
container: containerFor(client, 'Groups'),
id,
})
}
return {
...user,
group: user.groupId ? await getGroup(user.groupId) : undefined,
}
}
export async function getUsers(client: CosmosClient, ids: string[], logger?: Logger): Promise<User[]> {
return await queryItems<User>({
const users = await queryItems<User>({
container: containerFor(client, 'Users'),
query: createQuerySpec(
`SELECT u.id, u.name, u.imageUrl, u.coverImageUrl, u['group'], u.created FROM Users u WHERE ARRAY_CONTAINS(@ids, u.id)`,
{
ids: uniq(ids),
}
'SELECT u.id, u.name, u.imageUrl, u.coverImageUrl, u.groupId, u.created FROM Users u WHERE ARRAY_CONTAINS(@ids, u.id)',
{ ids: uniq(ids) }
),
logger,
})
const groups = await queryItems<Group>({
container: containerFor(client, 'Groups'),
query: createQuerySpec(
'SELECT g.id, g.name, g.imageUrl, g.coverImageUrl, g.iconImageUrl, g.created FROM Groups g WHERE ARRAY_CONTAINS(@ids, g.id)',
{ ids: uniq(compact(users.map(u => u.groupId))) }
),
logger,
})
return users.map(user => {
return {
...user,
group: groups.find(group => group.id === user.groupId),
groupId: undefined
}
})
}
export async function getUsersFromItems<T extends DatabaseItem>(client: CosmosClient, items: T[], logger?: Logger): Promise<User[]> {
@ -28,7 +64,7 @@ export async function getApprovedSubscriptions(client: CosmosClient, from: strin
container: containerFor(client, 'Users'),
query: createQuerySpec(
`SELECT u.id FROM Users u WHERE
u.subscriberId = @to
u.id = @to
u.pk = @from AND
u.t = @type AND
u.pending = false`,
@ -59,7 +95,7 @@ export async function getUserMembership(client: CosmosClient, userId: string, lo
})
if (!user) return
if (!user.group) return
if (!user.groupId) return
const memberships = await queryItems<GroupMembership>({
container: containerFor(client, 'Groups'),
@ -70,7 +106,7 @@ export async function getUserMembership(client: CosmosClient, userId: string, lo
g.userId = @user
`,
{
pk: user.group.id,
pk: user.groupId,
type: GroupItemType.Membership,
user: user.id,
}

6
src/lib/crypto.ts

@ -1,13 +1,13 @@
import argon2 from 'argon2'
import { hash, compare } from 'bcryptjs'
import { randomBytes } from 'crypto'
import jwt, { SignOptions, VerifyOptions } from 'jsonwebtoken'
export async function hashPassword(password: string): Promise<string> {
return await argon2.hash(password)
return await hash(password, 8)
}
export async function verifyPassword(hash: string, password: string): Promise<boolean> {
return await argon2.verify(hash, password)
return await compare(hash, password)
}
export const generateString = (length: number) => randomBytes(Math.max(Math.round(length / 2), 5)).toString('hex')

27
src/plugins/api/authentication.ts

@ -13,6 +13,7 @@ import { Server, IncomingMessage, ServerResponse } from 'http'
import { MIN_ID_LENGTH, MAX_ID_LENGTH, MAX_NAME_LENGTH, MIN_PASSWORD_LENGTH, INSTALLATION_PARTITION_KEY } from '../../constants'
import { tokenResponseSchema, selfSchema, errorSchema } from '../../schemas'
import { createAccessToken, createRefreshToken } from '../../lib/authentication'
import { getUser } from '../../lib/collections'
import { hashPassword, verifyPassword, JWT } from '../../lib/crypto'
import { containerFor, getItem, queryItems, normalize, createQuerySpec } from '../../lib/database'
import { badRequestError, badRequestFormError, unauthorizedError, serverError } from '../../lib/errors'
@ -25,7 +26,6 @@ import {
UserToken,
Group,
GroupInvitation,
GroupPartial,
GroupMembership,
UserItemType,
UserPrivacyType,
@ -109,7 +109,6 @@ function registerRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
let userPending = false
let invitation: GroupInvitation | undefined
let group: Group | undefined
let groupPartial: GroupPartial | undefined
if (request.body.group) {
group = await getItem<Group>({ container: groupContainer, id: request.body.group })
@ -141,12 +140,6 @@ function registerRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
if (group.registration === GroupRegistrationType.Closed && !invitation) return badRequestFormError(reply, 'group', 'Group registration closed')
if (group.registration === GroupRegistrationType.Approval) userPending = true
groupPartial = {
id: group.id,
name: group.name,
iconImageUrl: group.iconImageUrl,
}
}
const appContainer = containerFor(server.database.client, 'Apps')
@ -177,7 +170,7 @@ function registerRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
id,
pk: id,
t: UserItemType.User,
group: groupPartial,
groupId: group ? group.id : undefined,
name,
about,
email,
@ -380,22 +373,10 @@ function selfRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
if (!server.database) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getItem<User>({
container: containerFor(server.database.client, 'Users'),
id: request.viewer.id,
})
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer) return unauthorizedError(reply)
const group = viewer.group ? await getItem<Group>({
container: containerFor(server.database.client, 'Groups'),
id: viewer.group.id,
}) : undefined
return {
...viewer,
group,
}
return viewer
})
}

8
src/plugins/api/groups.ts

@ -137,7 +137,7 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const groupContainer = containerFor(server.database.client, 'Groups')
if (viewer.group) return badRequestError(reply)
if (viewer.groupId) return badRequestError(reply)
const { name, about, registration, imageUrl, coverImageUrl, iconImageUrl } = request.body
const id = normalize(name)
@ -176,11 +176,7 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
await viewerItem.replace<User>({
...viewer,
group: {
id: group.id,
name: group.name,
iconImageUrl: group.iconImageUrl,
},
groupId: group.id,
})
await groupContainer.items.create<GroupLog>({

114
src/plugins/api/posts.ts

@ -18,7 +18,7 @@ import { SHORT_TEXT_LENGTH, SUBSCRIBER_MAX_SIZE, INSTALLATION_PARTITION_KEY, APP
import { userSchema, postSchema, errorSchema } from '../../schemas'
import { unauthorizedError, serverError, badRequestError, badRequestFormError, notFoundError } from '../../lib/errors'
import { trimContent, createPostId } from '../../lib/utils'
import { getUsers, getApprovedSubscriptions, getUserBlocks } from '../../lib/collections'
import { getUsers, getApprovedSubscriptions, getUserBlocks, getUser } from '../../lib/collections'
import { containerFor, createQuerySpec, queryItems, getItem, normalize } from '../../lib/database'
import {
@ -106,7 +106,7 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
if (!viewer) return serverError(reply)
if (viewer.pending) return badRequestError(reply, 'User requires approval')
if (!viewer.group) return badRequestError(reply, 'User must belong to a group')
if (!viewer.groupId) return badRequestError(reply, 'User must belong to a group')
const postId = createPostId()
@ -167,15 +167,13 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
logger,
})
if (subscribers.length < SUBSCRIBER_MAX_SIZE) {
for (const subscriber of subscribers) {
await userContainer.items.create<UserTimelinePost>({
postId,
pk: subscriber.id!,
t: UserItemType.Timeline,
created: Date.now(),
})
}
for (const uid of [userId, ...subscribers.map(s => s.id)]) {
await userContainer.items.create<UserTimelinePost>({
id: postId,
pk: uid,
t: UserItemType.Timeline,
created: Date.now(),
})
}
return {
@ -305,10 +303,10 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
const id = normalize(request.params.id)
const userContainer = containerFor(server.database.client, 'Users')
const user = await getItem<User>({ container: userContainer, id })
const user = await getUser(server.database.client, id)
if (!user) return notFoundError(reply)
if (!user.group) return notFoundError(reply)
if (!user.groupId) return notFoundError(reply)
switch (user.privacy) {
case UserPrivacyType.Private:
@ -326,10 +324,10 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
const viewer = await getItem<User>({ container: userContainer, id: request.viewer.id })
if (!viewer) return serverError(reply)
if (!viewer.group) return unauthorizedError(reply)
if (!viewer.groupId) return unauthorizedError(reply)
const subscriptions = await getApprovedSubscriptions(server.database.client, user.id, request.viewer.id, request.log)
if (viewer.group.id !== user.group.id && subscriptions.length === 0) return unauthorizedError(reply)
if (viewer.groupId !== user.groupId && subscriptions.length === 0) return unauthorizedError(reply)
break
}
@ -338,9 +336,9 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
if (request.viewer) {
const viewer = await getItem<User>({ container: userContainer, id: request.viewer.id })
if (!viewer) return serverError(reply)
if (!viewer.group) return unauthorizedError(reply)
if (!viewer.groupId) return unauthorizedError(reply)
const blocks = await getUserBlocks(server.database.client, user.id, [viewer.id, viewer.group.id], request.log)
const blocks = await getUserBlocks(server.database.client, user.id, [viewer.id, viewer.groupId], request.log)
if (blocks.length > 0) return unauthorizedError(reply)
}
@ -359,18 +357,82 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
logger: request.log
})
const group = await getItem<Group>({ container: containerFor(server.database.client, 'Groups'), id: user.group.id })
return {
user: {
...user,
group,
},
user,
posts,
}
})
}
function timelineRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Query {
continuation?: string
}
const options: RouteShorthandOptions = {
schema: {
querystring: {
type: 'object',
properties: {
continuation: { type: 'string' },
},
},
response: {
200: {
type: 'object',
properties: {
posts: {
type: 'array',
items: postSchema,
},
},
},
400: errorSchema,
},
},
}
server.get<Query, DefaultParams, DefaultHeaders, DefaultBody>('/api/timeline', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const userContainer = containerFor(server.database.client, 'Users')
const postContainer = containerFor(server.database.client, 'Posts')
const { resources: timelinePosts, requestCharge, continuation } = await userContainer.items.query<UserTimelinePost>(
createQuerySpec('SELECT * FROM Users u WHERE u.pk = @pk AND u.t = @type ORDER BY u.created', {
pk: request.viewer.id,
type: UserItemType.Timeline,
}),
{
maxItemCount: 40,
continuation: request.query.continuation,
}
).fetchAll()
request.log.trace('Query: %d', requestCharge)
const posts = await queryItems<Post>({
container: postContainer,
query: createQuerySpec('SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@posts, p.id) ORDER BY p.created DESC', {
posts: timelinePosts.map(p => p.id),
}),
logger: request.log
})
const users = await getUsers(server.database.client, posts.map(p => p.userId))
return {
posts: posts.map(post => ({
...post,
user: users.find(u => u.id === post.userId),
userId: undefined,
})),
continuation,
}
})
}
function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Params {
id: string
@ -455,7 +517,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
if (request.viewer) {
const viewer = await getItem<User>({ container: containerFor(server.database.client, 'Users'), id: request.viewer.id })
if (!viewer) return serverError(reply)
if (!viewer.group) return unauthorizedError(reply)
if (!viewer.groupId) return unauthorizedError(reply)
const blockQuery = createQuerySpec(`
SELECT g.userId FROM Groups g WHERE
@ -465,7 +527,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
ARRAY_CONTAINS(@ids, g.userId)
`, {
viewer: viewer.id,
viewerGroup: viewer.group.id,
viewerGroup: viewer.groupId,
ids: userIds,
type: GroupItemType.Block,
})
@ -498,7 +560,9 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = async server => {
createPostRoute(server)
createAppPostRoute(server)
postsByUserRoute(server)
timelineRoute(server)
postRoute(server)
}

53
src/plugins/api/users.ts

@ -11,7 +11,7 @@ import {
import { Server, IncomingMessage, ServerResponse } from 'http'
import { unauthorizedError, serverError, notFoundError, badRequestError } from '../../lib/errors'
import { getUserBlocks } from '../../lib/collections'
import { getUserBlocks, getUser } from '../../lib/collections'
import { containerFor, createQuerySpec, queryItems, getItem, normalize } from '../../lib/database'
import { deleteMedia, attachMedia } from '../../lib/media'
@ -187,26 +187,37 @@ function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
if (!server.database) return serverError(reply)
const userContainer = containerFor(server.database.client, 'Users')
const user = await getItem<User>({ container: userContainer, id: request.params.id })
const user = await getUser(server.database.client, request.params.id)
if (!user) return notFoundError(reply)
let subscribed = false
let subscribedToYou = false
if (request.viewer) {
const viewer = await getItem<User>({ container: userContainer, id: request.viewer.id })
if (!viewer) return serverError(reply)
if (!viewer.group) return unauthorizedError(reply)
if (!viewer.groupId) return unauthorizedError(reply)
const blocks = await getUserBlocks(server.database.client, user.id, [viewer.id, viewer.group.id], request.log)
const blocks = await getUserBlocks(server.database.client, user.id, [viewer.id, viewer.groupId], request.log)
if (blocks.length > 0) return unauthorizedError(reply)
}
const group = user.group ? await getItem<Group>({
container: containerFor(server.database.client, 'Groups'),
id: user.group.id,
}) : undefined
subscribed = !!(await getItem<UserSubscription>({
container: userContainer,
id: user.id,
partitionKey: viewer.id,
}))
subscribedToYou = !!(await getItem<UserSubscription>({
container: userContainer,
id: viewer.id,
partitionKey: user.id,
}))
}
return {
...user,
group,
subscribed,
subscribedToYou,
}
})
}
@ -242,9 +253,9 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
if (!user) return notFoundError(reply)
if (!viewer) return serverError(reply)
if (!viewer.group) return unauthorizedError(reply)
if (!viewer.groupId) return unauthorizedError(reply)
const subscriptionQuery = createQuerySpec(`SELECT u.id FROM Users u WHERE u.subscriberId = @user AND u.pk = @viewer AND u.t = @type`, {
const subscriptionQuery = createQuerySpec(`SELECT u.id FROM Users u WHERE u.id = @user AND u.pk = @viewer AND u.t = @type`, {
user: user.id,
viewer: viewer.id,
type: UserItemType.Subscription,
@ -259,7 +270,7 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
case UserPrivacyType.Private:
return unauthorizedError(reply)
case UserPrivacyType.Group:
if (user.group !== viewer.group) return unauthorizedError(reply)
if (user.groupId !== viewer.groupId) return unauthorizedError(reply)
case UserPrivacyType.Subscribers:
pending = true
break
@ -273,7 +284,7 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
(g.blockedId = @viewer OR g.blockedId = @viewerGroup)
`, {
user: user.id,
viewerGroup: viewer.group.id,
viewerGroup: viewer.groupId,
type: GroupItemType.Block,
})
@ -286,7 +297,7 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
if (blocks.length > 0) return badRequestError(reply, 'Invalid operation')
await userContainer.items.create<UserSubscription>({
subscriberId: user.id,
id: user.id,
pk: request.viewer.id,
t: UserItemType.Subscription,
pending,
@ -324,7 +335,7 @@ function unsubscribeRoute(server: FastifyInstance<Server, IncomingMessage, Serve
if (!user) return notFoundError(reply)
if (!viewer) return serverError(reply)
const subscriptionQuery = createQuerySpec(`SELECT u.id FROM Users u WHERE u.subscriberId = @user AND u.pk = @viewer AND u.t = @type`, {
const subscriptionQuery = createQuerySpec(`SELECT u.id FROM Users u WHERE u.id = @user AND u.pk = @viewer AND u.t = @type`, {
user: user.id,
viewer: viewer.id,
type: UserItemType.Subscription,
@ -375,7 +386,7 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
const userContainer = containerFor(server.database.client, 'Users')
const user = await getItem<User>({ container: userContainer, id: request.params.id })
if (!user) return notFoundError(reply)
if (!user.group) return badRequestError(reply)
if (!user.groupId) return badRequestError(reply)
await userContainer.items.create<UserBlock>({
blockedId: user.id,
@ -387,7 +398,7 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
})
await containerFor(server.database.client, 'Groups').items.create<GroupBlock>({
pk: user.group.id,
pk: user.groupId,
t: GroupItemType.Block,
blockedId: user.id,
userId: request.viewer.id,
@ -423,7 +434,7 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const user = await getItem<User>({ container: userContainer, id: request.params.id })
if (!user) return notFoundError(reply)
if (!user.group) return badRequestError(reply, 'Invalid operation')
if (!user.groupId) return badRequestError(reply, 'Invalid operation')
const userBlockQuery = createQuerySpec(`SELECT u.id FROM Users u WHERE u.pk = @pk AND u.blockedId = @blocked AND u.t = @type`, {
pk: request.viewer.id,
@ -444,7 +455,7 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const groupBlockQuery = createQuerySpec(
`SELECT g.id FROM Groups g WHERE g.pk = @pk AND u.blockedId = @blocked AND u.userId = @viewer AND u.t = @type`,
{
pk: user.group.id,
pk: user.groupId,
blocked: user.id,
viewer: request.viewer.id,
type: GroupItemType.Block,
@ -458,7 +469,7 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
})
for (const groupBlock of groupBlocks) {
await groupContainer.item(groupBlock.id!, user.group.id).delete()
await groupContainer.item(groupBlock.id!, user.groupId).delete()
}
reply.code(204)

7
src/schemas.ts

@ -48,12 +48,13 @@ export const userSchema: JSONSchema = {
type: 'object',
properties: {
id: { type: 'string' },
group: groupListingSchema,
name: { type: 'string' },
about: { type: 'string' },
imageUrl: { type: 'string' },
coverImageUrl: { type: 'string' },
group: groupListingSchema,
subscription: { type: 'string' },
subscribed: { type: 'boolean' },
subscribedToYou: { type: 'boolean' },
membership: { type: 'string' },
posts: { type: 'number' },
awards: { type: 'number' },
@ -110,12 +111,12 @@ export const selfSchema: JSONSchema = {
type: 'object',
properties: {
id: { type: 'string' },
group: groupListingSchema,
name: { type: 'string' },
email: { type: 'string' },
about: { type: 'string' },
imageUrl: { type: 'string' },
coverImageUrl: { type: 'string' },
group: groupListingSchema,
requiresApproval: { type: 'boolean' },
privacy: { type: 'string' },
membership: { type: 'string' },

15
src/types/collections.ts

@ -114,12 +114,6 @@ export interface Group {
created: number
}
export interface GroupPartial {
id: string
name: string
iconImageUrl?: string
}
export interface GroupMembership {
id?: string
pk: string // Group ID
@ -176,7 +170,8 @@ export interface User {
id: string
pk: string // ID
t: UserItemType.User
group?: GroupPartial
groupId?: string
group?: Group
name: string
about?: string
imageUrl?: string
@ -218,8 +213,7 @@ export interface UserPost {
}
export interface UserSubscription {
id?: string
subscriberId: string
id: string
pk: string
t: UserItemType.Subscription
pending: boolean
@ -248,8 +242,7 @@ export interface UserTransaction {
}
export interface UserTimelinePost {
id?: string
postId: string
id: string
pk: string // userId
t: UserItemType.Timeline
created: number

Loading…
Cancel
Save