Fetch and use custom search queries

This commit is contained in:
unknown 2021-10-06 14:15:05 +02:00
parent da928f20a2
commit 591824dd0c
9 changed files with 136 additions and 80 deletions

View file

@ -1,5 +1,5 @@
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { getConfig, setTheme } from './store/actions';
import { fetchQueries, getConfig, setTheme } from './store/actions';
import 'external-svg-loader';
// Redux
@ -27,15 +27,18 @@ if (localStorage.theme) {
// Check for updates
checkVersion();
// fetch queries
store.dispatch<any>(fetchQueries());
const App = (): JSX.Element => {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/settings' component={Settings} />
<Route path='/applications' component={Apps} />
<Route path='/bookmarks' component={Bookmarks} />
<Route exact path="/" component={Home} />
<Route path="/settings" component={Settings} />
<Route path="/applications" component={Apps} />
<Route path="/bookmarks" component={Bookmarks} />
</Switch>
</BrowserRouter>
<NotificationCenter />

View file

@ -27,6 +27,7 @@ interface Props {
createNotification: (notification: NewNotification) => void;
updateConfig: (formData: SearchForm) => void;
loading: boolean;
customQueries: Query[];
}
const SearchSettings = (props: Props): JSX.Element => {
@ -81,7 +82,7 @@ const SearchSettings = (props: Props): JSX.Element => {
value={formData.defaultSearchProvider}
onChange={(e) => inputChangeHandler(e)}
>
{queries.map((query: Query, idx) => (
{[...queries, ...props.customQueries].map((query: Query, idx) => (
<option key={idx} value={query.prefix}>
{query.name}
</option>
@ -122,6 +123,7 @@ const SearchSettings = (props: Props): JSX.Element => {
const mapStateToProps = (state: GlobalState) => {
return {
loading: state.config.loading,
customQueries: state.config.customQueries,
};
};

View file

@ -26,8 +26,9 @@ import {
ClearNotificationAction,
// Config
GetConfigAction,
UpdateConfigAction
UpdateConfigAction,
} from './';
import { FetchQueriesAction } from './config';
export enum ActionTypes {
// Theme
@ -62,35 +63,37 @@ export enum ActionTypes {
clearNotification = 'CLEAR_NOTIFICATION',
// Config
getConfig = 'GET_CONFIG',
updateConfig = 'UPDATE_CONFIG'
updateConfig = 'UPDATE_CONFIG',
fetchQueries = 'FETCH_QUERIES',
}
export type Action =
export type Action =
// Theme
SetThemeAction |
| SetThemeAction
// Apps
GetAppsAction<any> |
PinAppAction |
AddAppAction |
DeleteAppAction |
UpdateAppAction |
ReorderAppsAction |
SortAppsAction |
| GetAppsAction<any>
| PinAppAction
| AddAppAction
| DeleteAppAction
| UpdateAppAction
| ReorderAppsAction
| SortAppsAction
// Categories
GetCategoriesAction<any> |
AddCategoryAction |
PinCategoryAction |
DeleteCategoryAction |
UpdateCategoryAction |
SortCategoriesAction |
ReorderCategoriesAction |
| GetCategoriesAction<any>
| AddCategoryAction
| PinCategoryAction
| DeleteCategoryAction
| UpdateCategoryAction
| SortCategoriesAction
| ReorderCategoriesAction
// Bookmarks
AddBookmarkAction |
DeleteBookmarkAction |
UpdateBookmarkAction |
| AddBookmarkAction
| DeleteBookmarkAction
| UpdateBookmarkAction
// Notifications
CreateNotificationAction |
ClearNotificationAction |
| CreateNotificationAction
| ClearNotificationAction
// Config
GetConfigAction |
UpdateConfigAction;
| GetConfigAction
| UpdateConfigAction
| FetchQueriesAction;

View file

@ -1,7 +1,7 @@
import axios from 'axios';
import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes';
import { Config, ApiResponse } from '../../interfaces';
import { Config, ApiResponse, Query } from '../../interfaces';
import { CreateNotificationAction } from './notification';
import { searchConfig } from '../../utility';
@ -13,18 +13,18 @@ export interface GetConfigAction {
export const getConfig = () => async (dispatch: Dispatch) => {
try {
const res = await axios.get<ApiResponse<Config[]>>('/api/config');
dispatch<GetConfigAction>({
type: ActionTypes.getConfig,
payload: res.data.data
})
payload: res.data.data,
});
// Set custom page title if set
document.title = searchConfig('customTitle', 'Flame');
} catch (err) {
console.log(err)
console.log(err);
}
}
};
export interface UpdateConfigAction {
type: ActionTypes.updateConfig;
@ -34,19 +34,41 @@ export interface UpdateConfigAction {
export const updateConfig = (formData: any) => async (dispatch: Dispatch) => {
try {
const res = await axios.put<ApiResponse<Config[]>>('/api/config', formData);
dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification,
payload: {
title: 'Success',
message: 'Settings updated'
}
})
message: 'Settings updated',
},
});
dispatch<UpdateConfigAction>({
type: ActionTypes.updateConfig,
payload: res.data.data
})
payload: res.data.data,
});
} catch (err) {
console.log(err);
}
}
};
export interface FetchQueriesAction {
type: ActionTypes.fetchQueries;
payload: Query[];
}
export const fetchQueries =
() => async (dispatch: Dispatch<FetchQueriesAction>) => {
try {
const res = await axios.get<ApiResponse<Query[]>>(
'/api/config/0/queries'
);
dispatch<FetchQueriesAction>({
type: ActionTypes.fetchQueries,
payload: res.data.data,
});
} catch (err) {
console.log(err);
}
};

View file

@ -1,36 +1,50 @@
import { ActionTypes, Action } from '../actions';
import { Config } from '../../interfaces';
import { Config, Query } from '../../interfaces';
export interface State {
loading: boolean;
config: Config[];
customQueries: Query[];
}
const initialState: State = {
loading: true,
config: []
}
config: [],
customQueries: [],
};
const getConfig = (state: State, action: Action): State => {
return {
...state,
loading: false,
config: action.payload
}
}
};
};
const updateConfig = (state: State, action: Action): State => {
return {
...state,
config: action.payload
}
}
config: action.payload,
};
};
const fetchQueries = (state: State, action: Action): State => {
return {
...state,
customQueries: action.payload,
};
};
const configReducer = (state: State = initialState, action: Action) => {
switch(action.type) {
case ActionTypes.getConfig: return getConfig(state, action);
case ActionTypes.updateConfig: return updateConfig(state, action);
default: return state;
switch (action.type) {
case ActionTypes.getConfig:
return getConfig(state, action);
case ActionTypes.updateConfig:
return updateConfig(state, action);
case ActionTypes.fetchQueries:
return fetchQueries(state, action);
default:
return state;
}
}
};
export default configReducer;
export default configReducer;

