enabling multi dashboards API+client
This commit is contained in:
Родитель
4a95285161
Коммит
a83a7076e8
|
@ -44,7 +44,132 @@ router.post('/dashboard.js', (req, res) => {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
function isAuthoeizedToSetup(req) {
|
|
||||||
|
router.get('/dashboards', (req, res) => {
|
||||||
|
|
||||||
|
let privateDashboard = path.join(__dirname, '..', 'dashboards');
|
||||||
|
let preconfDashboard = path.join(__dirname, '..', 'dashboards', 'preconfigured');
|
||||||
|
|
||||||
|
let script = '';
|
||||||
|
let files = fs.readdirSync(privateDashboard);
|
||||||
|
if (files && files.length) {
|
||||||
|
files.forEach((fileName) => {
|
||||||
|
let filePath = path.join(privateDashboard, fileName);
|
||||||
|
let stats = fs.statSync(filePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Ensuing this dashboard is loaded into the dashboards array on the page
|
||||||
|
script += `
|
||||||
|
(function (window) {
|
||||||
|
var dashboard = (function () {
|
||||||
|
${content}
|
||||||
|
})();
|
||||||
|
window.dashboardDefinitions = window.dashboardDefinitions || [];
|
||||||
|
window.dashboardDefinitions.push(dashboard);
|
||||||
|
})(window);
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let templates = fs.readdirSync(preconfDashboard);
|
||||||
|
if (templates && templates.length) {
|
||||||
|
templates.forEach((fileName) => {
|
||||||
|
let filePath = path.join(preconfDashboard, fileName);
|
||||||
|
let stats = fs.statSync(filePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Ensuing this dashboard is loaded into the dashboards array on the page
|
||||||
|
script += `
|
||||||
|
(function (window) {
|
||||||
|
var dashboardTemplate = (function () {
|
||||||
|
${content}
|
||||||
|
})();
|
||||||
|
window.dashboardTemplates = window.dashboardTemplates || [];
|
||||||
|
window.dashboardTemplates.push(dashboardTemplate);
|
||||||
|
})(window);
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(script);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/dashboards/:id', (req, res) => {
|
||||||
|
|
||||||
|
let dashboardId = req.params.id;
|
||||||
|
let privateDashboard = path.join(__dirname, '..', 'dashboards');
|
||||||
|
let preconfDashboard = path.join(__dirname, '..', 'dashboards', 'preconfigured');
|
||||||
|
|
||||||
|
let script = '';
|
||||||
|
let files = fs.readdirSync(privateDashboard) || [];
|
||||||
|
|
||||||
|
// Make sure the array only contains files
|
||||||
|
files = files.filter(fileName => fs.statSync(path.join(privateDashboard, fileName)).isFile());
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
|
||||||
|
let dashboardFile = null;
|
||||||
|
let dashboardIndex = parseInt(dashboardId);
|
||||||
|
if (!isNaN(dashboardIndex) && files.length > dashboardIndex) {
|
||||||
|
dashboardFile = files[dashboardIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dashboardFile) {
|
||||||
|
files.forEach(fileName => {
|
||||||
|
let filePath = path.join(privateDashboard, fileName);
|
||||||
|
|
||||||
|
let stats = fs.statSync(filePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
let dashboard = getJSONFromScript(filePath);
|
||||||
|
if (dashboard.url && dashboard.url.toLowerCase() === dashboardId.toLowerCase()) {
|
||||||
|
dashboardFile = fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashboardFile) {
|
||||||
|
let filePath = path.join(privateDashboard, dashboardFile);
|
||||||
|
let stats = fs.statSync(filePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Ensuing this dashboard is loaded into the dashboards array on the page
|
||||||
|
script += `
|
||||||
|
(function (window) {
|
||||||
|
var dashboard = (function () {
|
||||||
|
${content}
|
||||||
|
})();
|
||||||
|
window.dashboard = dashboard || null;
|
||||||
|
})(window);
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(script);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/dashboards/:id', (req, res) => {
|
||||||
|
let dashboardName = req.params.dashboard;
|
||||||
|
var content = (req.body && req.body.script) || '';
|
||||||
|
console.dir(content);
|
||||||
|
|
||||||
|
fs.writeFile(path.join(__dirname, '..', 'dashboards', 'dashboard.private.js'), content, err => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.end(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end(content);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
function isAuthorizedToSetup(req) {
|
||||||
if (!fs.existsSync(privateSetupPath)) { return true; }
|
if (!fs.existsSync(privateSetupPath)) { return true; }
|
||||||
|
|
||||||
let configString = fs.readFileSync(privateSetupPath, 'utf8');
|
let configString = fs.readFileSync(privateSetupPath, 'utf8');
|
||||||
|
@ -61,7 +186,7 @@ function isAuthoeizedToSetup(req) {
|
||||||
|
|
||||||
router.get('/setup', (req, res) => {
|
router.get('/setup', (req, res) => {
|
||||||
|
|
||||||
if (!isAuthoeizedToSetup(req)) {
|
if (!isAuthorizedToSetup(req)) {
|
||||||
return res.send({ error: new Error('User is not authorized to setup') });
|
return res.send({ error: new Error('User is not authorized to setup') });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +201,7 @@ router.get('/setup', (req, res) => {
|
||||||
|
|
||||||
router.post('/setup', (req, res) => {
|
router.post('/setup', (req, res) => {
|
||||||
|
|
||||||
if (!isAuthoeizedToSetup(req)) {
|
if (!isAuthorizedToSetup(req)) {
|
||||||
return res.send({ error: new Error('User is not authorized to setup') });
|
return res.send({ error: new Error('User is not authorized to setup') });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,4 +220,17 @@ router.post('/setup', (req, res) => {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
router
|
router
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSONFromScript(filePath) {
|
||||||
|
if (!fs.existsSync(filePath)) { return {}; }
|
||||||
|
|
||||||
|
let jsonScript = {};
|
||||||
|
let stats = fs.statSync(filePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
eval('jsonScript = (function () { ' + content + ' })();');
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonScript;
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ import * as request from 'xhr-request';
|
||||||
|
|
||||||
interface IConfigurationsActions {
|
interface IConfigurationsActions {
|
||||||
loadConfiguration(): any;
|
loadConfiguration(): any;
|
||||||
|
loadDashboard(id: string): any;
|
||||||
saveConfiguration(dashboard: IDashboardConfig): any;
|
saveConfiguration(dashboard: IDashboardConfig): any;
|
||||||
failure(error: any): void;
|
failure(error: any): void;
|
||||||
}
|
}
|
||||||
|
@ -14,17 +15,33 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
|
|
||||||
loadConfiguration() {
|
loadConfiguration() {
|
||||||
|
|
||||||
return (dispatcher: (dashboard: IDashboardConfig) => void) => {
|
return (dispatcher: (result: { dashboards: IDashboardConfig[], templates: IDashboardConfig[] }) => void) => {
|
||||||
|
|
||||||
this.getScript('/api/dashboard.js', () => {
|
this.getScript('/api/dashboards', () => {
|
||||||
let dashboards: IDashboardConfig[] = (window as any)['dashboards'];
|
let dashboards: IDashboardConfig[] = (window as any)['dashboardDefinitions'];
|
||||||
|
let templates: IDashboardConfig[] = (window as any)['dashboardTemplates'];
|
||||||
|
|
||||||
if (!dashboards || !dashboards.length) {
|
if (!dashboards || !dashboards.length) {
|
||||||
return this.failure(new Error('Could not load configuration'));
|
return this.failure(new Error('Could not load configuration'));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dashboard = dashboards[0];
|
return dispatcher({ dashboards, templates });
|
||||||
return dispatcher(dashboard);
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDashboard(id: string) {
|
||||||
|
|
||||||
|
return (dispatcher: (result: { dashboard: IDashboardConfig }) => void) => {
|
||||||
|
|
||||||
|
this.getScript('/api/dashboards/' + id, () => {
|
||||||
|
let dashboard: IDashboardConfig = (window as any)['dashboard'];
|
||||||
|
|
||||||
|
if (!dashboard) {
|
||||||
|
return this.failure(new Error('Could not load configuration for dashboard ' + id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatcher({ dashboard });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default class ConfigDashboard extends React.Component<IConfigDashboardPro
|
||||||
this.onSave = this.onSave.bind(this);
|
this.onSave = this.onSave.bind(this);
|
||||||
this.onSaveGoToDashboard = this.onSaveGoToDashboard.bind(this);
|
this.onSaveGoToDashboard = this.onSaveGoToDashboard.bind(this);
|
||||||
|
|
||||||
ConfigurationsActions.loadConfiguration();
|
//ConfigurationsActions.loadConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
onParamChange(connectionKey, paramKey, value) {
|
onParamChange(connectionKey, paramKey, value) {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import Chip from 'react-md/lib/Chips';
|
||||||
import AccountStore from '../../stores/AccountStore';
|
import AccountStore from '../../stores/AccountStore';
|
||||||
import AccountActions from '../../actions/AccountActions';
|
import AccountActions from '../../actions/AccountActions';
|
||||||
|
|
||||||
|
import ConfigurationsStore from '../../stores/ConfigurationsStore';
|
||||||
|
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
|
||||||
const avatarSrc = 'https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png';
|
const avatarSrc = 'https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png';
|
||||||
|
@ -47,9 +49,16 @@ export default class Navbar extends React.Component<any, any> {
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
});
|
});
|
||||||
AccountActions.updateAccount();
|
AccountActions.updateAccount();
|
||||||
|
|
||||||
|
ConfigurationsStore.listen((state) => {
|
||||||
|
this.setState({
|
||||||
|
dashboards: state.dashboards
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let { dashboards } = this.state;
|
||||||
let { children, title } = this.props;
|
let { children, title } = this.props;
|
||||||
let pathname = '/';
|
let pathname = '/';
|
||||||
try { pathname = window.location.pathname; } catch (e) { }
|
try { pathname = window.location.pathname; } catch (e) { }
|
||||||
|
@ -81,17 +90,6 @@ export default class Navbar extends React.Component<any, any> {
|
||||||
<ListItem
|
<ListItem
|
||||||
key="2"
|
key="2"
|
||||||
component={Link}
|
component={Link}
|
||||||
href="/dashboard"
|
|
||||||
active={pathname === '/dashboard'}
|
|
||||||
leftIcon={<FontIcon>dashboard</FontIcon>}
|
|
||||||
tileClassName="md-list-tile--mini"
|
|
||||||
primaryText={'Dashboard'}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
(
|
|
||||||
<ListItem
|
|
||||||
key="3"
|
|
||||||
component={Link}
|
|
||||||
href="/setup"
|
href="/setup"
|
||||||
active={pathname === '/setup'}
|
active={pathname === '/setup'}
|
||||||
leftIcon={<FontIcon>settings</FontIcon>}
|
leftIcon={<FontIcon>settings</FontIcon>}
|
||||||
|
@ -101,6 +99,30 @@ export default class Navbar extends React.Component<any, any> {
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
(dashboards || []).forEach((dashboard, index) => {
|
||||||
|
let name = dashboard.name || null;
|
||||||
|
let url = '/dashboard/' + (dashboard.url || index.toString());
|
||||||
|
let active = pathname === url;
|
||||||
|
if (!title && active && name) {
|
||||||
|
title = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationItems.push(
|
||||||
|
(
|
||||||
|
<ListItem
|
||||||
|
key={index + 4}
|
||||||
|
component={Link}
|
||||||
|
href={url}
|
||||||
|
active={active}
|
||||||
|
leftIcon={<FontIcon>{dashboard.icon || 'dashboard'}</FontIcon>}
|
||||||
|
tileClassName="md-list-tile--mini"
|
||||||
|
primaryText={name || 'Dashboard'}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
let toolbarActions =
|
let toolbarActions =
|
||||||
this.state.account ?
|
this.state.account ?
|
||||||
<Chip style={{ marginRight: 30 }} label={'Hello, ' + this.state.account.displayName} /> :
|
<Chip style={{ marginRight: 30 }} label={'Hello, ' + this.state.account.displayName} /> :
|
||||||
|
@ -129,6 +151,7 @@ export default class Navbar extends React.Component<any, any> {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
title = 'Ibex Dashboard';
|
title = 'Ibex Dashboard';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default class Config extends React.Component<any, IDashboardState> {
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
ConfigurationsActions.loadConfiguration();
|
//ConfigurationsActions.loadConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default class Dashboard extends React.Component<any, IDashboardState> {
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
ConfigurationsActions.loadConfiguration();
|
// ConfigurationsActions.loadConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ export default (
|
||||||
<Route path="/about" component={About} />
|
<Route path="/about" component={About} />
|
||||||
<Route path="/dashboard" component={Dashboard} />
|
<Route path="/dashboard" component={Dashboard} />
|
||||||
<Route path="/dashboard/config" component={Config} />
|
<Route path="/dashboard/config" component={Config} />
|
||||||
|
<Route path="/dashboard/:id" component={Dashboard}/>
|
||||||
<Route path="/setup" component={Setup} />
|
<Route path="/setup" component={Setup} />
|
||||||
<Route path="*" component={NotFound} />
|
<Route path="*" component={NotFound} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import configurationActions from '../actions/ConfigurationsActions';
|
||||||
|
|
||||||
interface IConfigurationsStoreState {
|
interface IConfigurationsStoreState {
|
||||||
dashboard: IDashboardConfig;
|
dashboard: IDashboardConfig;
|
||||||
|
dashboards: IDashboardConfig[];
|
||||||
|
templates: IDashboardConfig[];
|
||||||
connections: IDictionary;
|
connections: IDictionary;
|
||||||
connectionsMissing: boolean;
|
connectionsMissing: boolean;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
|
@ -15,6 +17,8 @@ interface IConfigurationsStoreState {
|
||||||
class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState> implements IConfigurationsStoreState {
|
class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState> implements IConfigurationsStoreState {
|
||||||
|
|
||||||
dashboard: IDashboardConfig;
|
dashboard: IDashboardConfig;
|
||||||
|
dashboards: IDashboardConfig[];
|
||||||
|
templates: IDashboardConfig[];
|
||||||
connections: IDictionary;
|
connections: IDictionary;
|
||||||
connectionsMissing: boolean;
|
connectionsMissing: boolean;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
|
@ -23,16 +27,38 @@ class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState>
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.dashboard = null;
|
this.dashboard = null;
|
||||||
|
this.dashboards = null;
|
||||||
|
this.templates = null;
|
||||||
this.connections = {};
|
this.connections = {};
|
||||||
this.connectionsMissing = false;
|
this.connectionsMissing = false;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
this.bindListeners({
|
this.bindListeners({
|
||||||
loadConfiguration: configurationActions.loadConfiguration
|
loadConfiguration: configurationActions.loadConfiguration,
|
||||||
|
loadDashboard: configurationActions.loadDashboard
|
||||||
});
|
});
|
||||||
|
|
||||||
|
configurationActions.loadConfiguration();
|
||||||
|
|
||||||
|
let pathname = window.location.pathname;
|
||||||
|
if (pathname === '/dashboard') {
|
||||||
|
configurationActions.loadDashboard("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathname.startsWith('/dashboard/')) {
|
||||||
|
let dashboardId = pathname.substring('/dashboard/'.length);
|
||||||
|
configurationActions.loadDashboard(dashboardId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadConfiguration(dashboard: IDashboardConfig) {
|
loadConfiguration(result: { dashboards: IDashboardConfig[], templates: IDashboardConfig[] }) {
|
||||||
|
let { dashboards,templates } = result;
|
||||||
|
this.dashboards = dashboards;
|
||||||
|
this.templates = templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDashboard(result: { dashboard: IDashboardConfig }) {
|
||||||
|
let { dashboard } = result;
|
||||||
this.dashboard = dashboard;
|
this.dashboard = dashboard;
|
||||||
|
|
||||||
if (this.dashboard && !this.loaded) {
|
if (this.dashboard && !this.loaded) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче