diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e27c80..0a372a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### v1.6.7 (2021-10-04) +- Add multiple labels to Docker Compose ([#90](https://github.com/pawelmalak/flame/issues/90)) +- Custom icons via Docker Compose labels ([#91](https://github.com/pawelmalak/flame/issues/91)) + ### v1.6.6 (2021-09-06) - Added local search (filter) for apps and bookmarks ([#47](https://github.com/pawelmalak/flame/issues/47)) diff --git a/client/.env b/client/.env index 32d9a29..482555e 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=1.6.6 \ No newline at end of file +REACT_APP_VERSION=1.6.7 \ No newline at end of file diff --git a/controllers/apps.js b/controllers/apps.js index ace8f23..8d6e68d 100644 --- a/controllers/apps.js +++ b/controllers/apps.js @@ -14,7 +14,7 @@ const k8s = require('@kubernetes/client-node'); exports.createApp = asyncWrapper(async (req, res, next) => { // Get config from database const pinApps = await Config.findOne({ - where: { key: 'pinAppsByDefault' } + where: { key: 'pinAppsByDefault' }, }); let app; @@ -28,7 +28,7 @@ exports.createApp = asyncWrapper(async (req, res, next) => { if (parseInt(pinApps.value)) { app = await App.create({ ..._body, - isPinned: true + isPinned: true, }); } else { app = await App.create(req.body); @@ -37,7 +37,7 @@ exports.createApp = asyncWrapper(async (req, res, next) => { res.status(201).json({ success: true, - data: app + data: app, }); }); @@ -47,16 +47,16 @@ exports.createApp = asyncWrapper(async (req, res, next) => { exports.getApps = asyncWrapper(async (req, res, next) => { // Get config from database const useOrdering = await Config.findOne({ - where: { key: 'useOrdering' } + where: { key: 'useOrdering' }, }); const useDockerApi = await Config.findOne({ - where: { key: 'dockerApps' } + where: { key: 'dockerApps' }, }); const useKubernetesApi = await Config.findOne({ - where: { key: 'kubernetesApps' } + where: { key: 'kubernetesApps' }, }); const unpinStoppedApps = await Config.findOne({ - where: { key: 'unpinStoppedApps' } + where: { key: 'unpinStoppedApps' }, }); const orderType = useOrdering ? useOrdering.value : 'createdAt'; @@ -69,7 +69,7 @@ exports.getApps = asyncWrapper(async (req, res, next) => { let { data } = await axios.get( 'http://localhost/containers/json?{"status":["running"]}', { - socketPath: '/var/run/docker.sock' + socketPath: '/var/run/docker.sock', } ); containers = data; @@ -79,10 +79,10 @@ exports.getApps = asyncWrapper(async (req, res, next) => { if (containers) { apps = await App.findAll({ - order: [[orderType, 'ASC']] + order: [[orderType, 'ASC']], }); - containers = containers.filter(e => Object.keys(e.Labels).length !== 0); + containers = containers.filter((e) => Object.keys(e.Labels).length !== 0); const dockerApps = []; for (const container of containers) { const labels = container.Labels; @@ -92,17 +92,16 @@ exports.getApps = asyncWrapper(async (req, res, next) => { 'flame.url' in labels && /^app/.test(labels['flame.type']) ) { - - for (let i=0; i < labels['flame.name'].split(';').length; i++) { + for (let i = 0; i < labels['flame.name'].split(';').length; i++) { const names = labels['flame.name'].split(';'); const urls = labels['flame.url'].split(';'); const icons = labels['flame.icon'].split(';'); dockerApps.push({ name: names[i] || names[0], url: urls[i] || urls[0], - icon: icons[i] || 'docker' + icon: icons[i] || 'docker', }); - } + } } } @@ -116,7 +115,7 @@ exports.getApps = asyncWrapper(async (req, res, next) => { if (apps.some((app) => app.name === item.name)) { const app = apps.filter((e) => e.name === item.name)[0]; - if(item.icon === 'custom') { + if (item.icon === 'custom') { await app.update({ name: item.name, url: item.url, @@ -149,9 +148,8 @@ exports.getApps = asyncWrapper(async (req, res, next) => { const kc = new k8s.KubeConfig(); kc.loadFromCluster(); const k8sNetworkingV1Api = kc.makeApiClient(k8s.NetworkingV1Api); - await k8sNetworkingV1Api.listIngressForAllNamespaces() - .then((res) => { - ingresses = res.body.items; + await k8sNetworkingV1Api.listIngressForAllNamespaces().then((res) => { + ingresses = res.body.items; }); } catch { logger.log("Can't connect to the kubernetes api", 'ERROR'); @@ -159,10 +157,12 @@ exports.getApps = asyncWrapper(async (req, res, next) => { if (ingresses) { apps = await App.findAll({ - order: [[orderType, 'ASC']] + order: [[orderType, 'ASC']], }); - ingresses = ingresses.filter(e => Object.keys(e.metadata.annotations).length !== 0); + ingresses = ingresses.filter( + (e) => Object.keys(e.metadata.annotations).length !== 0 + ); const kubernetesApps = []; for (const ingress of ingresses) { const annotations = ingress.metadata.annotations; @@ -175,7 +175,7 @@ exports.getApps = asyncWrapper(async (req, res, next) => { kubernetesApps.push({ name: annotations['flame.pawelmalak/name'], url: annotations['flame.pawelmalak/url'], - icon: annotations['flame.pawelmalak/icon'] || 'kubernetes' + icon: annotations['flame.pawelmalak/icon'] || 'kubernetes', }); } } @@ -187,13 +187,13 @@ exports.getApps = asyncWrapper(async (req, res, next) => { } for (const item of kubernetesApps) { - if (apps.some(app => app.name === item.name)) { - const app = apps.filter(e => e.name === item.name)[0]; + if (apps.some((app) => app.name === item.name)) { + const app = apps.filter((e) => e.name === item.name)[0]; await app.update({ ...item, isPinned: true }); } else { await App.create({ ...item, - isPinned: true + isPinned: true, }); } } @@ -202,11 +202,11 @@ exports.getApps = asyncWrapper(async (req, res, next) => { if (orderType == 'name') { apps = await App.findAll({ - order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']] + order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']], }); } else { apps = await App.findAll({ - order: [[orderType, 'ASC']] + order: [[orderType, 'ASC']], }); } @@ -214,14 +214,14 @@ exports.getApps = asyncWrapper(async (req, res, next) => { // Set header to fetch containers info every time res.status(200).setHeader('Cache-Control', 'no-store').json({ success: true, - data: apps + data: apps, }); return; } res.status(200).json({ success: true, - data: apps + data: apps, }); }); @@ -230,7 +230,7 @@ exports.getApps = asyncWrapper(async (req, res, next) => { // @access Public exports.getApp = asyncWrapper(async (req, res, next) => { const app = await App.findOne({ - where: { id: req.params.id } + where: { id: req.params.id }, }); if (!app) { @@ -241,7 +241,7 @@ exports.getApp = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: app + data: app, }); }); @@ -250,7 +250,7 @@ exports.getApp = asyncWrapper(async (req, res, next) => { // @access Public exports.updateApp = asyncWrapper(async (req, res, next) => { let app = await App.findOne({ - where: { id: req.params.id } + where: { id: req.params.id }, }); if (!app) { @@ -269,7 +269,7 @@ exports.updateApp = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: app + data: app, }); }); @@ -278,12 +278,12 @@ exports.updateApp = asyncWrapper(async (req, res, next) => { // @access Public exports.deleteApp = asyncWrapper(async (req, res, next) => { await App.destroy({ - where: { id: req.params.id } + where: { id: req.params.id }, }); res.status(200).json({ success: true, - data: {} + data: {}, }); }); @@ -295,13 +295,13 @@ exports.reorderApps = asyncWrapper(async (req, res, next) => { await App.update( { orderId }, { - where: { id } + where: { id }, } ); }); res.status(200).json({ success: true, - data: {} + data: {}, }); }); diff --git a/controllers/bookmark.js b/controllers/bookmark.js index 8077a8c..e745d4d 100644 --- a/controllers/bookmark.js +++ b/controllers/bookmark.js @@ -11,7 +11,7 @@ exports.createBookmark = asyncWrapper(async (req, res, next) => { let _body = { ...req.body, - categoryId: parseInt(req.body.categoryId) + categoryId: parseInt(req.body.categoryId), }; if (req.file) { @@ -22,57 +22,67 @@ exports.createBookmark = asyncWrapper(async (req, res, next) => { res.status(201).json({ success: true, - data: bookmark - }) -}) + data: bookmark, + }); +}); // @desc Get all bookmarks // @route GET /api/bookmarks // @access Public exports.getBookmarks = asyncWrapper(async (req, res, next) => { const bookmarks = await Bookmark.findAll({ - order: [[ Sequelize.fn('lower', Sequelize.col('name')), 'ASC' ]] + order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']], }); res.status(200).json({ success: true, - data: bookmarks - }) -}) + data: bookmarks, + }); +}); // @desc Get single bookmark // @route GET /api/bookmarks/:id // @access Public exports.getBookmark = asyncWrapper(async (req, res, next) => { const bookmark = await Bookmark.findOne({ - where: { id: req.params.id } + where: { id: req.params.id }, }); if (!bookmark) { - return next(new ErrorResponse(`Bookmark with id of ${req.params.id} was not found`, 404)); + return next( + new ErrorResponse( + `Bookmark with id of ${req.params.id} was not found`, + 404 + ) + ); } res.status(200).json({ success: true, - data: bookmark - }) -}) + data: bookmark, + }); +}); // @desc Update bookmark // @route PUT /api/bookmarks/:id // @access Public exports.updateBookmark = asyncWrapper(async (req, res, next) => { let bookmark = await Bookmark.findOne({ - where: { id: req.params.id } + where: { id: req.params.id }, }); if (!bookmark) { - return next(new ErrorResponse(`Bookmark with id of ${req.params.id} was not found`, 404)); + return next( + new ErrorResponse( + `Bookmark with id of ${req.params.id} was not found`, + 404 + ) + ); } let _body = { ...req.body, - categoryId: parseInt(req.body.categoryId) + categoryId: parseInt(req.body.categoryId), }; if (req.file) { @@ -83,20 +93,20 @@ exports.updateBookmark = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: bookmark - }) -}) + data: bookmark, + }); +}); // @desc Delete bookmark // @route DELETE /api/bookmarks/:id // @access Public exports.deleteBookmark = asyncWrapper(async (req, res, next) => { await Bookmark.destroy({ - where: { id: req.params.id } + where: { id: req.params.id }, }); res.status(200).json({ success: true, - data: {} - }) -}) \ No newline at end of file + data: {}, + }); +}); diff --git a/controllers/category.js b/controllers/category.js index 15fe1eb..0f1af58 100644 --- a/controllers/category.js +++ b/controllers/category.js @@ -3,7 +3,7 @@ const ErrorResponse = require('../utils/ErrorResponse'); const Category = require('../models/Category'); const Bookmark = require('../models/Bookmark'); const Config = require('../models/Config'); -const { Sequelize } = require('sequelize') +const { Sequelize } = require('sequelize'); // @desc Create new category // @route POST /api/categories @@ -11,7 +11,7 @@ const { Sequelize } = require('sequelize') exports.createCategory = asyncWrapper(async (req, res, next) => { // Get config from database const pinCategories = await Config.findOne({ - where: { key: 'pinCategoriesByDefault' } + where: { key: 'pinCategoriesByDefault' }, }); let category; @@ -20,8 +20,8 @@ exports.createCategory = asyncWrapper(async (req, res, next) => { if (parseInt(pinCategories.value)) { category = await Category.create({ ...req.body, - isPinned: true - }) + isPinned: true, + }); } else { category = await Category.create(req.body); } @@ -29,9 +29,9 @@ exports.createCategory = asyncWrapper(async (req, res, next) => { res.status(201).json({ success: true, - data: category - }) -}) + data: category, + }); +}); // @desc Get all categories // @route GET /api/categories @@ -39,7 +39,7 @@ exports.createCategory = asyncWrapper(async (req, res, next) => { exports.getCategories = asyncWrapper(async (req, res, next) => { // Get config from database const useOrdering = await Config.findOne({ - where: { key: 'useOrdering' } + where: { key: 'useOrdering' }, }); const orderType = useOrdering ? useOrdering.value : 'createdAt'; @@ -47,27 +47,31 @@ exports.getCategories = asyncWrapper(async (req, res, next) => { if (orderType == 'name') { categories = await Category.findAll({ - include: [{ - model: Bookmark, - as: 'bookmarks' - }], - order: [[ Sequelize.fn('lower', Sequelize.col('Category.name')), 'ASC' ]] + include: [ + { + model: Bookmark, + as: 'bookmarks', + }, + ], + order: [[Sequelize.fn('lower', Sequelize.col('Category.name')), 'ASC']], }); } else { categories = await Category.findAll({ - include: [{ - model: Bookmark, - as: 'bookmarks' - }], - order: [[ orderType, 'ASC' ]] + include: [ + { + model: Bookmark, + as: 'bookmarks', + }, + ], + order: [[orderType, 'ASC']], }); } res.status(200).json({ success: true, - data: categories - }) -}) + data: categories, + }); +}); // @desc Get single category // @route GET /api/categories/:id @@ -75,41 +79,53 @@ exports.getCategories = asyncWrapper(async (req, res, next) => { exports.getCategory = asyncWrapper(async (req, res, next) => { const category = await Category.findOne({ where: { id: req.params.id }, - include: [{ - model: Bookmark, - as: 'bookmarks' - }] + include: [ + { + model: Bookmark, + as: 'bookmarks', + }, + ], }); if (!category) { - return next(new ErrorResponse(`Category with id of ${req.params.id} was not found`, 404)) + return next( + new ErrorResponse( + `Category with id of ${req.params.id} was not found`, + 404 + ) + ); } res.status(200).json({ success: true, - data: category - }) -}) + data: category, + }); +}); // @desc Update category // @route PUT /api/categories/:id // @access Public exports.updateCategory = asyncWrapper(async (req, res, next) => { let category = await Category.findOne({ - where: { id: req.params.id } + where: { id: req.params.id }, }); if (!category) { - return next(new ErrorResponse(`Category with id of ${req.params.id} was not found`, 404)) + return next( + new ErrorResponse( + `Category with id of ${req.params.id} was not found`, + 404 + ) + ); } category = await category.update({ ...req.body }); res.status(200).json({ success: true, - data: category - }) -}) + data: category, + }); +}); // @desc Delete category // @route DELETE /api/categories/:id @@ -117,44 +133,54 @@ exports.updateCategory = asyncWrapper(async (req, res, next) => { exports.deleteCategory = asyncWrapper(async (req, res, next) => { const category = await Category.findOne({ where: { id: req.params.id }, - include: [{ - model: Bookmark, - as: 'bookmarks' - }] + include: [ + { + model: Bookmark, + as: 'bookmarks', + }, + ], }); if (!category) { - return next(new ErrorResponse(`Category with id of ${req.params.id} was not found`, 404)) + return next( + new ErrorResponse( + `Category with id of ${req.params.id} was not found`, + 404 + ) + ); } category.bookmarks.forEach(async (bookmark) => { await Bookmark.destroy({ - where: { id: bookmark.id } - }) - }) + where: { id: bookmark.id }, + }); + }); await Category.destroy({ - where: { id: req.params.id } - }) + where: { id: req.params.id }, + }); res.status(200).json({ success: true, - data: {} - }) -}) + data: {}, + }); +}); // @desc Reorder categories // @route PUT /api/categories/0/reorder // @access Public exports.reorderCategories = asyncWrapper(async (req, res, next) => { req.body.categories.forEach(async ({ id, orderId }) => { - await Category.update({ orderId }, { - where: { id } - }) - }) + await Category.update( + { orderId }, + { + where: { id }, + } + ); + }); res.status(200).json({ success: true, - data: {} - }) -}) \ No newline at end of file + data: {}, + }); +}); diff --git a/controllers/config.js b/controllers/config.js index 1a456cf..a9768d2 100644 --- a/controllers/config.js +++ b/controllers/config.js @@ -14,9 +14,9 @@ exports.createPair = asyncWrapper(async (req, res, next) => { res.status(201).json({ success: true, - data: pair - }) -}) + data: pair, + }); +}); // @desc Get all key:value pairs // @route GET /api/config @@ -27,14 +27,14 @@ exports.getAllPairs = asyncWrapper(async (req, res, next) => { if (req.query.keys) { // Check for specific keys to get in a single query - const keys = req.query.keys - .split(',') - .map((key) => { return { key } }); + const keys = req.query.keys.split(',').map((key) => { + return { key }; + }); pairs = await Config.findAll({ where: { - [Op.or]: keys - } + [Op.or]: keys, + }, }); } else { // Else get all @@ -43,16 +43,16 @@ exports.getAllPairs = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: pairs - }) -}) + data: pairs, + }); +}); // @desc Get single key:value pair // @route GET /api/config/:key // @access Public exports.getSinglePair = asyncWrapper(async (req, res, next) => { const pair = await Config.findOne({ - where: { key: req.params.key } + where: { key: req.params.key }, }); if (!pair) { @@ -61,16 +61,16 @@ exports.getSinglePair = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: pair - }) -}) + data: pair, + }); +}); // @desc Update value // @route PUT /api/config/:key // @access Public exports.updateValue = asyncWrapper(async (req, res, next) => { let pair = await Config.findOne({ - where: { key: req.params.key } + where: { key: req.params.key }, }); if (!pair) { @@ -78,41 +78,49 @@ exports.updateValue = asyncWrapper(async (req, res, next) => { } if (pair.isLocked) { - return next(new ErrorResponse(`Value of key ${req.params.key} is locked and can not be changed`, 400)); + return next( + new ErrorResponse( + `Value of key ${req.params.key} is locked and can not be changed`, + 400 + ) + ); } pair = await pair.update({ ...req.body }); res.status(200).json({ success: true, - data: pair - }) -}) + data: pair, + }); +}); // @desc Update multiple values // @route PUT /api/config/ // @access Public exports.updateValues = asyncWrapper(async (req, res, next) => { Object.entries(req.body).forEach(async ([key, value]) => { - await Config.update({ value }, { - where: { key } - }) - }) - + await Config.update( + { value }, + { + where: { key }, + } + ); + }); + const config = await Config.findAll(); res.status(200).send({ success: true, - data: config - }) -}) + data: config, + }); +}); // @desc Delete key:value pair // @route DELETE /api/config/:key // @access Public exports.deletePair = asyncWrapper(async (req, res, next) => { const pair = await Config.findOne({ - where: { key: req.params.key } + where: { key: req.params.key }, }); if (!pair) { @@ -120,16 +128,21 @@ exports.deletePair = asyncWrapper(async (req, res, next) => { } if (pair.isLocked) { - return next(new ErrorResponse(`Value of key ${req.params.key} is locked and can not be deleted`, 400)); + return next( + new ErrorResponse( + `Value of key ${req.params.key} is locked and can not be deleted`, + 400 + ) + ); } await pair.destroy(); res.status(200).json({ success: true, - data: {} - }) -}) + data: {}, + }); +}); // @desc Get custom CSS file // @route GET /api/config/0/css @@ -140,10 +153,9 @@ exports.getCss = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: content - }) -}) - + data: content, + }); +}); // @desc Update custom CSS file // @route PUT /api/config/0/css @@ -153,10 +165,13 @@ exports.updateCss = asyncWrapper(async (req, res, next) => { file.write(req.body.styles); // Copy file to docker volume - fs.copyFileSync(join(__dirname, '../public/flame.css'), join(__dirname, '../data/flame.css')); + fs.copyFileSync( + join(__dirname, '../public/flame.css'), + join(__dirname, '../data/flame.css') + ); res.status(200).json({ success: true, - data: {} - }) -}) \ No newline at end of file + data: {}, + }); +}); diff --git a/controllers/weather.js b/controllers/weather.js index 4a5392f..3acd1ad 100644 --- a/controllers/weather.js +++ b/controllers/weather.js @@ -9,14 +9,14 @@ const getExternalWeather = require('../utils/getExternalWeather'); exports.getWeather = asyncWrapper(async (req, res, next) => { const weather = await Weather.findAll({ order: [['createdAt', 'DESC']], - limit: 1 + limit: 1, }); res.status(200).json({ success: true, - data: weather - }) -}) + data: weather, + }); +}); // @desc Update weather // @route GET /api/weather/update @@ -26,6 +26,6 @@ exports.updateWeather = asyncWrapper(async (req, res, next) => { res.status(200).json({ success: true, - data: weather - }) -}) \ No newline at end of file + data: weather, + }); +});