nephele
Version:
Highly customizable and extensible WebDAV server for Node.js and Express.
158 lines (137 loc) • 4.07 kB
text/typescript
import type { Request } from 'express';
import type { AuthResponse, Resource } from '../Interfaces/index.js';
import {
ForbiddenError,
LockedError,
ResourceNotFoundError,
ResourceTreeNotCompleteError,
} from '../Errors/index.js';
import { Method } from './Method.js';
export class PUT extends Method {
async run(request: Request, response: AuthResponse) {
const { url } = this.getRequestData(request, response);
if (
await this.runPlugins(request, response, 'beginPut', {
method: this,
url,
})
) {
return;
}
await this.checkAuthorization(request, response, 'PUT');
let resource: Resource;
let newResource = false;
try {
resource = await response.locals.adapter.getResource(
url,
response.locals.baseUrl,
);
} catch (e: any) {
if (e instanceof ResourceNotFoundError) {
resource = await response.locals.adapter.newResource(
url,
response.locals.baseUrl,
);
newResource = true;
} else {
throw e;
}
}
if ((await resource.isCollection()) && !url.toString().endsWith('/')) {
response.set({
'Content-Location': `${url}/`,
});
}
if (
await this.runPlugins(request, response, 'prePut', {
method: this,
resource,
newResource,
})
) {
return;
}
try {
const parent = await this.getParentResource(request, response, resource);
if (!(await parent?.isCollection())) {
throw new ForbiddenError('Parent resource is not a collection.');
}
} catch (e: any) {
// Parent not found is handled separately.
if (e instanceof ResourceNotFoundError) {
throw new ResourceTreeNotCompleteError(
'One or more intermediate collections must be created before this resource.',
);
} else {
throw e;
}
}
const lockPermission = await this.getLockPermission(
request,
response,
resource,
response.locals.user,
);
// Check that the resource wouldn't be added to a locked collection.
if (newResource && lockPermission === 1) {
throw new LockedError(
'The user does not have permission to add a new resource to the locked collection.',
);
}
if (lockPermission === 0) {
throw new LockedError(
`The user does not have permission to ${
newResource ? 'create' : 'modify'
} the locked resource.`,
);
}
await this.checkConditionalHeaders(request, response);
if (
await this.runPlugins(request, response, 'beforePut', {
method: this,
resource,
newResource,
})
) {
return;
}
response.set({
'Cache-Control': 'private, no-cache',
Date: new Date().toUTCString(),
});
const contentLanguage = request.get('Content-Language');
let contentType = request.get('Content-Type');
if (contentType) {
contentType = contentType && contentType.split(';')[0];
if (
!contentType.match(
/^(application|audio|font|example|image|message|model|multipart|text|video|X-[a-zA-Z0-9_\-+.]+)\/[a-zA-Z0-9][a-zA-Z0-9_\-+.]*$/,
)
) {
contentType = undefined;
}
}
let stream = await this.getBodyStream(request, response);
await resource.setStream(stream, response.locals.user, contentType);
response.status(newResource ? 201 : 204); // Created or No Content
if (newResource) {
response.set({
Location: (await resource.getCanonicalUrl()).toString(),
});
}
response.end();
if (contentLanguage && contentLanguage !== '') {
try {
const properties = await resource.getProperties();
await properties.set('getcontentlanguage', contentLanguage);
} catch (e: any) {
// Ignore errors here.
}
}
await this.runPlugins(request, response, 'afterPut', {
method: this,
resource,
newResource,
});
}
}