Pushed version 1.6.2. Small formatting fixes

This commit is contained in:
unknown 2021-08-06 10:36:05 +02:00
parent 1962af01e6
commit a01661d0d5
6 changed files with 123 additions and 78 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
*.md

View File

@ -1,3 +1,7 @@
### v1.6.2 (2021-08-06)
- Fixed changelog link
- Added support for Docker API ([#14](https://github.com/pawelmalak/flame/issues/14))
### v1.6.1 (2021-07-28) ### v1.6.1 (2021-07-28)
- Added option to upload custom icons for bookmarks ([#52](https://github.com/pawelmalak/flame/issues/52)) - Added option to upload custom icons for bookmarks ([#52](https://github.com/pawelmalak/flame/issues/52))
- Fixed custom icons not updating ([#58](https://github.com/pawelmalak/flame/issues/58)) - Fixed custom icons not updating ([#58](https://github.com/pawelmalak/flame/issues/58))

View File

@ -1 +1 @@
REACT_APP_VERSION=1.6.1 REACT_APP_VERSION=1.6.2

View File

@ -2,10 +2,20 @@ import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
// Redux // Redux
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createNotification, updateConfig, sortApps, sortCategories } from '../../../store/actions'; import {
createNotification,
updateConfig,
sortApps,
sortCategories
} from '../../../store/actions';
// Typescript // Typescript
import { GlobalState, NewNotification, Query, SettingsForm } from '../../../interfaces'; import {
GlobalState,
NewNotification,
Query,
SettingsForm
} from '../../../interfaces';
// UI // UI
import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
@ -41,9 +51,9 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
appsSameTab: 0, appsSameTab: 0,
bookmarksSameTab: 0, bookmarksSameTab: 0,
searchSameTab: 0, searchSameTab: 0,
dockerApps:1, dockerApps: 1,
unpinStoppedApps: 1 unpinStoppedApps: 1
}) });
// Get config // Get config
useEffect(() => { useEffect(() => {
@ -60,9 +70,9 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
appsSameTab: searchConfig('appsSameTab', 0), appsSameTab: searchConfig('appsSameTab', 0),
bookmarksSameTab: searchConfig('bookmarksSameTab', 0), bookmarksSameTab: searchConfig('bookmarksSameTab', 0),
searchSameTab: searchConfig('searchSameTab', 0), searchSameTab: searchConfig('searchSameTab', 0),
dockerApps: searchConfig('dockerApps', 1), dockerApps: searchConfig('dockerApps', 0),
unpinStoppedApps: searchConfig('unpinStoppedApps', 1) unpinStoppedApps: searchConfig('unpinStoppedApps', 0)
}) });
}, [props.loading]); }, [props.loading]);
// Form handler // Form handler
@ -78,10 +88,13 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
// Sort apps and categories with new settings // Sort apps and categories with new settings
props.sortApps(); props.sortApps();
props.sortCategories(); props.sortCategories();
} };
// Input handler // Input handler
const inputChangeHandler = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>, isNumber?: boolean) => { const inputChangeHandler = (
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
isNumber?: boolean
) => {
let value: string | number = e.target.value; let value: string | number = e.target.value;
if (isNumber) { if (isNumber) {
@ -91,11 +104,11 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
setFormData({ setFormData({
...formData, ...formData,
[e.target.name]: value [e.target.name]: value
}) });
} };
return ( return (
<form onSubmit={(e) => formSubmitHandler(e)}> <form onSubmit={e => formSubmitHandler(e)}>
{/* OTHER OPTIONS */} {/* OTHER OPTIONS */}
<h2 className={classes.SettingsSection}>Miscellaneous</h2> <h2 className={classes.SettingsSection}>Miscellaneous</h2>
<InputGroup> <InputGroup>
@ -106,31 +119,35 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
name='customTitle' name='customTitle'
placeholder='Flame' placeholder='Flame'
value={formData.customTitle} value={formData.customTitle}
onChange={(e) => inputChangeHandler(e)} onChange={e => inputChangeHandler(e)}
/> />
</InputGroup> </InputGroup>
{/* BEAHVIOR OPTIONS */} {/* BEAHVIOR OPTIONS */}
<h2 className={classes.SettingsSection}>App Behavior</h2> <h2 className={classes.SettingsSection}>App Behavior</h2>
<InputGroup> <InputGroup>
<label htmlFor='pinAppsByDefault'>Pin new applications by default</label> <label htmlFor='pinAppsByDefault'>
Pin new applications by default
</label>
<select <select
id='pinAppsByDefault' id='pinAppsByDefault'
name='pinAppsByDefault' name='pinAppsByDefault'
value={formData.pinAppsByDefault} value={formData.pinAppsByDefault}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
</select> </select>
</InputGroup> </InputGroup>
<InputGroup> <InputGroup>
<label htmlFor='pinCategoriesByDefault'>Pin new categories by default</label> <label htmlFor='pinCategoriesByDefault'>
Pin new categories by default
</label>
<select <select
id='pinCategoriesByDefault' id='pinCategoriesByDefault'
name='pinCategoriesByDefault' name='pinCategoriesByDefault'
value={formData.pinCategoriesByDefault} value={formData.pinCategoriesByDefault}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -142,7 +159,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='useOrdering' id='useOrdering'
name='useOrdering' name='useOrdering'
value={formData.useOrdering} value={formData.useOrdering}
onChange={(e) => inputChangeHandler(e)} onChange={e => inputChangeHandler(e)}
> >
<option value='createdAt'>By creation date</option> <option value='createdAt'>By creation date</option>
<option value='name'>Alphabetical order</option> <option value='name'>Alphabetical order</option>
@ -155,18 +172,22 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='defaultSearchProvider' id='defaultSearchProvider'
name='defaultSearchProvider' name='defaultSearchProvider'
value={formData.defaultSearchProvider} value={formData.defaultSearchProvider}
onChange={(e) => inputChangeHandler(e)} onChange={e => inputChangeHandler(e)}
> >
{queries.map((query: Query) => (<option value={query.prefix}>{query.name}</option>))} {queries.map((query: Query) => (
<option value={query.prefix}>{query.name}</option>
))}
</select> </select>
</InputGroup> </InputGroup>
<InputGroup> <InputGroup>
<label htmlFor='searchSameTab'>Open search results in the same tab</label> <label htmlFor='searchSameTab'>
Open search results in the same tab
</label>
<select <select
id='searchSameTab' id='searchSameTab'
name='searchSameTab' name='searchSameTab'
value={formData.searchSameTab} value={formData.searchSameTab}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -178,7 +199,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='appsSameTab' id='appsSameTab'
name='appsSameTab' name='appsSameTab'
value={formData.appsSameTab} value={formData.appsSameTab}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -190,7 +211,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='bookmarksSameTab' id='bookmarksSameTab'
name='bookmarksSameTab' name='bookmarksSameTab'
value={formData.bookmarksSameTab} value={formData.bookmarksSameTab}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -205,7 +226,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='hideSearch' id='hideSearch'
name='hideSearch' name='hideSearch'
value={formData.hideSearch} value={formData.hideSearch}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -217,7 +238,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='hideHeader' id='hideHeader'
name='hideHeader' name='hideHeader'
value={formData.hideHeader} value={formData.hideHeader}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -229,7 +250,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='hideApps' id='hideApps'
name='hideApps' name='hideApps'
value={formData.hideApps} value={formData.hideApps}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -241,12 +262,14 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='hideCategories' id='hideCategories'
name='hideCategories' name='hideCategories'
value={formData.hideCategories} value={formData.hideCategories}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
</select> </select>
</InputGroup> </InputGroup>
{/* DOCKER SETTINGS */}
<h2 className={classes.SettingsSection}>Docker</h2> <h2 className={classes.SettingsSection}>Docker</h2>
<InputGroup> <InputGroup>
<label htmlFor='dockerApps'>Use Docker API</label> <label htmlFor='dockerApps'>Use Docker API</label>
@ -254,19 +277,21 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
id='dockerApps' id='dockerApps'
name='dockerApps' name='dockerApps'
value={formData.dockerApps} value={formData.dockerApps}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
</select> </select>
</InputGroup> </InputGroup>
<InputGroup> <InputGroup>
<label htmlFor='unpinStoppedApps'>Unpin stopped containers / other apps</label> <label htmlFor='unpinStoppedApps'>
Unpin stopped containers / other apps
</label>
<select <select
id='unpinStoppedApps' id='unpinStoppedApps'
name='unpinStoppedApps' name='unpinStoppedApps'
value={formData.unpinStoppedApps} value={formData.unpinStoppedApps}
onChange={(e) => inputChangeHandler(e, true)} onChange={e => inputChangeHandler(e, true)}
> >
<option value={1}>True</option> <option value={1}>True</option>
<option value={0}>False</option> <option value={0}>False</option>
@ -274,20 +299,20 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
</InputGroup> </InputGroup>
<Button>Save changes</Button> <Button>Save changes</Button>
</form> </form>
) );
} };
const mapStateToProps = (state: GlobalState) => { const mapStateToProps = (state: GlobalState) => {
return { return {
loading: state.config.loading loading: state.config.loading
} };
} };
const actions = { const actions = {
createNotification, createNotification,
updateConfig, updateConfig,
sortApps, sortApps,
sortCategories sortCategories
} };
export default connect(mapStateToProps, actions)(OtherSettings); export default connect(mapStateToProps, actions)(OtherSettings);

