fix favicon, basic auth (#259)
This commit is contained in:
parent
d7f21b3c6b
commit
02b06a07be
5 changed files with 124 additions and 90 deletions
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
Reference in a new issue