-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Obsługa sesji użytkownika i refactoring #10
base: master
Are you sure you want to change the base?
Changes from all commits
849aa13
78a06df
76afc98
03e42a2
b83a939
123425e
f2f5e34
2769750
72c1c3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
{ | ||
"presets": ["env"] | ||
} | ||
"presets": [ | ||
["env", { | ||
"targets": { | ||
"node": "11.9" | ||
} | ||
}] | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": ["node"] | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,164 +1,6 @@ | ||
import http from 'http'; | ||
import https from 'https'; | ||
import express from 'express'; | ||
import bodyParser from 'body-parser'; | ||
import WebSocket from 'ws'; | ||
import config, { sslConfig } from './config'; | ||
import mailer from './mailer'; | ||
import HttpServer from './server/http'; | ||
import WebSocketServer from './server/webSocket'; | ||
|
||
const app = express(); | ||
app.use(bodyParser.json()); | ||
const server = createServer(); | ||
server.listen(config.port.http, () => | ||
console.log(`Server is listening on port ${config.port.http}, over ${config.protocol.toUpperCase()} protocol.`) | ||
); | ||
|
||
const HTTP_ERROR_CODES = Object.freeze({ | ||
FORBIDDEN: 403, | ||
METHOD_NOT_ALLOWED: 405, | ||
WS_CLOSE_ERROR_CODE: 4000, | ||
}); | ||
const GROUP_REGEXPS = Object.freeze({ | ||
main: /^$|\/$/, | ||
activity: /^(\/?)activity/, | ||
}); | ||
const GROUP_NAMES = Object.freeze({ | ||
MAIN: 'main', | ||
ACTIVITY: 'activity', | ||
}); | ||
const ACTIONS = Object.freeze({ | ||
ADD_POST: 'add-post', | ||
}); | ||
|
||
const wss = new WebSocket.Server({ server }); | ||
wss.on('error', onWSSError); | ||
wss.on('connection', onWSSConnection); | ||
wss.on('close', onWSSClose); | ||
|
||
// check request | ||
app.all('*', onAll); | ||
|
||
// send new list html to users | ||
app.post('/', onPost); | ||
|
||
function createServer() { | ||
if (config.protocol === 'https') { | ||
return https.createServer( | ||
{ | ||
key: sslConfig.key, | ||
cert: sslConfig.cert, | ||
}, | ||
app | ||
); | ||
} | ||
|
||
return http.createServer(app); | ||
} | ||
|
||
let mailSent = false; | ||
|
||
function onWSSError(error) { | ||
console.error('WebSocket server error: ', error); | ||
|
||
if (!mailSent) { | ||
mailer.sendMail(`<p>Wystąpił błąd na serwerze WebSocket!<br><output>${JSON.stringify(error)}</output></p>`); | ||
mailSent = true; | ||
} | ||
} | ||
|
||
function onWSSConnection(ws) { | ||
ws.on('error', onWSError); | ||
ws.on('message', onWSMessage); | ||
} | ||
|
||
function onWSSClose(event) { | ||
console.log('WebSocket server closed: ', event); | ||
} | ||
|
||
function onWSMessage(event) { | ||
const parsedEvent = parseWSMessage(this, event); | ||
|
||
if (!parsedEvent) { | ||
return; | ||
} | ||
|
||
assignWSClientToGroup(this, parsedEvent.pathname); | ||
} | ||
|
||
function onWSError(event) { | ||
console.error('ws error event: ', event); | ||
|
||
if (!event.errno) { | ||
throw event; | ||
} | ||
} | ||
|
||
function onAll(req, res, next) { | ||
// check authorization | ||
if (req.headers.token !== config.token) { | ||
res.sendStatus(HTTP_ERROR_CODES.FORBIDDEN); | ||
return; | ||
} | ||
|
||
if (req.method !== 'POST') { | ||
res.sendStatus(HTTP_ERROR_CODES.METHOD_NOT_ALLOWED); | ||
return; | ||
} | ||
|
||
next(); | ||
} | ||
|
||
function parseWSMessage(ws, event) { | ||
let parsedEvent = {}; | ||
|
||
try { | ||
if (!event) { | ||
throw Error('ws empty message event'); | ||
} | ||
|
||
parsedEvent = JSON.parse(event); | ||
} catch (exception) { | ||
console.error('ws invalid JSON message: ', event, ' threw exception: ', exception); | ||
ws.close(HTTP_ERROR_CODES.WS_CLOSE_ERROR_CODE, 'Message is not valid JSON!'); | ||
|
||
return null; | ||
} | ||
|
||
if (!parsedEvent.pathname) { | ||
console.error('ws empty pathname: ', parsedEvent.pathname); | ||
ws.close(HTTP_ERROR_CODES.WS_CLOSE_ERROR_CODE, 'Empty pathname!'); | ||
|
||
return null; | ||
} | ||
|
||
return parsedEvent; | ||
} | ||
|
||
function assignWSClientToGroup(ws, pathname) { | ||
const pathName = pathname.replace(/\//g, ''); | ||
const groupName = Object.keys(GROUP_REGEXPS).find((groupName) => GROUP_REGEXPS[groupName].test(pathName)); | ||
|
||
if (!groupName) { | ||
console.error('ws unexpected groupName: ', groupName, ' from pathName: ', pathName); | ||
ws.close(HTTP_ERROR_CODES.WS_CLOSE_ERROR_CODE, 'Unexpected pathname!'); | ||
} | ||
|
||
ws._GROUP_NAME = groupName; | ||
} | ||
|
||
function onPost(req, res) { | ||
res.sendStatus(200); | ||
|
||
const { action } = req.body; | ||
const groupNames = [GROUP_NAMES.ACTIVITY]; | ||
|
||
if (action === ACTIONS.ADD_POST) { | ||
groupNames.push(GROUP_NAMES.MAIN); | ||
} | ||
|
||
for (const wsClient of wss.clients) { | ||
if (groupNames.includes(wsClient._GROUP_NAME)) { | ||
wsClient.send(JSON.stringify({ action })); | ||
} | ||
} | ||
} | ||
const httpServer = new HttpServer(); | ||
// eslint-disable-next-line no-new | ||
new WebSocketServer(httpServer); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import http from 'http'; | ||
import https from 'https'; | ||
import express from 'express'; | ||
import bodyParser from 'body-parser'; | ||
import config, { sslConfig } from '../config'; | ||
import { HTTP_STATUS_CODES } from '../vars'; | ||
|
||
class HttpServerCore { | ||
constructor() { | ||
this.server = null; | ||
this.app = null; | ||
this.socketClientsNotifier = () => console.error('Method not attached!'); | ||
} | ||
|
||
initConnectionWithQ2A(middlewareList) { | ||
this.app = express(); | ||
this.app.use(bodyParser.json()); | ||
|
||
if (!Array.isArray(middlewareList) || middlewareList.length === 0) { | ||
throw TypeError('middlewareList argument must be non empty array!'); | ||
} | ||
|
||
middlewareList.forEach(({ method, path, listener }) => { | ||
if (!method || !path || !listener) { | ||
throw ReferenceError(` | ||
middleware must contain: method, path and listener params! | ||
Received method: "${method}", path: "${path}", listener: "${listener}" | ||
`); | ||
} | ||
|
||
this.app[method](path, listener); | ||
}); | ||
} | ||
|
||
initServerForWebSocket() { | ||
this.server = this.createHttpServerWithOptionalCert(); | ||
this.server.listen(config.port.http, () => | ||
console.log(`Server is listening on port ${config.port.http}, over ${config.protocol.toUpperCase()} protocol.`) | ||
); | ||
} | ||
|
||
createHttpServerWithOptionalCert() { | ||
if (config.protocol === 'https') { | ||
return https.createServer( | ||
{ | ||
key: sslConfig.key, | ||
cert: sslConfig.cert, | ||
}, | ||
this.app | ||
); | ||
} | ||
|
||
return http.createServer(this.app); | ||
} | ||
|
||
attachSocketClientsNotifier(fn) { | ||
this.socketClientsNotifier = fn; | ||
} | ||
} | ||
|
||
class HttpServer extends HttpServerCore { | ||
constructor() { | ||
super(); | ||
this.initConnectionWithQ2A([ | ||
{ | ||
method: 'all', | ||
path: '*', | ||
listener: HttpServer.onAll, | ||
}, | ||
Comment on lines
+65
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handler dla nieobsługiwanych route'ów powinien być zarejestrowany jako ostatni żeby uniknąć konfliktów przy routingu. |
||
{ | ||
method: 'post', | ||
path: '/', | ||
listener: this.onPost.bind(this), | ||
}, | ||
]); | ||
Comment on lines
+74
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Teoretycznie brakuje handlera dla 404 oraz handlera błędów. |
||
this.initServerForWebSocket(); | ||
} | ||
|
||
static onAll(req, res, next) { | ||
if (req.headers.token !== config.token) { | ||
res.sendStatus(HTTP_STATUS_CODES.FORBIDDEN); | ||
return; | ||
} | ||
|
||
if (req.method !== 'POST') { | ||
res.sendStatus(HTTP_STATUS_CODES.METHOD_NOT_ALLOWED); | ||
return; | ||
} | ||
|
||
next(); | ||
} | ||
Comment on lines
+79
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
onPost(req, res) { | ||
console.log('(onPost) rq.body:', req.body); | ||
|
||
res.sendStatus(HTTP_STATUS_CODES.OK); | ||
this.socketClientsNotifier(req.body.action); | ||
} | ||
} | ||
|
||
export default HttpServer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moim zdaniem to jest przekombinowane, w expressie kolejność middleware/handler-ów ma kluczowe znaczenie. Taki sposób tworzenia serwera wprowadza większe zamieszanie niż z tego jest pożytek.