Skip to content

Commit

Permalink
feat: Added advanced server selection system
Browse files Browse the repository at this point in the history
  • Loading branch information
Ipmake committed Jan 15, 2025
1 parent 2f8d111 commit 0a203bd
Show file tree
Hide file tree
Showing 7 changed files with 477 additions and 29 deletions.
95 changes: 86 additions & 9 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,89 @@
import axios, { AxiosRequestConfig } from 'axios';
import express from 'express';
import https from 'https';
import { PerPlexed } from './types';
import { randomBytes } from 'crypto';

/*
* ENVIRONMENT VARIABLES
*
* PLEX_SERVER: The URL of the Plex server to proxy requests to
* DISABLE_PROXY: If set to true, the proxy will be disabled and all requests go directly to the Plex server from the frontend (NOT RECOMMENDED)
* DISABLE_TLS_VERIFY: If set to true, the proxy will not check any https ssl certificates
* PLEX_SERVERS: A comma separated list of Plex servers the frontend can connect to
* PROXY_PLEX_SERVER: The URL of the Plex server to proxy requests to
* FRONTEND_SERVER_CHECK_TIMEOUT?: The timeout in milliseconds for the proxy to check if the frontend server is reachable (default: 2000)
* DISABLE_PROXY?: If set to true, the proxy will be disabled and all requests go directly to the Plex server from the frontend (NOT RECOMMENDED)
* DISABLE_TLS_VERIFY?: If set to true, the proxy will not check any https ssl certificates
**/
const deploymentID = randomBytes(8).toString('hex');

const status: PerPlexed.Status = {
ready: false,
error: false,
message: 'Server is starting up...',
}

const app = express();

app.use(express.json());

if (!process.env.PLEX_SERVER) {
console.error('PLEX_SERVER environment variable not set');
process.exit(1);
}
(async () => {
if (process.env.PLEX_SERVER) {
status.error = true;
status.message = 'PLEX_SERVER has changed to PLEX_SERVERS. Please view the upgrade guide in the 1.0.0 release notes on github. https://github.com/Ipmake/PerPlexed/releases/tag/v1.0.0';
return;
}

if (!process.env.PLEX_SERVERS) {
status.error = true;
status.message = 'PLEX_SERVERS environment variable not set';
console.error('PLEX_SERVER environment variable not set');
return;
}

if (!process.env.PROXY_PLEX_SERVER && process.env.DISABLE_PROXY !== 'true') {
status.error = true;
status.message = 'PROXY_PLEX_SERVER environment variable not set. Please view the upgrade guide in the 1.0.0 release notes on github. https://github.com/Ipmake/PerPlexed/releases/tag/v1.0.0';
console.error('PROXY_PLEX_SERVER environment variable not set');
return;
}

if (process.env.PLEX_SERVERS) {
// check if the PLEX_SERVERS environment variable is a comma separated list and whether each server is a valid URL, the URL must not end with a /
const servers = process.env.PLEX_SERVERS.split(',');
const invalidServers = servers.filter((server) => !server.trim().match(/^https?:\/\/[^\/]+$/));

if (invalidServers.length > 0) {
status.error = true;
status.message = 'Invalid PLEX_SERVERS environment variable. The URL must start with http:// or https:// and must not end with a /';
console.error('Invalid PLEX_SERVERS environment variable. The URL must start with http:// or https:// and must not end with a /');
return;
}
}

if (process.env.PROXY_PLEX_SERVER && process.env.DISABLE_PROXY !== 'true') {
// check if the PROXY_PLEX_SERVER environment variable is a valid URL, the URL must not end with a /
if (!process.env.PROXY_PLEX_SERVER.match(/^https?:\/\/[^\/]+$/)) {
status.error = true;
status.message = 'Invalid PROXY_PLEX_SERVER environment variable. The URL must start with http:// or https:// and must not end with a /';
console.error('Invalid PROXY_PLEX_SERVER environment variable. The URL must start with http:// or https:// and must not end with a /');
return;
}

// check whether the PROXY_PLEX_SERVER is reachable
try {
await axios.get(`${process.env.PROXY_PLEX_SERVER}/identity`, {
timeout: 5000,
});
} catch (error) {
status.error = true;
status.message = 'Proxy cannot reach PROXY_PLEX_SERVER';
console.error('Proxy cannot reach PROXY_PLEX_SERVER');
return;
}
}

status.ready = true;
status.message = 'OK';
})();

