mirror of
https://github.com/pawelmalak/flame.git
synced 2025-06-28 09:14:04 -04:00
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 { 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 />
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -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;
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -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;
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue