@nephele/adapter-nymph
Version:
Nymph.js based deduping file adapter for the Nephele WebDAV server.
279 lines • 10.1 kB
JavaScript
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