mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 09:52:06 +03:00
2cae064575
refs 16728a3ef1
144 lines
3.7 KiB
JavaScript
144 lines
3.7 KiB
JavaScript
const _ = require('lodash');
|
|
const debug = require('@tryghost/debug')('services:url:urls');
|
|
const urlUtils = require('../../../shared/url-utils');
|
|
const logging = require('@tryghost/logging');
|
|
const errors = require('@tryghost/errors');
|
|
|
|
// This emits its own url added/removed events
|
|
const events = require('../../../server/lib/common/events');
|
|
|
|
/**
|
|
* This class keeps track of all urls in the system.
|
|
* Each resource has exactly one url. Each url is owned by exactly one url generator id.
|
|
* This is a connector for url generator and resources.
|
|
* Stores relative urls by default.
|
|
*
|
|
* We have to have a centralized place where we keep track of all urls, otherwise
|
|
* we will never know if we generate the same url twice. Furthermore, it's easier
|
|
* to ask a centralized class instance if you want a url for a resource than
|
|
* iterating over all url generators and asking for it.
|
|
* You can easily ask `this.urls[resourceId]`.
|
|
*/
|
|
class Urls {
|
|
constructor() {
|
|
this.urls = {};
|
|
}
|
|
|
|
/**
|
|
* @description Add a url to the system.
|
|
* @param {Object} options
|
|
*/
|
|
add(options) {
|
|
const url = options.url;
|
|
const generatorId = options.generatorId;
|
|
const resource = options.resource;
|
|
|
|
debug('cache', url);
|
|
|
|
if (this.urls[resource.data.id]) {
|
|
logging.error(new errors.InternalServerError({
|
|
message: 'This should not happen.',
|
|
code: 'URLSERVICE_RESOURCE_DUPLICATE'
|
|
}));
|
|
|
|
this.removeResourceId(resource.data.id);
|
|
}
|
|
|
|
this.urls[resource.data.id] = {
|
|
url: url,
|
|
generatorId: generatorId,
|
|
resource: resource
|
|
};
|
|
|
|
// @NOTE: Notify the whole system. Currently used for sitemaps service.
|
|
events.emit('url.added', {
|
|
url: {
|
|
relative: url,
|
|
absolute: urlUtils.createUrl(url, true)
|
|
},
|
|
resource: resource
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @description Get url by resource id.
|
|
* @param {String} id
|
|
* @returns {Object}
|
|
*/
|
|
getByResourceId(id) {
|
|
return this.urls[id];
|
|
}
|
|
|
|
/**
|
|
* @description Get all urls by generator id.
|
|
* @param {String} generatorId
|
|
* @returns {Array}
|
|
*/
|
|
getByGeneratorId(generatorId) {
|
|
return _.reduce(Object.keys(this.urls), (toReturn, resourceId) => {
|
|
if (this.urls[resourceId].generatorId === generatorId) {
|
|
toReturn.push(this.urls[resourceId]);
|
|
}
|
|
|
|
return toReturn;
|
|
}, []);
|
|
}
|
|
|
|
/**
|
|
* @description Get by url.
|
|
*
|
|
* @NOTE:
|
|
* It's is in theory possible that:
|
|
*
|
|
* - resource1 -> /welcome/
|
|
* - resource2 -> /welcome/
|
|
*
|
|
* But depending on the routing registration, you will always serve e.g. resource1,
|
|
* because the router it belongs to was registered first.
|
|
*/
|
|
getByUrl(url) {
|
|
return _.reduce(Object.keys(this.urls), (toReturn, resourceId) => {
|
|
if (this.urls[resourceId].url === url) {
|
|
toReturn.push(this.urls[resourceId]);
|
|
}
|
|
|
|
return toReturn;
|
|
}, []);
|
|
}
|
|
|
|
/**
|
|
* @description Remove url.
|
|
* @param id
|
|
*/
|
|
removeResourceId(id) {
|
|
if (!this.urls[id]) {
|
|
return;
|
|
}
|
|
|
|
debug('removed', this.urls[id].url, this.urls[id].generatorId);
|
|
|
|
events.emit('url.removed', {
|
|
url: this.urls[id].url,
|
|
resource: this.urls[id].resource
|
|
});
|
|
|
|
delete this.urls[id];
|
|
}
|
|
|
|
/**
|
|
* @description Reset instance.
|
|
*/
|
|
reset() {
|
|
this.urls = {};
|
|
}
|
|
|
|
/**
|
|
* @description Soft reset instance.
|
|
*/
|
|
softReset() {
|
|
this.urls = {};
|
|
}
|
|
}
|
|
|
|
module.exports = Urls;
|