UNPKG

20.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.serverOPDS_convert_v1_to_v2 = exports.serverOPDS_convert_v1_to_v2_PATH = void 0;
4var tslib_1 = require("tslib");
5var css2json = require("css2json");
6var debug_ = require("debug");
7var DotProp = require("dot-prop");
8var express = require("express");
9var jsonMarkup = require("json-markup");
10var morgan = require("morgan");
11var path = require("path");
12var request = require("request");
13var requestPromise = require("request-promise-native");
14var xmldom = require("xmldom");
15var serializable_1 = require("r2-lcp-js/dist/es5/src/serializable");
16var converter_1 = require("r2-opds-js/dist/es5/src/opds/converter");
17var opds_1 = require("r2-opds-js/dist/es5/src/opds/opds1/opds");
18var opds_entry_1 = require("r2-opds-js/dist/es5/src/opds/opds1/opds-entry");
19var UrlUtils_1 = require("r2-utils-js/dist/es5/src/_utils/http/UrlUtils");
20var JsonUtils_1 = require("r2-utils-js/dist/es5/src/_utils/JsonUtils");
21var BufferUtils_1 = require("r2-utils-js/dist/es5/src/_utils/stream/BufferUtils");
22var xml_js_mapper_1 = require("r2-utils-js/dist/es5/src/_utils/xml-js-mapper");
23var json_schema_validate_1 = require("../utils/json-schema-validate");
24var request_ext_1 = require("./request-ext");
25var server_trailing_slash_redirect_1 = require("./server-trailing-slash-redirect");
26var debug = debug_("r2:streamer#http/server-opds-convert-v1-to-v2");
27exports.serverOPDS_convert_v1_to_v2_PATH = "/opds-v1-v2-convert";
28function serverOPDS_convert_v1_to_v2(_server, topRouter) {
29 var _this = this;
30 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";
31 var routerOPDS_convert_v1_to_v2 = express.Router({ strict: false });
32 routerOPDS_convert_v1_to_v2.use(morgan("combined", { stream: { write: function (msg) { return debug(msg); } } }));
33 routerOPDS_convert_v1_to_v2.use(server_trailing_slash_redirect_1.trailingSlashRedirect);
34 routerOPDS_convert_v1_to_v2.get("/", function (_req, res) {
35 var html = "<html><head>";
36 html += "<script type=\"text/javascript\">function encodeURIComponent_RFC3986(str) { " +
37 "return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { " +
38 "return \"%\" + c.charCodeAt(0).toString(16); }); }" +
39 "function go(evt) {" +
40 "if (evt) { evt.preventDefault(); } var url = " +
41 "location.origin +" +
42 (" '" + exports.serverOPDS_convert_v1_to_v2_PATH + "/' +") +
43 " encodeURIComponent_RFC3986(document.getElementById(\"url\").value);" +
44 "location.href = url;}</script>";
45 html += "</head>";
46 html += "<body><h1>OPDS 1 -> 2 converter</h1>";
47 html += "<form onsubmit=\"go();return false;\">" +
48 "<input type=\"text\" name=\"url\" id=\"url\" size=\"80\">" +
49 "<input type=\"submit\" value=\"Go!\"></form>";
50 html += "</body></html>";
51 res.status(200).send(html);
52 });
53 routerOPDS_convert_v1_to_v2.param("urlEncoded", function (req, _res, next, value, _name) {
54 req.urlEncoded = value;
55 next();
56 });
57 routerOPDS_convert_v1_to_v2.get("/:" + request_ext_1._urlEncoded + "(*)", function (req, res) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
58 var reqparams, urlDecoded, isSecureHttp, rootUrl, failure, success, headers, needsStreamingResponse, response, err_1;
59 var _this = this;
60 return tslib_1.__generator(this, function (_a) {
61 switch (_a.label) {
62 case 0:
63 reqparams = req.params;
64 if (!reqparams.urlEncoded) {
65 reqparams.urlEncoded = req.urlEncoded;
66 }
67 urlDecoded = reqparams.urlEncoded;
68 debug(urlDecoded);
69 isSecureHttp = req.secure ||
70 req.protocol === "https" ||
71 req.get("X-Forwarded-Proto") === "https";
72 rootUrl = (isSecureHttp ? "https://" : "http://")
73 + req.headers.host;
74 failure = function (err) {
75 debug(err);
76 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
77 + err + "</p></body></html>");
78 };
79 success = function (response) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
80 var responseData, err_2, responseStr, responseXml, isEntry, opds1Feed, opds1Entry, opds2Feed, opds2Publication, funk, jsonObjOPDS1, jsonObjOPDS2, validationStr, doValidate, jsonSchemasRootpath, jsonSchemasNames, validationErrors, _i, validationErrors_1, err, val, valueStr, title, val, valueStr, title, pubIndex, jsonPubTitlePath, css, jsonPrettyOPDS1, jsonPrettyOPDS2;
81 return tslib_1.__generator(this, function (_a) {
82 switch (_a.label) {
83 case 0:
84 if (response.statusCode && (response.statusCode < 200 || response.statusCode >= 300)) {
85 failure("HTTP CODE " + response.statusCode);
86 return [2];
87 }
88 _a.label = 1;
89 case 1:
90 _a.trys.push([1, 3, , 4]);
91 return [4, BufferUtils_1.streamToBufferPromise(response)];
92 case 2:
93 responseData = _a.sent();
94 return [3, 4];
95 case 3:
96 err_2 = _a.sent();
97 debug(err_2);
98 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
99 + err_2 + "</p></body></html>");
100 return [2];
101 case 4:
102 responseStr = responseData.toString("utf8");
103 responseXml = new xmldom.DOMParser().parseFromString(responseStr);
104 if (!responseXml || !responseXml.documentElement) {
105 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
106 + "XML parse fail" + "</p></body></html>");
107 return [2];
108 }
109 isEntry = responseXml.documentElement.localName === "entry";
110 if (isEntry) {
111 opds1Entry = xml_js_mapper_1.XML.deserialize(responseXml, opds_entry_1.Entry);
112 try {
113 opds2Publication = converter_1.convertOpds1ToOpds2_EntryToPublication(opds1Entry);
114 }
115 catch (err) {
116 debug("OPDS 1 -> 2 conversion FAILED (Entry)");
117 debug(err);
118 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
119 + err + "</p></body></html>");
120 return [2];
121 }
122 }
123 else {
124 opds1Feed = xml_js_mapper_1.XML.deserialize(responseXml, opds_1.OPDS);
125 try {
126 opds2Feed = converter_1.convertOpds1ToOpds2(opds1Feed);
127 }
128 catch (err) {
129 debug("OPDS 1 -> 2 conversion FAILED");
130 debug(err);
131 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
132 + err + "</p></body></html>");
133 return [2];
134 }
135 }
136 funk = function (obj) {
137 if ((obj.href && typeof obj.href === "string") ||
138 (obj.Href && typeof obj.Href === "string")) {
139 var fullHref = obj.href ? obj.href : obj.Href;
140 var notFull = !UrlUtils_1.isHTTP(fullHref);
141 if (notFull) {
142 fullHref = UrlUtils_1.ensureAbsolute(urlDecoded, fullHref);
143 }
144 if ((obj.type && obj.type.indexOf("application/atom+xml") >= 0) ||
145 (obj.Type && obj.Type.indexOf("application/atom+xml") >= 0)) {
146 obj.__href__ = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_convert_v1_to_v2_PATH + "/")) +
147 exports.serverOPDS_convert_v1_to_v2_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(fullHref);
148 }
149 else if (notFull) {
150 obj.__href__ = fullHref;
151 }
152 }
153 };
154 jsonObjOPDS1 = serializable_1.TaJsonSerialize(opds1Entry ? opds1Entry : opds1Feed);
155 JsonUtils_1.traverseJsonObjects(jsonObjOPDS1, funk);
156 jsonObjOPDS2 = serializable_1.TaJsonSerialize(opds2Publication ? opds2Publication : opds2Feed);
157 doValidate = !reqparams.jsonPath || reqparams.jsonPath === "all";
158 if (doValidate) {
159 jsonSchemasRootpath = path.join(process.cwd(), "misc", "json-schema");
160 jsonSchemasNames = [
161 "opds/publication",
162 "opds/acquisition-object",
163 "opds/catalog-entry",
164 "opds/feed-metadata",
165 "opds/properties",
166 "webpub-manifest/publication",
167 "webpub-manifest/contributor-object",
168 "webpub-manifest/contributor",
169 "webpub-manifest/link",
170 "webpub-manifest/metadata",
171 "webpub-manifest/subcollection",
172 "webpub-manifest/properties",
173 "webpub-manifest/subject",
174 "webpub-manifest/subject-object",
175 "webpub-manifest/extensions/epub/metadata",
176 "webpub-manifest/extensions/epub/subcollections",
177 "webpub-manifest/extensions/epub/properties",
178 "webpub-manifest/extensions/presentation/metadata",
179 "webpub-manifest/extensions/presentation/properties",
180 "webpub-manifest/language-map",
181 ];
182 if (!opds2Publication) {
183 jsonSchemasNames.unshift("opds/feed");
184 }
185 validationErrors = json_schema_validate_1.jsonSchemaValidate(jsonSchemasRootpath, jsonSchemasNames, jsonObjOPDS2);
186 if (validationErrors) {
187 validationStr = "";
188 for (_i = 0, validationErrors_1 = validationErrors; _i < validationErrors_1.length; _i++) {
189 err = validationErrors_1[_i];
190 debug("JSON Schema validation FAIL.");
191 debug(err);
192 if (opds2Publication) {
193 val = DotProp.get(jsonObjOPDS2, err.jsonPath);
194 valueStr = (typeof val === "string") ?
195 "" + val :
196 ((val instanceof Array || typeof val === "object") ?
197 "" + JSON.stringify(val) :
198 "");
199 debug(valueStr);
200 title = DotProp.get(jsonObjOPDS2, "metadata.title");
201 debug(title);
202 validationStr +=
203 "\n\"" + title + "\"\n\n" + err.ajvMessage + ": " + valueStr + "\n\n'" + err.ajvDataPath.replace(/^\./, "") + "' (" + err.ajvSchemaPath + ")\n\n";
204 }
205 else {
206 val = DotProp.get(jsonObjOPDS2, err.jsonPath);
207 valueStr = (typeof val === "string") ?
208 "" + val :
209 ((val instanceof Array || typeof val === "object") ?
210 "" + JSON.stringify(val) :
211 "");
212 debug(valueStr);
213 title = "";
214 pubIndex = "";
215 if (/^publications\.[0-9]+/.test(err.jsonPath)) {
216 jsonPubTitlePath = err.jsonPath.replace(/^(publications\.[0-9]+).*/, "$1.metadata.title");
217 debug(jsonPubTitlePath);
218 title = DotProp.get(jsonObjOPDS2, jsonPubTitlePath);
219 debug(title);
220 pubIndex = err.jsonPath.replace(/^publications\.([0-9]+).*/, "$1");
221 debug(pubIndex);
222 }
223 validationStr +=
224 "\n___________INDEX___________ #" + pubIndex + " \"" + title + "\"\n\n" + err.ajvMessage + ": " + valueStr + "\n\n'" + err.ajvDataPath.replace(/^\./, "") + "' (" + err.ajvSchemaPath + ")\n\n";
225 }
226 }
227 }
228 }
229 JsonUtils_1.traverseJsonObjects(jsonObjOPDS2, funk);
230 css = css2json(jsonStyle);
231 jsonPrettyOPDS1 = jsonMarkup(jsonObjOPDS1, css);
232 jsonPrettyOPDS2 = jsonMarkup(jsonObjOPDS2, css);
233 res.status(200).send("<html><body>" +
234 "<h1>OPDS2 JSON " +
235 (opds2Publication ? "entry" : "feed") +
236 " (converted from OPDS1 XML/ATOM)</h1>" +
237 "<h2><a href=\"" + urlDecoded + "\">" + urlDecoded + "</a></h2>" +
238 "<hr>" +
239 "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"90%\" " +
240 "style=\"table-layout:fixed;width:90%\">" +
241 "<thead><tr><th>OPDS1</th><th>OPDS2</th></tr></thead>" +
242 "<tbody><tr>" +
243 "<td valign=\"top\" width=\"50%\">" +
244 "<div style=\"overflow-x: auto;margin:0;padding:0;width:100%;height:auto;\">" +
245 jsonPrettyOPDS1 + "</div></td>" +
246 "<td valign=\"top\" width=\"50%\">" +
247 "<div style=\"overflow-x: auto;margin:0;padding:0;width:100%;height:auto;\">" +
248 jsonPrettyOPDS2 + "</div></td>" +
249 "</tbody></tr>" +
250 "</table>" +
251 (doValidate ? (validationStr ? ("<hr><p><pre>" + validationStr + "</pre></p>") : ("<hr><p>JSON SCHEMA OK.</p>")) : "") +
252 "</body></html>");
253 return [2];
254 }
255 });
256 }); };
257 headers = {
258 "Accept": "application/json,application/xml",
259 "Accept-Language": "en-UK,en-US;q=0.7,en;q=0.5",
260 "User-Agent": "READIUM2",
261 };
262 needsStreamingResponse = true;
263 if (!needsStreamingResponse) return [3, 1];
264 request.get({
265 headers: headers,
266 method: "GET",
267 uri: urlDecoded,
268 })
269 .on("response", success)
270 .on("error", failure);
271 return [3, 7];
272 case 1:
273 response = void 0;
274 _a.label = 2;
275 case 2:
276 _a.trys.push([2, 4, , 5]);
277 return [4, requestPromise({
278 headers: headers,
279 method: "GET",
280 resolveWithFullResponse: true,
281 uri: urlDecoded,
282 })];
283 case 3:
284 response = _a.sent();
285 return [3, 5];
286 case 4:
287 err_1 = _a.sent();
288 failure(err_1);
289 return [2];
290 case 5: return [4, success(response)];
291 case 6:
292 _a.sent();
293 _a.label = 7;
294 case 7: return [2];
295 }
296 });
297 }); });
298 topRouter.use(exports.serverOPDS_convert_v1_to_v2_PATH, routerOPDS_convert_v1_to_v2);
299}
300exports.serverOPDS_convert_v1_to_v2 = serverOPDS_convert_v1_to_v2;
301//# sourceMappingURL=server-opds-convert-v1-to-v2.js.map
\No newline at end of file