@webda/core
Version:
Expose API with Lambda
153 lines • 4.82 kB
JavaScript
import * as fs from "fs";
import * as mime from "mime-types";
import * as path from "path";
import { WebdaError } from "../errors.js";
import { Service, ServiceParameters } from "./service.js";
/**
* ResourceService parameters
*/
export class ResourceServiceParameters extends ServiceParameters {
constructor(params) {
super(params);
this.url ?? (this.url = "resources");
this.rootRedirect ?? (this.rootRedirect = false);
if (!this.url.startsWith("/")) {
this.url = "/" + this.url;
}
if (!this.url.endsWith("/")) {
this.url += "/";
}
this.folder ?? (this.folder = "." + this.url);
if (!this.folder.endsWith("/")) {
this.folder += "/";
}
this.index ?? (this.index = "index.html");
this.indexFallback ?? (this.indexFallback = true);
}
}
/**
* This service expose a folder as web
*
* It is the same as `static` on `express`
*
* @category CoreServices
* @WebdaModda
*/
export default class ResourceService extends Service {
/**
* Load the parameters for a service
*/
loadParameters(params) {
return new ResourceServiceParameters(params);
}
/**
* Resolve resource folder
*/
computeParameters() {
super.computeParameters();
this._resolved = path.resolve(this.parameters.folder);
this.fileOnly = !fs.lstatSync(this._resolved).isDirectory();
}
/**
* Init the routes
*/
initRoutes() {
this.addRoute(this.parameters.url, ["GET"], this._serve, {
get: {
description: "Get resources",
summary: "Get file",
operationId: "getResource",
responses: {
"200": {
description: ""
},
"401": {
description: "Illegal resource"
},
"404": {
description: "File not found"
}
}
}
});
this.addRoute(this.parameters.url + "{+resource}", ["GET"], this._serve, {
get: {
description: "Get resources",
summary: "Get file",
operationId: "getResources",
responses: {
"200": {
description: ""
},
"401": {
description: "Illegal resource"
},
"404": {
description: "File not found"
}
}
}
}, true);
if (this.parameters.rootRedirect) {
this.addRoute("/", ["GET"], this._redirect, {
get: {
description: "Redirect / to the exposed url",
summary: "Serve resource",
operationId: "redirectRoottoResources",
responses: {
"302": {
description: ""
}
}
}
});
}
}
/**
* Handle / request and redirect to the resources folder
*
* @param ctx
*/
_redirect(ctx) {
ctx.redirect(ctx.getHttpContext().getAbsoluteUrl(this.parameters.url));
}
/**
* Serve the folder by itself, doing the mime detection
*
* @param ctx
*/
_serve(ctx) {
let file = this._resolved;
// If resource is not a file
if (!this.fileOnly) {
file = path.join(this._resolved, ctx.parameter("resource") || this.parameters.index);
}
// Avoid path transversal
if (!path.resolve(file).startsWith(this._resolved)) {
throw new WebdaError.Unauthorized(file);
}
if (!fs.existsSync(file)) {
file = path.join(this._resolved, this.parameters.index);
// Catch All for SPA
if (!(this.parameters.indexFallback && fs.existsSync(file))) {
throw new WebdaError.NotFound(file);
}
}
let mimetype = mime.lookup(file) || "application/octet-stream";
if (mimetype.startsWith("text/")) {
mimetype += "; charset=UTF-8";
}
ctx.writeHead(200, {
"content-type": mimetype,
"content-length": fs.lstatSync(file).size
});
const stream = fs.createReadStream(file);
return new Promise((resolve, reject) => {
stream.on("error", reject);
stream.on("end", resolve);
stream.pipe(ctx.getStream());
});
}
}
export { ResourceService };
//# sourceMappingURL=resource.js.map