impr: detect development mode based on config instead of window.location (fehmer) (#4703)

* impr: detect development mode based on config instead of window.location

* Allow access to /configure on the backend using non-local ip

* Use webpack define plugin

* take BACKEND_URL from env
This commit is contained in:
Christian Fehmer 2023-10-09 19:46:40 +02:00 committed by GitHub
parent e9c25f7b15
commit 0f6884a5be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 79 additions and 31 deletions

View file

@ -83,6 +83,12 @@ Within the `frontend/src/ts/constants` directory, duplicate `firebase-config-exa
4. The Firebase config will be visible below
5. Paste the config into `firebase-config.ts`
If you want to access the frontend from other machines on your network create a file `frontend/.env` with this content:
```
BACKEND_URL="http://<Your IP>:5005"
```
### Backend (optional)
Follow these steps if you want to work on anything involving the database/account system. Otherwise, you can skip this section.

View file

@ -52,6 +52,11 @@ function addApiRoutes(app: Application): void {
app.use("/configuration", configuration);
if (process.env.MODE === "dev") {
//disable csp to allow assets to load from unsecured http
app.use((req, res, next) => {
res.setHeader("Content-Security-Policy", "");
return next();
});
app.use("/configure", expressStatic(join(__dirname, "../../../private")));
}

View file

@ -6,7 +6,6 @@
"packages": {
"": {
"name": "monkeytype-frontend",
"version": "23.40.1",
"license": "GPL-3.0",
"dependencies": {
"axios": "0.21.4",
@ -48,6 +47,7 @@
"copy-webpack-plugin": "10.2.4",
"css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "3.4.1",
"dotenv": "16.3.1",
"eslint": "8.43.0",
"eslint-webpack-plugin": "3.1.1",
"extra-watch-webpack-plugin": "1.0.3",
@ -6497,6 +6497,18 @@
"tslib": "^2.0.3"
}
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",

View file

@ -8,7 +8,7 @@
"dep-graph": "madge -c -i \"dep-graph.png\" ./src/ts",
"build": "npx gulp build",
"build-live": "export COMMIT_HASH=`git rev-parse --short HEAD` && npx gulp build-production",
"dev": "concurrently \"webpack serve --config=./webpack/config.dev.js\" \"npx gulp watch\"",
"dev": "concurrently \"NODE_ENV=development webpack serve --config=./webpack/config.dev.js\" \"npx gulp watch\"",
"deploy-live": "npm run build-live && firebase deploy -P live --only hosting",
"deploy-preview": "npm run build-live && firebase hosting:channel:deploy preview -P live --expires 2h",
"tsc": "tsc",
@ -39,6 +39,7 @@
"copy-webpack-plugin": "10.2.4",
"css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "3.4.1",
"dotenv": "16.3.1",
"eslint": "8.43.0",
"eslint-webpack-plugin": "3.1.1",
"extra-watch-webpack-plugin": "1.0.3",

View file

@ -1,12 +1,9 @@
import endpoints from "./endpoints";
import { buildHttpClient } from "./adapters/axios-adapter";
const DEV_SERVER_HOST = "http://localhost:5005";
const PROD_SERVER_HOST = "https://api.monkeytype.com";
import { envConfig } from "../constants/env-config";
const API_PATH = "";
const BASE_URL =
window.location.hostname === "localhost" ? DEV_SERVER_HOST : PROD_SERVER_HOST;
const BASE_URL = envConfig.backendUrl;
const API_URL = `${BASE_URL}${API_PATH}`;
const httpClient = buildHttpClient(API_URL, 10000);

View file

@ -0,0 +1,14 @@
interface Config {
backendUrl: string;
isDevelopment: boolean;
}
// @ts-ignore
const backendUrl = BACKEND_URL;
// @ts-ignore
const isDevelopment = IS_DEVELOPMENT;
export const envConfig: Config = {
backendUrl,
isDevelopment,
};

View file

@ -520,7 +520,7 @@ async function signUp(): Promise<void> {
}
// Force user to use a capital letter, number, special character and reasonable length when setting up an account and changing password
if (!Misc.isLocalhost() && !Misc.isPasswordStrong(password)) {
if (!Misc.isDevEnvironment() && !Misc.isPasswordStrong(password)) {
Notifications.add(
"Password must contain at least one capital letter, number, a special character and must be between 8 and 64 characters long",
0,

View file

@ -32,7 +32,7 @@ async function updateFavicon(): Promise<void> {
let maincolor, bgcolor;
bgcolor = await ThemeColors.get("bg");
maincolor = await ThemeColors.get("main");
if (Misc.isLocalhost()) {
if (Misc.isDevEnvironment()) {
[maincolor, bgcolor] = [bgcolor, maincolor];
}
if (bgcolor === maincolor) {

View file

@ -160,7 +160,7 @@ function updateFooter(lb: LbKey): void {
}
if (
!Misc.isLocalhost() &&
!Misc.isDevEnvironment() &&
(DB.getSnapshot()?.typingStats?.timeTyping ?? 0) < 7200
) {
$(`#leaderboardsWrapper table.${side} tfoot`).html(`

View file

@ -1,5 +1,5 @@
import Ape from "../ape";
import { isLocalhost, secondsToString } from "../utils/misc";
import { isDevEnvironment, secondsToString } from "../utils/misc";
import * as Notifications from "./notifications";
import format from "date-fns/format";
import * as Alerts from "./alerts";
@ -22,7 +22,7 @@ async function getLatest(): Promise<MonkeyTypes.PSA[] | null> {
const response = await Ape.psas.get();
if (response.status === 500) {
if (isLocalhost()) {
if (isDevEnvironment()) {
Notifications.addBanner(
"Dev Info: Backend server not running",
0,

View file

@ -3,7 +3,7 @@ import { FirebaseApp, initializeApp } from "firebase/app";
import { getAuth, Auth as AuthType } from "firebase/auth";
import { firebaseConfig } from "./constants/firebase-config"; // eslint-disable-line require-path-exists/exists
import * as Notifications from "./elements/notifications";
import { createErrorMessage, isLocalhost } from "./utils/misc";
import { createErrorMessage, isDevEnvironment } from "./utils/misc";
// Initialize Firebase
export let app: FirebaseApp | undefined;
@ -16,7 +16,7 @@ try {
app = undefined;
Auth = undefined;
console.error("Authentication failed to initialize", e);
if (isLocalhost()) {
if (isDevEnvironment()) {
Notifications.addBanner(
createErrorMessage(e, "Authentication uninitialized") +
" Check your firebase-config.ts",

View file

@ -128,7 +128,7 @@ const checkPassword = (): void => {
).val() as string;
// Force user to use a capital letter, number, special character and reasonable length when setting up an account and changing password
if (!Misc.isLocalhost() && !Misc.isPasswordStrong(password)) {
if (!Misc.isDevEnvironment() && !Misc.isPasswordStrong(password)) {
if (password.length < 8) {
passwordIndicator.show("short", "Password must be at least 8 characters");
} else if (password.length > 64) {

View file

@ -25,7 +25,7 @@ import {
import {
createErrorMessage,
isElementVisible,
isLocalhost,
isDevEnvironment,
isPasswordStrong,
reloadAfter,
} from "../utils/misc";
@ -660,7 +660,7 @@ list["updatePassword"] = new SimplePopup(
};
}
if (!isLocalhost() && !isPasswordStrong(newPass)) {
if (!isDevEnvironment() && !isPasswordStrong(newPass)) {
return {
status: 0,
message:

View file

@ -13,11 +13,12 @@ import * as FunboxList from "./test/funbox/funbox-list";
//@ts-ignore
import Konami from "konami";
import { log } from "./controllers/analytics-controller";
import { envConfig } from "./constants/env-config";
if (Misc.isLocalhost()) {
if (Misc.isDevEnvironment()) {
$("footer .currentVersion .text").text("localhost");
$("body").prepend(
`<a class='button configureAPI' href='http://localhost:5005/configure/' target='_blank' aria-label="Configure API" data-balloon-pos="right"><i class="fas fa-fw fa-server"></i></a>`
`<a class='button configureAPI' href='${envConfig.backendUrl}/configure/' target='_blank' aria-label="Configure API" data-balloon-pos="right"><i class="fas fa-fw fa-server"></i></a>`
);
} else {
Misc.getLatestReleaseFromGitHub().then((v) => {
@ -88,7 +89,7 @@ if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
// disabling service workers on localhost - they dont really work well with local development
// and cause issues with hot reloading
if (Misc.isLocalhost()) {
if (Misc.isDevEnvironment()) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (const registration of registrations) {
// if (registration.scope !== "https://monkeytype.com/")

View file

@ -7,7 +7,7 @@ import * as ConfigEvent from "./observables/config-event";
import { debounce, throttle } from "throttle-debounce";
import * as TestUI from "./test/test-ui";
import { get as getActivePage } from "./states/active-page";
import { isLocalhost } from "./utils/misc";
import { isDevEnvironment } from "./utils/misc";
function updateKeytips(): void {
const modifierKey = window.navigator.userAgent.toLowerCase().includes("mac")
@ -39,7 +39,7 @@ function updateKeytips(): void {
}
}
if (isLocalhost()) {
if (isDevEnvironment()) {
window.onerror = function (error): void {
Notifications.add(error.toString(), -1);
};

View file

@ -1,4 +1,4 @@
import { isLocalhost } from "./misc";
import { isDevEnvironment } from "./misc";
const nativeLog = console.log;
const nativeWarn = console.warn;
@ -6,7 +6,7 @@ const nativeError = console.error;
let debugLogs = localStorage.getItem("debugLogs") === "true" ?? false;
if (isLocalhost()) {
if (isDevEnvironment()) {
debugLogs = true;
debug("Debug logs automatically enabled on localhost");
}

View file

@ -1,5 +1,6 @@
import * as Loader from "../elements/loader";
import { normal as normalBlend } from "color-blend";
import { envConfig } from "../constants/env-config";
async function fetchJson<T>(url: string): Promise<T> {
try {
@ -1487,12 +1488,8 @@ export function loadCSS(href: string, prepend = false): void {
}
}
export function isLocalhost(): boolean {
return (
location.hostname === "localhost" ||
location.hostname === "127.0.0.1" ||
location.hostname === ""
);
export function isDevEnvironment(): boolean {
return envConfig.isDevelopment;
}
export function getBinary(): string {
@ -1669,7 +1666,7 @@ export function reloadAfter(seconds: number): void {
}
export function updateTitle(title?: string): void {
const local = isLocalhost() ? "localhost - " : "";
const local = isDevEnvironment() ? "localhost - " : "";
if (!title) {
document.title =

View file

@ -3,6 +3,8 @@ const CopyPlugin = require("copy-webpack-plugin");
const CircularDependencyPlugin = require("circular-dependency-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
require("dotenv").config();
let circularImports = 0;
@ -19,6 +21,15 @@ const htmlWebpackPlugins = [
});
});
let BACKEND_URL = "https://api.monkeytype.com";
if (process.env.NODE_ENV === "development") {
if (process.env.BACKEND_URL) {
BACKEND_URL = process.env.BACKEND_URL;
} else {
BACKEND_URL = "http://localhost:5005";
}
}
/** @type { import('webpack').Configuration } */
const BASE_CONFIG = {
entry: {
@ -77,6 +88,10 @@ const BASE_CONFIG = {
},
},
plugins: [
new webpack.DefinePlugin({
BACKEND_URL: JSON.stringify(BACKEND_URL),
IS_DEVELOPMENT: JSON.stringify(process.env.NODE_ENV === "development"),
}),
new CircularDependencyPlugin({
exclude: /node_modules/,
include: /./,