diff --git a/WHATSNEW.md b/WHATSNEW.md index 543dae82..53bf08fe 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -8,7 +8,7 @@ This document attempts to track **major** changes and additions in ENiGMA½. For * Routes for the file base now default to `/_f/` prefixed instead of just `/f/`. If `/f/` is in your `config.hjson` you are encouraged to update it! * Finally, the system will search for `index.html` and `index.htm` in that order, if another suitable route cannot be established. * [WebFinger](/docs/_docs/servers/contentservers/webfinger-handler.md) support. -* New users now have randomly generated avatars assigned to them that can be served up via, for example, the ActivityPub [Web Handler](/docs/_docs/servers/contentservers/web-handlers.md). +* New users now have randomly generated avatars assigned to them that can be served up via the new System General [Web Handler](/docs/_docs/servers/contentservers/web-handlers.md). * CombatNet has shut down, so the module (`combatnet.js`) has been removed. ## 0.0.13-beta diff --git a/core/config_default.js b/core/config_default.js index 86852f22..a5a64426 100644 --- a/core/config_default.js +++ b/core/config_default.js @@ -294,6 +294,12 @@ module.exports = () => { staticRoot: paths.join(__dirname, './../www'), + handlers: { + systemGeneral: { + enabled: true, + }, + }, + resetPassword: { // // The following templates have these variables available to them: diff --git a/core/servers/content/web_handlers/activitypub.js b/core/servers/content/web_handlers/activitypub.js index 7530ce76..771b6874 100644 --- a/core/servers/content/web_handlers/activitypub.js +++ b/core/servers/content/web_handlers/activitypub.js @@ -17,9 +17,6 @@ const _ = require('lodash'); const enigma_assert = require('../../../enigma_assert'); const httpSignature = require('http-signature'); const async = require('async'); -const paths = require('path'); -const fs = require('fs'); -const mimeTypes = require('mime-types'); exports.moduleInfo = { name: 'ActivityPub', @@ -95,13 +92,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { }, }); - // default avatar routing - this.webServer.addRoute({ - method: 'GET', - path: /^\/_enig\/ap\/users\/.+\/avatar\/.+$/, - handler: this._avatarGetHandler.bind(this), - }); - // :TODO: NYI // this.webServer.addRoute({ // method: 'GET', @@ -267,37 +257,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { return this._getCollectionHandler('outbox', req, resp, signature); } - _avatarGetHandler(req, resp) { - const url = new URL(req.url, `https://${req.headers.host}`); - const filename = paths.basename(url.pathname); - if (!filename) { - return this.webServer.fileNotFound(resp); - } - - const storagePath = _.get(Config(), 'users.avatars.storagePath'); - if (!storagePath) { - return this.webServer.fileNotFound(resp); - } - - const localPath = paths.join(storagePath, filename); - fs.stat(localPath, (err, stats) => { - if (err || !stats.isFile()) { - return this.webServer.accessDenied(resp); - } - - const headers = { - 'Content-Type': - mimeTypes.contentType(paths.basename(localPath)) || - mimeTypes.contentType('.png'), - 'Content-Length': stats.size, - }; - - const readStream = fs.createReadStream(localPath); - resp.writeHead(200, headers); - readStream.pipe(resp); - }); - } - _accountNameFromUserPath(url, suffix) { const re = new RegExp(`^/_enig/ap/users/(.+)/${suffix}(\\?page=[0-9]+)?$`); const m = url.pathname.match(re); diff --git a/core/servers/content/web_handlers/system_general.js b/core/servers/content/web_handlers/system_general.js new file mode 100644 index 00000000..7b156f6b --- /dev/null +++ b/core/servers/content/web_handlers/system_general.js @@ -0,0 +1,76 @@ +const WebHandlerModule = require('../../../web_handler_module'); +const { Errors } = require('../../../enig_error'); +const EngiAssert = require('../../../enigma_assert'); +const Config = require('../../../config').get; + +// deps +const paths = require('path'); +const fs = require('fs'); +const mimeTypes = require('mime-types'); +const get = require('lodash/get'); + +exports.moduleInfo = { + name: 'SystemGeneral', + desc: 'A general handler for system routes', + author: 'NuSkooler', + packageName: 'codes.l33t.enigma.web.handler.general_system', +}; + +exports.getModule = class SystemGeneralWebHandler extends WebHandlerModule { + constructor() { + super(); + } + + init(webServer, cb) { + // we rely on the web server + this.webServer = webServer; + EngiAssert(webServer, 'System General Web Handler init without webServer'); + + this.log = webServer.logger().child({ webHandler: 'SysGeneral' }); + + const domain = this.webServer.getDomain(); + if (!domain) { + return cb(Errors.UnexpectedState('Web server does not have "domain" set')); + } + + // default avatar routing + this.webServer.addRoute({ + method: 'GET', + path: /^\/_enig\/users\/.+\/avatar\/.+\.(png|jpg|jpeg|gif|webp)$/, + handler: this._avatarGetHandler.bind(this), + }); + + return cb(null); + } + + _avatarGetHandler(req, resp) { + const url = new URL(req.url, `https://${req.headers.host}`); + const filename = paths.basename(url.pathname); + if (!filename) { + return this.webServer.fileNotFound(resp); + } + + const storagePath = get(Config(), 'users.avatars.storagePath'); + if (!storagePath) { + return this.webServer.fileNotFound(resp); + } + + const localPath = paths.join(storagePath, filename); + fs.stat(localPath, (err, stats) => { + if (err || !stats.isFile()) { + return this.webServer.accessDenied(resp); + } + + const headers = { + 'Content-Type': + mimeTypes.contentType(paths.basename(localPath)) || + mimeTypes.contentType('.png'), + 'Content-Length': stats.size, + }; + + const readStream = fs.createReadStream(localPath); + resp.writeHead(200, headers); + readStream.pipe(resp); + }); + } +}; diff --git a/core/servers/content/web_handlers/webfinger.js b/core/servers/content/web_handlers/webfinger.js index 32d1af88..4d735d27 100644 --- a/core/servers/content/web_handlers/webfinger.js +++ b/core/servers/content/web_handlers/webfinger.js @@ -11,7 +11,7 @@ const { } = require('../../../activitypub/util'); const _ = require('lodash'); -const enigma_assert = require('../../../enigma_assert'); +const EngiAssert = require('../../../enigma_assert'); exports.moduleInfo = { name: 'WebFinger', @@ -29,11 +29,9 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule { } init(webServer, cb) { - const config = Config(); - // we rely on the web server this.webServer = webServer; - enigma_assert(webServer, 'WebFinger Web Handler init without webServer'); + EngiAssert(webServer, 'WebFinger Web Handler init without webServer'); this.log = webServer.logger().child({ webHandler: 'WebFinger' }); diff --git a/core/user.js b/core/user.js index 705aa45f..b2b39e3f 100644 --- a/core/user.js +++ b/core/user.js @@ -533,7 +533,7 @@ module.exports = class User { const { makeUserUrl } = require('./activitypub/util'); const filename = paths.basename(outPath); const url = - makeUserUrl(webServer.instance, self, '/ap/users/') + + makeUserUrl(webServer.instance, self, '/users/') + `/avatar/${filename}`; apSettings.image = url;