|
|
@ -9,6 +9,7 @@ import { |
|
|
|
} from 'fastify' |
|
|
|
|
|
|
|
import { Server, IncomingMessage, ServerResponse } from 'http' |
|
|
|
import pick from 'lodash/pick' |
|
|
|
|
|
|
|
import { appSchema, errorSchema } from '../../schemas' |
|
|
|
import { getUsers } from '../../lib/collections' |
|
|
@ -16,11 +17,59 @@ import { generateString } from '../../lib/crypto' |
|
|
|
import { containerFor, getItem, normalize } from '../../lib/database' |
|
|
|
import { unauthorizedError, serverError, badRequestError, notFoundError } from '../../lib/errors' |
|
|
|
|
|
|
|
import { APP_PARTITION_KEY } from '../../constants' |
|
|
|
import { APP_PARTITION_KEY, MAX_NAME_LENGTH } from '../../constants' |
|
|
|
|
|
|
|
import { App, User } from '../../types/collections' |
|
|
|
import { PluginOptions } from '../../types' |
|
|
|
|
|
|
|
function availabilityRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|
|
|
interface Body { |
|
|
|
name: string |
|
|
|
} |
|
|
|
|
|
|
|
const options: RouteShorthandOptions = { |
|
|
|
schema: { |
|
|
|
body: { |
|
|
|
type: 'object', |
|
|
|
required: ['name'], |
|
|
|
properties: { |
|
|
|
name: { |
|
|
|
type: 'string', |
|
|
|
maxLength: MAX_NAME_LENGTH, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
response: { |
|
|
|
200: { |
|
|
|
type: 'object', |
|
|
|
properties: { |
|
|
|
id: { type: 'string' }, |
|
|
|
available: { type: 'boolean' }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
400: errorSchema, |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
server.post<DefaultQuery, DefaultParams, DefaultHeaders, Body>('/api/app/available', options, async (request, reply) => { |
|
|
|
if (!server.database) return serverError(reply) |
|
|
|
|
|
|
|
const id = normalize(request.body.name) |
|
|
|
|
|
|
|
const app = await getItem<App>({ |
|
|
|
container: containerFor(server.database.client, 'Apps'), |
|
|
|
partitionKey: APP_PARTITION_KEY, |
|
|
|
id, |
|
|
|
}) |
|
|
|
|
|
|
|
return { |
|
|
|
id, |
|
|
|
available: !app, |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function appsRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|
|
|
interface Query { |
|
|
|
sort?: string |
|
|
@ -299,6 +348,12 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp |
|
|
|
|
|
|
|
const options: RouteShorthandOptions = { |
|
|
|
schema: { |
|
|
|
params: { |
|
|
|
type: 'object', |
|
|
|
properties: { |
|
|
|
id: { type: 'string' }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
body: { |
|
|
|
type: 'object', |
|
|
|
required: ['version', 'name'], |
|
|
@ -394,11 +449,65 @@ function updateRoute(server: FastifyInstance<Server, IncomingMessage, ServerResp |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function getRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|
|
|
interface Params { |
|
|
|
id: string |
|
|
|
} |
|
|
|
|
|
|
|
const options: RouteShorthandOptions = { |
|
|
|
schema: { |
|
|
|
params: { |
|
|
|
type: 'object', |
|
|
|
properties: { |
|
|
|
id: { type: 'string' }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
response: { |
|
|
|
200: appSchema, |
|
|
|
400: errorSchema, |
|
|
|
} |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
server.get<DefaultQuery, Params, DefaultHeaders, DefaultBody>('/api/app/:id', options, async (request, reply) => { |
|
|
|
if (!server.database) return serverError(reply) |
|
|
|
|
|
|
|
const app = await getItem<App>({ |
|
|
|
container: containerFor(server.database.client, 'Apps'), |
|
|
|
id: request.params.id, |
|
|
|
partitionKey: APP_PARTITION_KEY, |
|
|
|
}) |
|
|
|
|
|
|
|
if (!app) return notFoundError(reply) |
|
|
|
|
|
|
|
const user = await getItem<User>({ |
|
|
|
container: containerFor(server.database.client, 'Users'), |
|
|
|
id: app.userId, |
|
|
|
}) |
|
|
|
|
|
|
|
if (request.viewer && request.viewer.id === app.userId) { |
|
|
|
const attributes = ['id', 'version', 'name', 'imageUrl', 'coverImageUrl', 'iconImageUrl', 'about', 'websiteUrl', 'companyName', 'rating', 'updated', 'created', 'publicKey', 'privateKey', 'revisions'] |
|
|
|
return { |
|
|
|
...pick(app, attributes), |
|
|
|
user, |
|
|
|
} |
|
|
|
} else { |
|
|
|
const attributes = ['id', 'version', 'name', 'imageUrl', 'coverImageUrl', 'iconImageUrl', 'about', 'websiteUrl', 'companyName', 'rating', 'updated', 'created'] |
|
|
|
return { |
|
|
|
...pick(app, attributes), |
|
|
|
user, |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = async server => { |
|
|
|
availabilityRoute(server) |
|
|
|
appsRoute(server) |
|
|
|
selfAppsRoute(server) |
|
|
|
createRoute(server) |
|
|
|
updateRoute(server) |
|
|
|
getRoute(server) |
|
|
|
} |
|
|
|
|
|
|
|
export default plugin |