UNPKG

@nephele/adapter-nymph

Version:

Nymph.js based deduping file adapter for the Nephele WebDAV server.

279 lines 10.1 kB
import { EntityUniqueConstraintError } from '@nymphjs/nymph'; import { Entity, nymphJoiProps } from '@nymphjs/nymph'; import { enforceTilmeld, tilmeldJoiProps } from '@nymphjs/tilmeld'; import Joi from 'joi'; import { BadRequestError, ForbiddenError, InternalServerError, ResourceExistsError, UnauthorizedError, } from 'nephele'; import { Lock as NymphLock } from './Lock.js'; export class Resource extends Entity { constructor() { super(); this.$clientEnabledMethods = []; this.$allowlistData = []; this.$allowlistTags = []; this.$privateData = []; this.$skipAcWhenSaving = false; this.$skipAcWhenDeleting = false; this.$data.name = ''; this.$data.size = 0; this.$data.contentType = ''; this.$data.collection = false; this.$data.hash = ''; this.$data.properties = {}; this.$data.parent = null; } async $getUniques() { return [ `${this.$data.user?.guid}:${this.$data.parent?.guid}:${this.$data.name}`, ]; } $setNymph(nymph) { this.$nymph = nymph; if (!this.$asleep()) { if (this.$data.user) { this.$data.user.$setNymph(nymph); } if (this.$data.group) { this.$data.group.$setNymph(nymph); } if (this.$data.parent) { this.$data.parent.$setNymph(nymph); } } } async $copy(destinationParent, name, existingResource) { const transaction = 'resource-copy-' + this.guid; const nymph = this.$nymph; const tnymph = await nymph.startTransaction(transaction); this.$setNymph(tnymph); try { if (existingResource) { existingResource.$setNymph(tnymph); if (await this.$nymph.getEntity({ class: this.$nymph.getEntityClass(Resource), skipAc: true }, { type: '&', ref: ['parent', existingResource], })) { throw new ForbiddenError('The destination resource is not empty.'); } if (!(await existingResource.$delete())) { throw new InternalServerError("Couldn't delete destination resource."); } } const newNymphResource = await this.$nymph .getEntityClass(Resource) .factory(); newNymphResource.name = name; newNymphResource.size = this.$data.size; newNymphResource.contentType = this.$data.contentType; newNymphResource.collection = this.$data.collection; newNymphResource.hash = this.$data.hash; newNymphResource.properties = JSON.parse(JSON.stringify(this.$data.properties)); newNymphResource.parent = destinationParent; if (!(await newNymphResource.$save())) { throw new InternalServerError("Couldn't save destination resource."); } await tnymph.commit(transaction); this.$setNymph(nymph); if (existingResource) { existingResource.$setNymph(nymph); } } catch (e) { await tnymph.rollback(transaction); this.$setNymph(nymph); if (existingResource) { existingResource.$setNymph(nymph); } try { if (existingResource) { existingResource.$refresh(); } } catch (e) { } throw e; } } async $move(destinationParent, name, existingResource) { if (await this.$nymph.getEntity({ class: this.$nymph.getEntityClass(Resource), skipAc: true }, { type: '&', ref: ['parent', this], })) { throw new ForbiddenError('This resource is not empty.'); } const transaction = 'resource-move-' + this.guid; const nymph = this.$nymph; const tnymph = await nymph.startTransaction(transaction); this.$setNymph(tnymph); try { if (existingResource) { existingResource.$setNymph(tnymph); if (await tnymph.getEntity({ class: tnymph.getEntityClass(Resource), skipAc: true }, { type: '&', ref: ['parent', existingResource], })) { throw new ForbiddenError('The destination resource is not empty.'); } if (!(await existingResource.$delete())) { throw new InternalServerError("Couldn't delete destination resource."); } } const locks = await tnymph.getEntities({ class: tnymph.getEntityClass(NymphLock), skipAc: true, }, { type: '&', ref: ['resource', this], }); for (let lock of locks) { if (!(await lock.$deleteSkipAC())) { throw new InternalServerError("Couldn't delete associated lock."); } } this.$data.name = name; this.$data.parent = destinationParent; if (!(await this.$save())) { throw new InternalServerError("Couldn't save destination resource."); } await tnymph.commit(transaction); this.$setNymph(nymph); if (existingResource) { existingResource.$setNymph(nymph); } } catch (e) { await tnymph.rollback(transaction); this.$setNymph(nymph); if (existingResource) { existingResource.$setNymph(nymph); } try { await this.$refresh(); if (existingResource) { existingResource.$refresh(); } } catch (e) { } throw e; } } async $save() { try { const tilmeld = enforceTilmeld(this); if (!tilmeld.gatekeeper()) { throw new UnauthorizedError('You must be logged in.'); } } catch (e) { } if (!this.$data.parent) { this.$data.parent = null; } try { Joi.attempt(this.$getValidatable(), Joi.object().keys({ ...nymphJoiProps, ...tilmeldJoiProps, name: Joi.string() .max(255) .pattern(/\//, { invert: true, name: 'must not contain forward slash', }) .required(), size: Joi.number().required(), contentType: Joi.string().trim(false).max(255).required(), collection: Joi.boolean().required(), hash: Joi.string().trim(false).hex().length(96).required(), properties: Joi.object().pattern(Joi.string().trim(false).max(2048), Joi.alternatives().try(Joi.string().trim(false).allow('').max(65536), Joi.array().items(Joi.string().trim(false).allow('').max(65536)))), parent: Joi.alternatives().try(Joi.any().allow(null), Joi.object().instance(Resource)), }), 'Invalid Resource: '); } catch (e) { throw new BadRequestError(e.message); } try { return await super.$save(); } catch (e) { if (e instanceof EntityUniqueConstraintError) { throw new ResourceExistsError('This resource already exists.'); } throw e; } } async $saveSkipAC() { this.$skipAcWhenSaving = true; return await this.$save(); } $tilmeldSaveSkipAC() { if (this.$skipAcWhenSaving) { this.$skipAcWhenSaving = false; return true; } return false; } async $delete() { if (this.$data.collection && (await this.$nymph.getEntity({ class: this.$nymph.getEntityClass(Resource), skipAc: true }, { type: '&', ref: ['parent', this], }))) { throw new ForbiddenError("This resource isn't empty."); } const transaction = 'resource-delete-' + this.guid; const nymph = this.$nymph; const tnymph = await nymph.startTransaction(transaction); this.$setNymph(tnymph); try { const locks = await tnymph.getEntities({ class: tnymph.getEntityClass(NymphLock), skipAc: true, }, { type: '&', ref: ['resource', this], }); for (let lock of locks) { if (!(await lock.$deleteSkipAC())) { throw new InternalServerError("Couldn't delete associated lock."); } } let success = await super.$delete(); if (success) { success = await tnymph.commit(transaction); } else { await tnymph.rollback(transaction); } this.$setNymph(nymph); if (!success) { await this.$refresh(); } return success; } catch (e) { await tnymph.rollback(transaction); this.$setNymph(nymph); try { await this.$refresh(); } catch (e) { } throw e; } } async $deleteSkipAC() { this.$skipAcWhenDeleting = true; return await this.$delete(); } $tilmeldDeleteSkipAC() { if (this.$skipAcWhenDeleting) { this.$skipAcWhenDeleting = false; return true; } return false; } } Resource.ETYPE = 'nephele_resource'; Resource.class = 'Resource'; Resource.clientEnabledStaticMethods = []; //# sourceMappingURL=Resource.js.map