diff --git a/.env b/.env index 4390587..9f1bd80 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ PORT=5005 NODE_ENV=development -VERSION=2.0.0 +VERSION=2.0.1 PASSWORD=flame_password SECRET=e02eb43d69953658c6d07311d6313f2d4467672cb881f96b29368ba1f3f4da4b \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b755d2..8543534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +### v2.0.1 (2021-11-19) +- Added option to display humidity in the weather widget ([#136](https://github.com/pawelmalak/flame/issues/136)) +- Added option to set default theme for all new users ([#165](https://github.com/pawelmalak/flame/issues/165)) +- Added option to hide header greetings and date separately ([#200](https://github.com/pawelmalak/flame/issues/200)) +- Fixed bug with broken basic auth ([#202](https://github.com/pawelmalak/flame/issues/202)) +- Fixed bug with parsing visibility value for apps and bookmarks when custom icon was used ([#203](https://github.com/pawelmalak/flame/issues/203)) +- Fixed bug with custom icons not working with apps when "pin by default" was disabled + ### v2.0.0 (2021-11-15) - Added authentication system: - Only logged in user can access settings ([#33](https://github.com/pawelmalak/flame/issues/33)) diff --git a/README.md b/README.md index 6995444..a53474e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Flame is self-hosted startpage for your server. Its design is inspired (heavily) - 📌 Pin your favourite items to the homescreen for quick and easy access - 🔍 Integrated search bar with local filtering, 11 web search providers and ability to add your own - 🔑 Authentication system to protect your settings, apps and bookmarks -- 🔨 Dozens of option to customize Flame interface to your needs, including support for custom CSS and 15 built-in color themes +- 🔨 Dozens of options to customize Flame interface to your needs, including support for custom CSS and 15 built-in color themes - ☀️ Weather widget with current temperature, cloud coverage and animated weather status - 🐳 Docker integration to automatically pick and add apps based on their labels diff --git a/client/.env b/client/.env index 6a5c8f3..aac1ed1 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=2.0.0 \ No newline at end of file +REACT_APP_VERSION=2.0.1 \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index 2e4c72e..68faaea 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,7 +1,13 @@ +import { useEffect } from 'react'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import 'external-svg-loader'; + +// Redux +import { useDispatch, useSelector } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { autoLogin, getConfig } from './store/action-creators'; import { actionCreators, store } from './store'; -import 'external-svg-loader'; +import { State } from './store/reducers'; // Utils import { checkVersion, decodeToken } from './utility'; @@ -12,9 +18,6 @@ import { Apps } from './components/Apps/Apps'; import { Settings } from './components/Settings/Settings'; import { Bookmarks } from './components/Bookmarks/Bookmarks'; import { NotificationCenter } from './components/NotificationCenter/NotificationCenter'; -import { useDispatch } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { useEffect } from 'react'; // Get config store.dispatch(getConfig()); @@ -25,6 +28,8 @@ if (localStorage.token) { } export const App = (): JSX.Element => { + const { config, loading } = useSelector((state: State) => state.config); + const dispath = useDispatch(); const { fetchQueries, setTheme, logout, createNotification } = bindActionCreators(actionCreators, dispath); @@ -46,7 +51,7 @@ export const App = (): JSX.Element => { } }, 1000); - // set theme + // set user theme if present if (localStorage.theme) { setTheme(localStorage.theme); } @@ -60,6 +65,13 @@ export const App = (): JSX.Element => { return () => window.clearInterval(tokenIsValid); }, []); + // If there is no user theme, set the default one + useEffect(() => { + if (!loading && !localStorage.theme) { + setTheme(config.defaultTheme, false); + } + }, [loading]); + return ( <> diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx index bdfa170..0b94465 100644 --- a/client/src/components/Apps/AppForm/AppForm.tsx +++ b/client/src/components/Apps/AppForm/AppForm.tsx @@ -61,7 +61,7 @@ export const AppForm = ({ app, modalHandler }: Props): JSX.Element => { } data.append('name', formData.name); data.append('url', formData.url); - data.append('isPublic', `${formData.isPublic}`); + data.append('isPublic', `${formData.isPublic ? 1 : 0}`); return data; }; diff --git a/client/src/components/Bookmarks/Form/BookmarksForm.tsx b/client/src/components/Bookmarks/Form/BookmarksForm.tsx index e5f790b..f0a3a43 100644 --- a/client/src/components/Bookmarks/Form/BookmarksForm.tsx +++ b/client/src/components/Bookmarks/Form/BookmarksForm.tsx @@ -77,7 +77,7 @@ export const BookmarksForm = ({ data.append('name', formData.name); data.append('url', formData.url); data.append('categoryId', `${formData.categoryId}`); - data.append('isPublic', `${formData.isPublic}`); + data.append('isPublic', `${formData.isPublic ? 1 : 0}`); return data; }; diff --git a/client/src/components/Home/Header/Header.tsx b/client/src/components/Home/Header/Header.tsx index f059b49..84da280 100644 --- a/client/src/components/Home/Header/Header.tsx +++ b/client/src/components/Home/Header/Header.tsx @@ -1,6 +1,10 @@ import { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; +// Redux +import { useSelector } from 'react-redux'; +import { State } from '../../../store/reducers'; + // CSS import classes from './Header.module.css'; @@ -12,6 +16,10 @@ import { getDateTime } from './functions/getDateTime'; import { greeter } from './functions/greeter'; export const Header = (): JSX.Element => { + const { hideHeader, hideDate, showTime } = useSelector( + (state: State) => state.config.config + ); + const [dateTime, setDateTime] = useState(getDateTime()); const [greeting, setGreeting] = useState(greeter()); @@ -28,14 +36,18 @@ export const Header = (): JSX.Element => { return (
-