View File

@ -28,7 +28,7 @@ exports.createApp = asyncWrapper(async (req, res, next) => {
app = await App.create({ app = await App.create({
..._body, ..._body,
isPinned: true isPinned: true
}) });
} else { } else {
app = await App.create(req.body); app = await App.create(req.body);
} }
@ -37,8 +37,8 @@ exports.createApp = asyncWrapper(async (req, res, next) => {
res.status(201).json({ res.status(201).json({
success: true, success: true,
data: app data: app
}) });
}) });
// @desc Get all apps // @desc Get all apps
// @route GET /api/apps // @route GET /api/apps
@ -58,37 +58,45 @@ exports.getApps = asyncWrapper(async (req, res, next) => {
const orderType = useOrdering ? useOrdering.value : 'createdAt'; const orderType = useOrdering ? useOrdering.value : 'createdAt';
let apps; let apps;
if (useDockerApi && useDockerApi.value == 1) {
if (useDockerApi && useDockerApi.value==1) {
let containers = null; let containers = null;
try { try {
let {data} = await axios.get('http://localhost/containers/json?{"status":["running"]}', { let { data } = await axios.get(
'http://localhost/containers/json?{"status":["running"]}',
{
socketPath: '/var/run/docker.sock' socketPath: '/var/run/docker.sock'
}); }
);
containers = data; containers = data;
} catch{logger.log("Can't connect to the docker socket","ERROR")} } catch {
logger.log("Can't connect to the docker socket", 'ERROR');
}
if (containers) { if (containers) {
apps = await App.findAll({ 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 = []; const dockerApps = [];
for (const container of containers) { for (const container of containers) {
const labels = container.Labels; const labels = container.Labels;
if ('flame.name' in labels && 'flame.url' in labels && /^app/.test(labels['flame.type'])) { if (
'flame.name' in labels &&
'flame.url' in labels &&
/^app/.test(labels['flame.type'])
) {
dockerApps.push({ dockerApps.push({
name: labels['flame.name'], name: labels['flame.name'],
url: labels['flame.url'], url: labels['flame.url'],
icon: labels['flame.icon'] || 'docker' icon: labels['flame.icon'] || 'docker'
}) });
} }
} }
if (unpinStoppedApps && unpinStoppedApps.value==1) { if (unpinStoppedApps && unpinStoppedApps.value == 1) {
for (const app of apps) { for (const app of apps) {
await app.update({ isPinned: false }); await app.update({ isPinned: false });
} }
@ -97,12 +105,12 @@ exports.getApps = asyncWrapper(async (req, res, next) => {
for (const item of dockerApps) { for (const item of dockerApps) {
if (apps.some(app => app.name === item.name)) { if (apps.some(app => app.name === item.name)) {
const app = apps.filter(e => e.name === item.name)[0]; const app = apps.filter(e => e.name === item.name)[0];
await app.update({ ...item,isPinned: true }); await app.update({ ...item, isPinned: true });
} else { } else {
await App.create({ await App.create({
...item, ...item,
isPinned: true isPinned: true
}) });
} }
} }
} }
@ -110,20 +118,20 @@ exports.getApps = asyncWrapper(async (req, res, next) => {
if (orderType == 'name') { if (orderType == 'name') {
apps = await App.findAll({ apps = await App.findAll({
order: [[ Sequelize.fn('lower', Sequelize.col('name')), 'ASC' ]] order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']]
}); });
} else { } else {
apps = await App.findAll({ apps = await App.findAll({
order: [[ orderType, 'ASC' ]] order: [[orderType, 'ASC']]
}); });
} }
// Set header to fetch containers info every time // Set header to fetch containers info every time
res.status(200).setHeader('Cache-Control','no-store').json({ res.status(200).setHeader('Cache-Control', 'no-store').json({
success: true, success: true,
data: apps data: apps
}) });
}) });
// @desc Get single app // @desc Get single app
// @route GET /api/apps/:id // @route GET /api/apps/:id
@ -134,14 +142,16 @@ exports.getApp = asyncWrapper(async (req, res, next) => {
}); });
if (!app) { if (!app) {
return next(new ErrorResponse(`App with id of ${req.params.id} was not found`, 404)); return next(
new ErrorResponse(`App with id of ${req.params.id} was not found`, 404)
);
} }
res.status(200).json({ res.status(200).json({
success: true, success: true,
data: app data: app
}) });
}) });
// @desc Update app // @desc Update app
// @route PUT /api/apps/:id // @route PUT /api/apps/:id
@ -152,7 +162,9 @@ exports.updateApp = asyncWrapper(async (req, res, next) => {
}); });
if (!app) { if (!app) {
return next(new ErrorResponse(`App with id of ${req.params.id} was not found`, 404)); return next(
new ErrorResponse(`App with id of ${req.params.id} was not found`, 404)
);
} }
let _body = { ...req.body }; let _body = { ...req.body };
@ -166,8 +178,8 @@ exports.updateApp = asyncWrapper(async (req, res, next) => {
res.status(200).json({ res.status(200).json({
success: true, success: true,
data: app data: app
}) });
}) });
// @desc Delete app // @desc Delete app
// @route DELETE /api/apps/:id // @route DELETE /api/apps/:id
@ -175,26 +187,29 @@ exports.updateApp = asyncWrapper(async (req, res, next) => {
exports.deleteApp = asyncWrapper(async (req, res, next) => { exports.deleteApp = asyncWrapper(async (req, res, next) => {
await App.destroy({ await App.destroy({
where: { id: req.params.id } where: { id: req.params.id }
}) });
res.status(200).json({ res.status(200).json({
success: true, success: true,
data: {} data: {}
}) });
}) });
// @desc Reorder apps // @desc Reorder apps
// @route PUT /api/apps/0/reorder // @route PUT /api/apps/0/reorder
// @access Public // @access Public
exports.reorderApps = asyncWrapper(async (req, res, next) => { exports.reorderApps = asyncWrapper(async (req, res, next) => {
req.body.apps.forEach(async ({ id, orderId }) => { req.body.apps.forEach(async ({ id, orderId }) => {
await App.update({ orderId }, { await App.update(
{ orderId },
{
where: { id } where: { id }
}) }
}) );
});
res.status(200).json({ res.status(200).json({
success: true, success: true,
data: {} data: {}
}) });
}) });

View File

@ -66,11 +66,11 @@
}, },
{ {
"key": "dockerApps", "key": "dockerApps",
"value": true "value": false
}, },
{ {
"key": "unpinStoppedApps", "key": "unpinStoppedApps",
"value": true "value": false
} }
] ]
} }