UNPKG

dita-streamer-js

Version:

Readium 2 'streamer' for NodeJS (TypeScript)

314 lines 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Server = exports.MAX_PREFETCH_LINKS = void 0; const child_process = require("child_process"); const debug_ = require("debug"); const express = require("express"); const fs = require("fs"); const http = require("http"); const https = require("https"); const path = require("path"); const tmp_1 = require("tmp"); const serializable_1 = require("r2-lcp-js/dist/es8-es2017/src/serializable"); const opds2_1 = require("r2-opds-js/dist/es8-es2017/src/opds/opds2/opds2"); const publication_parser_1 = require("r2-shared-js/dist/es8-es2017/src/parser/publication-parser"); const UrlUtils_1 = require("r2-utils-js/dist/es8-es2017/src/_utils/http/UrlUtils"); const self_signed_1 = require("../utils/self-signed"); const server_assets_1 = require("./server-assets"); const server_lcp_lsd_show_1 = require("./server-lcp-lsd-show"); const server_manifestjson_1 = require("./server-manifestjson"); const server_mediaoverlays_1 = require("./server-mediaoverlays"); const server_opds_browse_v1_1 = require("./server-opds-browse-v1"); const server_opds_browse_v2_1 = require("./server-opds-browse-v2"); const server_opds_convert_v1_to_v2_1 = require("./server-opds-convert-v1-to-v2"); const server_opds_local_feed_1 = require("./server-opds-local-feed"); const server_pub_1 = require("./server-pub"); const server_root_1 = require("./server-root"); const server_secure_1 = require("./server-secure"); const server_url_1 = require("./server-url"); const server_version_1 = require("./server-version"); const debug = debug_("r2:streamer#http/server"); exports.MAX_PREFETCH_LINKS = 10; class Server { constructor(options) { var _a; this.lcpBeginToken = "*-"; this.lcpEndToken = "-*"; this.disableReaders = options && options.disableReaders ? options.disableReaders : false; this.disableDecryption = options && options.disableDecryption ? options.disableDecryption : false; this.disableRemotePubUrl = options && options.disableRemotePubUrl ? options.disableRemotePubUrl : false; this.disableOPDS = options && options.disableOPDS ? options.disableOPDS : false; this.readers = (_a = options === null || options === void 0 ? void 0 : options.readers) !== null && _a !== void 0 ? _a : [ { title: "Reader NYPL", getUrl: (url) => `/readerNYPL/?url=${url}` }, { title: "Reader EPUB.js", getUrl: url => `https://s3.amazonaws.com/epubjs-manifest/examples/manifest.html?href=${url}` }, { title: "Reader HADRIEN", getUrl: url => `/readerHADRIEN/?manifest=true&href=${url}` }, { title: "Reader HADRIEN BASIC", getUrl: url => `https://hadriengardeur.github.io/webpub-manifest/examples/viewer/?manifest=true&href=${url}` }, ]; this.maxPrefetchLinks = options && options.maxPrefetchLinks ? options.maxPrefetchLinks : exports.MAX_PREFETCH_LINKS; this.publications = []; this.pathPublicationMap = {}; this.publicationsOPDSfeed = undefined; this.publicationsOPDSfeedNeedsUpdate = true; this.creatingPublicationsOPDS = false; this.opdsJsonFilePath = tmp_1.tmpNameSync({ prefix: "readium2-OPDS2-", postfix: ".json" }); this.expressApp = express(); server_secure_1.serverSecure(this, this.expressApp); const staticOptions = { etag: false, }; if (!this.disableReaders) { this.expressApp.use("/readerNYPL", express.static("misc/readers/reader-NYPL", staticOptions)); this.expressApp.use("/readerHADRIEN", express.static("misc/readers/reader-HADRIEN", staticOptions)); } server_root_1.serverRoot(this, this.expressApp); server_version_1.serverVersion(this, this.expressApp); if (!this.disableRemotePubUrl) { server_url_1.serverRemotePub(this, this.expressApp); server_lcp_lsd_show_1.serverLCPLSD_show(this, this.expressApp); } if (!this.disableOPDS) { server_opds_browse_v1_1.serverOPDS_browse_v1(this, this.expressApp); server_opds_browse_v2_1.serverOPDS_browse_v2(this, this.expressApp); server_opds_local_feed_1.serverOPDS_local_feed(this, this.expressApp); server_opds_convert_v1_to_v2_1.serverOPDS_convert_v1_to_v2(this, this.expressApp); } const routerPathBase64 = server_pub_1.serverPub(this, this.expressApp); server_manifestjson_1.serverManifestJson(this, routerPathBase64); server_mediaoverlays_1.serverMediaOverlays(this, routerPathBase64); server_assets_1.serverAssets(this, routerPathBase64); } preventRobots() { this.expressApp.get("/robots.txt", (_req, res) => { const robotsTxt = `User-agent: * Disallow: / `; res.header("Content-Type", "text/plain"); res.status(200).send(robotsTxt); }); } expressUse(pathf, func) { this.expressApp.use(pathf, func); } expressGet(paths, func) { this.expressApp.get(paths, func); } isStarted() { return (typeof this.serverInfo() !== "undefined") && (typeof this.httpServer !== "undefined") || (typeof this.httpsServer !== "undefined"); } isSecured() { return (typeof this.serverInfo() !== "undefined") && (typeof this.httpsServer !== "undefined"); } getSecureHTTPHeader(url) { return server_secure_1.serverSecureHTTPHeader(this, url); } async start(port, secure) { if (this.isStarted()) { return Promise.resolve(this.serverInfo()); } let envPort = 0; try { envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : 0; } catch (err) { debug(err); envPort = 0; } const p = port || envPort || 3000; debug(`PORT: ${port} || ${envPort} || 3000 => ${p}`); if (secure) { this.httpServer = undefined; return new Promise(async (resolve, reject) => { let certData; try { certData = await self_signed_1.generateSelfSignedData(); } catch (err) { debug(err); reject("err"); return; } this.httpsServer = https.createServer({ key: certData.private, cert: certData.cert }, this.expressApp).listen(p, () => { this.serverData = Object.assign(Object.assign({}, certData), { urlHost: "127.0.0.1", urlPort: p, urlScheme: "https" }); resolve(this.serverData); }); }); } else { this.httpsServer = undefined; return new Promise((resolve, _reject) => { this.httpServer = http.createServer(this.expressApp).listen(p, () => { this.serverData = { urlHost: "127.0.0.1", urlPort: p, urlScheme: "http", }; resolve(this.serverData); }); }); } } stop() { if (this.isStarted()) { if (this.httpServer) { this.httpServer.close(); this.httpServer = undefined; } if (this.httpsServer) { this.httpsServer.close(); this.httpsServer = undefined; } this.serverData = undefined; this.uncachePublications(); } } serverInfo() { return this.serverData; } serverUrl() { if (!this.isStarted()) { return undefined; } const info = this.serverInfo(); if (!info) { return undefined; } if (info.urlPort === 443 || info.urlPort === 80) { return `${info.urlScheme}://${info.urlHost}`; } return `${info.urlScheme}://${info.urlHost}:${info.urlPort}`; } setResponseCacheHeaders(res, enableCaching) { if (enableCaching) { res.setHeader("Cache-Control", "public,max-age=86400"); } else { res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); res.setHeader("Pragma", "no-cache"); res.setHeader("Expires", "0"); } } setResponseCORS(res) { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Ranges, Content-Range, Range, Link, Transfer-Encoding, X-Requested-With, Authorization, Accept, Origin, User-Agent, DNT, Cache-Control, Keep-Alive, If-Modified-Since"); res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Length, Accept-Ranges, Content-Range, Range, Link, Transfer-Encoding, X-Requested-With, Authorization, Accept, Origin, User-Agent, DNT, Cache-Control, Keep-Alive, If-Modified-Since"); } addPublications(pubs) { pubs.forEach((pub) => { if (this.publications.indexOf(pub) < 0) { this.publicationsOPDSfeedNeedsUpdate = true; this.publications.push(pub); } }); return pubs.map((pub) => { const pubid = UrlUtils_1.encodeURIComponent_RFC3986(Buffer.from(pub).toString("base64")); return `/pub/${pubid}/manifest.json`; }); } removePublications(pubs) { pubs.forEach((pub) => { this.uncachePublication(pub); const i = this.publications.indexOf(pub); if (i >= 0) { this.publicationsOPDSfeedNeedsUpdate = true; this.publications.splice(i, 1); } }); return pubs.map((pub) => { const pubid = UrlUtils_1.encodeURIComponent_RFC3986(Buffer.from(pub).toString("base64")); return `/pub/${pubid}/manifest.json`; }); } getPublications() { return this.publications; } async loadOrGetCachedPublication(filePath) { let publication = this.cachedPublication(filePath); if (!publication) { try { publication = await publication_parser_1.PublicationParsePromise(filePath); } catch (err) { debug(err); return Promise.reject(err); } this.cachePublication(filePath, publication); } return publication; } isPublicationCached(filePath) { return typeof this.cachedPublication(filePath) !== "undefined"; } cachedPublication(filePath) { return this.pathPublicationMap[filePath]; } cachePublication(filePath, pub) { if (!this.isPublicationCached(filePath)) { this.pathPublicationMap[filePath] = pub; } } uncachePublication(filePath) { if (this.isPublicationCached(filePath)) { const pub = this.cachedPublication(filePath); if (pub) { pub.freeDestroy(); } this.pathPublicationMap[filePath] = undefined; delete this.pathPublicationMap[filePath]; } } uncachePublications() { Object.keys(this.pathPublicationMap).forEach((filePath) => { this.uncachePublication(filePath); }); } publicationsOPDS() { if (this.publicationsOPDSfeedNeedsUpdate) { this.publicationsOPDSfeed = undefined; if (fs.existsSync(this.opdsJsonFilePath)) { fs.unlinkSync(this.opdsJsonFilePath); } } if (this.publicationsOPDSfeed) { return this.publicationsOPDSfeed; } debug(`OPDS2.json => ${this.opdsJsonFilePath}`); if (!fs.existsSync(this.opdsJsonFilePath)) { if (!this.creatingPublicationsOPDS) { this.creatingPublicationsOPDS = true; this.publicationsOPDSfeedNeedsUpdate = false; const jsFile = path.join(__dirname, "opds2-create-cli.js"); const args = [jsFile, this.opdsJsonFilePath]; this.publications.forEach((pub) => { const filePathBase64 = UrlUtils_1.encodeURIComponent_RFC3986(Buffer.from(pub).toString("base64")); args.push(filePathBase64); }); debug(`SPAWN OPDS2-create: ${args[0]}`); const child = child_process.spawn("node", args, { cwd: process.cwd(), env: process.env, }); child.stdout.on("data", (data) => { debug(data.toString()); }); child.stderr.on("data", (data) => { debug(data.toString()); }); } return undefined; } this.creatingPublicationsOPDS = false; const jsonStr = fs.readFileSync(this.opdsJsonFilePath, { encoding: "utf8" }); if (!jsonStr) { return undefined; } const json = global.JSON.parse(jsonStr); this.publicationsOPDSfeed = serializable_1.TaJsonDeserialize(json, opds2_1.OPDSFeed); return this.publicationsOPDSfeed; } } exports.Server = Server; //# sourceMappingURL=server.js.map