Dwayne Harris 4 years ago
parent
commit
b85af70635
  1. 3
      .vscode/settings.json
  2. 213
      package-lock.json
  3. 1
      package.json
  4. 22
      src/awards.ts
  5. 4
      src/lib/authentication.ts
  6. 1
      src/lib/collections.ts
  7. 10
      src/lib/crypto.ts
  8. 2
      src/lib/database.ts
  9. 2
      src/lib/utils.ts
  10. 63
      src/plugins/api/apps.ts
  11. 9
      src/plugins/api/authentication.ts
  12. 69
      src/plugins/api/groups.ts
  13. 14
      src/plugins/api/media.ts
  14. 29
      src/plugins/api/posts.ts
  15. 39
      src/plugins/api/users.ts
  16. 46
      src/schemas.ts
  17. 30
      src/server.ts
  18. 48
      src/types/collections.ts
  19. 9
      src/types/index.ts

3
.vscode/settings.json

@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib"
}

213
package-lock.json

@ -175,6 +175,11 @@
"@types/node": "*"
}
},
"@types/swagger-schema-official": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/@types/swagger-schema-official/-/swagger-schema-official-2.0.20.tgz",
"integrity": "sha512-GHdvwZQg7i+6AUMnP+tqYJJbt59SuYu8k9dzX8E6TJ5QFXKW9FxfdfqT5g+VcyuEua+/YaRzumqJ6wcQMuSP5Q=="
},
"@types/tunnel": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz",
@ -264,6 +269,14 @@
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
@ -352,8 +365,7 @@
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"base": {
"version": "0.11.2",
@ -445,7 +457,6 @@
"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"
@ -662,8 +673,7 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"configstore": {
"version": "3.1.2",
@ -817,6 +827,11 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"diagnostics": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
@ -865,6 +880,11 @@
"safe-buffer": "^5.0.1"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"enabled": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
@ -873,6 +893,11 @@
"env-variable": "0.0.x"
}
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@ -926,12 +951,27 @@
"is-symbol": "^1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
@ -1193,6 +1233,28 @@
}
}
},
"fastify-static": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/fastify-static/-/fastify-static-2.5.1.tgz",
"integrity": "sha512-aPpyT+ADHtZ/i35Ly6Cyyz9DA6HRwgJr0bJEMucmTxaXBIO5LaEsIOTzTP+U0/UBScer/mL4x2eSHp9l6WTnkw==",
"requires": {
"fastify-plugin": "^1.6.0",
"glob": "^7.1.4",
"readable-stream": "^3.4.0",
"send": "^0.16.0"
}
},
"fastify-swagger": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/fastify-swagger/-/fastify-swagger-2.5.0.tgz",
"integrity": "sha512-WF3LpwwZunLtxzs1Dukwg7P4uBR9f2cwXL/JKD5D4O9Leq/S2QUfMNZg0eHxe4bJy1aUS9tt7IAHvY3ybgbApg==",
"requires": {
"@types/swagger-schema-official": "^2.0.20",
"fastify-plugin": "^1.6.0",
"fastify-static": "^2.5.1",
"js-yaml": "^3.12.1"
}
},
"fastq": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz",
@ -1284,6 +1346,16 @@
"resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz",
"integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g=="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"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",
@ -1851,6 +1923,19 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"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",
@ -2005,6 +2090,29 @@
"depd": "2.0.0"
}
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
},
"dependencies": {
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}
}
},
"ienoopen": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz",
@ -2028,6 +2136,15 @@
"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",
@ -2295,6 +2412,15 @@
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
"dev": true
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@ -2525,6 +2651,11 @@
"reusify": "^1.0.2"
}
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.40.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
@ -2542,7 +2673,6 @@
"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"
}
@ -2786,6 +2916,14 @@
"isobject": "^3.0.1"
}
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -2850,8 +2988,7 @@
"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=",
"dev": true
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
"version": "1.0.2",
@ -3018,6 +3155,11 @@
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.3.tgz",
"integrity": "sha512-dy1yjycmn9blucmJLXOfZDx1ikZJUi6E8bBZLnhPG5gBrVhHXx2xVyqqgKBubVNEXmx51dBACMHpoMQK/N/AXQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@ -3234,6 +3376,46 @@
"resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz",
"integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@ -3257,6 +3439,11 @@
}
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -3494,6 +3681,11 @@
"readable-stream": "^3.0.0"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
@ -3520,6 +3712,11 @@
}
}
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",

