From 02b06a07be0ec69cebcccd71ce5c98995fe1e710 Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Mon, 25 Sep 2023 00:02:25 +0000 Subject: [PATCH] fix favicon, basic auth (#259) --- .erb/configs/webpack.config.remote.dev.ts | 1 + .erb/configs/webpack.config.remote.prod.ts | 1 + src/main/features/core/remote/index.ts | 63 ++++++--- src/remote/components/shell.tsx | 1 - src/remote/store/index.ts | 148 +++++++++++---------- 5 files changed, 124 insertions(+), 90 deletions(-) diff --git a/.erb/configs/webpack.config.remote.dev.ts b/.erb/configs/webpack.config.remote.dev.ts index a4f30baf..64332c6e 100644 --- a/.erb/configs/webpack.config.remote.dev.ts +++ b/.erb/configs/webpack.config.remote.dev.ts @@ -96,6 +96,7 @@ const configuration: webpack.Configuration = { new HtmlWebpackPlugin({ filename: path.join('index.html'), template: path.join(webpackPaths.srcRemotePath, 'index.ejs'), + favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'), minify: { collapseWhitespace: true, removeAttributeQuotes: true, diff --git a/.erb/configs/webpack.config.remote.prod.ts b/.erb/configs/webpack.config.remote.prod.ts index d86406d6..1dcc61ad 100644 --- a/.erb/configs/webpack.config.remote.prod.ts +++ b/.erb/configs/webpack.config.remote.prod.ts @@ -117,6 +117,7 @@ const configuration: webpack.Configuration = { new HtmlWebpackPlugin({ filename: 'index.html', template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), + favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'), minify: { collapseWhitespace: true, removeAttributeQuotes: true, diff --git a/src/main/features/core/remote/index.ts b/src/main/features/core/remote/index.ts index 98e217ff..4a43e81e 100644 --- a/src/main/features/core/remote/index.ts +++ b/src/main/features/core/remote/index.ts @@ -52,7 +52,9 @@ type SendData = ServerEvent & { function send({ client, event, data }: SendData): void { if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify({ data, event })); + if (client.alive) { + client.send(JSON.stringify({ data, event })); + } } } @@ -141,17 +143,9 @@ async function serveFile( res: ServerResponse, ): Promise { const fileName = `${file}.${extension}`; - let path: string; - - if (extension === 'ico') { - path = app.isPackaged - ? join(process.resourcesPath, 'assets', fileName) - : join(__dirname, '../../../../../assets', fileName); - } else { - path = app.isPackaged - ? join(__dirname, '../remote', fileName) - : join(__dirname, '../../../../../.erb/dll', fileName); - } + const path = app.isPackaged + ? join(__dirname, '../remote', fileName) + : join(__dirname, '../../../../../.erb/dll', fileName); let stats: Stats; @@ -291,7 +285,7 @@ const enableServer = (config: RemoteConfig): Promise => { break; } case '/favicon.ico': { - await serveFile(req, 'icon', 'ico', res); + await serveFile(req, 'favicon', 'ico', res); break; } case '/remote.css': { @@ -302,6 +296,12 @@ const enableServer = (config: RemoteConfig): Promise => { await serveFile(req, 'remote', 'js', res); break; } + case '/credentials': { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end(req.headers.authorization); + break; + } default: { res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); @@ -318,17 +318,42 @@ const enableServer = (config: RemoteConfig): Promise => { server.listen(config.port, resolve); wsServer = new WebSocketServer({ server }); - wsServer.on('connection', (ws, req) => { - if (!authorize(req)) { - ws.close(4003); - return; - } + wsServer.on('connection', (ws) => { + let authFail: number | undefined; - ws.alive = true; + if (!settings.username && !settings.password) { + ws.alive = true; + } else { + authFail = setTimeout(() => { + if (!ws.alive) { + ws.close(); + } + }, 10000) as unknown as number; + } ws.on('error', console.error); ws.on('message', (data) => { + if (!ws.alive) { + try { + const auth = data.toString().split(' ')[1]; + const [login, password] = Buffer.from(auth, 'base64') + .toString() + .split(':'); + + if (login === settings.username && password === settings.password) { + ws.alive = true; + } else { + ws.close(); + } + + clearTimeout(authFail); + } catch (e) { + console.error(e); + } + return; + } + try { const json = JSON.parse(data.toString()) as ClientEvent; const event = json.event; diff --git a/src/remote/components/shell.tsx b/src/remote/components/shell.tsx index ce188012..1244f6c9 100644 --- a/src/remote/components/shell.tsx +++ b/src/remote/components/shell.tsx @@ -26,7 +26,6 @@ export const Shell = () => {
()( existing.close(4001); } } - set((state) => { - const socket = new WebSocket( - // eslint-disable-next-line no-restricted-globals - location.href.replace('http', 'ws'), - ) as StatefulWebSocket; + set(async (state) => { + try { + const credentials = await fetch('/credentials'); + const authHeader = await credentials.text(); - socket.natural = false; - - socket.addEventListener('message', (message) => { - const { event, data } = JSON.parse(message.data) as ServerEvent; - - switch (event) { - case 'error': { - toast.error({ message: data, title: 'Socket error' }); - break; - } - case 'favorite': { - set((state) => { - if (state.info.song?.id === data.id) { - state.info.song.userFavorite = data.favorite; - } - }); - break; - } - case 'proxy': { - set((state) => { - if (state.info.song) { - state.info.song.imageUrl = `data:image/jpeg;base64,${data}`; - } - }); - break; - } - case 'rating': { - set((state) => { - if (state.info.song?.id === data.id) { - state.info.song.userRating = data.rating; - } - }); - break; - } - case 'song': { - set((nested) => { - nested.info = { ...nested.info, ...data }; - }); - } - } - }); - - socket.addEventListener('open', () => { - set({ connected: true }); - }); - - socket.addEventListener('close', (reason) => { - if (reason.code === 4002 || reason.code === 4003) { + const socket = new WebSocket( // eslint-disable-next-line no-restricted-globals - location.reload(); - } else if (reason.code === 4000) { - toast.warn({ - message: 'Feishin remote server is down', - title: 'Connection closed', - }); - } else if (reason.code !== 4001 && !socket.natural) { - toast.error({ - message: 'Socket closed for unexpected reason', - title: 'Connection closed', - }); - } + location.href.replace('http', 'ws'), + ) as StatefulWebSocket; - if (!socket.natural) { - set({ connected: false, info: {} }); - } - }); + socket.natural = false; - state.socket = socket; + socket.addEventListener('message', (message) => { + const { event, data } = JSON.parse(message.data) as ServerEvent; + + switch (event) { + case 'error': { + toast.error({ message: data, title: 'Socket error' }); + break; + } + case 'favorite': { + set((state) => { + if (state.info.song?.id === data.id) { + state.info.song.userFavorite = data.favorite; + } + }); + break; + } + case 'proxy': { + set((state) => { + if (state.info.song) { + state.info.song.imageUrl = `data:image/jpeg;base64,${data}`; + } + }); + break; + } + case 'rating': { + set((state) => { + if (state.info.song?.id === data.id) { + state.info.song.userRating = data.rating; + } + }); + break; + } + case 'song': { + set((nested) => { + nested.info = { ...nested.info, ...data }; + }); + } + } + }); + + socket.addEventListener('open', () => { + socket.send(authHeader); + set({ connected: true }); + }); + + socket.addEventListener('close', (reason) => { + if (reason.code === 4002 || reason.code === 4003) { + // eslint-disable-next-line no-restricted-globals + location.reload(); + } else if (reason.code === 4000) { + toast.warn({ + message: 'Feishin remote server is down', + title: 'Connection closed', + }); + } else if (reason.code !== 4001 && !socket.natural) { + toast.error({ + message: 'Socket closed for unexpected reason', + title: 'Connection closed', + }); + } + + if (!socket.natural) { + set({ connected: false, info: {} }); + } + }); + + state.socket = socket; + } catch (err) { + console.error(err); + } }); }, send: (data: ClientEvent) => {