Fetch and use custom search queries
This commit is contained in:
parent
da928f20a2
commit
591824dd0c
9 changed files with 136 additions and 80 deletions
|
@ -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 />
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue