r2-streamer-js
Version:
Readium 2 'streamer' for NodeJS (TypeScript)
220 lines • 11.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serverOPDS_local_feed_PATH_ = exports.serverOPDS_local_feed_PATH = void 0;
exports.serverOPDS_local_feed = serverOPDS_local_feed;
var crypto = require("crypto");
var css2json = require("css2json");
var debug_ = require("debug");
var DotProp = require("dot-prop");
var express = require("express");
var jsonMarkup = require("json-markup");
var path = require("path");
var serializable_1 = require("r2-lcp-js/dist/es5/src/serializable");
var opds2_link_1 = require("r2-opds-js/dist/es5/src/opds/opds2/opds2-link");
var UrlUtils_1 = require("r2-utils-js/dist/es5/src/_utils/http/UrlUtils");
var JsonUtils_1 = require("r2-utils-js/dist/es5/src/_utils/JsonUtils");
var json_schema_validate_1 = require("../utils/json-schema-validate");
var request_ext_1 = require("./request-ext");
var server_trailing_slash_redirect_1 = require("./server-trailing-slash-redirect");
var debug = debug_("r2:streamer#http/server-opds-local-feed");
exports.serverOPDS_local_feed_PATH = "/opds2";
exports.serverOPDS_local_feed_PATH_ = "/publications.json";
function serverOPDS_local_feed(server, topRouter) {
var jsonStyle = "\n.json-markup {\n line-height: 17px;\n font-size: 13px;\n font-family: monospace;\n white-space: pre;\n}\n.json-markup-key {\n font-weight: bold;\n}\n.json-markup-bool {\n color: firebrick;\n}\n.json-markup-string {\n color: green;\n}\n.json-markup-null {\n color: gray;\n}\n.json-markup-number {\n color: blue;\n}\n";
var routerOPDS_local_feed = express.Router({ strict: false });
routerOPDS_local_feed.get(["/", "/" + request_ext_1._show + "{/:" + request_ext_1._jsonPath + "}"], function (req, res) {
var _a;
var reqparams = req.params;
var isShow = req.url.indexOf("/show") >= 0 || req.query.show;
if (!reqparams.jsonPath && req.query.show) {
reqparams.jsonPath = req.query.show;
}
var isCanonical = req.query.canonical &&
req.query.canonical === "true";
var isSecureHttp = req.secure ||
req.protocol === "https" ||
req.get("X-Forwarded-Proto") === "https";
var rootUrl = (isSecureHttp ? "https://" : "http://")
+ req.headers.host;
var selfURL = rootUrl + exports.serverOPDS_local_feed_PATH + exports.serverOPDS_local_feed_PATH_;
var feed = server.publicationsOPDS();
if (!feed) {
var err = "Publications OPDS2 feed not available yet, try again later.";
debug(err);
res.status(503).send("<html><body><p>Resource temporarily unavailable</p><p>"
+ err + "</p></body></html>");
return;
}
if (!feed.findFirstLinkByRel("self")) {
feed.Links = [];
var selfLink = new opds2_link_1.OPDSLink();
selfLink.Href = selfURL;
selfLink.TypeLink = "application/opds+json";
selfLink.AddRel("self");
feed.Links.push(selfLink);
}
function absoluteURL(href) {
return rootUrl + "/pub/" + href;
}
function absolutizeURLs(jsonObj) {
(0, JsonUtils_1.traverseJsonObjects)(jsonObj, function (obj) {
if (obj.href && typeof obj.href === "string") {
if (!(0, UrlUtils_1.isHTTP)(obj.href)) {
obj.href = absoluteURL(obj.href);
}
if (isShow &&
(obj.type === "application/webpub+json"
|| obj.type === "application/audiobook+json"
|| obj.type === "application/divina+json") &&
obj.rel === "http://opds-spec.org/acquisition" &&
obj.href.endsWith("/manifest.json")) {
obj.href += "/show";
}
}
});
}
if (isShow) {
var objToSerialize = null;
if (reqparams.jsonPath) {
switch (reqparams.jsonPath) {
case "all": {
objToSerialize = feed;
break;
}
case "metadata": {
objToSerialize = feed.Metadata;
break;
}
case "links": {
objToSerialize = feed.Links;
break;
}
case "publications": {
objToSerialize = feed.Publications;
break;
}
default: {
objToSerialize = null;
}
}
}
else {
objToSerialize = feed;
}
if (!objToSerialize) {
objToSerialize = {};
}
var jsonObj = (0, serializable_1.TaJsonSerialize)(objToSerialize);
var validationStr = void 0;
var doValidate = !reqparams.jsonPath || reqparams.jsonPath === "all";
if (doValidate) {
var jsonSchemasRootpath = path.join(process.cwd(), "misc", "json-schema");
var jsonSchemasNames = [
"opds/feed",
"opds/publication",
"opds/acquisition-object",
"opds/catalog-entry",
"opds/feed-metadata",
"opds/properties",
"webpub-manifest/publication",
"webpub-manifest/contributor-object",
"webpub-manifest/contributor",
"webpub-manifest/link",
"webpub-manifest/a11y",
"webpub-manifest/metadata",
"webpub-manifest/subcollection",
"webpub-manifest/subject",
"webpub-manifest/subject-object",
"webpub-manifest/extensions/epub/metadata",
"webpub-manifest/extensions/epub/subcollections",
"webpub-manifest/extensions/epub/properties",
"webpub-manifest/extensions/presentation/metadata",
"webpub-manifest/extensions/presentation/properties",
"webpub-manifest/language-map",
];
var validationErrors = (0, json_schema_validate_1.jsonSchemaValidate)(jsonSchemasRootpath, jsonSchemasNames, jsonObj);
if (validationErrors) {
validationStr = "";
for (var _i = 0, validationErrors_1 = validationErrors; _i < validationErrors_1.length; _i++) {
var err = validationErrors_1[_i];
debug("JSON Schema validation FAIL.");
debug(err);
var val = err.jsonPath ? DotProp.get(jsonObj, err.jsonPath) : "";
var valueStr = (typeof val === "string") ?
"".concat(val) :
((val instanceof Array || typeof val === "object") ?
"".concat(JSON.stringify(val)) :
"");
debug(valueStr);
var title = "";
var pubIndex = "";
if (err.jsonPath && /^publications\.[0-9]+/.test(err.jsonPath)) {
var jsonPubTitlePath = err.jsonPath.replace(/^(publications\.[0-9]+).*/, "$1.metadata.title");
debug(jsonPubTitlePath);
title = DotProp.get(jsonObj, jsonPubTitlePath);
debug(title);
pubIndex = err.jsonPath.replace(/^publications\.([0-9]+).*/, "$1");
debug(pubIndex);
}
validationStr +=
"\n___________INDEX___________ #".concat(pubIndex, " \"").concat(title, "\"\n\n").concat(err.ajvMessage, ": ").concat(valueStr, "\n\n'").concat((_a = err.ajvDataPath) === null || _a === void 0 ? void 0 : _a.replace(/^\./, ""), "' (").concat(err.ajvSchemaPath, ")\n\n");
}
}
}
absolutizeURLs(jsonObj);
if (jsonObj.publications && jsonObj.publications.length) {
var i_1 = 0;
jsonObj.publications.forEach(function (pub) {
pub.___________INDEX___________ = i_1++;
});
}
var jsonPretty = jsonMarkup(jsonObj, css2json(jsonStyle));
res.status(200).send("<html><body>" +
"<h1>OPDS2 JSON feed</h1>" +
"<hr><p><pre>" + jsonPretty + "</pre></p>" +
(doValidate ? (validationStr ? ("<hr><p><pre>" + validationStr + "</pre></p>") : ("<hr><p>JSON SCHEMA OK.</p>")) : "") +
"</body></html>");
}
else {
server.setResponseCORS(res);
res.set("Content-Type", "application/opds+json; charset=utf-8");
var publicationsJsonObj = (0, serializable_1.TaJsonSerialize)(feed);
absolutizeURLs(publicationsJsonObj);
var publicationsJsonStr = isCanonical ?
global.JSON.stringify((0, JsonUtils_1.sortObject)(publicationsJsonObj), null, "") :
global.JSON.stringify(publicationsJsonObj, null, " ");
var checkSum = crypto.createHash("sha256");
checkSum.update(publicationsJsonStr);
var hash = checkSum.digest("hex");
var match = req.header("If-None-Match");
if (match === hash) {
debug("opds2 publications.json cache");
res.status(304);
res.end();
return;
}
res.setHeader("ETag", hash);
res.status(200).send(publicationsJsonStr);
}
});
var routerOPDS_local_feed_ = express.Router({ strict: false });
routerOPDS_local_feed_.use(server_trailing_slash_redirect_1.trailingSlashRedirect);
routerOPDS_local_feed_.get("/", function (req, res) {
var i = req.originalUrl.indexOf("?");
var pathWithoutQuery = req.originalUrl;
if (i >= 0) {
pathWithoutQuery = pathWithoutQuery.substr(0, i);
}
var redirect = pathWithoutQuery +
exports.serverOPDS_local_feed_PATH_ + "/show";
redirect = redirect.replace("//", "/");
if (i >= 0) {
redirect += req.originalUrl.substr(i);
}
debug("REDIRECT: ".concat(req.originalUrl, " ==> ").concat(redirect));
res.redirect(301, redirect);
});
routerOPDS_local_feed_.use(exports.serverOPDS_local_feed_PATH_, routerOPDS_local_feed);
topRouter.use(exports.serverOPDS_local_feed_PATH, routerOPDS_local_feed_);
}
//# sourceMappingURL=server-opds-local-feed.js.map