diff --git a/frontend/.env b/frontend/.env deleted file mode 100644 index 0e600cd13..000000000 --- a/frontend/.env +++ /dev/null @@ -1,7 +0,0 @@ -# Please override by creating a .env.local file at the same directory -# Required -REACT_APP_APIKEY="YOUR_SERVER_API_KEY" - -# Optional -REACT_APP_CAN_UPDATE=true -REACT_APP_HAS_UPDATE=false \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 000000000..3b530d7a7 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,24 @@ +# Override by duplicating and rename to .env.local +# The following environment variables will only be used during development + +# Required + +# API key of your backend +REACT_APP_APIKEY="REPLACE_ME_TO_YOUR_SERVER_API_KEY" + +# Address of your backend +REACT_APP_PROXY_URL=http://localhost:6767 + +# Optional + +# Allow Unsecured connection to your backend +REACT_APP_PROXY_SECURE=true + +# Allow websocket connection in Socket.IO +REACT_APP_ALLOW_WEBSOCKET=true + +# Display update section in settings +REACT_APP_CAN_UPDATE=true + +# Display update notification in notification center +REACT_APP_HAS_UPDATE=false diff --git a/frontend/README.md b/frontend/README.md index 05f866171..758aa35e9 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,10 +1,12 @@ # Bazarr Frontend ## Dependencies -* [Node.js](https://nodejs.org/) -* npm (included in Node.js) + +- [Node.js](https://nodejs.org/) +- npm (included in Node.js) ## Getting Started + 1. Clone or download this repository ``` @@ -17,18 +19,36 @@ ``` $ npm install ``` -4. Duplicate `.env` file and rename to `.env.local` + +3. Duplicate `.env.development` file and rename to `.env.local` ``` $ cp .env .env.local ``` -6. Fill any variable that defined in `.env.local` -7. Run Bazarr backend (Backend must listening on `http://localhost:6767`) + +4. Update your backend server's API key in `.env.local` + + ``` + # API key of your backend + REACT_APP_APIKEY="REPLACE_ME_TO_YOUR_SERVER_API_KEY" + ``` + +5. Change the address of your backend server (Optional) + + > http://localhost:6767 will be used by default + + ``` + # Address of your backend + REACT_APP_PROXY_URL=http://localhost:6767 + ``` + +6. Run Bazarr backend ``` $ python3 ../bazarr.py ``` -9. Run the web client for local development + +7. Run the web development tool ``` $ npm start diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9f7c5b024..c8e85beac 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,7 +28,7 @@ "@types/react-table": "^7", "axios": "^0.21", "bootstrap": "^4", - "http-proxy-middleware": "^0.19", + "http-proxy-middleware": "^2", "lodash": "^4", "rc-slider": "^9.7", "react": "^17", @@ -3505,6 +3505,14 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==" }, + "node_modules/@types/http-proxy": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz", + "integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/invariant": { "version": "2.2.34", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", @@ -10490,17 +10498,82 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/http-proxy-middleware": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", - "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", "dependencies": { + "@types/http-proxy": "^1.17.5", "http-proxy": "^1.18.1", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=12.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, "node_modules/https-browserify": { @@ -24838,6 +24911,14 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==" }, + "@types/http-proxy": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz", + "integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==", + "requires": { + "@types/node": "*" + } + }, "@types/invariant": { "version": "2.2.34", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", @@ -30290,14 +30371,60 @@ } }, "http-proxy-middleware": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", - "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", "requires": { + "@types/http-proxy": "^1.17.5", "http-proxy": "^1.18.1", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } } }, "https-browserify": { diff --git a/frontend/package.json b/frontend/package.json index 223b0b1d7..a036be86e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,7 +32,7 @@ "@types/react-table": "^7", "axios": "^0.21", "bootstrap": "^4", - "http-proxy-middleware": "^0.19", + "http-proxy-middleware": "^2", "lodash": "^4", "rc-slider": "^9.7", "react": "^17", diff --git a/frontend/src/@modules/socketio/index.ts b/frontend/src/@modules/socketio/index.ts index 23fbdd157..63c101089 100644 --- a/frontend/src/@modules/socketio/index.ts +++ b/frontend/src/@modules/socketio/index.ts @@ -1,6 +1,6 @@ import { debounce, forIn, remove, uniq } from "lodash"; import { io, Socket } from "socket.io-client"; -import { getBaseUrl } from "../../utilities"; +import { Environment } from "../../utilities"; import { conditionalLog, log } from "../../utilities/logger"; import { createDefaultReducer } from "./reducer"; @@ -12,9 +12,8 @@ class SocketIOClient { private reducers: SocketIO.Reducer[]; constructor() { - const baseUrl = getBaseUrl(); this.socket = io({ - path: `${baseUrl}/api/socket.io`, + path: `${Environment.baseUrl}/api/socket.io`, transports: ["polling", "websocket"], upgrade: true, rememberUpgrade: true, diff --git a/frontend/src/@redux/reducers/site.ts b/frontend/src/@redux/reducers/site.ts index 8999f973d..8381a95b4 100644 --- a/frontend/src/@redux/reducers/site.ts +++ b/frontend/src/@redux/reducers/site.ts @@ -1,6 +1,7 @@ import { createReducer } from "@reduxjs/toolkit"; import { intersectionWith, pullAllWith, remove, sortBy, uniqBy } from "lodash"; import apis from "../../apis"; +import { isProdEnv } from "../../utilities"; import { siteAddNotifications, siteAddProgress, @@ -59,7 +60,7 @@ const reducer = createReducer(defaultSite, (builder) => { state.initialized = "An Error Occurred When Initializing Bazarr UI"; }) .addCase(siteRedirectToAuth, (state) => { - if (process.env.NODE_ENV !== "production") { + if (!isProdEnv) { apis._resetApi("NEED_AUTH"); } state.auth = false; diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx index 5d26fbd37..fb16aef60 100644 --- a/frontend/src/App/index.tsx +++ b/frontend/src/App/index.tsx @@ -20,7 +20,7 @@ import Sidebar from "../Sidebar"; import Auth from "../special-pages/AuthPage"; import ErrorBoundary from "../special-pages/ErrorBoundary"; import LaunchError from "../special-pages/LaunchError"; -import { useBaseUrl, useHasUpdateInject } from "../utilities"; +import { Environment } from "../utilities"; import Header from "./Header"; import Router from "./Router"; @@ -35,9 +35,8 @@ const App: FunctionComponent = () => { const notify = useNotification("has-update", 10 * 1000); // Has any update? - const hasUpdate = useHasUpdateInject(); useEffectOnceWhen(() => { - if (hasUpdate) { + if (Environment.hasUpdate) { notify({ type: "info", message: "A new version of Bazarr is ready, restart is required", @@ -80,14 +79,12 @@ const App: FunctionComponent = () => { }; const MainRouter: FunctionComponent = () => { - const baseUrl = useBaseUrl(); - useEffect(() => { Socketio.initialize(); }, []); return ( - + diff --git a/frontend/src/Settings/General/index.tsx b/frontend/src/Settings/General/index.tsx index 0049f1506..7476ef3d0 100644 --- a/frontend/src/Settings/General/index.tsx +++ b/frontend/src/Settings/General/index.tsx @@ -6,11 +6,7 @@ import { import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { FunctionComponent, useState } from "react"; import { InputGroup } from "react-bootstrap"; -import { - copyToClipboard, - toggleState, - useCanUpdateInject, -} from "../../utilities"; +import { copyToClipboard, Environment, toggleState } from "../../utilities"; import { Button, Check, @@ -41,8 +37,6 @@ const baseUrlOverride = (settings: Settings) => const SettingsGeneralView: FunctionComponent = () => { const [copied, setCopy] = useState(false); - const canUpdate = useCanUpdateInject(); - return ( @@ -155,7 +149,7 @@ const SettingsGeneralView: FunctionComponent = () => { -