From b18269b40ef8a54a1f04b00f37eda17e1cc71fe8 Mon Sep 17 00:00:00 2001 From: qtchaos <72168435+qtchaos@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:26:17 +0200 Subject: [PATCH 1/3] chore: Remove references to official domain --- .github/CODE_OF_CONDUCT.md | 2 +- .github/CONTRIBUTING.md | 6 +++--- .github/SECURITY.md | 9 +++------ README.md | 6 +++--- package.json | 2 +- public/config.js | 2 +- src/backend/accounts/progress.ts | 2 ++ src/backend/helpers/report.ts | 7 +++++-- src/pages/parts/settings/ConnectionsPart.tsx | 4 ++-- src/setup/constants.ts | 4 ++-- 10 files changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 412c4af2..c45aba90 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -codeofconduct@movie-web.app. +our [Discord](https://discord.gg/gQYB6fGArX). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 926899a8..a9883859 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing Guidelines for movie-web -Thank you for investing your time in contributing to our project! Your contribution will be reflected on [movie-web.app](https://movie-web.app). +Thank you for investing your time in contributing to our project! Your contribution will be reflected on all of the community hosted instances that are on the latest version. Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. @@ -33,7 +33,7 @@ There are two places where to request features or report bugs: ### Discord Server If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server. -Discord Server +Discord Server ### GitHub Issues To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template. @@ -85,7 +85,7 @@ Here are some tips to make sure that your pull requests are :pinched_fingers: fi ### Language Contributions Language contributions help movie-web massively, allowing people worldwide to use our app! -We use weblate for crowdsourcing our translations. [Click here to go to our translation tool.](https://weblate.movie-web.app/projects/movie-web/website/) +We use weblate for crowdsourcing our translations. 1. First make sure you make an account. (click the link above) 2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language. diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 83bf16ae..b092f774 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,12 +2,9 @@ ## Supported Versions -The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app. - -Support is not provided for any forks or mirrors of movie-web. +The latest version of movie-web is the only version that is supported, as it is the only version that is being actively developed. ## Reporting a Vulnerability -There are two ways you can contact the movie-web maintainers to report a vulnerability: - - Email [security@movie-web.app](mailto:security@movie-web.app) - - Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app) +You can contact the movie-web maintainers to report a vulnerability: + - Report the vulnerability in the [movie-web Discord server](https://discord.gg/gQYB6fGArX) diff --git a/README.md b/README.md index 7f4662a2..7a2470a9 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@


- πŸ”΅ discord 🟒 website + πŸ”΅ discord 🟒 docs



# ⚑What is movie-web? -movie-web is a web app for watching movies easily. Check it out at movie-web.app. +movie-web is a web app for watching movies easily. This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface. @@ -57,7 +57,7 @@ pnpm build A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below -|[Selfhosting guide](https://docs.movie-web.app)| +|[Selfhosting guide](https://movie-web.github.io/docs)| |---| ## 🀝 Thanks to all Contributors diff --git a/package.json b/package.json index 810a689b..3d6724b3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "movie-web", "version": "4.4.2", "private": true, - "homepage": "https://movie-web.app", + "homepage": "https://github.com/movie-web/movie-web", "scripts": { "dev": "vite", "build": "vite build", diff --git a/public/config.js b/public/config.js index 68a9ca3f..011e8630 100644 --- a/public/config.js +++ b/public/config.js @@ -11,7 +11,7 @@ window.__CONFIG__ = { // Whether to disable hash-based routing, leave this as false if you don't know what this is VITE_NORMAL_ROUTER: false, - // The backend URL to communicate with, defaults to the movie-web hosted one at backend.movie-web.app + // The backend URL to communicate with VITE_BACKEND_URL: null, // A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-" and "movie-" diff --git a/src/backend/accounts/progress.ts b/src/backend/accounts/progress.ts index 511d1c5d..6b522079 100644 --- a/src/backend/accounts/progress.ts +++ b/src/backend/accounts/progress.ts @@ -2,6 +2,7 @@ import { ofetch } from "ofetch"; import { getAuthHeaders } from "@/backend/accounts/auth"; import { ProgressResponse } from "@/backend/accounts/user"; +import { BACKEND_URL } from "@/setup/constants"; import { AccountWithToken } from "@/stores/auth"; import { ProgressMediaItem, ProgressUpdateItem } from "@/stores/progress"; @@ -103,6 +104,7 @@ export async function removeProgress( episodeId?: string, seasonId?: string, ) { + if (!BACKEND_URL) return; await ofetch(`/users/${account.userId}/progress/${id}`, { method: "DELETE", headers: getAuthHeaders(account.token), diff --git a/src/backend/helpers/report.ts b/src/backend/helpers/report.ts index 4822a867..4aa19f0e 100644 --- a/src/backend/helpers/report.ts +++ b/src/backend/helpers/report.ts @@ -5,13 +5,14 @@ import { useCallback } from "react"; import { isExtensionActiveCached } from "@/backend/extension/messaging"; import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; +import { BACKEND_URL } from "@/setup/constants"; import { useAuthStore } from "@/stores/auth"; import { PlayerMeta } from "@/stores/player/slices/source"; // for anybody who cares - these are anonymous metrics. // They are just used for figuring out if providers are broken or not -const metricsEndpoint = "https://backend.movie-web.app/metrics/providers"; -const captchaMetricsEndpoint = "https://backend.movie-web.app/metrics/captcha"; +const metricsEndpoint = `${BACKEND_URL}/metrics/providers`; +const captchaMetricsEndpoint = `${BACKEND_URL}/metrics/captcha`; const batchId = () => nanoid(32); export type ProviderMetric = { @@ -44,6 +45,7 @@ function getStackTrace(error: Error, lines: number) { } export async function reportProviders(items: ProviderMetric[]): Promise { + if (!BACKEND_URL) return; return ofetch(metricsEndpoint, { method: "POST", body: { @@ -156,6 +158,7 @@ export function useReportProviders() { } export function reportCaptchaSolve(success: boolean) { + if (!BACKEND_URL) return; ofetch(captchaMetricsEndpoint, { method: "POST", body: { diff --git a/src/pages/parts/settings/ConnectionsPart.tsx b/src/pages/parts/settings/ConnectionsPart.tsx index 9b693e9f..990c5de0 100644 --- a/src/pages/parts/settings/ConnectionsPart.tsx +++ b/src/pages/parts/settings/ConnectionsPart.tsx @@ -55,7 +55,7 @@ function ProxyEdit({ proxyUrls, setProxyUrls }: ProxyEditProps) {

- + Proxy documentation @@ -125,7 +125,7 @@ function BackendEdit({ backendUrl, setBackendUrl }: BackendEditProps) {

- + Backend documentation diff --git a/src/setup/constants.ts b/src/setup/constants.ts index 27a9792b..def7c48f 100644 --- a/src/setup/constants.ts +++ b/src/setup/constants.ts @@ -1,6 +1,6 @@ export const APP_VERSION = import.meta.env.PACKAGE_VERSION; -export const DISCORD_LINK = "https://discord.movie-web.app"; +export const DISCORD_LINK = "https://discord.gg/gQYB6fGArX"; export const GITHUB_LINK = "https://github.com/movie-web/movie-web"; export const DONATION_LINK = "https://ko-fi.com/movieweb"; export const GA_ID = "G-44YVXRL61C"; -export const BACKEND_URL = "https://backend.movie-web.app"; +export const BACKEND_URL = import.meta.env.VITE_BACKEND_URL; From fcf42a4e8ad76d4790f32d1d375a81ba930d91b6 Mon Sep 17 00:00:00 2001 From: qtchaos <72168435+qtchaos@users.noreply.github.com> Date: Sat, 24 Feb 2024 02:08:01 +0200 Subject: [PATCH 2/3] fix: change useBackendUrl to possibly be undefined, add checks to avoid useless requests to nonexistent backend --- src/backend/accounts/progress.ts | 2 -- src/hooks/auth/useAuth.ts | 6 +++++- src/hooks/auth/useBackendUrl.ts | 2 +- src/pages/Settings.tsx | 3 ++- src/pages/onboarding/OnboardingProxy.tsx | 2 +- src/pages/parts/admin/BackendTestPart.tsx | 14 +++++++++++--- src/pages/parts/auth/LoginFormPart.tsx | 3 +++ src/pages/parts/auth/TrustBackendPart.tsx | 6 +++++- src/pages/parts/auth/VerifyPassphrasePart.tsx | 5 +++++ src/pages/parts/settings/AccountActionsPart.tsx | 2 +- src/pages/parts/settings/DeviceListPart.tsx | 1 + src/pages/parts/settings/SidebarPart.tsx | 7 ++++--- src/stores/bookmarks/BookmarkSyncer.tsx | 1 + src/stores/progress/ProgressSyncer.tsx | 1 + src/stores/subtitles/SettingsSyncer.tsx | 1 + 15 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/backend/accounts/progress.ts b/src/backend/accounts/progress.ts index 6b522079..511d1c5d 100644 --- a/src/backend/accounts/progress.ts +++ b/src/backend/accounts/progress.ts @@ -2,7 +2,6 @@ import { ofetch } from "ofetch"; import { getAuthHeaders } from "@/backend/accounts/auth"; import { ProgressResponse } from "@/backend/accounts/user"; -import { BACKEND_URL } from "@/setup/constants"; import { AccountWithToken } from "@/stores/auth"; import { ProgressMediaItem, ProgressUpdateItem } from "@/stores/progress"; @@ -104,7 +103,6 @@ export async function removeProgress( episodeId?: string, seasonId?: string, ) { - if (!BACKEND_URL) return; await ofetch(`/users/${account.userId}/progress/${id}`, { method: "DELETE", headers: getAuthHeaders(account.token), diff --git a/src/hooks/auth/useAuth.ts b/src/hooks/auth/useAuth.ts index f608d148..3ce49163 100644 --- a/src/hooks/auth/useAuth.ts +++ b/src/hooks/auth/useAuth.ts @@ -63,6 +63,7 @@ export function useAuth() { const login = useCallback( async (loginData: LoginData) => { + if (!backendUrl) return; const keys = await keysFromMnemonic(loginData.mnemonic); const publicKeyBase64Url = bytesToBase64Url(keys.publicKey); const { challenge } = await getLoginChallengeToken( @@ -87,7 +88,7 @@ export function useAuth() { ); const logout = useCallback(async () => { - if (!currentAccount) return; + if (!currentAccount || !backendUrl) return; try { await removeSession( backendUrl, @@ -102,6 +103,7 @@ export function useAuth() { const register = useCallback( async (registerData: RegistrationData) => { + if (!backendUrl) return; const { challenge } = await getRegisterChallengeToken( backendUrl, registerData.recaptchaToken, @@ -134,6 +136,7 @@ export function useAuth() { progressItems: Record, bookmarks: Record, ) => { + if (!backendUrl) return; if ( Object.keys(progressItems).length === 0 && Object.keys(bookmarks).length === 0 @@ -159,6 +162,7 @@ export function useAuth() { const restore = useCallback( async (account: AccountWithToken) => { + if (!backendUrl) return; let user: { user: UserResponse; session: SessionResponse }; try { user = await getUser(backendUrl, account.token); diff --git a/src/hooks/auth/useBackendUrl.ts b/src/hooks/auth/useBackendUrl.ts index e417ed06..64545227 100644 --- a/src/hooks/auth/useBackendUrl.ts +++ b/src/hooks/auth/useBackendUrl.ts @@ -1,7 +1,7 @@ import { conf } from "@/setup/config"; import { useAuthStore } from "@/stores/auth"; -export function useBackendUrl() { +export function useBackendUrl(): string | undefined { const backendUrl = useAuthStore((s) => s.backendUrl); return backendUrl ?? conf().BACKEND_URL; } diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 18b755ca..cebf1ddd 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -70,6 +70,7 @@ export function AccountSettings(props: { const url = useBackendUrl(); const { account } = props; const [sessionsResult, execSessions] = useAsyncFn(() => { + if (!url) return Promise.resolve([]); return getSessions(url, account); }, [account, url]); useEffect(() => { @@ -144,7 +145,7 @@ export function SettingsPage() { ); const saveChanges = useCallback(async () => { - if (account) { + if (account && backendUrl) { if ( state.appLanguage.changed || state.theme.changed || diff --git a/src/pages/onboarding/OnboardingProxy.tsx b/src/pages/onboarding/OnboardingProxy.tsx index e67c0769..00584eef 100644 --- a/src/pages/onboarding/OnboardingProxy.tsx +++ b/src/pages/onboarding/OnboardingProxy.tsx @@ -43,7 +43,7 @@ export function OnboardingProxyPage() { throw new Error("onboarding.proxy.input.errorNotProxy"); setProxySet([url]); - if (account) { + if (account && backendUrl) { await updateSettings(backendUrl, account, { proxyUrls: [url], }); diff --git a/src/pages/parts/admin/BackendTestPart.tsx b/src/pages/parts/admin/BackendTestPart.tsx index 449e9643..aadd9397 100644 --- a/src/pages/parts/admin/BackendTestPart.tsx +++ b/src/pages/parts/admin/BackendTestPart.tsx @@ -32,13 +32,21 @@ export function BackendTestPart() { value: null, }); + if (!backendUrl) { + return setStatus({ + hasTested: true, + success: false, + errorText: "Backend URL is not set", + value: null, + }); + } + try { const backendData = await getBackendMeta(backendUrl); return setStatus({ hasTested: true, success: true, - errorText: - "Failed to call backend, double check the URL key and your internet connection", + errorText: "", value: backendData, }); } catch (err) { @@ -46,7 +54,7 @@ export function BackendTestPart() { hasTested: true, success: false, errorText: - "Failed to call backend, double check the URL key and your internet connection", + "Failed to call backend, double check the URL, your internet connection, and ensure CORS is properly configured on your backend.", value: null, }); } diff --git a/src/pages/parts/auth/LoginFormPart.tsx b/src/pages/parts/auth/LoginFormPart.tsx index e4bed3ec..ac63fad2 100644 --- a/src/pages/parts/auth/LoginFormPart.tsx +++ b/src/pages/parts/auth/LoginFormPart.tsx @@ -52,6 +52,9 @@ export function LoginFormPart(props: LoginFormPartProps) { throw err; } + if (!account) + throw new Error(t("auth.login.validationError") ?? undefined); + await importData(account, progressItems, bookmarkItems); await restore(account); diff --git a/src/pages/parts/auth/TrustBackendPart.tsx b/src/pages/parts/auth/TrustBackendPart.tsx index 3fa818ef..0d71e403 100644 --- a/src/pages/parts/auth/TrustBackendPart.tsx +++ b/src/pages/parts/auth/TrustBackendPart.tsx @@ -22,8 +22,12 @@ interface TrustBackendPartProps { export function TrustBackendPart(props: TrustBackendPartProps) { const navigate = useNavigate(); const backendUrl = useBackendUrl(); - const hostname = useMemo(() => new URL(backendUrl).hostname, [backendUrl]); + const hostname = useMemo( + () => (backendUrl ? new URL(backendUrl).hostname : ""), + [backendUrl], + ); const result = useAsync(() => { + if (!backendUrl) return Promise.resolve(null); return getBackendMeta(backendUrl); }, [backendUrl]); const { t } = useTranslation(); diff --git a/src/pages/parts/auth/VerifyPassphrasePart.tsx b/src/pages/parts/auth/VerifyPassphrasePart.tsx index 2f040ccb..1d6ea62a 100644 --- a/src/pages/parts/auth/VerifyPassphrasePart.tsx +++ b/src/pages/parts/auth/VerifyPassphrasePart.tsx @@ -47,6 +47,8 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) { const [result, execute] = useAsyncFn( async (inputMnemonic: string) => { + if (!backendUrl) + throw new Error(t("auth.verify.noBackendUrl") ?? undefined); if (!props.mnemonic || !props.userData) throw new Error(t("auth.verify.invalidData") ?? undefined); @@ -68,6 +70,9 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) { recaptchaToken, }); + if (!account) + throw new Error(t("auth.verify.registrationFailed") ?? undefined); + await importData(account, progressItems, bookmarkItems); await updateSettings(backendUrl, account, { diff --git a/src/pages/parts/settings/AccountActionsPart.tsx b/src/pages/parts/settings/AccountActionsPart.tsx index e19aa9e9..f1e50dbf 100644 --- a/src/pages/parts/settings/AccountActionsPart.tsx +++ b/src/pages/parts/settings/AccountActionsPart.tsx @@ -18,7 +18,7 @@ export function AccountActionsPart() { const deleteModal = useModal("account-delete"); const [deleteResult, deleteExec] = useAsyncFn(async () => { - if (!account) return; + if (!account || !url) return; await deleteUser(url, account); await logout(); deleteModal.hide(); diff --git a/src/pages/parts/settings/DeviceListPart.tsx b/src/pages/parts/settings/DeviceListPart.tsx index afaea589..c7326059 100644 --- a/src/pages/parts/settings/DeviceListPart.tsx +++ b/src/pages/parts/settings/DeviceListPart.tsx @@ -24,6 +24,7 @@ export function Device(props: { const token = useAuthStore((s) => s.account?.token); const [result, exec] = useAsyncFn(async () => { if (!token) throw new Error("No token present"); + if (!url) throw new Error("No backend set"); await removeSession(url, token, props.id); props.onRemove?.(); }, [url, token, props.id]); diff --git a/src/pages/parts/settings/SidebarPart.tsx b/src/pages/parts/settings/SidebarPart.tsx index 47469a5e..13db06fe 100644 --- a/src/pages/parts/settings/SidebarPart.tsx +++ b/src/pages/parts/settings/SidebarPart.tsx @@ -14,9 +14,9 @@ import { useAuthStore } from "@/stores/auth"; const rem = 16; -function SecureBadge(props: { url: string }) { +function SecureBadge(props: { url: string | undefined }) { const { t } = useTranslation(); - const secure = props.url.startsWith("https://"); + const secure = props.url ? props.url.startsWith("https://") : false; return (

@@ -68,6 +68,7 @@ export function SidebarPart() { const backendUrl = useBackendUrl(); const backendMeta = useAsync(async () => { + if (!backendUrl) return; return getBackendMeta(backendUrl); }, [backendUrl]); @@ -159,7 +160,7 @@ export function SidebarPart() {

- {backendUrl.replace(/https?:\/\//, "")} + {backendUrl?.replace(/https?:\/\//, "") ?? "β€”"}

diff --git a/src/stores/bookmarks/BookmarkSyncer.tsx b/src/stores/bookmarks/BookmarkSyncer.tsx index 1f3b705d..7d4d0bc0 100644 --- a/src/stores/bookmarks/BookmarkSyncer.tsx +++ b/src/stores/bookmarks/BookmarkSyncer.tsx @@ -60,6 +60,7 @@ export function BookmarkSyncer() { useEffect(() => { const interval = setInterval(() => { (async () => { + if (!url) return; const state = useBookmarkStore.getState(); const user = useAuthStore.getState(); await syncBookmarks( diff --git a/src/stores/progress/ProgressSyncer.tsx b/src/stores/progress/ProgressSyncer.tsx index c9b4eb58..67aae877 100644 --- a/src/stores/progress/ProgressSyncer.tsx +++ b/src/stores/progress/ProgressSyncer.tsx @@ -62,6 +62,7 @@ export function ProgressSyncer() { useEffect(() => { const interval = setInterval(() => { (async () => { + if (!url) return; const state = useProgressStore.getState(); const user = useAuthStore.getState(); await syncProgress( diff --git a/src/stores/subtitles/SettingsSyncer.tsx b/src/stores/subtitles/SettingsSyncer.tsx index 48b25bbe..ca3c0817 100644 --- a/src/stores/subtitles/SettingsSyncer.tsx +++ b/src/stores/subtitles/SettingsSyncer.tsx @@ -16,6 +16,7 @@ export function SettingsSyncer() { useEffect(() => { const interval = setInterval(() => { (async () => { + if (!url) return; const state = useSubtitleStore.getState(); const user = useAuthStore.getState(); if (state.lastSync.lastSelectedLanguage === state.lastSelectedLanguage) From 130b4f5cc39478dd90e10f24c5fb0f0ce90420bc Mon Sep 17 00:00:00 2001 From: qtchaos <72168435+qtchaos@users.noreply.github.com> Date: Sun, 25 Feb 2024 21:31:08 +0200 Subject: [PATCH 3/3] feat: disable creating account when server is not set --- src/assets/locales/en.json | 2 + src/pages/parts/auth/TrustBackendPart.tsx | 72 ++++++++++++++--------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index c6a43875..55922380 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -55,6 +55,8 @@ "text": "Did you configure it correctly?", "title": "Failed to reach server" }, + "noHostTitle": "Server not configured!", + "noHost": "The server has not been configured, therefore you cannot create an account", "host": "You are connecting to <0>{{hostname}} - please confirm you trust it before making an account", "no": "Go back", "title": "Do you trust this server?", diff --git a/src/pages/parts/auth/TrustBackendPart.tsx b/src/pages/parts/auth/TrustBackendPart.tsx index 0d71e403..16c16c66 100644 --- a/src/pages/parts/auth/TrustBackendPart.tsx +++ b/src/pages/parts/auth/TrustBackendPart.tsx @@ -23,7 +23,7 @@ export function TrustBackendPart(props: TrustBackendPartProps) { const navigate = useNavigate(); const backendUrl = useBackendUrl(); const hostname = useMemo( - () => (backendUrl ? new URL(backendUrl).hostname : ""), + () => (backendUrl ? new URL(backendUrl).hostname : undefined), [backendUrl], ); const result = useAsync(() => { @@ -54,38 +54,52 @@ export function TrustBackendPart(props: TrustBackendPartProps) { return ( } > - - - + {hostname ? ( + + + + ) : ( +

{t("auth.trust.noHost")}

+ )}
-
- {cardContent} -
- - - - -

- - . - -

+ {hostname ? ( + <> +
+ {cardContent} +
+ + + + +

+ + . + +

+ + ) : ( + + + + )}
); }