UNPKG

novel-opds-now

Version:

按需生成 epub,此模組不使用排程任務來生成 epub

421 lines 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports._putDeepEntryListMapToServer = exports._publishDeepEntryListMapToIPFS = exports.fixDeepEntryListMap = exports.mergeDeepEntryListMap = exports.saveDeepEntryListMapToMixin = exports.saveDeepEntryListMapToServer = exports.saveDeepEntryListMapToFile = exports._saveDeepEntryListMapToFile = exports.stringifyDeepEntryListMap = exports.pokeDeepEntryListMap = exports._pokeDeepEntryListMap = exports._backupDeepEntryListMap = exports._writeDeepEntryListMapToMfs = exports.enableOverwriteServer = exports.enableForceSave = exports._saveDeepEntryListMapToServer = exports.loadDeepEntryListMapFromMixin = exports.loadDeepEntryListMapFromServer = exports.loadDeepEntryListMapFromFile = exports._getDeepEntryListMapBoth = exports._setDeepEntryListMapBoth = exports.appendDeepEntryListMap = exports._handlePath = exports.appendDeepEntryListMapByStatResult = exports.pathDeepEntryListMapJson = exports.newEntryListMap = exports.deepEntryListMap = void 0; const tslib_1 = require("tslib"); const fs_extra_1 = require("fs-extra"); const path_1 = require("path"); const const_1 = require("../../const"); const bluebird_1 = (0, tslib_1.__importDefault)(require("bluebird")); const lodash_1 = require("lodash"); const micromatch_1 = require("micromatch"); const db_api_1 = require("@demonovel/db-api"); const put_1 = require("fetch-ipfs/put"); const ipfs_server_list_1 = require("ipfs-server-list"); const use_1 = require("../use"); const to_ipfs_url_1 = require("to-ipfs-url"); const pokeAll_1 = require("../pokeAll"); const logger_1 = (0, tslib_1.__importDefault)(require("debug-color2/logger")); const admin_1 = require("../../dev/admin"); const raceFetchServerList_1 = require("../../util/raceFetchServerList"); const cid_to_string_1 = require("@lazy-ipfs/cid-to-string"); const is_same_cid_1 = require("@lazy-ipfs/is-same-cid"); const _ipfsFilesCopy_1 = require("./_ipfsFilesCopy"); const to_cid_1 = require("@lazy-ipfs/to-cid"); const filterPokeAllSettledResult_1 = require("poke-ipfs/lib/util/filterPokeAllSettledResult"); const array_hyper_unique_1 = require("array-hyper-unique"); const bluebird_allsettled_1 = require("bluebird-allsettled"); exports.deepEntryListMap = new Map(); exports.newEntryListMap = new Map(); let _notOK = true; let _overwriteServer = false; const filename = 'novel-opds-now.cids.json'; const file = (0, path_1.join)(const_1.__root, 'test', 'data', filename); const msf_file = `/.cache/${filename}`; const rootKey = 'ipfs'; const dataKey = 'deepEntryListMap'; function pathDeepEntryListMapJson() { return msf_file; } exports.pathDeepEntryListMapJson = pathDeepEntryListMapJson; function appendDeepEntryListMapByStatResult(path, entry) { return appendDeepEntryListMap(path, entry.cid, entry.type === 'directory'); } exports.appendDeepEntryListMapByStatResult = appendDeepEntryListMapByStatResult; function _handlePath(path, isDirectory) { if (isDirectory && path[path.length - 1] !== '/') { path += '/'; } if (path[0] !== '/') { path = '/' + path; } return path; } exports._handlePath = _handlePath; function appendDeepEntryListMap(path, cid, isDirectory, forceAdd) { path = _handlePath(path, isDirectory); if (forceAdd || /^\/\.cache\//.test(path)) { } else if (!/^\/novel-opds-now\//.test(path) || !cid || !isDirectory && (0, micromatch_1.isMatch)(path, [ '*.{jpg,txt}', '**/*.{jpg,txt}', ])) { return false; } cid = cid.toString(); if (exports.deepEntryListMap.get(path) !== cid && exports.newEntryListMap.get(path) !== cid) { _setDeepEntryListMapBoth(path, cid); (0, exports.saveDeepEntryListMapToFile)(); } return true; } exports.appendDeepEntryListMap = appendDeepEntryListMap; function _setDeepEntryListMapBoth(path, cid, isDirectory) { path = _handlePath(path, isDirectory); cid = cid.toString(); exports.newEntryListMap.set(path, cid); exports.deepEntryListMap.set(path, cid); } exports._setDeepEntryListMapBoth = _setDeepEntryListMapBoth; function _getDeepEntryListMapBoth(path, isDirectory) { path = _handlePath(path, isDirectory); return exports.newEntryListMap.get(path) || exports.deepEntryListMap.get(path); } exports._getDeepEntryListMapBoth = _getDeepEntryListMapBoth; function loadDeepEntryListMapFromFile() { return bluebird_1.default.resolve((0, fs_extra_1.readJSON)(file)) .then((map) => { mergeDeepEntryListMap(map, exports.deepEntryListMap); }) .catchReturn(null) .thenReturn(exports.deepEntryListMap); } exports.loadDeepEntryListMapFromFile = loadDeepEntryListMapFromFile; function loadDeepEntryListMapFromServer() { return (0, db_api_1.getFileRecord)({ siteID: rootKey, novelID: dataKey, fetchOptions: { timeout: 20 * 1000, }, }) .then(async (raw) => { return (0, raceFetchServerList_1.raceFetchAll)((0, raceFetchServerList_1.raceFetchServerList)(null, raw.data.href), 60 * 1000) .then(buf => JSON.parse(String(buf))) .tap(row => { if (!row.length) { const e = new TypeError(`deepEntryListMap data is broken`); e.data = row; return Promise.reject(e); } }); }) .tap((map) => { let tmp = new Map(); mergeDeepEntryListMap(map, tmp); mergeDeepEntryListMap(fixDeepEntryListMap(tmp), exports.deepEntryListMap, _overwriteServer); fixDeepEntryListMap(exports.deepEntryListMap); }) .tapCatch(e => { logger_1.default.error(`loadDeepEntryListMapFromServer`, e); }) .catchReturn(null) .thenReturn(exports.deepEntryListMap); } exports.loadDeepEntryListMapFromServer = loadDeepEntryListMapFromServer; function loadDeepEntryListMapFromMixin() { return loadDeepEntryListMapFromFile() .then(loadDeepEntryListMapFromServer) .thenReturn(exports.deepEntryListMap); } exports.loadDeepEntryListMapFromMixin = loadDeepEntryListMapFromMixin; function _saveDeepEntryListMapToServer() { return loadDeepEntryListMapFromServer() .then(async () => { if (_notOK === false || exports.newEntryListMap.size || _overwriteServer) { _notOK = false; await mergeDeepEntryListMap(exports.newEntryListMap, exports.deepEntryListMap); if (!exports.deepEntryListMap.size) { return; } const ipfs = await (0, use_1.getIPFSFromCache)(); const peerID = await (ipfs === null || ipfs === void 0 ? void 0 : ipfs.id().then(m => m.id).catch(e => null)); if (ipfs && (0, admin_1.ipfsMainPeerID)(peerID)) { let stat = await ipfs.files.stat(`/novel-opds-now/`, { hash: true, }).catch(e => null); if (stat === null || stat === void 0 ? void 0 : stat.cid) { await _setDeepEntryListMapBoth(`/novel-opds-now/`, stat.cid, true); await mergeDeepEntryListMap(exports.newEntryListMap, exports.deepEntryListMap); (0, pokeAll_1.pokeAll)((0, cid_to_string_1.cidToString)(stat.cid), ipfs, { filename, hidden: true, timeout: 20 * 1000, }); } } let { cid, content } = await _publishDeepEntryListMapToIPFS(ipfs, exports.deepEntryListMap); (0, exports.pokeDeepEntryListMap)(cid, peerID); if (ipfs) { if ((0, admin_1.ipfsMainPeerID)(peerID)) { let stat = await ipfs.files.stat(`/novel-opds-now/`, { hash: true, }).catch(e => null); if (stat === null || stat === void 0 ? void 0 : stat.cid) { await _setDeepEntryListMapBoth(`/novel-opds-now/`, stat.cid, true); (0, pokeAll_1.pokeAll)((0, cid_to_string_1.cidToString)(stat.cid), ipfs, { filename, hidden: true, timeout: 20 * 1000, }); } } await _writeDeepEntryListMapToMfs(content); } await _backupDeepEntryListMap(cid, peerID); _setDeepEntryListMapBoth(pathDeepEntryListMapJson(), cid); return _putDeepEntryListMapToServer(ipfs, cid) .tap(v => { var _a, _b; if (!v.error && ((_a = v === null || v === void 0 ? void 0 : v.data) === null || _a === void 0 ? void 0 : _a.href)) { _overwriteServer = false; } logger_1.default.debug(`_saveDeepEntryListMapToServer`, v.timestamp, v.error, '\n' + ((_b = v === null || v === void 0 ? void 0 : v.data) === null || _b === void 0 ? void 0 : _b.href)); }); } }) .tapCatch(e => logger_1.default.error(`_saveDeepEntryListMapToServer`, e)) .catchReturn(null) .thenReturn(exports.deepEntryListMap); } exports._saveDeepEntryListMapToServer = _saveDeepEntryListMapToServer; function enableForceSave() { _notOK = false; } exports.enableForceSave = enableForceSave; function enableOverwriteServer() { _overwriteServer = true; } exports.enableOverwriteServer = enableOverwriteServer; function _writeDeepEntryListMapToMfs(content) { return (0, use_1.getIPFSFromCache)().then(ipfs => { if (!ipfs) { return null; } if (typeof content !== 'string') { content = stringifyDeepEntryListMap(content); } return ipfs.files.write(pathDeepEntryListMapJson(), content, { timeout: 10 * 1000, parents: true, create: true, }) .then(async () => { let cid = await ipfs.files.stat(pathDeepEntryListMapJson(), { hash: true, timeout: 3000, }).then(m => m.cid); return _backupDeepEntryListMap(cid, await (0, use_1.useIPFSFromCache)().then(m => { var _a, _b; return (_b = (_a = m === null || m === void 0 ? void 0 : m.ipfs) === null || _a === void 0 ? void 0 : _a.peerId) === null || _b === void 0 ? void 0 : _b.id; })); }); }) .catch(e => logger_1.default.error(`_writeDeepEntryListMapToMfs`, e)); } exports._writeDeepEntryListMapToMfs = _writeDeepEntryListMapToMfs; async function _backupDeepEntryListMap(cid, peerID) { if (1) { return; } const ipfs = await (0, use_1.getIPFSFromCache)(); const timeout = 10 * 1000; if (typeof peerID === 'undefined') { peerID = await (ipfs === null || ipfs === void 0 ? void 0 : ipfs.id({ timeout }).then(m => m.id).catch(e => null)); } if ((0, admin_1.ipfsMainPeerID)(peerID)) { let old_cid = _getDeepEntryListMapBoth(pathDeepEntryListMapJson()); if ((old_cid || cid) && !(0, is_same_cid_1.isSameCID)(old_cid, cid)) { let bak = pathDeepEntryListMapJson() + '.bak'; let bak_cid = old_cid !== null && old_cid !== void 0 ? old_cid : cid; await (0, _ipfsFilesCopy_1._ipfsFilesCopyCID)(ipfs, bak_cid, bak, { timeout, extraOptions: { overwrite: true, }, }).catch(e => null); _setDeepEntryListMapBoth(bak, bak_cid); } await ipfs.files.stat('/.cache', { hash: true, timeout, }).then(m => { _setDeepEntryListMapBoth('/.cache', m.cid, true); }) .catch(e => null); _setDeepEntryListMapBoth(pathDeepEntryListMapJson(), cid); } } exports._backupDeepEntryListMap = _backupDeepEntryListMap; function _pokeDeepEntryListMap(cid, peerID) { return bluebird_1.default.resolve() .then(async () => { const ipfs = await (0, use_1.getIPFSFromCache)(); const timeout = 10 * 1000; if (typeof peerID === 'undefined') { peerID = await (ipfs === null || ipfs === void 0 ? void 0 : ipfs.id({ timeout }).then(m => m.id).catch(e => null)); } cid = cid || await ipfs.files.stat(pathDeepEntryListMapJson(), { hash: true, timeout, }).then(m => m.cid); if (!cid) { return; } await _backupDeepEntryListMap(cid, peerID); let ls = await bluebird_1.default .map([ pathDeepEntryListMapJson(), pathDeepEntryListMapJson() + '.bak', '/.cache/', ], file => { return ipfs.files.stat(pathDeepEntryListMapJson(), { hash: true, timeout, }).then(m => m.cid).catch(e => null); }) .tap(ls => { ls.unshift((0, to_cid_1.toCID)(cid)); }); (0, array_hyper_unique_1.array_unique_overwrite)(ls); return (0, bluebird_allsettled_1.allSettled)(ls.map((cid, index) => { let p = (0, pokeAll_1.pokeAll)(cid, ipfs, { timeout: 20 * 1000, hidden: true, }); if (index === 0) { p = p.tap(settledResults => { let list = (0, filterPokeAllSettledResult_1.getPokeAllSettledResultWithHref)(settledResults !== null && settledResults !== void 0 ? settledResults : []); if (list === null || list === void 0 ? void 0 : list.length) { logger_1.default.info(`[IPFS]`, `pokeAll:end`, `結束於 ${list.length}${settledResults.length} 節點中請求分流`, dataKey, '\n' + list[list.length - 1]); } }); } return p; })); }) .catch(e => { logger_1.default.warn(`_pokeDeepEntryListMap`, cid, String(e)); }); } exports._pokeDeepEntryListMap = _pokeDeepEntryListMap; exports.pokeDeepEntryListMap = (0, lodash_1.debounce)(_pokeDeepEntryListMap, 30 * 1000); function stringifyDeepEntryListMap(deepEntryListMap) { let ls = [...new Map([...deepEntryListMap])]; return JSON.stringify((0, lodash_1.sortBy)(ls, '0'), null, 2); } exports.stringifyDeepEntryListMap = stringifyDeepEntryListMap; function _saveDeepEntryListMapToFile() { return bluebird_1.default.resolve() .then(() => { if (_notOK === true && !exports.newEntryListMap.size) { return; } _notOK = true; let ls = new Map([...exports.deepEntryListMap, ...exports.newEntryListMap]); if (!ls.size) { return; } const content = stringifyDeepEntryListMap(ls); (0, fs_extra_1.outputFileSync)(file, content); logger_1.default.debug(`_saveDeepEntryListMapToFile`, ls.size); return _writeDeepEntryListMapToMfs(content); }) .catchReturn(null) .thenReturn(exports.deepEntryListMap) .finally(() => { (0, exports.pokeDeepEntryListMap)(); }); } exports._saveDeepEntryListMapToFile = _saveDeepEntryListMapToFile; exports.saveDeepEntryListMapToFile = (0, lodash_1.debounce)(_saveDeepEntryListMapToFile, 10 * 60 * 1000); exports.saveDeepEntryListMapToServer = (0, lodash_1.debounce)(_saveDeepEntryListMapToServer, 5 * 60 * 1000); function saveDeepEntryListMapToMixin() { return bluebird_1.default.mapSeries([ exports.saveDeepEntryListMapToServer, exports.saveDeepEntryListMapToFile, ], (r) => r).thenReturn(exports.deepEntryListMap); } exports.saveDeepEntryListMapToMixin = saveDeepEntryListMapToMixin; function mergeDeepEntryListMap(input, target, keepExists) { var _a; if (input) { if (keepExists) { for (const [path, cid] of (Array.isArray(input) ? input.values() : input.entries())) { if ((_a = target.get(path)) === null || _a === void 0 ? void 0 : _a.length) { continue; } target.set(path, cid); } } else { for (const [path, cid] of (Array.isArray(input) ? input.values() : input.entries())) { target.set(path, cid); } } } return target; } exports.mergeDeepEntryListMap = mergeDeepEntryListMap; function fixDeepEntryListMap(deepEntryListMap) { deepEntryListMap.forEach((value, key, map) => { if (value.includes('novel-opds-now')) { deepEntryListMap.set(value, key); deepEntryListMap.delete(key); logger_1.default.warn(`fixDeepEntryListMap`, value); } }); return deepEntryListMap; } exports.fixDeepEntryListMap = fixDeepEntryListMap; async function _publishDeepEntryListMapToIPFS(ipfs, deepEntryListMap) { let cid; const content = stringifyDeepEntryListMap(deepEntryListMap); await (0, put_1.publishToIPFSRace)({ path: filename, content, }, [ ipfs, ...(0, ipfs_server_list_1.filterList)('API'), ], { addOptions: { pin: true, }, timeout: 60 * 1000, }) .each((settledResult, index) => { var _a, _b; let value = (_a = settledResult.value) !== null && _a !== void 0 ? _a : (_b = settledResult.reason) === null || _b === void 0 ? void 0 : _b.value; if (value === null || value === void 0 ? void 0 : value.length) { const { status } = settledResult; value.forEach((result, i) => { cid = result.cid.toString(); }); } }); return { cid, content, }; } exports._publishDeepEntryListMapToIPFS = _publishDeepEntryListMapToIPFS; function _putDeepEntryListMapToServer(ipfs, cid) { return (0, db_api_1.putFileRecord)({ siteID: rootKey, novelID: dataKey, data: { timestamp: Date.now(), exists: true, filename, href: (0, to_ipfs_url_1.toLink)(cid), }, }); } exports._putDeepEntryListMapToServer = _putDeepEntryListMapToServer; //# sourceMappingURL=deepEntryListMap.js.map