From b3b87c3802496d594b145904da0e45155bfe6b22 Mon Sep 17 00:00:00 2001 From: lesion Date: Thu, 22 Jun 2023 13:48:41 +0200 Subject: [PATCH] allow plugins to implements its own API, fix #283 --- server/api/controller/plugins.js | 20 ++++++++- server/api/index.js | 76 ++++++++++++++++---------------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/server/api/controller/plugins.js b/server/api/controller/plugins.js index 30b3b8d0..b0d2a9cb 100644 --- a/server/api/controller/plugins.js +++ b/server/api/controller/plugins.js @@ -32,6 +32,22 @@ const pluginController = { res.json() }, + async routeAPI (req, res, next) { + const pluginName = req.params.plugin + const plugin = pluginController.plugins.find(p => p.configuration.name === pluginName) + if (!plugin) { + log.warn(`Plugin ${pluginName} not found`) + return res.sendStatus(404) + } + + if (typeof plugin.routeAPI !== 'function') { + log.warn(`Plugin ${pluginName} does not expose a 'routeAPI' function`) + return res.sendStatus(404) + } + + return plugin.routeAPI(req, res, next) + }, + async testPlugin (req, res) { const pluginName = req.params.plugin const plugin = pluginController.plugins.find(p => p.configuration.name === pluginName) @@ -39,7 +55,7 @@ const pluginController = { log.warn(`Plugin ${pluginName} not found`) return res.sendStatus(404) } - + if (typeof plugin.onTest !== 'function') { log.warn(`Plugin ${pluginName} does not expose an 'onTest' function`) return res.sendStatus(404) @@ -122,7 +138,7 @@ const pluginController = { } } catch (e) { log.warn(`Unable to load plugin ${pluginFile}: ${String(e)}`) - } + } }, _load() { diff --git a/server/api/index.js b/server/api/index.js index bfc95181..6b3a5b6f 100644 --- a/server/api/index.js +++ b/server/api/index.js @@ -31,25 +31,25 @@ module.exports = () => { const api = express.Router() api.use(express.urlencoded({ extended: false })) api.use(express.json()) - + if (process.env.NODE_ENV !== 'test') { api.use(DDOSProtectionApiRateLimiter) } - - + + if (config.status !== 'READY') { - + api.post('/settings', settingsController.setRequest) api.post('/setup/db', setupController.setupDb) api.post('/setup/restart', setupController.restart) api.post('/settings/smtp', settingsController.testSMTP) api.get('/locale/:locale', localeController.get) - + } else { - + const { isAuth, isAdmin } = require('./auth') const upload = multer({ storage }) - + /** * Get current authenticated user * @category User @@ -72,25 +72,25 @@ module.exports = () => { */ api.get('/ping', (_req, res) => res.sendStatus(200)) api.get('/user', isAuth, (req, res) => res.json(req.user)) - + api.post('/user/recover', SPAMProtectionApiRateLimiter, userController.forgotPassword) api.post('/user/check_recover_code', userController.checkRecoverCode) api.post('/user/recover_password', SPAMProtectionApiRateLimiter, userController.updatePasswordWithRecoverCode) - + // register and add users api.post('/user/register', SPAMProtectionApiRateLimiter, userController.register) api.post('/user', isAdmin, userController.create) - + // update user api.put('/user', isAuth, userController.update) - + // delete user api.delete('/user/:id', isAdmin, userController.remove) api.delete('/user', isAuth, userController.remove) - + // get all users api.get('/users', isAdmin, userController.getAll) - + /** * Get events * @category Event @@ -110,9 +110,9 @@ module.exports = () => { * [https://demo.gancio.org/api/events](https://demo.gancio.org/api/events) * [usage example](https://framagit.org/les/gancio/-/blob/master/webcomponents/src/GancioEvents.svelte#L18-42) */ - + api.get('/events', cors, eventController.select) - + /** * Add a new event * @category Event @@ -134,25 +134,25 @@ module.exports = () => { * @param {array} [recurrent.days] - array of days * @param {image} [image] - Image */ - + // allow anyone to add an event (anon event has to be confirmed, flood protection) api.post('/event', eventController.isAnonEventAllowed, SPAMProtectionApiRateLimiter, upload.single('image'), eventController.add) - + // api.get('/event/search', eventController.search) - + api.put('/event', isAuth, upload.single('image'), eventController.update) api.get('/event/import', eventController.isAnonEventAllowed, helpers.importURL) - + // remove event api.delete('/event/:id', isAuth, eventController.remove) - + // get tags/places api.get('/event/meta', eventController.searchMeta) - + // add event notification TODO // api.post('/event/notification', eventController.addNotification) // api.delete('/event/notification/:code', eventController.delNotification) - + api.post('/settings', isAdmin, settingsController.setRequest) api.get('/settings', isAdmin, settingsController.getAll) api.post('/settings/logo', isAdmin, multer({ dest: config.upload_path }).single('logo'), settingsController.setLogo) @@ -160,21 +160,21 @@ module.exports = () => { api.post('/settings/headerImage', isAdmin, multer({ dest: config.upload_path }).single('headerImage'), settingsController.setHeaderImage) api.post('/settings/smtp', isAdmin, settingsController.testSMTP) api.get('/settings/smtp', isAdmin, settingsController.getSMTPSettings) - + // get unconfirmed events api.get('/event/unconfirmed', isAdmin, eventController.getUnconfirmed) - + // [un]confirm event api.put('/event/confirm/:event_id', isAuth, eventController.confirm) api.put('/event/unconfirm/:event_id', isAuth, eventController.unconfirm) - + // get event api.get('/event/detail/:event_slug.:format?', cors, eventController.get) - + // export events (rss/ics) api.get('/export/:format', cors, exportController.export) - - + + // - PLACES api.get('/places', isAdmin, placeController.getAll) api.get('/place/:placeName', cors, placeController.getEvents) @@ -184,15 +184,15 @@ module.exports = () => { // - GEOCODING api.get('/placeOSM/Nominatim/:place_details', helpers.isGeocodingEnabled, geocodingController.nominatimRateLimit, geocodingController._nominatim) api.get('/placeOSM/Photon/:place_details', helpers.isGeocodingEnabled, geocodingController.photonRateLimit, geocodingController._photon) - + // - TAGS api.get('/tags', isAdmin, tagController.getAll) api.get('/tag', cors, tagController.search) api.get('/tag/:tag', cors, tagController.getEvents) api.delete('/tag/:tag', isAdmin, tagController.remove) api.put('/tag', isAdmin, tagController.updateTag) - - + + // - FEDIVERSE INSTANCES, MODERATION, RESOURCES api.get('/instances', isAdmin, instanceController.getAll) api.get('/instances/:instance_domain', isAdmin, instanceController.get) @@ -201,13 +201,13 @@ module.exports = () => { api.put('/resources/:resource_id', isAdmin, resourceController.hide) api.delete('/resources/:resource_id', isAdmin, resourceController.remove) api.get('/resources', isAdmin, resourceController.getAll) - + // - ADMIN ANNOUNCEMENTS api.get('/announcements', isAdmin, announceController.getAll) api.post('/announcements', isAdmin, announceController.add) api.put('/announcements/:announce_id', isAdmin, announceController.update) api.delete('/announcements/:announce_id', isAdmin, announceController.remove) - + // - COLLECTIONS api.get('/collections/:name', cors, collectionController.getEvents) api.get('/collections', collectionController.getAll) @@ -216,12 +216,14 @@ module.exports = () => { api.get('/filter/:collection_id', isAdmin, collectionController.getFilters) api.post('/filter', isAdmin, collectionController.addFilter) api.delete('/filter/:id', isAdmin, collectionController.removeFilter) - + // - PLUGINS api.get('/plugins', isAdmin, pluginController.getAll) api.post('/plugin/test/:plugin', isAdmin, pluginController.testPlugin) api.put('/plugin/:plugin', isAdmin, pluginController.togglePlugin) - + + api.use('/plugin/:plugin', pluginController.routeAPI) + // OAUTH api.get('/clients', isAuth, oauthController.getClients) api.get('/client/:client_id', isAuth, oauthController.getClient) @@ -230,9 +232,9 @@ module.exports = () => { // CUSTOM LOCALE api.get('/locale/:locale', localeController.get) } - + api.use((_req, res) => res.sendStatus(404)) - + // Handle 500 api.use((error, _req, res, _next) => { log.error('[API ERROR]', error)