From f2294def05af3f5586b26770ee8c36177de5024e Mon Sep 17 00:00:00 2001 From: ntr Date: Sun, 4 Sep 2022 20:36:42 +1000 Subject: [PATCH] this is podracing --- api/index.ts | 18 +-- api/package-lock.json | 11 ++ api/src/api.test.ts | 5 +- api/src/api.ts | 60 ++++---- client/.eslintrc.js | 31 ++++ client/package.json | 10 -- client/src/{actions.tsx => actions.ts} | 5 +- client/src/components/header.tsx | 1 + client/src/store.tsx | 3 - client/src/ws.ts | 202 ++++++++++++------------- docker-compose.yaml | 3 - nginx/localhost.nginx.conf | 7 +- 12 files changed, 187 insertions(+), 169 deletions(-) create mode 100644 client/.eslintrc.js rename client/src/{actions.tsx => actions.ts} (75%) diff --git a/api/index.ts b/api/index.ts index fd7e954..4d12a0c 100644 --- a/api/index.ts +++ b/api/index.ts @@ -5,15 +5,15 @@ import { Events } from './src/events'; async function start() { const log = createLog(); - process.on('uncaughtException', (err, origin) => { - log.error({ err, origin }, 'uncaughtException'); - process.exit(1); - }); + // process.on('uncaughtException', (err, origin) => { + // log.error({ err, origin }, 'uncaughtException'); + // process.exit(1); + // }); - process.on('unhandledRejection', (reason, promise) => { - log.error({ reason, promise }, 'unhandledRejection'); - process.exit(1); - }); + // process.on('unhandledRejection', (reason, promise) => { + // log.error({ reason, promise }, 'unhandledRejection'); + // process.exit(1); + // }); process.on('SIGTERM', () => { log.debug('SIGTERM received, exiting'); @@ -31,7 +31,7 @@ async function start() { // events.emit(Events.StartupSync); // events.emit(Events.ListingsUpdated); - await api.listen(); + api.listen(); } start(); diff --git a/api/package-lock.json b/api/package-lock.json index c58903c..9e6fd95 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -16,6 +16,7 @@ "bluebird": "^3.7.2", "bunyan": "^1.8.15", "cheerio": "^1.0.0-rc.12", + "json-rpc-2.0": "^1.4.1", "koa": "^2.13.4", "koa-body": "^5.0.0", "koa-passport": "^5.0.0", @@ -1872,6 +1873,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-rpc-2.0": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.4.1.tgz", + "integrity": "sha512-OX1NJhpIfuK4GjDnJ/gKtZy1HOYo0l4eL0a4rb0rNeQheX1xlyQ9+JMmPzs/sFNthpS/TXKPWlGo09X7B5l81A==" + }, "node_modules/keygrip": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", @@ -5094,6 +5100,11 @@ "argparse": "^2.0.1" } }, + "json-rpc-2.0": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.4.1.tgz", + "integrity": "sha512-OX1NJhpIfuK4GjDnJ/gKtZy1HOYo0l4eL0a4rb0rNeQheX1xlyQ9+JMmPzs/sFNthpS/TXKPWlGo09X7B5l81A==" + }, "keygrip": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", diff --git a/api/src/api.test.ts b/api/src/api.test.ts index 65f10cf..fe88d4d 100644 --- a/api/src/api.test.ts +++ b/api/src/api.test.ts @@ -13,12 +13,13 @@ const api = axios.create({ withCredentials: true }); +let server; before(function setup() { - services.api.listen(); + server = services.api.listen(); }); after(function teardown(done) { - services.api.server.close(done) + server.close(done) }); describe('api', function apiTests() { diff --git a/api/src/api.ts b/api/src/api.ts index 9341d30..b7c4e48 100644 --- a/api/src/api.ts +++ b/api/src/api.ts @@ -22,10 +22,7 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) { const WS_PORT = 40055; const SECRET = 'ohja4Eif9eidah3aj5ta2haa4iemooth'; - const app = new koa(); - const server = http.createServer(app.callback()); - const wsApp = koaWebsocket(new koa()); - const wsServer = http.createServer(wsApp.callback()); + const app = koaWebsocket(new koa()); app.use(async (ctx, next) => { try { @@ -42,18 +39,8 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) { const router = koaRouter(); function listen() { - return new Promise((resolve, reject) => { - app.on('error', err => { - reject(err); - }) - - server.listen(PORT, () => { - log.info('listening', { PORT }); - }); - - wsApp.listen(WS_PORT); - log.info('ws listening', { WS_PORT }); - }); + const server = app.listen(PORT); + log.info({ PORT }, 'listening'); } app.use(cors({ credentials: true })); @@ -160,27 +147,34 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) { ctx.body = { success: true }; }); - const wsRouter = koaRouter(); - // Using routes - wsRouter.get('/ws', async (ctx, next) => { - // `ctx` is the regular koa context created from the `ws` onConnection `socket.upgradeReq` object. - // the websocket is added to the context on `ctx.websocket`. - ctx.websocket.send('Hello World'); - ctx.websocket.on('message', (message) => { - // do something with the message from client - if (message.toString() === 'ping') { - ctx.websocket.send('pong'); - } - }); - return next; - }); - app.use(router.routes()); - wsApp.ws.use(wsRouter.routes()); + + app.ws.use(passport.initialize()); + app.ws.use(passport.session()); + app.ws.use(async (ctx, next) => { + log.info('new ws connection', { user: ctx.state.user }); + ctx.websocket.on('message', async function incoming(message) { + log.info(message.toString()); + try { + const parsed = JSON.parse(message.toString()); + log.info(parsed); + // const response = await rpc.receive(parsed); + // rpc.send(ws, response); + } catch (e) { + log.warn(e); + log.warn(message.toString()); + // rpc.send(ws, [RpcMessageType.Error, e.message || e.msg]) + } + return true; + }); + + ctx.websocket.on('close', function onClose() { + log.info('ws disconnected', { user: ctx.state.user }); + }); + }); return { app, - server, listen, }; } diff --git a/client/.eslintrc.js b/client/.eslintrc.js new file mode 100644 index 0000000..e5690ff --- /dev/null +++ b/client/.eslintrc.js @@ -0,0 +1,31 @@ +module.exports = { + parser: "@typescript-eslint/parser", + extends: [ + "preact", + "plugin:@typescript-eslint/recommended" + ], + ignorePatterns: [ + "build/" + ], + rules: { + // prevents stupid complaints a la + // (req) { + // req.something = x; + // } + 'no-param-reassign': [2, { props: false }], + 'no-multi-spaces': [0], + 'max-len': ['error', 120], + 'import/no-extraneous-dependencies': [0], + 'prefer-arrow-callback': [0], + 'arrow-body-style': [0], + 'no-return-assign': 0, + 'no-console': [0], + // i like loops + 'no-plusplus': [0], + + 'no-await-in-loop': [0], + indent: ['error', 4], + 'keyword-spacing': ['error'], + 'key-spacing': ['error'], + }, +}; \ No newline at end of file diff --git a/client/package.json b/client/package.json index 97236b9..6a68949 100644 --- a/client/package.json +++ b/client/package.json @@ -8,16 +8,6 @@ "start": "preact --no-clear watch", "lint": "eslint src" }, - "eslintConfig": { - "parser": "@typescript-eslint/parser", - "extends": [ - "preact", - "plugin:@typescript-eslint/recommended" - ], - "ignorePatterns": [ - "build/" - ] - }, "dependencies": { "lodash": "^4.17.21", "preact": "^10.10.0", diff --git a/client/src/actions.tsx b/client/src/actions.ts similarity index 75% rename from client/src/actions.tsx rename to client/src/actions.ts index 0b52797..01e154b 100644 --- a/client/src/actions.tsx +++ b/client/src/actions.ts @@ -1,3 +1,6 @@ +import { route } from 'preact-router'; +import http from './http'; + const actions = store => ({ setUser: function setUser(state, user) { return { user }; @@ -7,7 +10,7 @@ const actions = store => ({ return { offers }; }, - logout: async function logout(store, e) { + logout: async function logout() { await http.postLogout(); route('/login'); return { user: null }; diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx index 668680f..e280a39 100644 --- a/client/src/components/header.tsx +++ b/client/src/components/header.tsx @@ -51,4 +51,5 @@ const Header = ({ user, logout }) => { ); } +// @ts-ignore unistore types export default connect(['user'], actions)(Header); diff --git a/client/src/store.tsx b/client/src/store.tsx index 51546d6..b6276cb 100644 --- a/client/src/store.tsx +++ b/client/src/store.tsx @@ -1,8 +1,5 @@ -import { route } from 'preact-router'; import unistore from 'unistore' -import http from './http'; - export default function createStore() { return unistore({ bundles: [], diff --git a/client/src/ws.ts b/client/src/ws.ts index 98dc8ba..7afdb5c 100644 --- a/client/src/ws.ts +++ b/client/src/ws.ts @@ -1,117 +1,113 @@ -// const SOCKET_URL = (window.location.host.includes('localhost') || window.location.host === '' || window.location.host.includes('192.168.0')) -// ? `ws://${window.location.hostname}:40080` -// : `${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`; - const SOCKET_URL = `${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`; export default function createSocket(store) { - let ws; + let ws; - // ------------- - // Outgoing - // ------------- - function send(msg) { - if (msg[0] !== 'Ping') console.log('outgoing msg', msg); - if (ws.readyState !== 1) return false; - try { - ws.send(JSON.stringify(msg)); - } catch (e) { - console.warn(e); - } - } - - // let ping; - function sendPing() { - // ping = Date.now(); - send(['Ping', {}]); - - // setTimeout(sendPing, 50); - } - - function sendSourcesAvailable() { - send(['SourcesAvailable', {}]); - } - - let pongTimeout; - function onPong() { - pongTimeout = setTimeout(sendPing, 1000); - } - - // ------------- - // Incoming - // ------------- - function onSourcesAvailable(clouds) { - // store.dispatch(http.LIBRARY_RECEIVE_POINTCLOUDS, clouds); - } - - // ------------- - // Setup - // ------------- - // when the server sends a reply it will have one of these message types - // this object wraps the reply types to a function - const handlers = { - SourcesAvailable: onSourcesAvailable, - - Pong: onPong, - Error: errHandler, - }; - - function errHandler(error) { - // store.dispatch(http.LIBRARY_EVENT, { level: 'error', log: error }); - console.error(error); - } - - function onMessage(event) { - const res = event.data; - const [msgType, params] = JSON.parse(res); - if (msgType !== 'Pong') { - console.log(msgType, params); + // ------------- + // Outgoing + // ------------- + function send(msg) { + if (msg[0] !== 'Ping') console.log('outgoing msg', msg); + if (ws.readyState !== 1) return false; + try { + ws.send(JSON.stringify(msg)); + } catch (e) { + console.warn(e); + } } - // check for error and split into response type and data - if (!handlers[msgType]) return console.error(`${msgType} handler missing`); - return handlers[msgType](params); - } + // let ping; + function sendPing() { + // ping = Date.now(); + send(['Ping', {}]); - // Connection opened - function onOpen() { - sendPing(); - } - - function onError(event) { - console.error('WebSocket error', event); - } - - function onClose(event) { - console.error('WebSocket closed', event); - return setTimeout(connect, 2000); - } - - function connect() { - if (ws) { - clearTimeout(pongTimeout); - ws.removeEventListener('open', onOpen); - ws.removeEventListener('message', onMessage); - ws.removeEventListener('error', onError); - ws.removeEventListener('close', onClose); - ws = null; + // setTimeout(sendPing, 50); } - console.log('connecting to websocket', SOCKET_URL); - ws = new WebSocket(SOCKET_URL); + function sendSourcesAvailable() { + send(['SourcesAvailable', {}]); + } - // Listen for messages - ws.addEventListener('open', onOpen); - ws.addEventListener('message', onMessage); - ws.addEventListener('error', onError); - ws.addEventListener('close', onClose); + let pongTimeout; + function onPong() { + pongTimeout = setTimeout(sendPing, 1000); + } - return ws; - } + // ------------- + // Incoming + // ------------- + function onSourcesAvailable(clouds) { + // store.dispatch(http.LIBRARY_RECEIVE_POINTCLOUDS, clouds); + } - return { - connect, + // ------------- + // Setup + // ------------- + // when the server sends a reply it will have one of these message types + // this object wraps the reply types to a function + const handlers = { + SourcesAvailable: onSourcesAvailable, - sendSourcesAvailable, - }; + Pong: onPong, + Error: errHandler, + }; + + function errHandler(error) { + // store.dispatch(http.LIBRARY_EVENT, { level: 'error', log: error }); + console.error(error); + } + + function onMessage(event) { + const res = event.data; + const [msgType, params] = JSON.parse(res); + if (msgType !== 'Pong') { + console.log(msgType, params); + } + + // check for error and split into response type and data + if (!handlers[msgType]) return console.error(`${msgType} handler missing`); + return handlers[msgType](params); + } + + // Connection opened + function onOpen() { + sendPing(); + } + + function onError(event) { + console.error('WebSocket error', event); + } + + function onClose(event) { + console.error('WebSocket closed', event); + return setTimeout(connect, 2000); + } + + function connect() { + if (ws) { + clearTimeout(pongTimeout); + ws.removeEventListener('open', onOpen); + ws.removeEventListener('message', onMessage); + ws.removeEventListener('error', onError); + ws.removeEventListener('close', onClose); + ws = null; + } + + console.log('connecting to websocket', SOCKET_URL); + ws = new WebSocket(SOCKET_URL); + + // Listen for messages + ws.addEventListener('open', onOpen); + ws.addEventListener('message', onMessage); + ws.addEventListener('error', onError); + ws.addEventListener('close', onClose); + + return ws; + } + + return { + connect, + + sendSourcesAvailable, + }; } diff --git a/docker-compose.yaml b/docker-compose.yaml index 06b2c7f..b64b4f7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -22,9 +22,6 @@ services: DATABASE_URL: 'postgresql://mcrates:digdeep@pg:5432/mcrates?schema=public' volumes: - "./api:/opt/mcrates/api" - ports: - - '41337:41337' - - '40055:40055' client: image: node:16 diff --git a/nginx/localhost.nginx.conf b/nginx/localhost.nginx.conf index 4fe6c1e..57432a5 100644 --- a/nginx/localhost.nginx.conf +++ b/nginx/localhost.nginx.conf @@ -2,10 +2,6 @@ upstream mcrates_api { server api:41337; } -upstream mcrates_ws { - server api:40055; -} - upstream mcrates_client { server client:8080; } @@ -22,10 +18,11 @@ server { } location /api/ws { - proxy_pass http://mcrates_ws; + proxy_pass http://mcrates_api; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; proxy_read_timeout 600s; }