Dwayne Harris 4 years ago
parent
commit
794ed6cb02
  1. 4
      src/constants.ts
  2. 105
      src/plugins/api/apps.ts
  3. 6
      src/plugins/api/authentication.ts
  4. 83
      src/plugins/api/groups.ts
  5. 1
      src/schemas.ts

4
src/constants.ts

@ -10,3 +10,7 @@ export const GROUP_LISTING_PARTITION_KEY = 'gpk'
export const APP_PARTITION_KEY = 'apk'
export const INSTALLATION_PARTITION_KEY = 'ipk'
export const MEDIA_PARTITION_KEY = 'pk'
export const ADMINS = [
'dwayneh@gmail.com',
]

105
src/plugins/api/apps.ts

@ -12,14 +12,14 @@ import { Server, IncomingMessage, ServerResponse } from 'http'
import pick from 'lodash/pick'
import { appSchema, errorSchema } from '../../schemas'
import { getUsers, userIdIsValid, userIsValid, updateItem } from '../../lib/collections'
import { getUsers, userIdIsValid, userIsValid, updateItem, getUser } from '../../lib/collections'
import { generateString } from '../../lib/crypto'
import { containerFor, getItem, normalize, queryItems, createQuerySpec } from '../../lib/database'
import { unauthorizedError, serverError, badRequestError, notFoundError, forbiddenError } from '../../lib/errors'
import { attachMedia, deleteMedia } from '../../lib/media'
import { createInstallationId } from '../../lib/utils'
import { APP_PARTITION_KEY, MAX_NAME_LENGTH, INSTALLATION_PARTITION_KEY } from '../../constants'
import { APP_PARTITION_KEY, MAX_NAME_LENGTH, INSTALLATION_PARTITION_KEY, ADMINS } from '../../constants'
import { App, User, Installation, InstallationSettings } from '../../types/collections'
import { PluginOptions } from '../../types'
@ -754,11 +754,68 @@ function installationsRoute(server: FastifyInstance<Server, IncomingMessage, Ser
})
}
function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Headers {
adminkey: string
function pendingRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Query {
continuation?: string
}
const options: RouteShorthandOptions = {
schema: {
description: 'Get Apps awaiting activation.',
tags: ['app', 'admin'],
querystring: {
type: 'object',
properties: {
continuation: { type: 'string' },
},
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
apps: {
type: 'array',
items: appSchema,
},
continuation: { type: 'string' },
}
},
400: errorSchema,
},
},
}
server.get<Query, DefaultParams, DefaultHeaders, DefaultBody>('/v1/apps/pending', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer || !ADMINS.includes(viewer.email)) return forbiddenError(reply)
const { continuation } = request.query
const container = containerFor(server.database.client, 'Apps')
const { resources: apps, requestCharge, continuation: newContinuation } = await container.items.query<App>(
createQuerySpec('SELECT * FROM Apps a WHERE a.pk = @pk AND a.active = false', {
pk: APP_PARTITION_KEY,
}),
{
maxItemCount: 20,
continuation,
}
).fetchAll()
request.log.trace('Query: %d', requestCharge)
return {
apps,
continuation: newContinuation,
}
})
}
function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Params {
id: string
}
@ -766,14 +823,7 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Activate an App.',
tags: ['app'],
headers: {
type: 'object',
required: ['adminkey'],
properties: {
'adminkey': { type: 'string' },
},
},
tags: ['app', 'admin'],
params: {
type: 'object',
properties: {
@ -790,9 +840,12 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
},
}
server.post<DefaultQuery, Params, Headers, DefaultBody>('/v1/app/:id/activate', options, async (request, reply) => {
server.post<DefaultQuery, Params, DefaultHeaders, DefaultBody>('/v1/app/:id/activate', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (request.headers.adminkey !== process.env.ADMIN_KEY) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer || !ADMINS.includes(viewer.email)) return forbiddenError(reply)
await updateItem<App>(containerFor(server.database.client, 'Apps').item(request.params.id, APP_PARTITION_KEY), { active: true })
reply.code(204)
@ -800,10 +853,6 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
}
function setPreinstallRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Headers {
adminkey: string
}
interface Params {
id: string
}
@ -811,14 +860,7 @@ 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'],
properties: {
adminkey: { type: 'string' },
},
},
tags: ['app', 'admin'],
params: {
type: 'object',
properties: {
@ -837,7 +879,14 @@ function setPreinstallRoute(server: FastifyInstance<Server, IncomingMessage, Ser
server.post<DefaultQuery, Params, Headers, DefaultBody>('/v1/app/:id/preinstall', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (request.headers.adminkey !== process.env.ADMIN_KEY) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getItem<User>({
container: containerFor(server.database.client, 'Users'),
id: request.viewer.id,
})
if (!viewer || !ADMINS.includes(viewer.email)) return forbiddenError(reply)
await updateItem<App>(containerFor(server.database.client, 'Apps').item(request.params.id, APP_PARTITION_KEY), {
preinstall: true,

6
src/plugins/api/authentication.ts

@ -28,6 +28,7 @@ import {
INSTALLATION_PARTITION_KEY,
APP_PARTITION_KEY,
USER_LISTING_PARTITION_KEY,
ADMINS,
} from '../../constants'
import {
@ -462,7 +463,10 @@ function selfRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer) return unauthorizedError(reply)
return viewer
return {
...viewer,
admin: ADMINS.includes(viewer.email)
}
})
}

83
src/plugins/api/groups.ts

@ -10,7 +10,7 @@ import {
import { Server, IncomingMessage, ServerResponse } from 'http'
import { MIN_ID_LENGTH, MAX_NAME_LENGTH, GROUP_LISTING_PARTITION_KEY, USER_LISTING_PARTITION_KEY } from '../../constants'
import { MIN_ID_LENGTH, MAX_NAME_LENGTH, GROUP_LISTING_PARTITION_KEY, USER_LISTING_PARTITION_KEY, ADMINS } from '../../constants'
import { errorSchema, groupSchema, userSchema } from '../../schemas'
import { unauthorizedError, badRequestError, notFoundError, serverError, forbiddenError } from '../../lib/errors'
import { getUsers, getUserMembership, getUser, updateItem } from '../../lib/collections'
@ -528,11 +528,69 @@ function unblockRoute(server: FastifyInstance<Server, IncomingMessage, ServerRes
})
}
function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Headers {
adminkey: string
function pendingRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Query {
continuation?: string
}
const options: RouteShorthandOptions = {
schema: {
description: 'Get Groups awaiting activation.',
tags: ['group', 'admin'],
querystring: {
type: 'object',
properties: {
continuation: { type: 'string' },
},
},
response: {
200: {
description: 'Successful response.',
type: 'object',
properties: {
groups: {
type: 'array',
items: groupSchema,
},
continuation: { type: 'string' },
}
},
400: errorSchema,
},
},
}
server.get<Query, DefaultParams, DefaultHeaders, DefaultBody>('/v1/groups/pending', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer || !ADMINS.includes(viewer.email)) return forbiddenError(reply)
const { continuation } = request.query
const container = containerFor(server.database.client, 'Groups')
const { resources: groups, requestCharge, continuation: newContinuation } = await container.items.query<Group>(
createQuerySpec('SELECT * FROM Groups g WHERE g.t = @type AND g.status = @status', {
type: GroupItemType.Group,
status: GroupStatus.Pending,
}),
{
maxItemCount: 20,
continuation,
}
).fetchAll()
request.log.trace('Query: %d', requestCharge)
return {
groups,
continuation: newContinuation,
}
})
}
function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Params {
id: string
}
@ -540,14 +598,7 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
const options: RouteShorthandOptions = {
schema: {
description: 'Activate a Group.',
tags: ['group'],
headers: {
type: 'object',
required: ['adminkey'],
properties: {
adminkey: { type: 'string' },
},
},
tags: ['group', 'admin'],
params: {
type: 'object',
properties: {
@ -564,9 +615,12 @@ function activateRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
},
}
server.post<DefaultQuery, Params, Headers, DefaultBody>('/v1/group/:id/activate', options, async (request, reply) => {
server.post<DefaultQuery, Params, DefaultHeaders, DefaultBody>('/v1/group/:id/activate', options, async (request, reply) => {
if (!server.database) return serverError(reply)
if (request.headers.adminkey !== process.env.ADMIN_KEY) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply)
const viewer = await getUser(server.database.client, request.viewer.id)
if (!viewer || !ADMINS.includes(viewer.email)) return forbiddenError(reply)
const container = containerFor(server.database.client, 'Groups')
const groupItem = container.item(request.params.id, request.params.id)
@ -1164,6 +1218,7 @@ const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = a
updateRoute(server)
blockRoute(server)
unblockRoute(server)
pendingRoute(server)
activateRoute(server)
listRoute(server)
membersRoute(server)

1
src/schemas.ts

@ -155,6 +155,7 @@ export const selfSchema: JSONSchema = {
membership: { type: 'string' },
posts: { type: 'number' },
points: { type: 'number' },
admin: { type: 'boolean' },
created: { type: 'number' },
},
}

Loading…
Cancel
Save