app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] [${req.method}] ${req.url}`);
Expand All @@ -27,29 +93,40 @@ app.use((req, res, next) => {
next();
});

app.get('/status', (req, res) => {
res.send(status);
});

app.get('/config', (req, res) => {
res.send({
PLEX_SERVER: process.env.PLEX_SERVER,
PLEX_SERVERS: (process.env.PLEX_SERVERS as string).split(",").map((server) => server.trim()),
DEPLOYMENTID: deploymentID,
CONFIG: {
DISABLE_PROXY: process.env.DISABLE_PROXY === 'true',
FRONTEND_SERVER_CHECK_TIMEOUT: parseInt(process.env.FRONTEND_SERVER_CHECK_TIMEOUT || '2000'),
}
});
});

app.use(express.static('www'));

app.post('/proxy', (req, res) => {
if(process.env.DISABLE_PROXY === 'true') return res.status(400).send('Proxy is disabled');

const { url, method, headers, data } = req.body;
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

// the url must start with a / to prevent the server from making requests to external servers
if (!url || !url.startsWith('/')) return res.status(400).send('Invalid URL');

// check that the url doesn't include any harmful characters that could be used for directory traversal
if (url.match(/\.\./)) return res.status(400).send('Invalid URL');

// the method must be one of the allowed methods [GET, POST, PUT]
if (!method || !['GET', 'POST', 'PUT'].includes(method)) return res.status(400).send('Invalid method');

const config: AxiosRequestConfig = {
url: `${process.env.PLEX_SERVER}${url}`,
url: `${process.env.PROXY_PLEX_SERVER}${url}`,
method,
headers: {
...headers,
Expand Down
7 changes: 7 additions & 0 deletions backend/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export namespace PerPlexed {
export interface Status {
ready: boolean;
error: boolean;
message: string;
}
}
26 changes: 25 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ import Home from "./pages/Home";
import Library from "./pages/Library";
import BigReader from "./components/BigReader";
import { useWatchListCache } from "./states/WatchListCache";
import Startup, { useStartupState } from "./pages/Startup";

function AppManager() {
const { loading } = useStartupState();
const [showApp, setShowApp] = React.useState(false);
const [fadeOut, setFadeOut] = React.useState(false);

useEffect(() => {
if (loading) return;

setFadeOut(true);
setTimeout(() => setShowApp(true), 500);
}, [loading]);

if (!showApp) {
return (
<div style={{ opacity: fadeOut ? 0 : 1, transition: "opacity 0.5s" }}>
<Startup />
</div>
);
}

return <App />;
}

function App() {
const location = useLocation();
Expand Down Expand Up @@ -63,4 +87,4 @@ function App() {
);
}

export default App;
export default AppManager;
19 changes: 0 additions & 19 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { CssBaseline, createTheme } from "@mui/material";
import { QueryClient, QueryClientProvider } from "react-query";
import { BrowserRouter } from "react-router-dom";
import { makeid, uuidv4 } from "./plex/QuickFunctions";
import axios from "axios";
import { getBackendURL } from "./backendURL";

import "@fontsource-variable/quicksand";
import '@fontsource-variable/rubik';
Expand All @@ -19,23 +17,6 @@ if(!localStorage.getItem("clientID")) localStorage.setItem("clientID", makeid(24

sessionStorage.setItem("sessionID", uuidv4());

(async () => {
let reload = false;

const config = await axios.get(`${getBackendURL()}/config`);
if(config.data.PLEX_SERVER !== localStorage.getItem("server")) {
localStorage.setItem("server", config.data.PLEX_SERVER);
reload = true;
}

if(JSON.stringify(config.data.CONFIG) !== localStorage.getItem("config")) {
localStorage.setItem("config", JSON.stringify(config.data.CONFIG));
reload = true;
}

if(reload) return window.location.reload();
})();

interface ConfigInterface {
[key: string]: any;
}
Expand Down
Loading

0 comments on commit 0a203bd

Please sign in to comment.