Changed config api. Split config controllers into separate files. Split bookmarks controllers into separate files

This commit is contained in:
Paweł Malak 2021-10-22 14:00:38 +02:00
parent 76e50624e7
commit cfb471e578
23 changed files with 579 additions and 602 deletions

View File

@ -1,6 +1,7 @@
### v1.7.1 (TBA) ### v1.7.1 (TBA)
- Fixed search action not being triggered by Numpad Enter - Fixed search action not being triggered by Numpad Enter
- Fixed search bar not redirecting to valid URL if it starts with capital letter ([#118](https://github.com/pawelmalak/flame/issues/118)) - Fixed search bar not redirecting to valid URL if it starts with capital letter ([#118](https://github.com/pawelmalak/flame/issues/118))
- Performance improvements
### v1.7.0 (2021-10-11) ### v1.7.0 (2021-10-11)
- Search bar will now redirect if valid URL or IP is provided ([#67](https://github.com/pawelmalak/flame/issues/67)) - Search bar will now redirect if valid URL or IP is provided ([#67](https://github.com/pawelmalak/flame/issues/67))

View File

@ -5,14 +5,17 @@ import { App, ApiResponse, NewApp, Config } from '../../interfaces';
import { CreateNotificationAction } from './notification'; import { CreateNotificationAction } from './notification';
export interface GetAppsAction<T> { export interface GetAppsAction<T> {
type: ActionTypes.getApps | ActionTypes.getAppsSuccess | ActionTypes.getAppsError; type:
| ActionTypes.getApps
| ActionTypes.getAppsSuccess
| ActionTypes.getAppsError;
payload: T; payload: T;
} }
export const getApps = () => async (dispatch: Dispatch) => { export const getApps = () => async (dispatch: Dispatch) => {
dispatch<GetAppsAction<undefined>>({ dispatch<GetAppsAction<undefined>>({
type: ActionTypes.getApps, type: ActionTypes.getApps,
payload: undefined payload: undefined,
}); });
try { try {
@ -20,12 +23,12 @@ export const getApps = () => async (dispatch: Dispatch) => {
dispatch<GetAppsAction<App[]>>({ dispatch<GetAppsAction<App[]>>({
type: ActionTypes.getAppsSuccess, type: ActionTypes.getAppsSuccess,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface PinAppAction { export interface PinAppAction {
type: ActionTypes.pinApp; type: ActionTypes.pinApp;
@ -35,33 +38,38 @@ export interface PinAppAction {
export const pinApp = (app: App) => async (dispatch: Dispatch) => { export const pinApp = (app: App) => async (dispatch: Dispatch) => {
try { try {
const { id, isPinned, name } = app; const { id, isPinned, name } = app;
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, { isPinned: !isPinned }); const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, {
isPinned: !isPinned,
});
const status = isPinned ? 'unpinned from Homescreen' : 'pinned to Homescreen'; const status = isPinned
? 'unpinned from Homescreen'
: 'pinned to Homescreen';
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `App ${name} ${status}` message: `App ${name} ${status}`,
} },
}) });
dispatch<PinAppAction>({ dispatch<PinAppAction>({
type: ActionTypes.pinApp, type: ActionTypes.pinApp,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface AddAppAction { export interface AddAppAction {
type: ActionTypes.addAppSuccess; type: ActionTypes.addAppSuccess;
payload: App; payload: App;
} }
export const addApp = (formData: NewApp | FormData) => async (dispatch: Dispatch) => { export const addApp =
(formData: NewApp | FormData) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.post<ApiResponse<App>>('/api/apps', formData); const res = await axios.post<ApiResponse<App>>('/api/apps', formData);
@ -69,25 +77,25 @@ export const addApp = (formData: NewApp | FormData) => async (dispatch: Dispatch
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `App added` message: `App added`,
} },
}) });
await dispatch<AddAppAction>({ await dispatch<AddAppAction>({
type: ActionTypes.addAppSuccess, type: ActionTypes.addAppSuccess,
payload: res.data.data payload: res.data.data,
}) });
// Sort apps // Sort apps
dispatch<any>(sortApps()) dispatch<any>(sortApps());
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface DeleteAppAction { export interface DeleteAppAction {
type: ActionTypes.deleteApp, type: ActionTypes.deleteApp;
payload: number payload: number;
} }
export const deleteApp = (id: number) => async (dispatch: Dispatch) => { export const deleteApp = (id: number) => async (dispatch: Dispatch) => {
@ -98,79 +106,85 @@ export const deleteApp = (id: number) => async (dispatch: Dispatch) => {
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: 'App deleted' message: 'App deleted',
} },
}) });
dispatch<DeleteAppAction>({ dispatch<DeleteAppAction>({
type: ActionTypes.deleteApp, type: ActionTypes.deleteApp,
payload: id payload: id,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface UpdateAppAction { export interface UpdateAppAction {
type: ActionTypes.updateApp; type: ActionTypes.updateApp;
payload: App; payload: App;
} }
export const updateApp = (id: number, formData: NewApp | FormData) => async (dispatch: Dispatch) => { export const updateApp =
(id: number, formData: NewApp | FormData) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, formData); const res = await axios.put<ApiResponse<App>>(
`/api/apps/${id}`,
formData
);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `App updated` message: `App updated`,
} },
}) });
await dispatch<UpdateAppAction>({ await dispatch<UpdateAppAction>({
type: ActionTypes.updateApp, type: ActionTypes.updateApp,
payload: res.data.data payload: res.data.data,
}) });
// Sort apps // Sort apps
dispatch<any>(sortApps()) dispatch<any>(sortApps());
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface ReorderAppsAction { export interface ReorderAppsAction {
type: ActionTypes.reorderApps; type: ActionTypes.reorderApps;
payload: App[] payload: App[];
} }
interface ReorderQuery { interface ReorderQuery {
apps: { apps: {
id: number; id: number;
orderId: number; orderId: number;
}[] }[];
} }
export const reorderApps = (apps: App[]) => async (dispatch: Dispatch) => { export const reorderApps = (apps: App[]) => async (dispatch: Dispatch) => {
try { try {
const updateQuery: ReorderQuery = { apps: [] } const updateQuery: ReorderQuery = { apps: [] };
apps.forEach((app, index) => updateQuery.apps.push({ apps.forEach((app, index) =>
updateQuery.apps.push({
id: app.id, id: app.id,
orderId: index + 1 orderId: index + 1,
})) })
);
await axios.put<ApiResponse<{}>>('/api/apps/0/reorder', updateQuery); await axios.put<ApiResponse<{}>>('/api/apps/0/reorder', updateQuery);
dispatch<ReorderAppsAction>({ dispatch<ReorderAppsAction>({
type: ActionTypes.reorderApps, type: ActionTypes.reorderApps,
payload: apps payload: apps,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
export interface SortAppsAction { export interface SortAppsAction {
type: ActionTypes.sortApps; type: ActionTypes.sortApps;
@ -179,13 +193,13 @@ export interface SortAppsAction {
export const sortApps = () => async (dispatch: Dispatch) => { export const sortApps = () => async (dispatch: Dispatch) => {
try { try {
const res = await axios.get<ApiResponse<Config>>('/api/config/useOrdering'); const res = await axios.get<ApiResponse<Config>>('/api/config');
dispatch<SortAppsAction>({ dispatch<SortAppsAction>({
type: ActionTypes.sortApps, type: ActionTypes.sortApps,
payload: res.data.data.value payload: res.data.data.useOrdering,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };

View File

@ -1,133 +1,157 @@
import axios from 'axios'; import axios from 'axios';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes'; import { ActionTypes } from './actionTypes';
import { Category, ApiResponse, NewCategory, Bookmark, NewBookmark, Config } from '../../interfaces'; import {
Category,
ApiResponse,
NewCategory,
Bookmark,
NewBookmark,
Config,
} from '../../interfaces';
import { CreateNotificationAction } from './notification'; import { CreateNotificationAction } from './notification';
/** /**
* GET CATEGORIES * GET CATEGORIES
*/ */
export interface GetCategoriesAction<T> { export interface GetCategoriesAction<T> {
type: ActionTypes.getCategories | ActionTypes.getCategoriesSuccess | ActionTypes.getCategoriesError; type:
| ActionTypes.getCategories
| ActionTypes.getCategoriesSuccess
| ActionTypes.getCategoriesError;
payload: T; payload: T;
} }
export const getCategories = () => async (dispatch: Dispatch) => { export const getCategories = () => async (dispatch: Dispatch) => {
dispatch<GetCategoriesAction<undefined>>({ dispatch<GetCategoriesAction<undefined>>({
type: ActionTypes.getCategories, type: ActionTypes.getCategories,
payload: undefined payload: undefined,
}) });
try { try {
const res = await axios.get<ApiResponse<Category[]>>('/api/categories'); const res = await axios.get<ApiResponse<Category[]>>('/api/categories');
dispatch<GetCategoriesAction<Category[]>>({ dispatch<GetCategoriesAction<Category[]>>({
type: ActionTypes.getCategoriesSuccess, type: ActionTypes.getCategoriesSuccess,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* ADD CATEGORY * ADD CATEGORY
*/ */
export interface AddCategoryAction { export interface AddCategoryAction {
type: ActionTypes.addCategory, type: ActionTypes.addCategory;
payload: Category payload: Category;
} }
export const addCategory = (formData: NewCategory) => async (dispatch: Dispatch) => { export const addCategory =
(formData: NewCategory) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.post<ApiResponse<Category>>('/api/categories', formData); const res = await axios.post<ApiResponse<Category>>(
'/api/categories',
formData
);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Category ${formData.name} created` message: `Category ${formData.name} created`,
} },
}) });
dispatch<AddCategoryAction>({ dispatch<AddCategoryAction>({
type: ActionTypes.addCategory, type: ActionTypes.addCategory,
payload: res.data.data payload: res.data.data,
}) });
dispatch<any>(sortCategories()); dispatch<any>(sortCategories());
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* ADD BOOKMARK * ADD BOOKMARK
*/ */
export interface AddBookmarkAction { export interface AddBookmarkAction {
type: ActionTypes.addBookmark, type: ActionTypes.addBookmark;
payload: Bookmark payload: Bookmark;
} }
export const addBookmark = (formData: NewBookmark | FormData) => async (dispatch: Dispatch) => { export const addBookmark =
(formData: NewBookmark | FormData) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.post<ApiResponse<Bookmark>>('/api/bookmarks', formData); const res = await axios.post<ApiResponse<Bookmark>>(
'/api/bookmarks',
formData
);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Bookmark created` message: `Bookmark created`,
} },
}) });
dispatch<AddBookmarkAction>({ dispatch<AddBookmarkAction>({
type: ActionTypes.addBookmark, type: ActionTypes.addBookmark,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* PIN CATEGORY * PIN CATEGORY
*/ */
export interface PinCategoryAction { export interface PinCategoryAction {
type: ActionTypes.pinCategory, type: ActionTypes.pinCategory;
payload: Category payload: Category;
} }
export const pinCategory = (category: Category) => async (dispatch: Dispatch) => { export const pinCategory =
(category: Category) => async (dispatch: Dispatch) => {
try { try {
const { id, isPinned, name } = category; const { id, isPinned, name } = category;
const res = await axios.put<ApiResponse<Category>>(`/api/categories/${id}`, { isPinned: !isPinned }); const res = await axios.put<ApiResponse<Category>>(
`/api/categories/${id}`,
{ isPinned: !isPinned }
);
const status = isPinned ? 'unpinned from Homescreen' : 'pinned to Homescreen'; const status = isPinned
? 'unpinned from Homescreen'
: 'pinned to Homescreen';
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Category ${name} ${status}` message: `Category ${name} ${status}`,
} },
}) });
dispatch<PinCategoryAction>({ dispatch<PinCategoryAction>({
type: ActionTypes.pinCategory, type: ActionTypes.pinCategory,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* DELETE CATEGORY * DELETE CATEGORY
*/ */
export interface DeleteCategoryAction { export interface DeleteCategoryAction {
type: ActionTypes.deleteCategory, type: ActionTypes.deleteCategory;
payload: number payload: number;
} }
export const deleteCategory = (id: number) => async (dispatch: Dispatch) => { export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
@ -138,62 +162,67 @@ export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Category deleted` message: `Category deleted`,
} },
}) });
dispatch<DeleteCategoryAction>({ dispatch<DeleteCategoryAction>({
type: ActionTypes.deleteCategory, type: ActionTypes.deleteCategory,
payload: id payload: id,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* UPDATE CATEGORY * UPDATE CATEGORY
*/ */
export interface UpdateCategoryAction { export interface UpdateCategoryAction {
type: ActionTypes.updateCategory, type: ActionTypes.updateCategory;
payload: Category payload: Category;
} }
export const updateCategory = (id: number, formData: NewCategory) => async (dispatch: Dispatch) => { export const updateCategory =
(id: number, formData: NewCategory) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.put<ApiResponse<Category>>(`/api/categories/${id}`, formData); const res = await axios.put<ApiResponse<Category>>(
`/api/categories/${id}`,
formData
);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Category ${formData.name} updated` message: `Category ${formData.name} updated`,
} },
}) });
dispatch<UpdateCategoryAction>({ dispatch<UpdateCategoryAction>({
type: ActionTypes.updateCategory, type: ActionTypes.updateCategory,
payload: res.data.data payload: res.data.data,
}) });
dispatch<any>(sortCategories()); dispatch<any>(sortCategories());
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* DELETE BOOKMARK * DELETE BOOKMARK
*/ */
export interface DeleteBookmarkAction { export interface DeleteBookmarkAction {
type: ActionTypes.deleteBookmark, type: ActionTypes.deleteBookmark;
payload: { payload: {
bookmarkId: number, bookmarkId: number;
categoryId: number categoryId: number;
} };
} }
export const deleteBookmark = (bookmarkId: number, categoryId: number) => async (dispatch: Dispatch) => { export const deleteBookmark =
(bookmarkId: number, categoryId: number) => async (dispatch: Dispatch) => {
try { try {
await axios.delete<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`); await axios.delete<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`);
@ -201,48 +230,53 @@ export const deleteBookmark = (bookmarkId: number, categoryId: number) => async
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: 'Bookmark deleted' message: 'Bookmark deleted',
} },
}) });
dispatch<DeleteBookmarkAction>({ dispatch<DeleteBookmarkAction>({
type: ActionTypes.deleteBookmark, type: ActionTypes.deleteBookmark,
payload: { payload: {
bookmarkId, bookmarkId,
categoryId categoryId,
} },
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* UPDATE BOOKMARK * UPDATE BOOKMARK
*/ */
export interface UpdateBookmarkAction { export interface UpdateBookmarkAction {
type: ActionTypes.updateBookmark, type: ActionTypes.updateBookmark;
payload: Bookmark payload: Bookmark;
} }
export const updateBookmark = ( export const updateBookmark =
(
bookmarkId: number, bookmarkId: number,
formData: NewBookmark | FormData, formData: NewBookmark | FormData,
category: { category: {
prev: number, prev: number;
curr: number curr: number;
} }
) => async (dispatch: Dispatch) => { ) =>
async (dispatch: Dispatch) => {
try { try {
const res = await axios.put<ApiResponse<Bookmark>>(`/api/bookmarks/${bookmarkId}`, formData); const res = await axios.put<ApiResponse<Bookmark>>(
`/api/bookmarks/${bookmarkId}`,
formData
);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,
payload: { payload: {
title: 'Success', title: 'Success',
message: `Bookmark updated` message: `Bookmark updated`,
} },
}) });
// Check if category was changed // Check if category was changed
const categoryWasChanged = category.curr !== category.prev; const categoryWasChanged = category.curr !== category.prev;
@ -253,26 +287,26 @@ export const updateBookmark = (
type: ActionTypes.deleteBookmark, type: ActionTypes.deleteBookmark,
payload: { payload: {
bookmarkId, bookmarkId,
categoryId: category.prev categoryId: category.prev,
} },
}) });
// Add bookmark to the new category // Add bookmark to the new category
dispatch<AddBookmarkAction>({ dispatch<AddBookmarkAction>({
type: ActionTypes.addBookmark, type: ActionTypes.addBookmark,
payload: res.data.data payload: res.data.data,
}) });
} else { } else {
// Else update only name/url/icon // Else update only name/url/icon
dispatch<UpdateBookmarkAction>({ dispatch<UpdateBookmarkAction>({
type: ActionTypes.updateBookmark, type: ActionTypes.updateBookmark,
payload: res.data.data payload: res.data.data,
}) });
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* SORT CATEGORIES * SORT CATEGORIES
@ -284,16 +318,16 @@ export interface SortCategoriesAction {
export const sortCategories = () => async (dispatch: Dispatch) => { export const sortCategories = () => async (dispatch: Dispatch) => {
try { try {
const res = await axios.get<ApiResponse<Config>>('/api/config/useOrdering'); const res = await axios.get<ApiResponse<Config>>('/api/config');
dispatch<SortCategoriesAction>({ dispatch<SortCategoriesAction>({
type: ActionTypes.sortCategories, type: ActionTypes.sortCategories,
payload: res.data.data.value payload: res.data.data.useOrdering,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };
/** /**
* REORDER CATEGORIES * REORDER CATEGORIES
@ -307,25 +341,31 @@ interface ReorderQuery {
categories: { categories: {
id: number; id: number;
orderId: number; orderId: number;
}[] }[];
} }
export const reorderCategories = (categories: Category[]) => async (dispatch: Dispatch) => { export const reorderCategories =
(categories: Category[]) => async (dispatch: Dispatch) => {
try { try {
const updateQuery: ReorderQuery = { categories: [] } const updateQuery: ReorderQuery = { categories: [] };
categories.forEach((category, index) => updateQuery.categories.push({ categories.forEach((category, index) =>
updateQuery.categories.push({
id: category.id, id: category.id,
orderId: index + 1 orderId: index + 1,
})) })
);
await axios.put<ApiResponse<{}>>('/api/categories/0/reorder', updateQuery); await axios.put<ApiResponse<{}>>(
'/api/categories/0/reorder',
updateQuery
);
dispatch<ReorderCategoriesAction>({ dispatch<ReorderCategoriesAction>({
type: ActionTypes.reorderCategories, type: ActionTypes.reorderCategories,
payload: categories payload: categories,
}) });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} };

View File

@ -3,16 +3,15 @@ import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes'; import { ActionTypes } from './actionTypes';
import { Config, ApiResponse, Query } from '../../interfaces'; import { Config, ApiResponse, Query } from '../../interfaces';
import { CreateNotificationAction } from './notification'; import { CreateNotificationAction } from './notification';
import { searchConfig } from '../../utility';
export interface GetConfigAction { export interface GetConfigAction {
type: ActionTypes.getConfig; type: ActionTypes.getConfig;
payload: Config[]; payload: Config;
} }
export const getConfig = () => async (dispatch: Dispatch) => { export const getConfig = () => async (dispatch: Dispatch) => {
try { try {
const res = await axios.get<ApiResponse<Config[]>>('/api/config'); const res = await axios.get<ApiResponse<Config>>('/api/config');
dispatch<GetConfigAction>({ dispatch<GetConfigAction>({
type: ActionTypes.getConfig, type: ActionTypes.getConfig,
@ -20,7 +19,7 @@ export const getConfig = () => async (dispatch: Dispatch) => {
}); });
// Set custom page title if set // Set custom page title if set
document.title = searchConfig('customTitle', 'Flame'); document.title = res.data.data.customTitle;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -28,12 +27,12 @@ export const getConfig = () => async (dispatch: Dispatch) => {
export interface UpdateConfigAction { export interface UpdateConfigAction {
type: ActionTypes.updateConfig; type: ActionTypes.updateConfig;
payload: Config[]; payload: Config;
} }
export const updateConfig = (formData: any) => async (dispatch: Dispatch) => { export const updateConfig = (formData: any) => async (dispatch: Dispatch) => {
try { try {
const res = await axios.put<ApiResponse<Config[]>>('/api/config', formData); const res = await axios.put<ApiResponse<Config>>('/api/config', formData);
dispatch<CreateNotificationAction>({ dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification, type: ActionTypes.createNotification,

View File

@ -1,15 +1,16 @@
import { ActionTypes, Action } from '../actions'; import { ActionTypes, Action } from '../actions';
import { Config, Query } from '../../interfaces'; import { Config, Query } from '../../interfaces';
import { configTemplate } from '../../utility';
export interface State { export interface State {
loading: boolean; loading: boolean;
config: Config[]; config: Config;
customQueries: Query[]; customQueries: Query[];
} }
const initialState: State = { const initialState: State = {
loading: true, loading: true,
config: [], config: configTemplate,
customQueries: [], customQueries: [],
}; };

View File

@ -1,8 +1,8 @@
const App = require('../../models/App'); const App = require('../../../models/App');
const axios = require('axios'); const axios = require('axios');
const Logger = require('../../utils/Logger'); const Logger = require('../../../utils/Logger');
const logger = new Logger(); const logger = new Logger();
const loadConfig = require('../../utils/loadConfig'); const loadConfig = require('../../../utils/loadConfig');
const useDocker = async (apps) => { const useDocker = async (apps) => {
const { const {
@ -50,7 +50,7 @@ const useDocker = async (apps) => {
for (const container of containers) { for (const container of containers) {
let labels = container.Labels; let labels = container.Labels;
// todo // Traefik labels for URL configuration
if (!('flame.url' in labels)) { if (!('flame.url' in labels)) {
for (const label of Object.keys(labels)) { for (const label of Object.keys(labels)) {
if (/^traefik.*.frontend.rule/.test(label)) { if (/^traefik.*.frontend.rule/.test(label)) {

View File

@ -1,112 +0,0 @@
const asyncWrapper = require('../middleware/asyncWrapper');
const ErrorResponse = require('../utils/ErrorResponse');
const Bookmark = require('../models/Bookmark');
const { Sequelize } = require('sequelize');
// @desc Create new bookmark
// @route POST /api/bookmarks
// @access Public
exports.createBookmark = asyncWrapper(async (req, res, next) => {
let bookmark;
let _body = {
...req.body,
categoryId: parseInt(req.body.categoryId),
};
if (req.file) {
_body.icon = req.file.filename;
}
bookmark = await Bookmark.create(_body);
res.status(201).json({
success: true,
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']],
});
res.status(200).json({
success: true,
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 },
});
if (!bookmark) {
return next(
new ErrorResponse(
`Bookmark with id of ${req.params.id} was not found`,
404
)
);
}
res.status(200).json({
success: true,
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 },
});
if (!bookmark) {
return next(
new ErrorResponse(
`Bookmark with id of ${req.params.id} was not found`,
404
)
);
}
let _body = {
...req.body,
categoryId: parseInt(req.body.categoryId),
};
if (req.file) {
_body.icon = req.file.filename;
}
bookmark = await bookmark.update(_body);
res.status(200).json({
success: true,
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 },
});
res.status(200).json({
success: true,
data: {},
});
});

View File

@ -0,0 +1,27 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const Bookmark = require('../../models/Bookmark');
// @desc Create new bookmark
// @route POST /api/bookmarks
// @access Public
const createBookmark = asyncWrapper(async (req, res, next) => {
let bookmark;
let _body = {
...req.body,
categoryId: parseInt(req.body.categoryId),
};
if (req.file) {
_body.icon = req.file.filename;
}
bookmark = await Bookmark.create(_body);
res.status(201).json({
success: true,
data: bookmark,
});
});
module.exports = createBookmark;

View File

@ -0,0 +1,18 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const Bookmark = require('../../models/Bookmark');
// @desc Delete bookmark
// @route DELETE /api/bookmarks/:id
// @access Public
const deleteBookmark = asyncWrapper(async (req, res, next) => {
await Bookmark.destroy({
where: { id: req.params.id },
});
res.status(200).json({
success: true,
data: {},
});
});
module.exports = deleteBookmark;

View File

@ -0,0 +1,19 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const Bookmark = require('../../models/Bookmark');
const { Sequelize } = require('sequelize');
// @desc Get all bookmarks
// @route GET /api/bookmarks
// @access Public
const getAllBookmarks = asyncWrapper(async (req, res, next) => {
const bookmarks = await Bookmark.findAll({
order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']],
});
res.status(200).json({
success: true,
data: bookmarks,
});
});
module.exports = getAllBookmarks;

View File

@ -0,0 +1,28 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const ErrorResponse = require('../../utils/ErrorResponse');
const Bookmark = require('../../models/Bookmark');
// @desc Get single bookmark
// @route GET /api/bookmarks/:id
// @access Public
const getSingleBookmark = asyncWrapper(async (req, res, next) => {
const bookmark = await Bookmark.findOne({
where: { id: req.params.id },
});
if (!bookmark) {
return next(
new ErrorResponse(
`Bookmark with the id of ${req.params.id} was not found`,
404
)
);
}
res.status(200).json({
success: true,
data: bookmark,
});
});
module.exports = getSingleBookmark;

View File

@ -0,0 +1,7 @@
module.exports = {
createBookmark: require('./createBookmark'),
getAllBookmarks: require('./getAllBookmarks'),
getSingleBookmark: require('./getSingleBookmark'),
updateBookmark: require('./updateBookmark'),
deleteBookmark: require('./deleteBookmark'),
};

View File

@ -0,0 +1,39 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const ErrorResponse = require('../../utils/ErrorResponse');
const Bookmark = require('../../models/Bookmark');
// @desc Update bookmark
// @route PUT /api/bookmarks/:id
// @access Public
const updateBookmark = asyncWrapper(async (req, res, next) => {
let bookmark = await Bookmark.findOne({
where: { id: req.params.id },
});
if (!bookmark) {
return next(
new ErrorResponse(
`Bookmark with id of ${req.params.id} was not found`,
404
)
);
}
let _body = {
...req.body,
categoryId: parseInt(req.body.categoryId),
};
if (req.file) {
_body.icon = req.file.filename;
}
bookmark = await bookmark.update(_body);
res.status(200).json({
success: true,
data: bookmark,
});
});
module.exports = updateBookmark;

View File

@ -4,15 +4,13 @@ const Category = require('../models/Category');
const Bookmark = require('../models/Bookmark'); const Bookmark = require('../models/Bookmark');
const Config = require('../models/Config'); const Config = require('../models/Config');
const { Sequelize } = require('sequelize'); const { Sequelize } = require('sequelize');
const loadConfig = require('../utils/loadConfig');
// @desc Create new category // @desc Create new category
// @route POST /api/categories // @route POST /api/categories
// @access Public // @access Public
exports.createCategory = asyncWrapper(async (req, res, next) => { exports.createCategory = asyncWrapper(async (req, res, next) => {
// Get config from database const { pinCategoriesByDefault: pinCategories } = await loadConfig();
const pinCategories = await Config.findOne({
where: { key: 'pinCategoriesByDefault' },
});
let category; let category;
@ -37,12 +35,8 @@ exports.createCategory = asyncWrapper(async (req, res, next) => {
// @route GET /api/categories // @route GET /api/categories
// @access Public // @access Public
exports.getCategories = asyncWrapper(async (req, res, next) => { exports.getCategories = asyncWrapper(async (req, res, next) => {
// Get config from database const { useOrdering: orderType } = await loadConfig();
const useOrdering = await Config.findOne({
where: { key: 'useOrdering' },
});
const orderType = useOrdering ? useOrdering.value : 'createdAt';
let categories; let categories;
if (orderType == 'name') { if (orderType == 'name') {

View File

@ -1,177 +0,0 @@
const asyncWrapper = require('../middleware/asyncWrapper');
const ErrorResponse = require('../utils/ErrorResponse');
const Config = require('../models/Config');
const { Op } = require('sequelize');
const File = require('../utils/File');
const { join } = require('path');
const fs = require('fs');
// @desc Insert new key:value pair
// @route POST /api/config
// @access Public
exports.createPair = asyncWrapper(async (req, res, next) => {
const pair = await Config.create(req.body);
res.status(201).json({
success: true,
data: pair,
});
});
// @desc Get all key:value pairs
// @route GET /api/config
// @route GET /api/config?keys=foo,bar,baz
// @access Public
exports.getAllPairs = asyncWrapper(async (req, res, next) => {
let pairs;
if (req.query.keys) {
// Check for specific keys to get in a single query
const keys = req.query.keys.split(',').map((key) => {
return { key };
});
pairs = await Config.findAll({
where: {
[Op.or]: keys,
},
});
} else {
// Else get all
pairs = await Config.findAll();
}
res.status(200).json({
success: true,
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 },
});
if (!pair) {
return next(new ErrorResponse(`Key ${req.params.key} was not found`, 404));
}
res.status(200).json({
success: true,
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 },
});
if (!pair) {
return next(new ErrorResponse(`Key ${req.params.key} was not found`, 404));
}
if (pair.isLocked) {
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,
});
});
// @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 },
}
);
});
const config = await Config.findAll();
res.status(200).send({
success: true,
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 },
});
if (!pair) {
return next(new ErrorResponse(`Key ${req.params.key} was not found`, 404));
}
if (pair.isLocked) {
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: {},
});
});
// @desc Get custom CSS file
// @route GET /api/config/0/css
// @access Public
exports.getCss = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../public/flame.css'));
const content = file.read();
res.status(200).json({
success: true,
data: content,
});
});
// @desc Update custom CSS file
// @route PUT /api/config/0/css
// @access Public
exports.updateCss = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../public/flame.css'));
file.write(req.body.styles, false);
// Copy file to docker volume
fs.copyFileSync(
join(__dirname, '../public/flame.css'),
join(__dirname, '../data/flame.css')
);
res.status(200).json({
success: true,
data: {},
});
});

View File

@ -0,0 +1,18 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const File = require('../../utils/File');
const { join } = require('path');
// @desc Get custom CSS file
// @route GET /api/config/0/css
// @access Public
const getCSS = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../../public/flame.css'));
const content = file.read();
res.status(200).json({
success: true,
data: content,
});
});
module.exports = getCSS;

View File

@ -0,0 +1,16 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const loadConfig = require('../../utils/loadConfig');
// @desc Get config
// @route GET /api/config
// @access Public
const getConfig = asyncWrapper(async (req, res, next) => {
const config = await loadConfig();
res.status(200).json({
success: true,
data: config,
});
});
module.exports = getConfig;

View File

@ -0,0 +1,6 @@
module.exports = {
getCSS: require('./getCSS'),
updateCSS: require('./updateCSS'),
getConfig: require('./getConfig'),
updateConfig: require('./updateConfig'),
};

View File

@ -0,0 +1,24 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const File = require('../../utils/File');
const { join } = require('path');
// @desc Update custom CSS file
// @route PUT /api/config/0/css
// @access Public
const updateCSS = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../../public/flame.css'));
file.write(req.body.styles, false);
// Copy file to docker volume
fs.copyFileSync(
join(__dirname, '../../public/flame.css'),
join(__dirname, '../../data/flame.css')
);
res.status(200).json({
success: true,
data: {},
});
});
module.exports = updateCSS;

View File

@ -0,0 +1,24 @@
const asyncWrapper = require('../../middleware/asyncWrapper');
const loadConfig = require('../../utils/loadConfig');
const { writeFile } = require('fs/promises');
// @desc Update config
// @route PUT /api/config/
// @access Public
const updateConfig = asyncWrapper(async (req, res, next) => {
const existingConfig = await loadConfig();
const newConfig = {
...existingConfig,
...req.body,
};
await writeFile('data/config.json', JSON.stringify(newConfig));
res.status(200).send({
success: true,
data: newConfig,
});
});
module.exports = updateConfig;

View File

@ -11,7 +11,7 @@ const storage = multer.diskStorage({
}, },
filename: (req, file, cb) => { filename: (req, file, cb) => {
cb(null, Date.now() + '--' + file.originalname); cb(null, Date.now() + '--' + file.originalname);
} },
}); });
const supportedTypes = ['jpg', 'jpeg', 'png', 'svg', 'svg+xml']; const supportedTypes = ['jpg', 'jpeg', 'png', 'svg', 'svg+xml'];

View File

@ -4,20 +4,17 @@ const upload = require('../middleware/multer');
const { const {
createBookmark, createBookmark,
getBookmarks, getAllBookmarks,
getBookmark, getSingleBookmark,
updateBookmark, updateBookmark,
deleteBookmark deleteBookmark,
} = require('../controllers/bookmark'); } = require('../controllers/bookmarks');
router router.route('/').post(upload, createBookmark).get(getAllBookmarks);
.route('/')
.post(upload, createBookmark)
.get(getBookmarks);
router router
.route('/:id') .route('/:id')
.get(getBookmark) .get(getSingleBookmark)
.put(upload, updateBookmark) .put(upload, updateBookmark)
.delete(deleteBookmark); .delete(deleteBookmark);

View File

@ -2,20 +2,14 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const { const {
createPair, getCSS,
getAllPairs, updateCSS,
getSinglePair, getConfig,
updateValue, updateConfig,
updateValues,
deletePair,
updateCss,
getCss,
} = require('../controllers/config'); } = require('../controllers/config');
router.route('/').post(createPair).get(getAllPairs).put(updateValues); router.route('/').get(getConfig).put(updateConfig);
router.route('/:key').get(getSinglePair).put(updateValue).delete(deletePair); router.route('/0/css').get(getCSS).put(updateCSS);
router.route('/0/css').get(getCss).put(updateCss);
module.exports = router; module.exports = router;