1
package.json

@ -29,6 +29,7 @@
"fastify": "^2.10.0",
"fastify-cors": "^2.2.0",
"fastify-helmet": "^3.0.2",
"fastify-swagger": "^2.5.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",

22
src/awards.ts

@ -1,22 +0,0 @@
import { AwardDefinition } from './types'
const awards: AwardDefinition[] = [
{
id: 'like',
name: 'Like',
imageUrl: '',
text: '',
cost: 0,
reward: 0,
},
{
id: 'bronze',
name: 'Bronze',
imageUrl: '',
text: 'Bronze Award!',
cost: 2000,
reward: 200,
},
]
export default awards

4
src/lib/authentication.ts

@ -2,9 +2,7 @@ import { JWT } from '../lib/crypto'
import { createRefreshToken as createToken } from '../lib/utils'
import { UserToken, UserItemType } from '../types/collections'
export async function createAccessToken(userId: string): Promise<string> {
return await JWT.sign({ sub: userId }, { expiresIn: process.env.TOKEN_EXPIRATION })
}
export const createAccessToken = async (userId: string) => await JWT.sign({ sub: userId }, { expiresIn: process.env.TOKEN_EXPIRATION })
export function createRefreshToken(userId: string, userAgent: string, ip: string): UserToken {
return {

1
src/lib/collections.ts

@ -27,6 +27,7 @@ export async function getUser(client: CosmosClient, id: string): Promise<User |
group: user.groupId ? await getGroup(user.groupId) : undefined,
}
}
export async function getUsers(client: CosmosClient, ids: string[], logger?: Logger): Promise<User[]> {
const users = await queryItems<User>({
container: containerFor(client, 'Users'),

10
src/lib/crypto.ts

@ -2,14 +2,8 @@ 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 hash(password, 8)
}
export async function comparePassword(password: string, hash: string): Promise<boolean> {
return await compare(password, hash)
}
export const hashPassword = async (password: string) => await hash(password, 8)
export const comparePassword = async (password: string, hash: string) => await compare(password, hash)
export const generateString = (length: number) => randomBytes(Math.max(Math.round(length / 2), 5)).toString('hex')
export namespace JWT {

2
src/lib/database.ts

@ -40,7 +40,7 @@ interface GetItemOptions {
export async function getItem<T>(options: GetItemOptions): Promise<T | undefined> {
const { container, id, partitionKey, logger } = options
const { resource, requestCharge } = await container.item(id, partitionKey || id).read<T>()
const { resource, requestCharge } = await container.item(id, partitionKey ?? id).read<T>()
if (logger) logger.trace('Get: %d', requestCharge)
return resource

2
src/lib/utils.ts

@ -10,9 +10,9 @@ export function trimContent(content?: string, length: number = 128): string {
export const createId = () => v4().replace(/-/g, '')
export const createPostId = () => 'p' + createId()
export const createInvitationCode = () => generateString(8)
export const createInstallationId = () => 'i' + createId()
export const createRefreshToken = () => 'r' + createId()
export const createInvitationCode = () => generateString(8)
export async function wait(ms: number = 5000): Promise<void> {
return new Promise(resolve => {

63
src/plugins/api/apps.ts

@ -21,7 +21,7 @@ import { createInstallationId } from '../../lib/utils'
import { APP_PARTITION_KEY, MAX_NAME_LENGTH, INSTALLATION_PARTITION_KEY } from '../../constants'
import { App, User, Installation, UserSettings, InstallationSettings } from '../../types/collections'
import { App, User, Installation, InstallationSettings } from '../../types/collections'
import { PluginOptions } from '../../types'
function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
@ -31,6 +31,8 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
const options: RouteShorthandOptions = {
schema: {
description: 'Check App ID availability.',
tags: ['app'],
body: {
type: 'object',
required: ['name'],
@ -43,6 +45,7 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
id: { type: 'string' },
@ -80,6 +83,8 @@ function appsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const options: RouteShorthandOptions = {
schema: {
description: 'Get the list of Apps.',
tags: ['app'],
querystring: {
type: 'object',
properties: {
@ -89,6 +94,7 @@ function appsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
apps: {
@ -139,6 +145,8 @@ function selfAppsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Get installed Apps.',
tags: ['app'],
querystring: {
type: 'object',
properties: {
@ -147,6 +155,7 @@ function selfAppsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
apps: {
@ -206,6 +215,8 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Create a new App.',
tags: ['app'],
body: {
type: 'object',
required: ['version', 'name'],
@ -228,6 +239,7 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
},
response: {
200: {
description: 'App created.',
type: 'object',
properties: {
id: { type: 'string' },
@ -330,6 +342,8 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Update an App.',
tags: ['app'],
params: {
type: 'object',
properties: {
@ -356,6 +370,10 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
},
},
response: {
204: {
description: 'App updated.',
type: 'object',
},
400: errorSchema,
}
},
@ -400,7 +418,7 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
await appItem.replace<App>({
...app,
version,
name: name || app.name,
name: name ?? app.name,
imageUrl,
coverImageUrl,
iconImageUrl,
@ -447,6 +465,8 @@ function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
const options: RouteShorthandOptions = {
schema: {
description: 'Get App.',
tags: ['app'],
params: {
type: 'object',
properties: {
@ -495,6 +515,8 @@ function installRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const options: RouteShorthandOptions = {
schema: {
description: 'Install an App.',
tags: ['app'],
params: {
type: 'object',
properties: {
@ -502,8 +524,12 @@ function installRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
},
},
response: {
204: {
description: 'App installed.',
type: 'object',
},
400: errorSchema,
}
},
},
}
@ -575,6 +601,8 @@ function uninstallRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
const options: RouteShorthandOptions = {
schema: {
description: 'Uninstall App.',
tags: ['app'],
params: {
type: 'object',
properties: {
@ -582,8 +610,12 @@ function uninstallRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
},
},
response: {
204: {
description: 'App uninstalled.',
type: 'object',
},
400: errorSchema,
}
},
},
}
@ -642,8 +674,11 @@ function uninstallRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
function installationsRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
const options: RouteShorthandOptions = {
schema: {
description: 'Get authenticated user Installations.',
tags: ['app'],
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
installations: {
@ -722,6 +757,8 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Activate an App.',
tags: ['app'],
headers: {
type: 'object',
required: ['adminkey'],
@ -736,6 +773,10 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
},
},
response: {
204: {
description: 'App activated.',
type: 'object',
},
400: errorSchema,
}
},
@ -769,6 +810,8 @@ function setPreinstallRoute(server: FastifyInstance<Server, IncomingMessage, Ser
const options: RouteShorthandOptions = {
schema: {
description: 'Update an App to be preinstalled on new User accounts.',
tags: ['app'],
headers: {
type: 'object',
required: ['adminkey'],
@ -783,6 +826,10 @@ function setPreinstallRoute(server: FastifyInstance<Server, IncomingMessage, Ser
},
},
response: {
204: {
description: 'Successful response.',
type: 'object',
},
400: errorSchema,
}
},
@ -817,6 +864,8 @@ function updateSettingsRoute(server: FastifyInstance<Server, IncomingMessage, Se
const options: RouteShorthandOptions = {
schema: {
description: 'Update Installation settings.',
tags: ['app'],
body: {
type: 'object',
required: ['settings'],
@ -825,8 +874,12 @@ function updateSettingsRoute(server: FastifyInstance<Server, IncomingMessage, Se
},
},
response: {
204: {
description: 'Successful response.',
type: 'object',
},
400: errorSchema,
}
},
},
}

9
src/plugins/api/authentication.ts

@ -55,6 +55,8 @@ function registerRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Create a new User.',
tags: ['authentication'],
body: {
type: 'object',
required: ['id', 'email', 'password', 'theme'],
@ -187,7 +189,6 @@ function registerRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
autoPlayGifs: true,
},
installations,
awards: 0,
points: 0,
balance: 0,
posts: 0,
@ -250,6 +251,8 @@ function authenticateRoute(server: FastifyInstance<Server, IncomingMessage, Serv
const options: RouteShorthandOptions = {
schema: {
description: 'Authenticate a User.',
tags: ['authentication'],
body: {
type: 'object',
required: ['id', 'password'],
@ -300,6 +303,8 @@ function refreshRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const options: RouteShorthandOptions = {
schema: {
description: 'Get new Access and Refresh tokens.',
tags: ['authentication'],
headers: {
type: 'object',
properties: {
@ -370,6 +375,8 @@ function refreshRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
function selfRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
const options: RouteShorthandOptions = {
schema: {
description: 'Get authenticated User.',
tags: ['authentication'],
response: {
200: selfSchema,
400: errorSchema,

69
src/plugins/api/groups.ts

@ -45,6 +45,8 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
const options: RouteShorthandOptions = {
schema: {
description: 'Check Group ID availability.',
tags: ['group'],
body: {
type: 'object',
required: ['name'],
@ -57,6 +59,7 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
id: { type: 'string' },
@ -98,6 +101,8 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Create a new Group.',
tags: ['group'],
body: {
type: 'object',
required: ['name', 'registration'],
@ -119,7 +124,8 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
},
},
response: {
200: {
201: {
description: 'Group created.',
type: 'object',
properties: {
id: { type: 'string' },
@ -196,6 +202,8 @@ function createRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
if (coverImageUrl) await attachMedia(mediaContainer, coverImageUrl)
if (iconImageUrl) await attachMedia(mediaContainer, iconImageUrl)
reply.code(201)
return {
id: group.id,
}
@ -209,6 +217,8 @@ function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
const options: RouteShorthandOptions = {
schema: {
description: 'Get a Group.',
tags: ['group'],
params: {
type: 'object',
properties: {
@ -217,6 +227,7 @@ function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
},
response: {
200: groupListingSchema,
400: errorSchema,
},
},
}
@ -286,6 +297,8 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Update a Group.',
tags: ['group'],
body: {
type: 'object',
properties: {
@ -306,6 +319,10 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
},
},
response: {
204: {
description: 'Group updated.',
type: 'object',
},
400: errorSchema,
},
},
@ -402,6 +419,8 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
const options: RouteShorthandOptions = {
schema: {
description: 'Block a Group.',
tags: ['group'],
params: {
type: 'object',
properties: {
@ -414,6 +433,13 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
description: { type: 'string' },
},
},
response: {
204: {
description: 'Group blocked.',
type: 'object',
},
400: errorSchema,
},
},
}
@ -453,12 +479,21 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const options: RouteShorthandOptions = {
schema: {
description: 'Unblock a Group.',
tags: ['group'],
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
response: {
204: {
description: 'Group unblocked.',
type: 'object',
},
400: errorSchema,
},
},
}
@ -483,6 +518,7 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
query: userBlockQuery,
logger: request.log
})
for (const userBlock of userBlocks) {
await userContainer.item(userBlock.id!, request.viewer.id).delete()
}
@ -522,6 +558,8 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Activate a Group.',
tags: ['group'],
headers: {
type: 'object',
required: ['adminkey'],
@ -535,6 +573,13 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
id: { type: 'string' },
},
},
response: {
204: {
description: 'Group activated.',
type: 'object',
},
400: errorSchema,
},
},
}
@ -570,9 +615,7 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
registration: group.registration,
members: 1,
posts: 0,
awards: 0,
points: 0,
latestAwards: [],
created: Date.now(),
})
}
@ -590,6 +633,8 @@ function listRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const options: RouteShorthandOptions = {
schema: {
description: 'Get a list of Groups.',
tags: ['group'],
querystring: {
type: 'object',
properties: {
@ -606,6 +651,7 @@ function listRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
groups: {
@ -655,6 +701,8 @@ function membersRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const options: RouteShorthandOptions = {
schema: {
description: 'Get Group members.',
tags: ['group'],
querystring: {
type: 'object',
properties: {
@ -667,6 +715,7 @@ function membersRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
members: {
@ -730,6 +779,8 @@ function createInvitationRoute(server: FastifyInstance<Server, IncomingMessage,
const options: RouteShorthandOptions = {
schema: {
description: 'Create a Group Invitation.',
tags: ['group'],
body: {
type: 'object',
properties: {
@ -738,12 +789,14 @@ function createInvitationRoute(server: FastifyInstance<Server, IncomingMessage,
},
},
response: {
200: {
201: {
description: 'Group Invitation created.',
type: 'object',
properties: {
code: { type: 'string' },
},
},
400: errorSchema,
},
},
}
@ -782,6 +835,8 @@ function createInvitationRoute(server: FastifyInstance<Server, IncomingMessage,
created: Date.now(),
})
reply.code(201)
return {
code,
}
@ -795,6 +850,8 @@ function invitationsRoute(server: FastifyInstance<Server, IncomingMessage, Serve
const options: RouteShorthandOptions = {
schema: {
description: 'Get a list of Group Invitations.',
tags: ['group'],
querystring: {
type: 'object',
properties: {
@ -803,6 +860,7 @@ function invitationsRoute(server: FastifyInstance<Server, IncomingMessage, Serve
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
invitations: {
@ -875,6 +933,8 @@ function logsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const options: RouteShorthandOptions = {
schema: {
description: 'Get Group logs.',
tags: ['group'],
querystring: {
type: 'object',
properties: {
@ -883,6 +943,7 @@ function logsRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
logs: {

14
src/plugins/api/media.ts

@ -23,6 +23,8 @@ import { PluginOptions } from '../../types'
function getSASRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
const options: RouteShorthandOptions = {
schema: {
description: 'Get a shared access signature.',
tags: ['media'],
response: {
200: {
type: 'object',
@ -53,6 +55,8 @@ function addRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
const options: RouteShorthandOptions = {
schema: {
description: 'Create a Media item.',
tags: ['media'],
body: {
type: 'object',
required: ['name', 'size', 'type', 'originalName'],
@ -64,6 +68,10 @@ function addRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
},
},
response: {
204: {
description: 'Media item created.',
type: 'object',
},
400: errorSchema,
},
},
@ -104,6 +112,8 @@ function deleteRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Delete a media item.',
tags: ['media'],
body: {
type: 'object',
required: ['name'],
@ -112,6 +122,10 @@ function deleteRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
},
},
response: {
204: {
description: 'Media item deleted.',
type: 'object',
},
400: errorSchema,
},
},

29
src/plugins/api/posts.ts

@ -10,11 +10,11 @@ import {
Logger,
} from 'fastify'
import { Server, IncomingMessage, ServerResponse, request } from 'http'
import { Server, IncomingMessage, ServerResponse } from 'http'
import { createHmac } from 'crypto'
import { CosmosClient } from '@azure/cosmos'
import { SHORT_TEXT_LENGTH, SUBSCRIBER_MAX_SIZE, INSTALLATION_PARTITION_KEY, APP_PARTITION_KEY } from '../../constants'
import { SHORT_TEXT_LENGTH, INSTALLATION_PARTITION_KEY, APP_PARTITION_KEY } from '../../constants'
import { userSchema, postSchema, errorSchema } from '../../schemas'
import { unauthorizedError, serverError, badRequestError, badRequestFormError, notFoundError } from '../../lib/errors'
import { trimContent, createPostId } from '../../lib/utils'
@ -31,7 +31,6 @@ import {
GroupBlock,
PostRelationship,
Status,
PostItemType,
UserItemType,
UserPrivacyType,
GroupItemType,
@ -148,7 +147,6 @@ async function createPost(client: CosmosClient, userId: string, appId: string, b
const post: Post = {
id: postId,
pk: postId,
t: PostItemType.Post,
userId,
appId,
root: newPostRelationship ? newPostRelationship.pk : postId,
@ -158,9 +156,7 @@ async function createPost(client: CosmosClient, userId: string, appId: string, b
visible: body.visible,
attachments: body.attachments,
data: body.data,
awards: 0,
replies: 0,
latestAwards: [],
created: Date.now(),
}
@ -202,9 +198,12 @@ async function createPost(client: CosmosClient, userId: string, appId: string, b
function createPostRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
const options: RouteShorthandOptions = {
schema: {
description: 'Create a Post.',
tags: ['post'],
body: postBodySchema,
response: {
200: {
201: {
description: 'Post created.',
type: 'object',
properties: {
id: { type: 'string' },
@ -239,6 +238,7 @@ function createPostRoute(server: FastifyInstance<Server, IncomingMessage, Server
if (!app) return serverError(reply)
reply.code(201)
return await createPost(server.database.client, request.viewer.id, app.id, request.body, reply, request.log)
})
}
@ -251,6 +251,8 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
const options: RouteShorthandOptions = {
schema: {
description: 'Create a Post (App).',
tags: ['post'],
headers: {
type: 'object',
properties: {
@ -260,7 +262,8 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
},
body: postBodySchema,
response: {
200: {
201: {
description: 'Post created.',
type: 'object',
properties: {
id: { type: 'string' },
@ -302,6 +305,7 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
return badRequestError(reply, 'Invalid signature')
}
reply.code(201)
return await createPost(server.database.client, installation.userId, app.id, request.body, reply, request.log)
})
}
@ -313,6 +317,8 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
const options: RouteShorthandOptions = {
schema: {
description: 'Get a list of User Posts.',
tags: ['post'],
params: {
type: 'object',
properties: {
@ -321,6 +327,7 @@ function postsByUserRoute(server: FastifyInstance<Server, IncomingMessage, Serve
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
user: userSchema,
@ -408,6 +415,8 @@ function timelineRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Get the authenticated User Timeline.',
tags: ['post'],
querystring: {
type: 'object',
properties: {
@ -416,6 +425,7 @@ function timelineRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
posts: {
@ -478,6 +488,8 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const options: RouteShorthandOptions = {
schema: {
description: 'Get a Post.',
tags: ['post'],
params: {
type: 'object',
properties: {
@ -486,6 +498,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
post: postSchema,

39
src/plugins/api/users.ts

@ -40,6 +40,8 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
const options: RouteShorthandOptions = {
schema: {
description: 'Check User ID availability.',
tags: ['user'],
body: {
type: 'object',
required: ['name'],
@ -52,6 +54,7 @@ function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, Serv
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
id: { type: 'string' },
@ -94,6 +97,8 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp
const options: RouteShorthandOptions = {
schema: {
description: 'Update the authenticated User.',
tags: ['user'],
body: {
type: 'object',
properties: {
@ -173,6 +178,8 @@ function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespons
const options: RouteShorthandOptions = {
schema: {
description: 'Get a User.',
tags: ['user'],
params: {
type: 'object',
properties: {
@ -254,6 +261,8 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
const options: RouteShorthandOptions = {
schema: {
description: 'Subscribe to a User.',
tags: ['user'],
params: {
type: 'object',
properties: {
@ -261,6 +270,10 @@ function subscribeRoute(server: FastifyInstance<Server, IncomingMessage, ServerR
},
},
response: {
204: {
description: 'Subscribed.',
type: 'object',
},
400: errorSchema,
},
},
@ -348,12 +361,21 @@ function unsubscribeRoute(server: FastifyInstance<Server, IncomingMessage, Serve
const options: RouteShorthandOptions = {
schema: {
description: 'Unsubscribe from a User.',
tags: ['user'],
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
response: {
204: {
description: 'Unsubscribed.',
type: 'object',
},
400: errorSchema,
},
},
}
@ -405,6 +427,8 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
const options: RouteShorthandOptions = {
schema: {
description: 'Block a User.',
tags: ['user'],
params: {
type: 'object',
properties: {
@ -418,8 +442,12 @@ function blockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespo
},
},
response: {
204: {
description: 'User blocked.',
type: 'object',
},
400: errorSchema,
}
},
},
}
@ -460,12 +488,21 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
const options: RouteShorthandOptions = {
schema: {
description: 'Unblock a User.',
tags: ['user'],
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
response: {
204: {
description: 'User unblocked.',
type: 'object',
},
400: errorSchema,
},
},
}

46
src/schemas.ts

@ -1,6 +1,7 @@
import { JSONSchema } from 'fastify'
export const tokenResponseSchema: JSONSchema = {
description: 'Token response.',
type: 'object',
properties: {
id: { type: 'string' },
@ -19,18 +20,8 @@ export const userSettingsSchema: JSONSchema = {
},
}
export const awardSchema: JSONSchema = {
type: 'object',
properties: {
userId: { type: 'string' },
imageUrl: { type: 'string' },
text: { type: 'string' },
userText: { type: 'string' },
created: { type: 'number' },
},
}
export const groupListingSchema: JSONSchema = {
description: 'Group entity.',
type: 'object',
properties: {
id: { type: 'string' },
@ -43,14 +34,9 @@ export const groupListingSchema: JSONSchema = {
requiresApproval: { type: 'boolean' },
members: { type: 'number' },
posts: { type: 'number' },
awards: { type: 'number' },
points: { type: 'number' },
created: { type: 'number' },
membership: { type: 'string' },
latestAwards: {
type: 'array',
items: awardSchema,
},
},
}
@ -64,6 +50,7 @@ export const subscriptionSchema: JSONSchema = {
}
export const userSchema: JSONSchema = {
description: 'User entity.',
type: 'object',
properties: {
id: { type: 'string' },
@ -79,31 +66,33 @@ export const userSchema: JSONSchema = {
},
membership: { type: 'string' },
posts: { type: 'number' },
awards: { type: 'number' },
points: { type: 'number' },
created: { type: 'number' },
},
}
export const attachmentSchema: JSONSchema = {
type: 'object',
properties: {
url: { type: 'string' },
text: { type: 'string' },
cover: { type: 'string' },
},
}
export const postSchema: JSONSchema = {
description: 'Post entity.',
type: 'object',
properties: {
id: { type: 'string' },
userId: { type: 'string' },
appId: { type: 'string' },
user: userSchema,
appId: { type: 'string' },
text: { type: 'string' },
cover: { type: 'string' },
attachments: {
type: 'array',
items: {
type: 'object',
properties: {
url: { type: 'string' },
text: { type: 'string' },
cover: { type: 'string' },
},
},
items: attachmentSchema,
},
data: {
type: 'object',
@ -116,6 +105,7 @@ export const postSchema: JSONSchema = {
}
export const appSchema: JSONSchema = {
description: 'App entity.',
type: 'object',
properties: {
id: { type: 'string' },
@ -133,7 +123,6 @@ export const appSchema: JSONSchema = {
users: { type: 'number' },
updated: { type: 'number' },
created: { type: 'number' },
publicKey: { type: 'string' },
privateKey: { type: 'string' },
composerUrl: { type: 'string' },
@ -148,6 +137,7 @@ export const appSchema: JSONSchema = {
}
export const selfSchema: JSONSchema = {
description: 'Authenticated User entity.',
type: 'object',
properties: {
id: { type: 'string' },
@ -163,13 +153,13 @@ export const selfSchema: JSONSchema = {
privacy: { type: 'string' },
membership: { type: 'string' },
posts: { type: 'number' },
awards: { type: 'number' },
points: { type: 'number' },
created: { type: 'number' },
},
}
export const errorSchema: JSONSchema = {
description: 'Client error.',
type: 'object',
properties: {
message: { type: 'string' },

30
src/server.ts

@ -2,6 +2,7 @@ import { config } from 'dotenv'
import fastify from 'fastify'
import helmet from 'fastify-helmet'
import cors from 'fastify-cors'
import swagger from 'fastify-swagger'
import api from './plugins/api'
@ -17,10 +18,39 @@ const server = fastify({
server.register(helmet)
server.register(cors, {
origin: true,
credentials: false,
maxAge: 2592000,
})
server.register(swagger, {
routePrefix: '/docs',
exposeRoute: true,
swagger: {
info: {
title: 'Flexor API',
description: 'API server for Flexor.',
version: '1.0.0',
},
tags: [
{ name: 'app', description: 'App endpoints.' },
{ name: 'authentication', description: 'Authentication endpoints.' },
{ name: 'group', description: 'Group endpoints.' },
{ name: 'media', description: 'Media endpoints.' },
{ name: 'post', description: 'Post endpoints.' },
{ name: 'user', description: 'User endpoints.' },
],
host: 'localhost',
schemes: ['http'],
},
})
server.register(api)
server.ready(err => {
if (err) throw err
server.swagger()
})
const start = async () => {
try {
await server.listen(parseInt(process.env.PORT!))

48
src/types/collections.ts

@ -38,11 +38,6 @@ export enum UserPrivacyType {
Private = 'private',
}
export enum UserTransactionType {
Purchase = 'purchase',
Award = 'award',
}
export enum GroupStatus {
Pending = 'pending',
Paid = 'paid',
@ -79,11 +74,6 @@ export enum BlockType {
Group = 'group',
}
export enum PostItemType {
Post = 'post',
Award = 'award',
}
export interface GroupListing {
id: string
pk: typeof GROUP_LISTING_PARTITION_KEY
@ -92,9 +82,7 @@ export interface GroupListing {
registration: GroupRegistrationType
members: number
posts: number
awards: number
points: number
latestAwards: PostAwardPartial[]
created: number
}
@ -190,7 +178,6 @@ export interface User {
passwordHash: string
settings: UserSettings
installations: string[]
awards: number // Total Awards
points: number
balance: number // Currency (Flex)
posts: number
@ -250,17 +237,6 @@ export interface UserBlock {
created: number
}
export interface UserTransaction {
id: string
pk: string
t: UserItemType.Transaction
transactionType: UserTransactionType
fromUserId: string
toUserId: string
amount: number
created: number
}
export interface UserTimelinePost {
id: string
pk: string // userId
@ -287,7 +263,6 @@ export interface PostData {
export interface Post {
id: string
pk: string // postId
t: PostItemType.Post
userId: string
appId: string
root: string
@ -299,29 +274,6 @@ export interface Post {
data?: PostData
visible: boolean
replies: number
awards: number
latestAwards: PostAwardPartial[]
created: number
}
export interface PostAward {
id: string
pk: string // postId
t: PostItemType.Award
userId: string
imageUrl: string
text: string
userText: string
cost: number
reward: number
created: number
}
export interface PostAwardPartial {
userId: string
imageUrl: string
text: string
userText: string
created: number
}

9
src/types/index.ts

@ -16,13 +16,4 @@ export interface DatabaseItem {
id: string
}
export interface AwardDefinition {
id: string
name: string
imageUrl: string
text: string
cost: number
reward: number
}
export interface PluginOptions {}
Loading…
Cancel
Save