{dateTime}

+ {(!hideDate || showTime) &&

{dateTime}

} + Go to Settings - -

{greeting}

- -
+ + {!hideHeader && ( + +

{greeting}

+ +
+ )}
); }; diff --git a/client/src/components/Home/Header/functions/getDateTime.ts b/client/src/components/Home/Header/functions/getDateTime.ts index 69cd78b..45ffc9d 100644 --- a/client/src/components/Home/Header/functions/getDateTime.ts +++ b/client/src/components/Home/Header/functions/getDateTime.ts @@ -30,22 +30,42 @@ export const getDateTime = (): string => { const useAmericanDate = localStorage.useAmericanDate === 'true'; const showTime = localStorage.showTime === 'true'; + const hideDate = localStorage.hideDate === 'true'; - const p = parseTime; + // Date + let dateEl = ''; - const time = `${p(now.getHours())}:${p(now.getMinutes())}:${p( - now.getSeconds() - )}`; - - const timeEl = showTime ? ` - ${time}` : ''; - - if (!useAmericanDate) { - return `${days[now.getDay()]}, ${now.getDate()} ${ - months[now.getMonth()] - } ${now.getFullYear()}${timeEl}`; - } else { - return `${days[now.getDay()]}, ${ - months[now.getMonth()] - } ${now.getDate()} ${now.getFullYear()}${timeEl}`; + if (!hideDate) { + if (!useAmericanDate) { + dateEl = `${days[now.getDay()]}, ${now.getDate()} ${ + months[now.getMonth()] + } ${now.getFullYear()}`; + } else { + dateEl = `${days[now.getDay()]}, ${ + months[now.getMonth()] + } ${now.getDate()} ${now.getFullYear()}`; + } } + + // Time + const p = parseTime; + let timeEl = ''; + + if (showTime) { + const time = `${p(now.getHours())}:${p(now.getMinutes())}:${p( + now.getSeconds() + )}`; + + timeEl = time; + } + + // Separator + let separator = ''; + + if (!hideDate && showTime) { + separator = ' - '; + } + + // Output + return `${dateEl}${separator}${timeEl}`; }; diff --git a/client/src/components/Home/Home.tsx b/client/src/components/Home/Home.tsx index 43b2373..d7f3872 100644 --- a/client/src/components/Home/Home.tsx +++ b/client/src/components/Home/Home.tsx @@ -98,7 +98,7 @@ export const Home = (): JSX.Element => {
)} - {!config.hideHeader ?
:
} +
{!config.hideApps ? ( diff --git a/client/src/components/Settings/DockerSettings/DockerSettings.tsx b/client/src/components/Settings/DockerSettings/DockerSettings.tsx index 54410d8..d950501 100644 --- a/client/src/components/Settings/DockerSettings/DockerSettings.tsx +++ b/client/src/components/Settings/DockerSettings/DockerSettings.tsx @@ -59,7 +59,7 @@ export const DockerSettings = (): JSX.Element => { {/* CUSTOM DOCKER SOCKET HOST */} - + { > - + inputChangeHandler(e)} + > + {themes.map((theme: Theme, idx) => ( + + ))} + + + + + + )} + + ); +}; diff --git a/client/src/components/Themer/themes.json b/client/src/components/Settings/Themer/themes.json similarity index 100% rename from client/src/components/Themer/themes.json rename to client/src/components/Settings/Themer/themes.json diff --git a/client/src/components/Settings/UISettings/UISettings.tsx b/client/src/components/Settings/UISettings/UISettings.tsx index 78d8b14..b0de452 100644 --- a/client/src/components/Settings/UISettings/UISettings.tsx +++ b/client/src/components/Settings/UISettings/UISettings.tsx @@ -66,7 +66,7 @@ export const UISettings = (): JSX.Element => { return (
formSubmitHandler(e)}> - {/* OTHER OPTIONS */} + {/* === OTHER OPTIONS === */} {/* PAGE TITLE */} @@ -81,6 +81,50 @@ export const UISettings = (): JSX.Element => { /> + {/* === HEADER OPTIONS === */} + + {/* HIDE HEADER */} + + + + + + {/* HIDE DATE */} + + + + + + {/* HIDE TIME */} + + + + + {/* DATE FORMAT */} @@ -95,7 +139,52 @@ export const UISettings = (): JSX.Element => { - {/* BEAHVIOR OPTIONS */} + {/* CUSTOM GREETINGS */} + + + inputChangeHandler(e)} + /> + + Greetings must be separated with semicolon. Only 4 messages can be + used + + + + {/* CUSTOM DAYS */} + + + inputChangeHandler(e)} + /> + Names must be separated with semicolon + + + {/* CUSTOM MONTHS */} + + + inputChangeHandler(e)} + /> + Names must be separated with semicolon + + + {/* === BEAHVIOR OPTIONS === */} {/* PIN APPS */} @@ -172,82 +261,7 @@ export const UISettings = (): JSX.Element => { - {/* HEADER OPTIONS */} - - {/* HIDE HEADER */} - - - - - - {/* CUSTOM GREETINGS */} - - - inputChangeHandler(e)} - /> - - Greetings must be separated with semicolon. Only 4 messages can be - used - - - - {/* CUSTOM DAYS */} - - - inputChangeHandler(e)} - /> - Names must be separated with semicolon - - - {/* CUSTOM MONTHS */} - - - inputChangeHandler(e)} - /> - Names must be separated with semicolon - - - {/* SHOW TIME */} - - - - - - {/* MODULES OPTIONS */} + {/* === MODULES OPTIONS === */} {/* HIDE APPS */} diff --git a/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx b/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx index c587517..19ba7d4 100644 --- a/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx +++ b/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx @@ -11,7 +11,7 @@ import { State } from '../../../store/reducers'; import { ApiResponse, Weather, WeatherForm } from '../../../interfaces'; // UI -import { InputGroup, Button } from '../../UI'; +import { InputGroup, Button, SettingsHeadline } from '../../UI'; // Utils import { inputHandler, weatherSettingsTemplate } from '../../../utility'; @@ -84,6 +84,8 @@ export const WeatherSettings = (): JSX.Element => { return ( formSubmitHandler(e)}> + + {/* API KEY */} { + + {/* LAT */} - + { + {/* LONG */} - + { /> + + {/* TEMPERATURE */} + {/* WEATHER DATA */} + + + + + ); diff --git a/client/src/components/Themer/Themer.tsx b/client/src/components/Themer/Themer.tsx deleted file mode 100644 index 9c53f58..0000000 --- a/client/src/components/Themer/Themer.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Fragment } from 'react'; -import { useDispatch } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { actionCreators } from '../../store'; - -import classes from './Themer.module.css'; - -import { themes } from './themes.json'; -import { Theme } from '../../interfaces/Theme'; -import { ThemePreview } from './ThemePreview'; - -export const Themer = (): JSX.Element => { - const dispatch = useDispatch(); - const { setTheme } = bindActionCreators(actionCreators, dispatch); - - return ( - -
-
- {themes.map( - (theme: Theme, idx: number): JSX.Element => ( - - ) - )} -
-
-
- ); -}; diff --git a/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx b/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx index 7b84da8..1664eff 100644 --- a/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx +++ b/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx @@ -13,22 +13,14 @@ import classes from './WeatherWidget.module.css'; // UI import { WeatherIcon } from '../../UI'; import { State } from '../../../store/reducers'; +import { weatherTemplate } from '../../../utility/templateObjects/weatherTemplate'; export const WeatherWidget = (): JSX.Element => { - const { loading, config } = useSelector((state: State) => state.config); + const { loading: configLoading, config } = useSelector( + (state: State) => state.config + ); - const [weather, setWeather] = useState({ - externalLastUpdate: '', - tempC: 0, - tempF: 0, - isDay: 1, - cloud: 0, - conditionText: '', - conditionCode: 1000, - id: -1, - createdAt: new Date(), - updatedAt: new Date(), - }); + const [weather, setWeather] = useState(weatherTemplate); const [isLoading, setIsLoading] = useState(true); // Initial request to get data @@ -65,8 +57,7 @@ export const WeatherWidget = (): JSX.Element => { return (
- {isLoading || - loading || + {configLoading || (config.WEATHER_API_KEY && weather.id > 0 && (
@@ -76,12 +67,15 @@ export const WeatherWidget = (): JSX.Element => { />
+ {/* TEMPERATURE */} {config.isCelsius ? ( {weather.tempC}°C ) : ( {weather.tempF}°F )} - {weather.cloud}% + + {/* ADDITIONAL DATA */} + {weather[config.weatherData]}%
))} diff --git a/client/src/interfaces/Config.ts b/client/src/interfaces/Config.ts index 7302d36..cab413f 100644 --- a/client/src/interfaces/Config.ts +++ b/client/src/interfaces/Config.ts @@ -1,3 +1,5 @@ +import { WeatherData } from '../types'; + export interface Config { WEATHER_API_KEY: string; lat: number; @@ -25,4 +27,8 @@ export interface Config { daySchema: string; monthSchema: string; showTime: boolean; + defaultTheme: string; + isKilometer: boolean; + weatherData: WeatherData; + hideDate: boolean; } diff --git a/client/src/interfaces/Forms.ts b/client/src/interfaces/Forms.ts index 7272f21..ad3763f 100644 --- a/client/src/interfaces/Forms.ts +++ b/client/src/interfaces/Forms.ts @@ -1,8 +1,11 @@ +import { WeatherData } from '../types'; + export interface WeatherForm { WEATHER_API_KEY: string; lat: number; long: number; isCelsius: boolean; + weatherData: WeatherData; } export interface SearchForm { @@ -27,6 +30,7 @@ export interface OtherSettingsForm { daySchema: string; monthSchema: string; showTime: boolean; + hideDate: boolean; } export interface DockerSettingsForm { @@ -35,3 +39,7 @@ export interface DockerSettingsForm { kubernetesApps: boolean; unpinStoppedApps: boolean; } + +export interface ThemeSettingsForm { + defaultTheme: string; +} diff --git a/client/src/interfaces/Weather.ts b/client/src/interfaces/Weather.ts index c71ac48..143ebf8 100644 --- a/client/src/interfaces/Weather.ts +++ b/client/src/interfaces/Weather.ts @@ -8,4 +8,7 @@ export interface Weather extends Model { cloud: number; conditionText: string; conditionCode: number; -} \ No newline at end of file + humidity: number; + windK: number; + windM: number; +} diff --git a/client/src/store/action-creators/config.ts b/client/src/store/action-creators/config.ts index bcc41e4..6b516f7 100644 --- a/client/src/store/action-creators/config.ts +++ b/client/src/store/action-creators/config.ts @@ -8,17 +8,10 @@ import { UpdateQueryAction, } from '../actions/config'; import axios from 'axios'; -import { - ApiResponse, - Config, - DockerSettingsForm, - OtherSettingsForm, - Query, - SearchForm, - WeatherForm, -} from '../../interfaces'; +import { ApiResponse, Config, Query } from '../../interfaces'; import { ActionType } from '../action-types'; import { storeUIConfig, applyAuth } from '../../utility'; +import { ConfigFormData } from '../../types'; const keys: (keyof Config)[] = [ 'useAmericanDate', @@ -26,6 +19,7 @@ const keys: (keyof Config)[] = [ 'daySchema', 'monthSchema', 'showTime', + 'hideDate', ]; export const getConfig = () => async (dispatch: Dispatch) => { @@ -50,9 +44,7 @@ export const getConfig = () => async (dispatch: Dispatch) => { }; export const updateConfig = - ( - formData: WeatherForm | OtherSettingsForm | SearchForm | DockerSettingsForm - ) => + (formData: ConfigFormData) => async (dispatch: Dispatch) => { try { const res = await axios.put>( diff --git a/client/src/store/action-creators/theme.ts b/client/src/store/action-creators/theme.ts index ba52d27..8eb6fef 100644 --- a/client/src/store/action-creators/theme.ts +++ b/client/src/store/action-creators/theme.ts @@ -2,14 +2,18 @@ import { Dispatch } from 'redux'; import { SetThemeAction } from '../actions/theme'; import { ActionType } from '../action-types'; import { Theme } from '../../interfaces/Theme'; -import { themes } from '../../components/Themer/themes.json'; +import { themes } from '../../components/Settings/Themer/themes.json'; export const setTheme = - (name: string) => (dispatch: Dispatch) => { + (name: string, remeberTheme: boolean = true) => + (dispatch: Dispatch) => { const theme = themes.find((theme) => theme.name === name); if (theme) { - localStorage.setItem('theme', name); + if (remeberTheme) { + localStorage.setItem('theme', name); + } + loadTheme(theme); dispatch({ diff --git a/client/src/types/ConfigFormData.ts b/client/src/types/ConfigFormData.ts new file mode 100644 index 0000000..a67d8af --- /dev/null +++ b/client/src/types/ConfigFormData.ts @@ -0,0 +1,14 @@ +import { + DockerSettingsForm, + OtherSettingsForm, + SearchForm, + ThemeSettingsForm, + WeatherForm, +} from '../interfaces'; + +export type ConfigFormData = + | WeatherForm + | SearchForm + | DockerSettingsForm + | OtherSettingsForm + | ThemeSettingsForm; diff --git a/client/src/types/WeatherData.ts b/client/src/types/WeatherData.ts new file mode 100644 index 0000000..81a5863 --- /dev/null +++ b/client/src/types/WeatherData.ts @@ -0,0 +1 @@ +export type WeatherData = 'cloud' | 'humidity'; diff --git a/client/src/types/index.ts b/client/src/types/index.ts new file mode 100644 index 0000000..6ea0efd --- /dev/null +++ b/client/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './ConfigFormData'; +export * from './WeatherData'; diff --git a/client/src/utility/applyAuth.ts b/client/src/utility/applyAuth.ts index 95dcfb2..8ec0776 100644 --- a/client/src/utility/applyAuth.ts +++ b/client/src/utility/applyAuth.ts @@ -1,4 +1,4 @@ export const applyAuth = () => { const token = localStorage.getItem('token') || ''; - return { Authorization: `Bearer ${token}` }; + return { 'Authorization-Flame': `Bearer ${token}` }; }; diff --git a/client/src/utility/templateObjects/configTemplate.ts b/client/src/utility/templateObjects/configTemplate.ts index 6f51305..8d58153 100644 --- a/client/src/utility/templateObjects/configTemplate.ts +++ b/client/src/utility/templateObjects/configTemplate.ts @@ -28,4 +28,8 @@ export const configTemplate: Config = { monthSchema: 'January;February;March;April;May;June;July;August;September;October;November;December', showTime: false, + defaultTheme: 'tron', + isKilometer: true, + weatherData: 'cloud', + hideDate: false, }; diff --git a/client/src/utility/templateObjects/settingsTemplate.ts b/client/src/utility/templateObjects/settingsTemplate.ts index f175318..a122830 100644 --- a/client/src/utility/templateObjects/settingsTemplate.ts +++ b/client/src/utility/templateObjects/settingsTemplate.ts @@ -2,6 +2,7 @@ import { DockerSettingsForm, OtherSettingsForm, SearchForm, + ThemeSettingsForm, WeatherForm, } from '../../interfaces'; @@ -21,6 +22,7 @@ export const otherSettingsTemplate: OtherSettingsForm = { monthSchema: 'January;February;March;April;May;June;July;August;September;October;November;December', showTime: false, + hideDate: false, }; export const weatherSettingsTemplate: WeatherForm = { @@ -28,6 +30,7 @@ export const weatherSettingsTemplate: WeatherForm = { lat: 0, long: 0, isCelsius: true, + weatherData: 'cloud', }; export const searchSettingsTemplate: SearchForm = { @@ -43,3 +46,7 @@ export const dockerSettingsTemplate: DockerSettingsForm = { kubernetesApps: true, unpinStoppedApps: true, }; + +export const themeSettingsTemplate: ThemeSettingsForm = { + defaultTheme: 'tron', +}; diff --git a/client/src/utility/templateObjects/weatherTemplate.ts b/client/src/utility/templateObjects/weatherTemplate.ts new file mode 100644 index 0000000..1f8dfd6 --- /dev/null +++ b/client/src/utility/templateObjects/weatherTemplate.ts @@ -0,0 +1,17 @@ +import { Weather } from '../../interfaces'; + +export const weatherTemplate: Weather = { + externalLastUpdate: '', + tempC: 0, + tempF: 0, + isDay: 1, + cloud: 0, + conditionText: '', + conditionCode: 1000, + id: -1, + createdAt: new Date(), + updatedAt: new Date(), + humidity: 0, + windK: 0, + windM: 0, +}; diff --git a/controllers/apps/createApp.js b/controllers/apps/createApp.js index a8208fa..4e0c887 100644 --- a/controllers/apps/createApp.js +++ b/controllers/apps/createApp.js @@ -8,25 +8,20 @@ const loadConfig = require('../../utils/loadConfig'); const createApp = asyncWrapper(async (req, res, next) => { const { pinAppsByDefault } = await loadConfig(); - let app; - let _body = { ...req.body }; + let body = { ...req.body }; - if (_body.icon) { - _body.icon = _body.icon.trim(); + if (body.icon) { + body.icon = body.icon.trim(); } if (req.file) { - _body.icon = req.file.filename; + body.icon = req.file.filename; } - if (pinAppsByDefault) { - app = await App.create({ - ..._body, - isPinned: true, - }); - } else { - app = await App.create(req.body); - } + const app = await App.create({ + ...body, + isPinned: pinAppsByDefault, + }); res.status(201).json({ success: true, diff --git a/controllers/apps/updateApp.js b/controllers/apps/updateApp.js index 290e4ef..cc626e4 100644 --- a/controllers/apps/updateApp.js +++ b/controllers/apps/updateApp.js @@ -18,17 +18,17 @@ const updateApp = asyncWrapper(async (req, res, next) => { ); } - let _body = { ...req.body }; + let body = { ...req.body }; - if (_body.icon) { - _body.icon = _body.icon.trim(); + if (body.icon) { + body.icon = body.icon.trim(); } if (req.file) { - _body.icon = req.file.filename; + body.icon = req.file.filename; } - app = await app.update(_body); + app = await app.update(body); res.status(200).json({ success: true, diff --git a/controllers/bookmarks/createBookmark.js b/controllers/bookmarks/createBookmark.js index 7bf6a01..4b5fb18 100644 --- a/controllers/bookmarks/createBookmark.js +++ b/controllers/bookmarks/createBookmark.js @@ -7,20 +7,20 @@ const Bookmark = require('../../models/Bookmark'); const createBookmark = asyncWrapper(async (req, res, next) => { let bookmark; - let _body = { + let body = { ...req.body, categoryId: parseInt(req.body.categoryId), }; - if (_body.icon) { - _body.icon = _body.icon.trim(); + if (body.icon) { + body.icon = body.icon.trim(); } if (req.file) { - _body.icon = req.file.filename; + body.icon = req.file.filename; } - bookmark = await Bookmark.create(_body); + bookmark = await Bookmark.create(body); res.status(201).json({ success: true, diff --git a/controllers/bookmarks/updateBookmark.js b/controllers/bookmarks/updateBookmark.js index 2899364..448b16c 100644 --- a/controllers/bookmarks/updateBookmark.js +++ b/controllers/bookmarks/updateBookmark.js @@ -19,20 +19,20 @@ const updateBookmark = asyncWrapper(async (req, res, next) => { ); } - let _body = { + let body = { ...req.body, categoryId: parseInt(req.body.categoryId), }; - if (_body.icon) { - _body.icon = _body.icon.trim(); + if (body.icon) { + body.icon = body.icon.trim(); } if (req.file) { - _body.icon = req.file.filename; + body.icon = req.file.filename; } - bookmark = await bookmark.update(_body); + bookmark = await bookmark.update(body); res.status(200).json({ success: true, diff --git a/controllers/categories/createCategory.js b/controllers/categories/createCategory.js index f25b57f..ada5645 100644 --- a/controllers/categories/createCategory.js +++ b/controllers/categories/createCategory.js @@ -8,16 +8,10 @@ const loadConfig = require('../../utils/loadConfig'); const createCategory = asyncWrapper(async (req, res, next) => { const { pinCategoriesByDefault: pinCategories } = await loadConfig(); - let category; - - if (pinCategories) { - category = await Category.create({ - ...req.body, - isPinned: true, - }); - } else { - category = await Category.create(req.body); - } + const category = await Category.create({ + ...req.body, + isPinned: pinCategories, + }); res.status(201).json({ success: true, diff --git a/db/migrations/03_weather.js b/db/migrations/03_weather.js new file mode 100644 index 0000000..bba45df --- /dev/null +++ b/db/migrations/03_weather.js @@ -0,0 +1,35 @@ +const { DataTypes } = require('sequelize'); +const { INTEGER, FLOAT } = DataTypes; +const loadConfig = require('../../utils/loadConfig'); +const getExternalWeather = require('../../utils/getExternalWeather'); + +const up = async (query) => { + await query.addColumn('weather', 'humidity', { + type: INTEGER, + }); + + await query.addColumn('weather', 'windK', { + type: FLOAT, + }); + + await query.addColumn('weather', 'windM', { + type: FLOAT, + }); + + const { WEATHER_API_KEY: secret } = await loadConfig(); + + if (secret) { + await getExternalWeather(); + } +}; + +const down = async (query) => { + await query.removeColumn('weather', 'humidity'); + await query.removeColumn('weather', 'windK'); + await query.removeColumn('weather', 'windM'); +}; + +module.exports = { + up, + down, +}; diff --git a/middleware/auth.js b/middleware/auth.js index c73d0ca..334eeeb 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,7 +1,7 @@ const jwt = require('jsonwebtoken'); const auth = (req, res, next) => { - const authHeader = req.header('Authorization'); + const authHeader = req.header('Authorization-Flame'); let token; let tokenIsValid = false; diff --git a/models/Weather.js b/models/Weather.js index c85c29f..2ac7961 100644 --- a/models/Weather.js +++ b/models/Weather.js @@ -1,16 +1,23 @@ const { DataTypes } = require('sequelize'); const { sequelize } = require('../db'); -const Weather = sequelize.define('Weather', { - externalLastUpdate: DataTypes.STRING, - tempC: DataTypes.FLOAT, - tempF: DataTypes.FLOAT, - isDay: DataTypes.INTEGER, - cloud: DataTypes.INTEGER, - conditionText: DataTypes.TEXT, - conditionCode: DataTypes.INTEGER -}, { - tableName: 'weather' -}); +const Weather = sequelize.define( + 'Weather', + { + externalLastUpdate: DataTypes.STRING, + tempC: DataTypes.FLOAT, + tempF: DataTypes.FLOAT, + isDay: DataTypes.INTEGER, + cloud: DataTypes.INTEGER, + conditionText: DataTypes.TEXT, + conditionCode: DataTypes.INTEGER, + humidity: DataTypes.INTEGER, + windK: DataTypes.FLOAT, + windM: DataTypes.FLOAT, + }, + { + tableName: 'weather', + } +); -module.exports = Weather; \ No newline at end of file +module.exports = Weather; diff --git a/utils/getExternalWeather.js b/utils/getExternalWeather.js index 20edac4..8171807 100644 --- a/utils/getExternalWeather.js +++ b/utils/getExternalWeather.js @@ -21,6 +21,9 @@ const getExternalWeather = async () => { cloud: cursor.cloud, conditionText: cursor.condition.text, conditionCode: cursor.condition.code, + humidity: cursor.humidity, + windK: cursor.wind_kph, + windM: cursor.wind_mph, }); return weatherData; } catch (err) { diff --git a/utils/init/initialConfig.json b/utils/init/initialConfig.json index 8f9b2f8..575a2e7 100644 --- a/utils/init/initialConfig.json +++ b/utils/init/initialConfig.json @@ -24,5 +24,9 @@ "greetingsSchema": "Good evening!;Good afternoon!;Good morning!;Good night!", "daySchema": "Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday", "monthSchema": "January;February;March;April;May;June;July;August;September;October;November;December", - "showTime": false + "showTime": false, + "defaultTheme": "tron", + "isKilometer": true, + "weatherData": "cloud", + "hideDate": false }