Merge pull request #72 from pruizlezcano/docker-api

Added Docker API
This commit is contained in:
pawelmalak 2021-08-06 10:28:58 +02:00 committed by GitHub
commit 1962af01e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 3 deletions

View file

@ -74,6 +74,7 @@ services:
container_name: flame
volumes:
- <host_dir>:/app/data
- /var/run/docker.sock:/var/sock/docker.sock # optional but required for Docker integration feature
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.

View file

@ -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 => {
<option value={0}>False</option>
</select>
</InputGroup>
<h2 className={classes.SettingsSection}>Docker</h2>
<InputGroup>
<label htmlFor='dockerApps'>Use Docker API</label>
<select
id='dockerApps'
name='dockerApps'
value={formData.dockerApps}
onChange={(e) => inputChangeHandler(e, true)}
>
<option value={1}>True</option>
<option value={0}>False</option>
</select>
</InputGroup>
<InputGroup>
<label htmlFor='unpinStoppedApps'>Unpin stopped containers / other apps</label>
<select
id='unpinStoppedApps'
name='unpinStoppedApps'
value={formData.unpinStoppedApps}
onChange={(e) => inputChangeHandler(e, true)}
>
<option value={1}>True</option>
<option value={0}>False</option>
</select>
</InputGroup>
<Button>Save changes</Button>
</form>
)

View file

@ -18,4 +18,6 @@ export interface SettingsForm {
appsSameTab: number;
bookmarksSameTab: number;
searchSameTab: number;
dockerApps: number;
unpinStoppedApps: number;
}

View file

@ -3,6 +3,9 @@ const ErrorResponse = require('../utils/ErrorResponse');
const App = require('../models/App');
const Config = require('../models/Config');
const { Sequelize } = require('sequelize');
const axios = require('axios');
const Logger = require('../utils/Logger');
const logger = new Logger();
// @desc Create new app
// @route POST /api/apps
@ -45,10 +48,66 @@ 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 containers = null;
try {
let {data} = await axios.get('http://localhost/containers/json?{"status":["running"]}', {
socketPath: '/var/run/docker.sock'
});
containers = data;
} catch{logger.log("Can't connect to the docker socket","ERROR")}
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 && /^app/.test(labels['flame.type'])) {
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 +118,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
})

View file

@ -63,6 +63,14 @@
{
"key": "defaultSearchProvider",
"value": "d"
},
{
"key": "dockerApps",
"value": true
},
{
"key": "unpinStoppedApps",
"value": true
}
]
}