"error",
"last"
],
+ "indent": [
+ "warn",
+ 2,
+ {
+ "SwitchCase": 1
+ }
+ ],
"sonarjs/cognitive-complexity": "warn",
"sonarjs/no-duplicate-string": "warn",
"keyword-spacing": "error",
--- /dev/null
+{
+ "MD013": false,
+ "MD024": false
+}
## [Unreleased]
+## [v1.1.0] -
+
+### Added
+
+- Caching of topic contents for Postfix database backends. This should greatly reduce the db load when many subscribers to a topic are delivered an update.
+- Minor cleanup to generated HTML pages.
+
## [v1.0.0] - 2021-08-01
### Added
-Everything. MVP first stable release.
+- Everything. MVP first stable release.
---
-[Unreleased]: https://git.squeep.com/?p=websub-hub;a=commitdiff;h=HEAD;hp=v1.0.0
+[Unreleased]: https://git.squeep.com/?p=websub-hub;a=commitdiff;h=HEAD;hp=v1.1.0
+[v1.1.0]: https://git.squeep.com/?p=websub-hub;a=commitdiff;h=v1.1.0;hp=v1.0.0
[v1.0.0]: https://git.squeep.com/?p=websub-hub;a=commitdiff;h=v1.0.0;hp=v0.0.0
\ No newline at end of file
"dev": true
},
"@squeep/api-dingus": {
- "version": "git+https://git.squeep.com/squeep-api-dingus/#16db6709ab8407b1f696e3d5f92aa6980f182f39",
- "from": "git+https://git.squeep.com/squeep-api-dingus/#v1.0.0",
+ "version": "git+https://git.squeep.com/squeep-api-dingus/#12b96f53e7976b74296c1e024432b88749e6c4b0",
+ "from": "git+https://git.squeep.com/squeep-api-dingus/#v1.1-dev",
"requires": {
"mime-db": "^1.49.0",
"uuid": "^8.3.2"
},
"dependencies": {
- "mime-db": {
- "version": "1.49.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
- "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
- },
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
}
}
},
+ "mime-db": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
+ "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
+ },
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
}
},
"tar": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
- "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
+ "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
const authData = req.getHeader(Enum.Header.Authorization);
if (authData
&& await this.isValidAuthorization(authData, ctx)) {
- return true;
+ return true;
}
return this.requestBasic(res);
}
* @param {Number} jitter
* @returns {Number}
*/
- const attemptRetrySeconds = (attempt, retryBackoffSeconds = [60, 120, 360, 1440, 7200, 43200, 86400], jitter = 0.618) => {
+const attemptRetrySeconds = (attempt, retryBackoffSeconds = [60, 120, 360, 1440, 7200, 43200, 86400], jitter = 0.618) => {
const maxAttempt = retryBackoffSeconds.length - 1;
if (typeof attempt !== 'number' || attempt < 0) {
attempt = 0;
* @param {Array} array
* @param {Number} per
*/
- const arrayChunk = (array, per = 1) => {
+const arrayChunk = (array, per = 1) => {
const nChunks = Math.ceil(array.length / per);
return Array.from(Array(nChunks), (_, i) => array.slice(i * per, (i + 1) * per));
}
* @param {Array} dst
* @param {Array} src
*/
- const stackSafePush = (dst, src) => {
+const stackSafePush = (dst, src) => {
const jsEngineMaxArguments = 2**16; // Current as of Node 12
arrayChunk(src, jsEngineMaxArguments).forEach((items) => {
Array.prototype.push.apply(dst, items);
* @param {String} method
* @param {arguments} args
*/
- _notImplemented(method, args) {
+ _notImplemented(method, args) {
this.logger.error(_fileScope(method), 'abstract method called', Array.from(args));
throw new DBErrors.NotImplemented(method);
}
* Some engines will also perform other initializations or async actions which
* are easier handled outside the constructor.
*/
- async initialize() {
+ async initialize() {
const _scope = _fileScope('initialize');
const currentSchema = await this._currentSchema();
* @param {String} callback
* @param {*} topicId
*/
- async subscriptionGet(dbCtx, callback, topicId) {
+ async subscriptionGet(dbCtx, callback, topicId) {
this._notImplemented('subscriptionGet', arguments);
}
* @param {String=} data.httpRemoteAddr
* @param {String=} data.httpFrom
*/
- async subscriptionUpsert(dbCtx, data) {
+ async subscriptionUpsert(dbCtx, data) {
this._notImplemented('subscriptionUpsert', arguments);
}
* @param {*} topicId
* @returns {Boolean}
*/
- async topicFetchRequested(dbCtx, topicId) {
+ async topicFetchRequested(dbCtx, topicId) {
this._notImplemented('topicPublish', arguments);
}
* @param {Boolean} claim
* @returns {*} verificationId
*/
- async verificationInsert(dbCtx, verification) {
+ async verificationInsert(dbCtx, verification) {
this._notImplemented('verificationInsert', arguments);
}
* @param {String} data.reason
* @param {Boolean} data.isPublisherValidated
*/
- async verificationUpdate(dbCtx, verificationId, data) {
+ async verificationUpdate(dbCtx, verificationId, data) {
this._notImplemented('verificationUpdate', arguments);
- }
+ }
/**
let topics;
try {
topics = await dbCtx.manyOrNone(this.statement.topicGetInfoAll);
- } catch (e) {
+ } catch (e) {
this.logger.error(_scope, 'failed', { error: e, topics });
throw e;
}
let topics;
try {
topics = this.statement.topicGetInfoAll.all();
- } catch (e) {
+ } catch (e) {
this.logger.error(_scope, 'failed', { error: e, topics });
throw e;
}
.map(([name, value]) => ({ name, value })),
};
links.push(link);
- });
-
+ });
});
feedParser.on('readable', () => {
let _item;
const link = {
target: attributes.href,
attributes: Object.entries(attributes)
- .filter(([name]) => name !== 'href')
- .map(([name, value]) => ({ name, value })),
+ .filter(([name]) => name !== 'href')
+ .map(([name, value]) => ({ name, value })),
};
links.push(link);
}
return links;
}
+
/**
* Attempt to resolve a relative target URI
* @param {String} uri
* @param {http.ServerResponse} res
* @param {object} ctx
*/
- async getHealthcheck(res, ctx) {
+ async getHealthcheck(res, ctx) {
const _scope = _fileScope('getHealthcheck');
const health = 'happy';
* @param {Object} ctx
* @param {String} newPath
*/
- async handlerRedirect(req, res, ctx, newPath) {
+ async handlerRedirect(req, res, ctx, newPath) {
const _scope = _fileScope('handlerRedirect');
this.logger.debug(_scope, 'called', { req: common.requestLogData(req), ctx });
await this.manager.getTopicDetails(res, ctx);
}
+
/**
- * Same as super.ingestBody, but if no body was send, do not parse (and
+ * Same as super.ingestBody, but if no body was sent, do not parse (and
* thus avoid possible unsupported media type error).
* @param {http.ClientRequest} req
* @param {http.ServerResponse} res
* @param {http.ServerResponse} res
* @param {Object} ctx
*/
- async handlerUpdateTopic(req, res, ctx) {
+ async handlerUpdateTopic(req, res, ctx) {
const _scope = _fileScope('handlerUpdateTopic');
this.logger.debug(_scope, 'called', { req: common.requestLogData(req), ctx });
* @param {Object} ctx
*/
async handlerUpdateSubscription(req, res, ctx) {
- const _scope = _fileScope('handlerUpdateSubscription');
- this.logger.debug(_scope, 'called', { req: common.requestLogData(req), ctx });
+ const _scope = _fileScope('handlerUpdateSubscription');
+ this.logger.debug(_scope, 'called', { req: common.requestLogData(req), ctx });
- this.setResponseType(this.responseTypes, req, res, ctx);
+ this.setResponseType(this.responseTypes, req, res, ctx);
- await this.authenticator.required(req, res, ctx);
+ await this.authenticator.required(req, res, ctx);
- await this.maybeIngestBody(req, res, ctx);
- ctx.method = req.method;
- await this.manager.updateSubscription(res, ctx);
-}
+ await this.maybeIngestBody(req, res, ctx);
+ ctx.method = req.method;
+ await this.manager.updateSubscription(res, ctx);
+ }
/**
this.setResponseType(this.responseTypes, req, res, ctx);
await this.serveFile(req, res, ctx, this.staticPath, file || ctx.params.file);
- this.logger.info(_scope, 'finished', { ctx });
+ this.logger.info(_scope, 'finished', { ctx: { ...ctx, responseBody: common.logTruncate((ctx.responseBody || '').toString(), 100) } });
}
</li>
</ul>
</div>`
- : `
+ : `
<h2>Private Hub</h2>
<p>
This hub only serves specific topics.
* @param {Number} seconds
* @returns {String}
*/
- const secondsToPeriod = (seconds) => {
+const secondsToPeriod = (seconds) => {
let value = seconds;
const result = [];
<ol>
${navLinks.map((l) => renderNavLink(l)).join('\n')}
</ol>`
- : '') + `
+ : '') + `
</nav>
</header>
<main>`;
isSettled = true;
rejected = rej;
throw rej;
- });
+ });
Object.defineProperties(promise, {
isSettled: { get: () => isSettled },