View file

@ -1,6 +1,6 @@
import { queries } from './searchQueries.json';
import { Query, SearchResult } from '../interfaces';
import { store } from '../store/store';
import { searchConfig } from '.';
export const searchParser = (searchQuery: string): SearchResult => {
@ -16,6 +16,8 @@ export const searchParser = (searchQuery: string): SearchResult => {
},
};
const customQueries = store.getState().config.customQueries;
// Check if url or ip was passed
const urlRegex =
/^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?|^((http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
@ -33,7 +35,9 @@ export const searchParser = (searchQuery: string): SearchResult => {
? encodeURIComponent(splitQuery[2])
: encodeURIComponent(searchQuery);
const query = queries.find((q: Query) => q.prefix === prefix);
const query = [...queries, ...customQueries].find(
(q: Query) => q.prefix === prefix
);
// If search provider was found
if (query) {

View file

@ -162,7 +162,7 @@ exports.getCss = asyncWrapper(async (req, res, next) => {
// @access Public
exports.updateCss = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../public/flame.css'));
file.write(req.body.styles);
file.write(req.body.styles, false);
// Copy file to docker volume
fs.copyFileSync(
@ -175,3 +175,16 @@ exports.updateCss = asyncWrapper(async (req, res, next) => {
data: {},
});
});
// @desc Get custom queries file
// @route GET /api/config/0/queries
// @access Public
exports.getQueries = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../data/customQueries.json'));
const content = JSON.parse(file.read());
res.status(200).json({
success: true,
data: content.queries,
});
});

View file

@ -10,23 +10,15 @@ const {
deletePair,
updateCss,
getCss,
getQueries,
} = require('../controllers/config');
router
.route('/')
.post(createPair)
.get(getAllPairs)
.put(updateValues);
router.route('/').post(createPair).get(getAllPairs).put(updateValues);
router
.route('/:key')
.get(getSinglePair)
.put(updateValue)
.delete(deletePair);
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;
router.route('/0/queries').get(getQueries);
module.exports = router;

View file

@ -3,7 +3,7 @@ const fs = require('fs');
class File {
constructor(path) {
this.path = path;
this.content = '';
this.content = null;
}
read() {
@ -16,10 +16,13 @@ class File {
}
}
write(data) {
write(data, isJSON) {
this.content = data;
fs.writeFileSync(this.path, this.content);
fs.writeFileSync(
this.path,
isJSON ? JSON.stringify(this.content) : this.content
);
}
}
module.exports = File;
module.exports = File;