fix favicon, basic auth (#259)

This commit is contained in:
Kendall Garner 2023-09-25 00:02:25 +00:00 committed by GitHub
parent d7f21b3c6b
commit 02b06a07be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 90 deletions

View file

@ -96,6 +96,7 @@ const configuration: webpack.Configuration = {
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: path.join('index.html'), filename: path.join('index.html'),
template: path.join(webpackPaths.srcRemotePath, 'index.ejs'), template: path.join(webpackPaths.srcRemotePath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: { minify: {
collapseWhitespace: true, collapseWhitespace: true,
removeAttributeQuotes: true, removeAttributeQuotes: true,

View file

@ -117,6 +117,7 @@ const configuration: webpack.Configuration = {
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: 'index.html', filename: 'index.html',
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
favicon: path.join(webpackPaths.assetsPath, 'icons', 'favicon.ico'),
minify: { minify: {
collapseWhitespace: true, collapseWhitespace: true,
removeAttributeQuotes: true, removeAttributeQuotes: true,

View file

@ -52,7 +52,9 @@ type SendData = ServerEvent & {
function send({ client, event, data }: SendData): void { function send({ client, event, data }: SendData): void {
if (client.readyState === WebSocket.OPEN) { 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, res: ServerResponse,
): Promise<void> { ): Promise<void> {
const fileName = `${file}.${extension}`; const fileName = `${file}.${extension}`;
let path: string; const path = app.isPackaged
? join(__dirname, '../remote', fileName)
if (extension === 'ico') { : join(__dirname, '../../../../../.erb/dll', fileName);
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);
}
let stats: Stats; let stats: Stats;
@ -291,7 +285,7 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
break; break;
} }
case '/favicon.ico': { case '/favicon.ico': {
await serveFile(req, 'icon', 'ico', res); await serveFile(req, 'favicon', 'ico', res);
break; break;
} }
case '/remote.css': { case '/remote.css': {
@ -302,6 +296,12 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
await serveFile(req, 'remote', 'js', res); await serveFile(req, 'remote', 'js', res);
break; break;
} }
case '/credentials': {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(req.headers.authorization);
break;
}
default: { default: {
res.statusCode = 404; res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Type', 'text/plain');
@ -318,17 +318,42 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
server.listen(config.port, resolve); server.listen(config.port, resolve);
wsServer = new WebSocketServer({ server }); wsServer = new WebSocketServer({ server });
wsServer.on('connection', (ws, req) => { wsServer.on('connection', (ws) => {
if (!authorize(req)) { let authFail: number | undefined;
ws.close(4003);
return;
}
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('error', console.error);
ws.on('message', (data) => { 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 { try {
const json = JSON.parse(data.toString()) as ClientEvent; const json = JSON.parse(data.toString()) as ClientEvent;
const event = json.event; const event = json.event;

View file

@ -26,7 +26,6 @@ export const Shell = () => {
<Grid.Col span="auto"> <Grid.Col span="auto">
<div> <div>
<Image <Image
bg="rgb(25, 25, 25)"
fit="contain" fit="contain"
height={60} height={60}
src="/favicon.ico" src="/favicon.ico"

View file

@ -99,80 +99,88 @@ export const useRemoteStore = create<SettingsSlice>()(
existing.close(4001); existing.close(4001);
} }
} }
set((state) => { set(async (state) => {
const socket = new WebSocket( try {
// eslint-disable-next-line no-restricted-globals const credentials = await fetch('/credentials');
location.href.replace('http', 'ws'), const authHeader = await credentials.text();
) as StatefulWebSocket;
socket.natural = false; const socket = new WebSocket(
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) {
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
location.reload(); location.href.replace('http', 'ws'),
} else if (reason.code === 4000) { ) as StatefulWebSocket;
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) { socket.natural = false;
set({ connected: false, info: {} });
}
});
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) => { send: (data: ClientEvent) => {