diff --git a/server/README.md b/server/README.md index 882cfdb..b357a99 100644 --- a/server/README.md +++ b/server/README.md @@ -1,27 +1,4 @@ # Hastic server -REST server for managing data for analytics. +It is a REST server based on KoaJS -Running on 8000 port. - -# Build - -``` -npm install -npm run build -``` - -# Run - -``` -npm start -``` - -# Development - -You should have `nodemon` module installed to run development server. - -``` -npm i -g nodemon -npm run dev -``` diff --git a/server/build/webpack.base.conf.js b/server/build/webpack.base.conf.js index 4f43557..e824c58 100644 --- a/server/build/webpack.base.conf.js +++ b/server/build/webpack.base.conf.js @@ -16,20 +16,18 @@ module.exports = { }, context: resolve('./src'), entry: './index', - devtool: 'inline-source-map', output: { filename: "server.js", path: resolve('dist') }, plugins: [ - new webpack.optimize.OccurrenceOrderPlugin(), - new webpack.HotModuleReplacementPlugin(), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('development') - }) + new webpack.optimize.OccurrenceOrderPlugin() ], resolve: { - extensions: [".ts", ".js"] + extensions: [".ts", ".js"], + alias: { + 'any-promise': 'es6-promise' + } }, module: { loaders: [ diff --git a/server/build/webpack.dev.conf.js b/server/build/webpack.dev.conf.js index ddaca27..51b4208 100644 --- a/server/build/webpack.dev.conf.js +++ b/server/build/webpack.dev.conf.js @@ -1,8 +1,13 @@ -var base = require('./webpack.base.conf'); +const base = require('./webpack.base.conf'); + +const webpack = require('webpack'); base.watch = true; -base.externals = [ +base.devtool = 'inline-source-map'; + +base.externals = base.externals ? base.externals : []; +base.externals.push( function(context, request, callback) { if(request[0] == '.') { callback(); @@ -10,5 +15,11 @@ base.externals = [ callback(null, "require('" + request + "')"); } } -] +); + +base.plugins = base.plugins ? base.plugins : []; +base.plugins.push(new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('development') +})); + module.exports = base; \ No newline at end of file diff --git a/server/package.json b/server/package.json index 628e87a..73b0d5b 100644 --- a/server/package.json +++ b/server/package.json @@ -17,10 +17,13 @@ "url": "https://github.com/hastic/hastic-server/issues" }, "homepage": "https://github.com/hastic/hastic-server#readme", - "dependencies": { - }, + "dependencies": {}, "devDependencies": { - "@types/express": "^4.11.1", + "@types/axios": "^0.14.0", + "@types/koa": "^2.0.45", + "@types/koa-bodyparser": "^4.2.0", + "@types/koa-router": "^7.0.28", + "axios": "^0.18.0", "babel-core": "^6.26.3", "babel-loader": "^6.4.1", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", @@ -29,14 +32,15 @@ "babel-polyfill": "^6.26.0", "babel-preset-es2015": "^6.24.1", "babel-runtime": "^6.26.0", - "nodemon": "^1.17.3", - "ts-loader": "^3.5.0", - "typescript": "^2.8.3", - "webpack": "^3.5.6", "encoding": "^0.1.12", "event-stream": "^3.3.4", - "express": "^4.16.3", "fast-csv": "^2.4.1", - "node-fetch": "^2.1.2" + "koa": "^2.5.1", + "koa-bodyparser": "^4.2.1", + "koa-router": "^7.4.0", + "nodemon": "^1.17.3", + "ts-loader": "^3.5.0", + "typescript": "^2.8.3", + "webpack": "^3.5.6" } } diff --git a/server/src/index.ts b/server/src/index.ts index 150163f..87a09d5 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,5 +1,6 @@ -import * as express from 'express'; -import * as bodyParser from 'body-parser'; +import * as Koa from 'koa'; +import * as Router from 'koa-router'; + import { router as anomaliesRouter } from './routes/anomalies'; import { router as segmentsRouter } from './routes/segments'; @@ -9,23 +10,28 @@ import { checkDataFolders } from './services/data'; checkDataFolders(); -const app = express(); +var app = new Koa(); const PORT = process.env.HASTIC_PORT || 8000; -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: true })); - -app.use(function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); - res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); - next(); +app.use(async function(ctx) { + ctx.response.header('Access-Control-Allow-Origin', '*'); + ctx.response.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); + ctx.response.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); }); -app.use('/anomalies', anomaliesRouter); -app.use('/segments', segmentsRouter); -app.use('/alerts', alertsRouter); -app.use('/', (req, res) => res.send({ status: 'OK' })); +var anRouter = new Router(); +anRouter.use('/anomalies', anomaliesRouter.routes(), anomaliesRouter.allowedMethods()); + +var seRouter = new Router(); +anRouter.use('/segments', segmentsRouter.routes(), segmentsRouter.allowedMethods()); + +var seRouter = new Router(); +anRouter.use('/alerts', alertsRouter.routes(), alertsRouter.allowedMethods()); + +var rootRoute = new Router(); +rootRoute.get('/', async (ctx) => { + ctx.body = { status: 'OK' }; +}); app.listen(PORT, () => { console.log(`Server is running on :${PORT}`) diff --git a/server/src/routes/alerts.ts b/server/src/routes/alerts.ts index 195b000..56905dc 100644 --- a/server/src/routes/alerts.ts +++ b/server/src/routes/alerts.ts @@ -1,34 +1,29 @@ -import * as express from 'express'; -import {AnomalyId, getAnomalyIdByName, loadAnomalyById} from '../services/anomalyType'; +import { AnomalyId, getAnomalyIdByName, loadAnomalyById } from '../services/anomalyType'; import { getAlertsAnomalies, saveAlertsAnomalies } from '../services/alerts'; -function getAlert(req, res) { - try { - let anomalyId: AnomalyId = req.query.anomaly_id; - let anomaly = loadAnomalyById(anomalyId) - if (anomaly == null) { - anomalyId = getAnomalyIdByName(anomalyId.toLowerCase()); - } +import * as Router from 'koa-router'; - let alertsAnomalies = getAlertsAnomalies(); - let pos = alertsAnomalies.indexOf(anomalyId); - - let enable: boolean = (pos !== -1); - res.status(200).send({ - enable - }); - } catch(e) { - res.status(500).send({ - code: 500, - message: 'Internal error' - }); + +function getAlert(ctx: Router.IRouterContext) { + + let anomalyId: AnomalyId = ctx.request.body.query.anomaly_id; + let anomaly = loadAnomalyById(anomalyId) + if (anomaly == null) { + anomalyId = getAnomalyIdByName(anomalyId.toLowerCase()); } + + let alertsAnomalies = getAlertsAnomalies(); + let pos = alertsAnomalies.indexOf(anomalyId); + + let enable: boolean = (pos !== -1); + ctx.response.body = { enable }; + } -function changeAlert(req, res) { - try { - let anomalyId: AnomalyId = req.body.anomaly_id; - let enable: boolean = req.body.enable; +function changeAlert(ctx: Router.IRouterContext) { + + let anomalyId: AnomalyId = ctx.request.body.anomaly_id; + let enable: boolean = ctx.body.enable; let anomaly = loadAnomalyById(anomalyId) if (anomaly == null) { @@ -44,18 +39,10 @@ function changeAlert(req, res) { alertsAnomalies.splice(pos, 1); saveAlertsAnomalies(alertsAnomalies); } - res.status(200).send({ - status: 'Ok' - }); - } catch(e) { - res.status(500).send({ - code: 500, - message: 'Internal error' - }); - } + ctx.response.body = { status: 'Ok' }; } -export const router = express.Router(); +export const router = new Router(); router.get('/', getAlert); router.post('/', changeAlert); diff --git a/server/src/routes/anomalies.ts b/server/src/routes/anomalies.ts index 2024601..a27465d 100644 --- a/server/src/routes/anomalies.ts +++ b/server/src/routes/anomalies.ts @@ -1,4 +1,4 @@ -import * as express from 'express'; +import * as Router from 'koa-router'; import { Datasource, @@ -130,7 +130,8 @@ function deleteAnomaly(req, res) { } } -export const router = express.Router(); + +export var router = new Router(); router.get('/status', sendAnomalyTypeStatus); router.get('/', getAnomaly); diff --git a/server/src/routes/segments.ts b/server/src/routes/segments.ts index 6ffa755..3b180bb 100644 --- a/server/src/routes/segments.ts +++ b/server/src/routes/segments.ts @@ -1,51 +1,48 @@ -import * as express from 'express'; +import * as Router from 'koa-router'; + import { getLabeledSegments, insertSegments, removeSegments, } from '../services/segments'; -import {runLearning} from '../services/analytics'; -import {Anomaly, AnomalyId, getAnomalyIdByName, loadAnomalyById} from '../services/anomalyType'; +import { + Anomaly, AnomalyId, getAnomalyIdByName, loadAnomalyById +} from '../services/anomalyType'; -async function sendSegments(req, res) { - try { - let anomalyId: AnomalyId = req.query.anomaly_id; - let anomaly:Anomaly = loadAnomalyById(anomalyId); - if(anomaly === null) { - anomalyId = getAnomalyIdByName(anomalyId); - } +import { runLearning } from '../services/analytics'; - let lastSegmentId = req.query.last_segment; - let timeFrom = req.query.from; - let timeTo = req.query.to; - let segments = getLabeledSegments(anomalyId); +async function sendSegments(ctx: Router.IRouterContext) { - // Id filtering - if(lastSegmentId !== undefined) { - segments = segments.filter(el => el.id > lastSegmentId); - } + let anomalyId: AnomalyId = ctx.query.anomaly_id; + let anomaly:Anomaly = loadAnomalyById(anomalyId); + if(anomaly === null) { + anomalyId = getAnomalyIdByName(anomalyId); + } - // Time filtering - if(timeFrom !== undefined) { - segments = segments.filter(el => el.finish > timeFrom); - } + let lastSegmentId = ctx.query.last_segment; + let timeFrom = ctx.query.from; + let timeTo = ctx.query.to; - if(timeTo !== undefined) { - segments = segments.filter(el => el.start < timeTo); - } + let segments = getLabeledSegments(anomalyId); - let payload = JSON.stringify({ - segments - }); - res.status(200).send(payload); - } catch(e) { - res.status(500).send({ - code: 500, - message: 'Internal error' - }); + // Id filtering + if(lastSegmentId !== undefined) { + segments = segments.filter(el => el.id > lastSegmentId); + } + + // Time filtering + if(timeFrom !== undefined) { + segments = segments.filter(el => el.finish > timeFrom); + } + + if(timeTo !== undefined) { + segments = segments.filter(el => el.start < timeTo); } + + ctx.response.body = { segments } + } async function updateSegments(req, res) { @@ -74,7 +71,7 @@ async function updateSegments(req, res) { } } -export const router = express.Router(); +export const router = new Router(); router.get('/', sendSegments); router.patch('/', updateSegments); diff --git a/server/src/services/alerts.ts b/server/src/services/alerts.ts index 0cf09f3..a942e78 100644 --- a/server/src/services/alerts.ts +++ b/server/src/services/alerts.ts @@ -1,22 +1,26 @@ import { getJsonDataSync, writeJsonDataSync } from './json'; -import * as path from 'path'; -import * as fs from 'fs'; import { AnomalyId } from './anomalyType'; -import { ANOMALIES_PATH } from '../config'; import { runPredict } from './analytics'; import { sendNotification } from './notification'; import { getLabeledSegments } from './segments'; -function getAlertsAnomalies() : AnomalyId[] { - let filename = path.join(ANOMALIES_PATH, `alerts_anomalies.json`); - if(!fs.existsSync(filename)) { +import { ANOMALIES_PATH } from '../config'; + +import * as path from 'path'; +import * as fs from 'fs'; + + +const ALERTS_DB_PATH = path.join(ANOMALIES_PATH, `alerts_anomalies.json`); + +function getAlertsAnomalies(): AnomalyId[] { + if(!fs.existsSync(ALERTS_DB_PATH)) { saveAlertsAnomalies([]); } - return getJsonDataSync(path.join(ANOMALIES_PATH, `alerts_anomalies.json`)); + return getJsonDataSync(ALERTS_DB_PATH); } function saveAlertsAnomalies(anomalies: AnomalyId[]) { - return writeJsonDataSync(path.join(ANOMALIES_PATH, `alerts_anomalies.json`), anomalies); + return writeJsonDataSync(ALERTS_DB_PATH, anomalies); } function processAlerts(anomalyId) { diff --git a/server/src/services/notification.ts b/server/src/services/notification.ts index 1975af0..ee48f7e 100644 --- a/server/src/services/notification.ts +++ b/server/src/services/notification.ts @@ -1,7 +1,7 @@ -import fetch from 'node-fetch'; +import axios from 'axios'; import { loadAnomalyById } from './anomalyType'; -function sendNotification(anomalyId, active) { +export async function sendNotification(anomalyId, active) { let anomalyName = loadAnomalyById(anomalyId).name; console.log('Notification ' + anomalyName); @@ -16,16 +16,20 @@ function sendNotification(anomalyId, active) { } let endpoint = process.env.HASTIC_ALERT_ENDPOINT; - if(endpoint !== undefined) { - fetch(endpoint, { + if(endpoint === undefined) { + console.error(`Can't send alert, env HASTIC_ALERT_ENDPOINT is undefined`); + return; + } + + try { + var data = await axios.post(endpoint, { method: 'POST', body: JSON.stringify(notification) }) - .then(data => console.log(data)) - .catch(err => console.error(`Can't send alert to ${endpoint}. Error: ${err}`)); - } else { - console.error(`Can't send alert, env HASTIC_ALERT_ENDPOINT is undefined`); + console.log(data); + } catch(err) { + console.error(`Can't send alert to ${endpoint}. Error: ${err}`) } + } -export { sendNotification }