From b53509aa691d5889fe8cd9dd4afba227c59cc653 Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Date: Wed, 4 Aug 2021 22:19:35 +0200 Subject: [PATCH] docker api --- README.md | 15 +++++ .../Settings/OtherSettings/OtherSettings.tsx | 33 ++++++++++- client/src/interfaces/Forms.ts | 2 + controllers/apps.js | 55 ++++++++++++++++++- utils/initialConfig.json | 8 +++ 5 files changed, 110 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5c1abe2..4074c61 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ services: container_name: flame volumes: - :/app/data + - /var/run/docker.sock:/var/sock/docker.sock # Docker socket ports: - 5005:5005 restart: unless-stopped @@ -155,6 +156,20 @@ To use search bar you need to type your search query with selected prefix. For e - Format: `www.domain.com`, `domain.com`, `sub.domain.com`, `local`, `ip`, `ip:port` - Redirect: `http://{dest}` +### Docker integration + +In order to use the Docker integration, each container must have the following labels: + +```yml +labels: + - flame.type=application # "app" works too + - flame.name=My container + - flame.url=https://example.com + - flame.icon=icon-name # Optional, default is "docker" +``` + +And you must have activated the Docker sync option in the settings panel. + ### Custom CSS > This is an experimental feature. Its behaviour might change in the future. diff --git a/client/src/components/Settings/OtherSettings/OtherSettings.tsx b/client/src/components/Settings/OtherSettings/OtherSettings.tsx index 199e9ff..cdf5302 100644 --- a/client/src/components/Settings/OtherSettings/OtherSettings.tsx +++ b/client/src/components/Settings/OtherSettings/OtherSettings.tsx @@ -40,7 +40,9 @@ const OtherSettings = (props: ComponentProps): JSX.Element => { useOrdering: 'createdAt', appsSameTab: 0, bookmarksSameTab: 0, - searchSameTab: 0 + searchSameTab: 0, + dockerApps:1, + unpinStoppedApps: 1 }) // Get config @@ -57,7 +59,9 @@ const OtherSettings = (props: ComponentProps): JSX.Element => { useOrdering: searchConfig('useOrdering', 'createdAt'), appsSameTab: searchConfig('appsSameTab', 0), bookmarksSameTab: searchConfig('bookmarksSameTab', 0), - searchSameTab: searchConfig('searchSameTab', 0) + searchSameTab: searchConfig('searchSameTab', 0), + dockerApps: searchConfig('dockerApps', 1), + unpinStoppedApps: searchConfig('unpinStoppedApps', 1) }) }, [props.loading]); @@ -243,6 +247,31 @@ const OtherSettings = (props: ComponentProps): JSX.Element => { +

Docker

+ + + + + + + + ) diff --git a/client/src/interfaces/Forms.ts b/client/src/interfaces/Forms.ts index 139a638..8717d03 100644 --- a/client/src/interfaces/Forms.ts +++ b/client/src/interfaces/Forms.ts @@ -18,4 +18,6 @@ export interface SettingsForm { appsSameTab: number; bookmarksSameTab: number; searchSameTab: number; + dockerApps: number; + unpinStoppedApps: number; } diff --git a/controllers/apps.js b/controllers/apps.js index 92c1736..128e657 100644 --- a/controllers/apps.js +++ b/controllers/apps.js @@ -3,6 +3,7 @@ const ErrorResponse = require('../utils/ErrorResponse'); const App = require('../models/App'); const Config = require('../models/Config'); const { Sequelize } = require('sequelize'); +const axios = require('axios'); // @desc Create new app // @route POST /api/apps @@ -45,10 +46,61 @@ exports.getApps = asyncWrapper(async (req, res, next) => { const useOrdering = await Config.findOne({ where: { key: 'useOrdering' } }); + const useDockerApi = await Config.findOne({ + where: { key: 'dockerApps' } + }); + const unpinStoppedApps = await Config.findOne({ + where: { key: 'unpinStoppedApps' } + }); const orderType = useOrdering ? useOrdering.value : 'createdAt'; let apps; + + if (useDockerApi && useDockerApi.value==1) { + let {data:containers} = await axios.get('http://localhost/containers/json?{"status":["running"]}', { + socketPath: '/var/run/docker.sock' + }); + + if (containers) { + apps = await App.findAll({ + order: [[ orderType, 'ASC' ]] + }); + + containers = containers.filter((e) => Object.keys(e.Labels).length !== 0); + const dockerApps = []; + for (const container of containers) { + const labels = container.Labels; + + if ('flame.name' in labels && 'flame.url' in labels && (labels['flame.type']==='application' || labels['flame.type']==='app')) { + dockerApps.push({ + name: labels['flame.name'], + url: labels['flame.url'], + icon: labels['flame.icon'] || 'docker' + }) + } + } + + if (unpinStoppedApps && unpinStoppedApps.value==1) { + for (const app of apps) { + await app.update({ isPinned: false }); + } + } + + for (const item of dockerApps) { + 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 + }) + } + } + } + } + if (orderType == 'name') { apps = await App.findAll({ order: [[ Sequelize.fn('lower', Sequelize.col('name')), 'ASC' ]] @@ -59,7 +111,8 @@ exports.getApps = asyncWrapper(async (req, res, next) => { }); } - res.status(200).json({ + // Set header to fetch containers info every time + res.status(200).setHeader('Cache-Control','no-store').json({ success: true, data: apps }) diff --git a/utils/initialConfig.json b/utils/initialConfig.json index 39d2e08..0d0613c 100644 --- a/utils/initialConfig.json +++ b/utils/initialConfig.json @@ -63,6 +63,14 @@ { "key": "defaultSearchProvider", "value": "d" + }, + { + "key": "dockerApps", + "value": true + }, + { + "key": "unpinStoppedApps", + "value": true } ] }