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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
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 { Config, ApiResponse } from '../../interfaces'; import { Config, ApiResponse, Query } from '../../interfaces';
import { CreateNotificationAction } from './notification'; import { CreateNotificationAction } from './notification';
import { searchConfig } from '../../utility'; import { searchConfig } from '../../utility';
@ -16,15 +16,15 @@ export const getConfig = () => async (dispatch: Dispatch) => {
dispatch<GetConfigAction>({ dispatch<GetConfigAction>({
type: ActionTypes.getConfig, type: ActionTypes.getConfig,
payload: res.data.data payload: res.data.data,
}) });
// Set custom page title if set // Set custom page title if set
document.title = searchConfig('customTitle', 'Flame'); document.title = searchConfig('customTitle', 'Flame');
} catch (err) { } catch (err) {
console.log(err) console.log(err);
}
} }
};
export interface UpdateConfigAction { export interface UpdateConfigAction {
type: ActionTypes.updateConfig; type: ActionTypes.updateConfig;
@ -34,19 +34,41 @@ export interface UpdateConfigAction {
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,
payload: { payload: {
title: 'Success', title: 'Success',
message: 'Settings updated' message: 'Settings updated',
} },
}) });
dispatch<UpdateConfigAction>({ dispatch<UpdateConfigAction>({
type: ActionTypes.updateConfig, type: ActionTypes.updateConfig,
payload: res.data.data payload: res.data.data,
}) });
} catch (err) { } catch (err) {
console.log(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 { ActionTypes, Action } from '../actions';
import { Config } from '../../interfaces'; import { Config, Query } from '../../interfaces';
export interface State { export interface State {
loading: boolean; loading: boolean;
config: Config[]; config: Config[];
customQueries: Query[];
} }
const initialState: State = { const initialState: State = {
loading: true, loading: true,
config: [] config: [],
} customQueries: [],
};
const getConfig = (state: State, action: Action): State => { const getConfig = (state: State, action: Action): State => {
return { return {
...state,
loading: false, loading: false,
config: action.payload };
} };
}
const updateConfig = (state: State, action: Action): State => { const updateConfig = (state: State, action: Action): State => {
return { return {
...state, ...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) => { const configReducer = (state: State = initialState, action: Action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.getConfig: return getConfig(state, action); case ActionTypes.getConfig:
case ActionTypes.updateConfig: return updateConfig(state, action); return getConfig(state, action);
default: return state; 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 { queries } from './searchQueries.json';
import { Query, SearchResult } from '../interfaces'; import { Query, SearchResult } from '../interfaces';
import { store } from '../store/store';
import { searchConfig } from '.'; import { searchConfig } from '.';
export const searchParser = (searchQuery: string): SearchResult => { 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 // Check if url or ip was passed
const urlRegex = 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])$/; /^(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(splitQuery[2])
: encodeURIComponent(searchQuery); : 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 search provider was found
if (query) { if (query) {

View file

@ -162,7 +162,7 @@ exports.getCss = asyncWrapper(async (req, res, next) => {
// @access Public // @access Public
exports.updateCss = asyncWrapper(async (req, res, next) => { exports.updateCss = asyncWrapper(async (req, res, next) => {
const file = new File(join(__dirname, '../public/flame.css')); 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 // Copy file to docker volume
fs.copyFileSync( fs.copyFileSync(
@ -175,3 +175,16 @@ exports.updateCss = asyncWrapper(async (req, res, next) => {
data: {}, 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, deletePair,
updateCss, updateCss,
getCss, getCss,
getQueries,
} = require('../controllers/config'); } = require('../controllers/config');
router router.route('/').post(createPair).get(getAllPairs).put(updateValues);
.route('/')
.post(createPair)
.get(getAllPairs)
.put(updateValues);
router router.route('/:key').get(getSinglePair).put(updateValue).delete(deletePair);
.route('/:key')
.get(getSinglePair)
.put(updateValue)
.delete(deletePair);
router router.route('/0/css').get(getCss).put(updateCss);
.route('/0/css')
.get(getCss) router.route('/0/queries').get(getQueries);
.put(updateCss);
module.exports = router; module.exports = router;

View file

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