directus
313 строк · 7.2 Кб
1import { useEnv } from '@directus/env';2import { ErrorCode, InvalidPayloadError, isDirectusError } from '@directus/errors';3import type { PrimaryKey } from '@directus/types';4import express from 'express';5import Joi from 'joi';6import { REFRESH_COOKIE_OPTIONS, SESSION_COOKIE_OPTIONS, UUID_REGEX } from '../constants.js';7import { respond } from '../middleware/respond.js';8import useCollection from '../middleware/use-collection.js';9import { validateBatch } from '../middleware/validate-batch.js';10import { SharesService } from '../services/shares.js';11import type { AuthenticationMode } from '../types/index.js';12import asyncHandler from '../utils/async-handler.js';13import { sanitizeQuery } from '../utils/sanitize-query.js';14
15const router = express.Router();16const env = useEnv();17
18router.use(useCollection('directus_shares'));19
20const sharedLoginSchema = Joi.object({21share: Joi.string().required(),22password: Joi.string(),23mode: Joi.string().valid('cookie', 'json', 'session').optional(),24}).unknown();25
26router.post(27'/auth',28asyncHandler(async (req, res, next) => {29// This doesn't use accountability, as the user isn't logged in at this point30const service = new SharesService({31schema: req.schema,32});33
34const { error } = sharedLoginSchema.validate(req.body);35
36if (error) {37throw new InvalidPayloadError({ reason: error.message });38}39
40const mode: AuthenticationMode = req.body.mode ?? 'json';41
42const { accessToken, refreshToken, expires } = await service.login(req.body, {43session: mode === 'session',44});45
46const payload = { expires } as { expires: number; access_token?: string; refresh_token?: string };47
48if (mode === 'json') {49payload.refresh_token = refreshToken;50payload.access_token = accessToken;51}52
53if (mode === 'cookie') {54res.cookie(env['REFRESH_TOKEN_COOKIE_NAME'] as string, refreshToken, REFRESH_COOKIE_OPTIONS);55payload.access_token = accessToken;56}57
58if (mode === 'session') {59res.cookie(env['SESSION_COOKIE_NAME'] as string, accessToken, SESSION_COOKIE_OPTIONS);60}61
62res.locals['payload'] = { data: payload };63
64return next();65}),66respond,67);68
69const sharedInviteSchema = Joi.object({70share: Joi.string().required(),71emails: Joi.array().items(Joi.string()),72}).unknown();73
74router.post(75'/invite',76asyncHandler(async (req, _res, next) => {77const service = new SharesService({78schema: req.schema,79accountability: req.accountability,80});81
82const { error } = sharedInviteSchema.validate(req.body);83
84if (error) {85throw new InvalidPayloadError({ reason: error.message });86}87
88await service.invite(req.body);89
90return next();91}),92respond,93);94
95router.post(96'/',97asyncHandler(async (req, res, next) => {98const service = new SharesService({99accountability: req.accountability,100schema: req.schema,101});102
103const savedKeys: PrimaryKey[] = [];104
105if (Array.isArray(req.body)) {106const keys = await service.createMany(req.body);107savedKeys.push(...keys);108} else {109const key = await service.createOne(req.body);110savedKeys.push(key);111}112
113try {114if (Array.isArray(req.body)) {115const items = await service.readMany(savedKeys, req.sanitizedQuery);116res.locals['payload'] = { data: items };117} else {118const item = await service.readOne(savedKeys[0]!, req.sanitizedQuery);119res.locals['payload'] = { data: item };120}121} catch (error: any) {122if (isDirectusError(error, ErrorCode.Forbidden)) {123return next();124}125
126throw error;127}128
129return next();130}),131respond,132);133
134const readHandler = asyncHandler(async (req, res, next) => {135const service = new SharesService({136accountability: req.accountability,137schema: req.schema,138});139
140const records = await service.readByQuery(req.sanitizedQuery);141
142res.locals['payload'] = { data: records || null };143return next();144});145
146router.get('/', validateBatch('read'), readHandler, respond);147router.search('/', validateBatch('read'), readHandler, respond);148
149router.get(150`/info/:pk(${UUID_REGEX})`,151asyncHandler(async (req, res, next) => {152const service = new SharesService({153schema: req.schema,154});155
156const record = await service.readOne(req.params['pk']!, {157fields: ['id', 'collection', 'item', 'password', 'max_uses', 'times_used', 'date_start', 'date_end'],158filter: {159_and: [160{161_or: [162{163date_start: {164_lte: new Date().toISOString(),165},166},167{168date_start: {169_null: true,170},171},172],173},174{175_or: [176{177date_end: {178_gte: new Date().toISOString(),179},180},181{182date_end: {183_null: true,184},185},186],187},188],189},190});191
192res.locals['payload'] = { data: record || null };193return next();194}),195respond,196);197
198router.get(199`/:pk(${UUID_REGEX})`,200asyncHandler(async (req, res, next) => {201const service = new SharesService({202accountability: req.accountability,203schema: req.schema,204});205
206const record = await service.readOne(req.params['pk']!, req.sanitizedQuery);207
208res.locals['payload'] = { data: record || null };209return next();210}),211respond,212);213
214router.patch(215'/',216validateBatch('update'),217asyncHandler(async (req, res, next) => {218const service = new SharesService({219accountability: req.accountability,220schema: req.schema,221});222
223let keys: PrimaryKey[] = [];224
225if (Array.isArray(req.body)) {226keys = await service.updateBatch(req.body);227} else if (req.body.keys) {228keys = await service.updateMany(req.body.keys, req.body.data);229} else {230const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability);231keys = await service.updateByQuery(sanitizedQuery, req.body.data);232}233
234try {235const result = await service.readMany(keys, req.sanitizedQuery);236res.locals['payload'] = { data: result };237} catch (error: any) {238if (isDirectusError(error, ErrorCode.Forbidden)) {239return next();240}241
242throw error;243}244
245return next();246}),247respond,248);249
250router.patch(251`/:pk(${UUID_REGEX})`,252asyncHandler(async (req, res, next) => {253const service = new SharesService({254accountability: req.accountability,255schema: req.schema,256});257
258const primaryKey = await service.updateOne(req.params['pk']!, req.body);259
260try {261const item = await service.readOne(primaryKey, req.sanitizedQuery);262res.locals['payload'] = { data: item || null };263} catch (error: any) {264if (isDirectusError(error, ErrorCode.Forbidden)) {265return next();266}267
268throw error;269}270
271return next();272}),273respond,274);275
276router.delete(277'/',278asyncHandler(async (req, _res, next) => {279const service = new SharesService({280accountability: req.accountability,281schema: req.schema,282});283
284if (Array.isArray(req.body)) {285await service.deleteMany(req.body);286} else if (req.body.keys) {287await service.deleteMany(req.body.keys);288} else {289const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability);290await service.deleteByQuery(sanitizedQuery);291}292
293return next();294}),295respond,296);297
298router.delete(299`/:pk(${UUID_REGEX})`,300asyncHandler(async (req, _res, next) => {301const service = new SharesService({302accountability: req.accountability,303schema: req.schema,304});305
306await service.deleteOne(req.params['pk']!);307
308return next();309}),310respond,311);312
313export default router;314