123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- import type { AuthRequestHandler } from '../auth'
- import type { Context } from '../types'
- import { ObjectId } from 'mongodb'
- import type { SearchResult } from '../api'
- import type { WithId } from 'mongodb'
- import type { Herd, HerdCreate, HerdUpdate } from './types'
- import { http, query, validate as v } from '@edge/misc-utils'
- /** Create a herd. */
- export function createHerd({ model }: Context): AuthRequestHandler {
- interface RequestData {
- herd: HerdCreate<string>
- }
- interface ResponseData {
- herd: WithId<Herd>
- }
- const readRequestData = v.validate<RequestData>({
- herd: {
- _account: v.seq(v.str, v.exactLength(24)),
- name: v.seq(v.str, v.minLength(1)),
- },
- })
- return async function (req, res, next) {
- if (!req.account) return http.unauthorized(res, next)
- try {
- // Read input
- const input = readRequestData(req.body)
- // Assert ability to assign herd
- if (!req.account._id.equals(input.herd._account)) return http.forbidden(res, next)
- // Create herd
- const herd = await model.herd.create({ ...input.herd, _account: req.account._id })
- if (!herd) return http.notFound(res, next, { reason: 'unexpectedly failed to get new herd' })
- // Send output
- const output: ResponseData = { herd }
- res.send(output)
- next()
- } catch (err) {
- const name = (err as Error).name
- if (name === 'ValidateError') {
- const ve = err as v.ValidateError
- return http.badRequest(res, next, { param: ve.param, reason: ve.message })
- }
- return next(err)
- }
- }
- }
- /** Delete a herd. */
- export function deleteHerd({ model }: Context): AuthRequestHandler {
- interface ResponseData {
- herd: WithId<Herd>
- /** Number of tasks deleted */
- tasks: {
- deletedCount: number
- }
- }
- return async function (req, res, next) {
- if (!req.account) return http.unauthorized(res, next)
- try {
- // Assert access to herd
- const herd = await model.herd.collection.findOne({ _id: new ObjectId(req.params.id) })
- if (!herd) return http.notFound(res, next)
- if (!req.account._id.equals(herd._account)) return http.forbidden(res, next)
- // Delete herd
- const result = await model.herd.delete(herd._id)
- // Send output
- const output: ResponseData = {
- herd: result.herd as WithId<Herd>,
- tasks: {
- deletedCount: result.deletedCount,
- },
- }
- res.send(output)
- next()
- } catch (err) {
- next(err)
- }
- }
- }
- /** Get a herd. */
- export function getHerd({ model }: Context): AuthRequestHandler {
- interface ResponseData {
- herd: WithId<Herd>
- }
- return async function (req, res, next) {
- if (!req.account) return http.unauthorized(res, next)
- try {
- // Assert access to herd
- const herd = await model.herd.collection.findOne({ _id: new ObjectId(req.params.id) })
- if (!herd) return http.notFound(res, next)
- if (!req.account._id.equals(herd._account)) return http.forbidden(res, next)
- // Send output
- const output: ResponseData = { herd }
- res.send(output)
- next()
- } catch (err) {
- return next(err)
- }
- }
- }
- /** Search herds. */
- export function searchHerds({ model }: Context): AuthRequestHandler {
- type ResponseData = SearchResult<{
- herd: WithId<Herd>
- }>
- return async function (req, res, next) {
- if (!req.account) return http.unauthorized(res, next)
- // Read parameters
- const limit = query.integer(req.query.limit, 1, 100) || 10
- const page = query.integer(req.query.page, 1) || 1
- const search = query.str(req.query.search)
- const sort = query.sorts(req.query.sort, ['name'], ['name', 'ASC'])
- // Build filter and skip
- const filter: Record<string, unknown> = {
- _account: req.account._id,
- }
- if (search) filter.$text = { $search: search }
- const skip = (page - 1) * limit
- try {
- // Get total documents count for filter
- const totalCount = await model.herd.collection.countDocuments(filter)
- // Build cursor
- let cursor = model.herd.collection.find(filter)
- for (const [prop, dir] of sort) {
- cursor = cursor.sort(prop, dir === 'ASC' ? 1 : -1)
- }
- cursor = cursor.skip(skip).limit(limit)
- // Get results and send output
- const data = await cursor.toArray()
- const output: ResponseData = {
- results: data.map(herd => ({ herd })),
- metadata: { limit, page, totalCount },
- }
- res.send(output)
- next()
- } catch (err) {
- next(err)
- }
- }
- }
- /** Update a herd. */
- export function updateHerd({ model }: Context): AuthRequestHandler {
- interface RequestData {
- herd: HerdUpdate<string>
- }
- interface ResponseData {
- herd: WithId<Herd>
- }
- const readRequestData = v.validate<RequestData>({
- herd: {
- _account: v.seq(v.optional, v.str, v.exactLength(24)),
- name: v.seq(v.optional, v.str, v.minLength(1)),
- },
- })
- return async function (req, res, next) {
- if (!req.account) return http.unauthorized(res, next)
- try {
- // Assert access to herd
- let herd = await model.herd.collection.findOne({ _id: new ObjectId(req.params.id) })
- if (!herd) return http.notFound(res, next)
- if (!req.account._id.equals(herd._account)) return http.forbidden(res, next)
- // Read input
- const input = readRequestData(req.body)
- if (!input.herd._account && !input.herd.name) {
- return http.badRequest(res, next, { reason: 'no changes' })
- }
- // Assert ability to assign herd, if specified in update
- if (input.herd._account) {
- if (!req.account._id.equals(input.herd._account)) return http.forbidden(res, next)
- }
- // Update herd
- herd = await model.herd.update(herd._id, {
- ...input.herd,
- _account: input.herd._account && new ObjectId(input.herd._account) || undefined,
- })
- if (!herd) return http.notFound(res, next)
- // Send output
- const output: ResponseData = { herd }
- res.send(output)
- next()
- } catch (err) {
- const name = (err as Error).name
- if (name === 'ValidateError') {
- const ve = err as v.ValidateError
- return http.badRequest(res, next, { param: ve.param, reason: ve.message })
- }
- return next(err)
- }
- }
- }
|