From c50c1f627e1b0d91001c0f44eb6ffbe56ee0da36 Mon Sep 17 00:00:00 2001 From: Marco <35533749+marcopeocchi@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:24:50 +0200 Subject: [PATCH] 78 404 when the application put under nginx subdirectory with proxy pass (#79) * use http.FileServer insetead of custom middleware * fixed behavior under reverse proxy * enabled reverse proxy subfolder as "domain value" * domain validation * code refactoring * code refactoring * updated translation --- frontend/src/assets/i18n.yaml | 10 ++++++++++ frontend/src/atoms/settings.ts | 29 ++++++++++++++++------------- frontend/src/router.tsx | 4 ++-- frontend/src/utils.ts | 10 +++++++--- frontend/src/views/Settings.tsx | 16 +++++++++++++++- frontend/vite.config.ts | 4 +--- main.go | 3 ++- server/server.go | 8 ++------ 8 files changed, 55 insertions(+), 29 deletions(-) diff --git a/frontend/src/assets/i18n.yaml b/frontend/src/assets/i18n.yaml index 7699263..f3cd3f4 100644 --- a/frontend/src/assets/i18n.yaml +++ b/frontend/src/assets/i18n.yaml @@ -34,6 +34,7 @@ languages: clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may close this window) restartAppMessage: Needs a page reload to take effect + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder italian: urlInput: URL di YouTube o di qualsiasi altro servizio supportato statusTitle: Stato @@ -67,6 +68,7 @@ languages: clipboardAction: URL copiato negli appunti playlistCheckbox: Download playlist (richiederà tempo, puoi chiudere la finestra dopo l'inoltro) restartAppMessage: La finestra deve essere ricaricata perché abbia effetto + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder chinese: urlInput: YouTube 或其他受支持服务的视频网址 statusTitle: 状态 @@ -100,6 +102,7 @@ languages: archiveTitle: 归档 clipboardAction: 复制 URL 到剪贴板 playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder spanish: urlInput: URL de YouTube u otro servicio compatible statusTitle: Estado @@ -132,6 +135,7 @@ languages: archiveTitle: Archive clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder russian: urlInput: URL-адрес YouTube или любого другого поддерживаемого сервиса statusTitle: Статус @@ -164,6 +168,7 @@ languages: archiveTitle: Архив clipboardAction: URL скопирован в буфер обмена playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder korean: urlInput: YouTube나 다른 지원되는 사이트의 URL statusTitle: 상태 @@ -196,6 +201,7 @@ languages: archiveTitle: Archive clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder japanese: urlInput: YouTubeまたはサポート済み動画のURL statusTitle: 状態 @@ -229,6 +235,7 @@ languages: archiveTitle: Archive clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder catalan: urlInput: URL de YouTube o d'un altre servei compatible statusTitle: Estat @@ -261,6 +268,7 @@ languages: archiveTitle: Archive clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder ukrainian: urlInput: URL-адреса YouTube або будь-якого іншого підтримуваного сервісу statusTitle: Статус @@ -293,6 +301,7 @@ languages: archiveTitle: Архів clipboardAction: URL скопійовано в буфер обміну playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder polish: urlInput: Adres URL YouTube lub innej obsługiwanej usługi statusTitle: Status @@ -325,3 +334,4 @@ languages: archiveTitle: Archiwum clipboardAction: Adres URL zostanie skopiowany do schowka playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) + servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder diff --git a/frontend/src/atoms/settings.ts b/frontend/src/atoms/settings.ts index f63a6cb..b5b299b 100644 --- a/frontend/src/atoms/settings.ts +++ b/frontend/src/atoms/settings.ts @@ -1,18 +1,6 @@ import { atom, selector } from 'recoil' import { prefersDarkMode } from '../utils' -export type Language = - | 'english' - | 'chinese' - | 'russian' - | 'italian' - | 'spanish' - | 'korean' - | 'japanese' - | 'catalan' - | 'ukrainian' - | 'polish' - export const languages = [ 'english', 'chinese', @@ -26,6 +14,8 @@ export const languages = [ 'polish', ] as const +export type Language = (typeof languages)[number] + export type Theme = 'light' | 'dark' | 'system' export type ThemeNarrowed = 'light' | 'dark' @@ -40,6 +30,7 @@ export interface SettingsState { pathOverriding: boolean enableCustomArgs: boolean listView: boolean + servedFromReverseProxy: boolean } export const languageState = atom({ @@ -133,9 +124,20 @@ export const listViewState = atom({ ] }) +export const servedFromReverseProxyState = atom({ + key: 'servedFromReverseProxyState', + default: localStorage.getItem('reverseProxy') === "true", + effects: [ + ({ onSet }) => + onSet(a => localStorage.setItem('reverseProxy', a.toString())) + ] +}) + export const serverAddressAndPortState = selector({ key: 'serverAddressAndPortState', - get: ({ get }) => `${get(serverAddressState)}:${get(serverPortState)}` + get: ({ get }) => get(servedFromReverseProxyState) + ? `${get(serverAddressState)}` + : `${get(serverAddressState)}:${get(serverPortState)}` }) export const serverURL = selector({ @@ -184,5 +186,6 @@ export const settingsState = selector({ pathOverriding: get(pathOverridingState), enableCustomArgs: get(enableCustomArgsState), listView: get(listViewState), + servedFromReverseProxy: get(servedFromReverseProxyState), }) }) \ No newline at end of file diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index a7bed11..3221541 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,6 +1,6 @@ import { CircularProgress } from '@mui/material' import { Suspense, lazy } from 'react' -import { createBrowserRouter } from 'react-router-dom' +import { createHashRouter } from 'react-router-dom' import Layout from './Layout' const Home = lazy(() => import('./views/Home')) @@ -10,7 +10,7 @@ const Settings = lazy(() => import('./views/Settings')) const ErrorBoundary = lazy(() => import('./components/ErrorBoundary')) -export const router = createBrowserRouter([ +export const router = createHashRouter([ { path: '/', Component: () => , diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index fc93bbd..00e979c 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -19,9 +19,13 @@ export function validateIP(ipAddr: string): boolean { * @param domainName * @returns domain validity test */ -export function validateDomain(domainName: string): boolean { - let domainRegex = /[^@ \t\r\n]+.[^@ \t\r\n]+\.[^@ \t\r\n]+/ - return domainRegex.test(domainName) || domainName === 'localhost' +export function validateDomain(url: string): boolean { + const urlRegex = /(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ + const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ + + const [name, slug] = url.split('/') + + return urlRegex.test(url) || name === 'localhost' && slugRegex.test(slug) } /** diff --git a/frontend/src/views/Settings.tsx b/frontend/src/views/Settings.tsx index b04533e..cebf117 100644 --- a/frontend/src/views/Settings.tsx +++ b/frontend/src/views/Settings.tsx @@ -1,5 +1,6 @@ import { Button, + Checkbox, Container, FormControl, FormControlLabel, @@ -36,6 +37,7 @@ import { languages, latestCliArgumentsState, pathOverridingState, + servedFromReverseProxyState, serverAddressState, serverPortState, themeState @@ -48,6 +50,7 @@ import { validateDomain, validateIP } from '../utils' // NEED ABSOLUTELY TO BE SPLIT IN MULTIPLE COMPONENTS export default function Settings() { + const [reverseProxy, setReverseProxy] = useRecoilState(servedFromReverseProxyState) const [formatSelection, setFormatSelection] = useRecoilState(formatSelectionState) const [pathOverriding, setPathOverriding] = useRecoilState(pathOverridingState) const [fileRenaming, setFileRenaming] = useRecoilState(fileRenamingState) @@ -154,16 +157,27 @@ export default function Settings() { InputProps={{ startAdornment: ws://, }} - sx={{ mb: 2 }} /> serverPort$.next(e.currentTarget.value)} error={isNaN(Number(serverPort)) || Number(serverPort) > 65535} + /> + + + setReverseProxy(state => !state)} + /> + } + label={i18n.t('servedFromReverseProxyCheckbox')} sx={{ mb: 2 }} /> diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 374cf87..8c2698c 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,6 @@ import react from '@vitejs/plugin-react-swc' import ViteYaml from '@modyfi/vite-plugin-yaml' import { defineConfig } from 'vite' -import { resolve } from 'path' export default defineConfig(() => { return { @@ -9,10 +8,9 @@ export default defineConfig(() => { react(), ViteYaml(), ], - root: resolve(__dirname, '.'), + base: '', build: { emptyOutDir: true, - outDir: resolve(__dirname, 'dist'), } } }) diff --git a/main.go b/main.go index 977b11c..9bd3bef 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,8 @@ var ( requireAuth bool rpcSecret string - //go:embed frontend/dist + //go:embed frontend/dist/index.html + //go:embed frontend/dist/assets/* frontend embed.FS ) diff --git a/server/server.go b/server/server.go index 75fa9db..16e8f29 100644 --- a/server/server.go +++ b/server/server.go @@ -43,7 +43,6 @@ func RunBlocking(port int, frontend fs.FS) { mq: mq, }) - // http-post go gracefulShutdown(srv, &db) go autoPersist(time.Minute*5, &db) @@ -59,12 +58,9 @@ func newServer(c serverConfig) *http.Server { r.Use(cors.AllowAll().Handler) r.Use(middleware.Logger) - sh := middlewares.NewSpaHandler("index.html", c.frontend) - sh.AddClientRoute("/settings") - sh.AddClientRoute("/archive") - sh.AddClientRoute("/login") + app := http.FileServer(http.FS(c.frontend)) - r.Get("/*", sh.Handler()) + r.Mount("/", app) // Archive routes r.Route("/archive", func(r chi.Router) {