feat: Allow updating CloudFlare settings in visitor mode

This commit is contained in:
Peifan Li
2025-12-27 00:54:45 -05:00
parent a059f5e1d1
commit 2902ba81db
4 changed files with 77 additions and 19 deletions

View File

@@ -178,7 +178,7 @@ export const updateSettings = async (
const mergedSettings = { ...defaultSettings, ...existingSettings };
// Check if visitor mode is enabled
// If visitor mode is enabled, only allow disabling it (setting visitorMode to false)
// If visitor mode is enabled, only allow disabling it (setting visitorMode to false) or updating CloudFlare settings
if (mergedSettings.visitorMode === true) {
// If visitorMode is being explicitly set to false, allow the update
if (newSettings.visitorMode === false) {
@@ -206,12 +206,30 @@ export const updateSettings = async (
});
return;
}
// If visitor mode is enabled and trying to change other settings (without explicitly disabling visitor mode), block it
res.status(403).json({
success: false,
error: "Visitor mode is enabled. Only disabling visitor mode is allowed.",
});
return;
// Allow CloudFlare tunnel settings updates (read-only access mechanism, doesn't violate visitor mode)
const isOnlyCloudflareUpdate =
(newSettings.cloudflaredTunnelEnabled !== undefined ||
newSettings.cloudflaredToken !== undefined) &&
Object.keys(newSettings).every(
(key) =>
key === "cloudflaredTunnelEnabled" ||
key === "cloudflaredToken" ||
key === "visitorMode"
);
if (isOnlyCloudflareUpdate) {
// Allow CloudFlare settings updates even in visitor mode
// Continue with normal settings update flow below
} else {
// If visitor mode is enabled and trying to change other settings (without explicitly disabling visitor mode), block it
res.status(403).json({
success: false,
error:
"Visitor mode is enabled. Only disabling visitor mode or updating CloudFlare settings is allowed.",
});
return;
}
}
// Validate settings if needed
@@ -282,6 +300,15 @@ export const updateSettings = async (
}
}
// Preserve CloudFlare settings if not explicitly provided (to prevent stopping tunnel when enabling visitor mode)
if (newSettings.cloudflaredTunnelEnabled === undefined) {
newSettings.cloudflaredTunnelEnabled =
existingSettings.cloudflaredTunnelEnabled;
}
if (newSettings.cloudflaredToken === undefined) {
newSettings.cloudflaredToken = existingSettings.cloudflaredToken;
}
storageService.saveSettings(newSettings);
// Check for moveSubtitlesToVideoFolder change
@@ -315,11 +342,16 @@ export const updateSettings = async (
}
// Handle Cloudflare Tunnel settings changes
if (
// Only process changes if the values were explicitly provided (not undefined)
const cloudflaredEnabledChanged =
newSettings.cloudflaredTunnelEnabled !== undefined &&
newSettings.cloudflaredTunnelEnabled !==
existingSettings.cloudflaredTunnelEnabled ||
newSettings.cloudflaredToken !== existingSettings.cloudflaredToken
) {
existingSettings.cloudflaredTunnelEnabled;
const cloudflaredTokenChanged =
newSettings.cloudflaredToken !== undefined &&
newSettings.cloudflaredToken !== existingSettings.cloudflaredToken;
if (cloudflaredEnabledChanged || cloudflaredTokenChanged) {
// If we are enabling it (or it was enabled and config changed)
if (newSettings.cloudflaredTunnelEnabled) {
// Determine port
@@ -342,8 +374,8 @@ export const updateSettings = async (
cloudflaredService.start(undefined, port);
}
}
} else {
// If disabled, stop
} else if (cloudflaredEnabledChanged) {
// Only stop if explicitly disabled (not if it was undefined)
cloudflaredService.stop();
}
}

View File

@@ -26,12 +26,15 @@ export const visitorModeSettingsMiddleware = (
return;
}
// For POST requests, check if it's trying to disable visitor mode or verify password
// For POST requests, check if it's trying to disable visitor mode, verify password, or update CloudFlare settings
if (req.method === "POST") {
// Allow verify-password requests
if (req.path.includes("/verify-password") || req.url.includes("/verify-password")) {
next();
return;
if (
req.path.includes("/verify-password") ||
req.url.includes("/verify-password")
) {
next();
return;
}
const body = req.body || {};
@@ -41,10 +44,29 @@ export const visitorModeSettingsMiddleware = (
next();
return;
}
// Allow CloudFlare tunnel settings updates (read-only access mechanism, doesn't violate visitor mode)
const isOnlyCloudflareUpdate =
(body.cloudflaredTunnelEnabled !== undefined ||
body.cloudflaredToken !== undefined) &&
Object.keys(body).every(
(key) =>
key === "cloudflaredTunnelEnabled" ||
key === "cloudflaredToken" ||
key === "visitorMode"
);
if (isOnlyCloudflareUpdate) {
// Allow CloudFlare settings updates even in visitor mode
next();
return;
}
// Block all other settings updates
res.status(403).json({
success: false,
error: "Visitor mode is enabled. Only disabling visitor mode is allowed.",
error:
"Visitor mode is enabled. Only disabling visitor mode or updating CloudFlare settings is allowed.",
});
return;
}

View File

@@ -7,10 +7,11 @@ import { useCloudflareStatus } from '../../hooks/useCloudflareStatus';
interface CloudflareSettingsProps {
enabled?: boolean;
token?: string;
visitorMode?: boolean;
onChange: (field: string, value: string | number | boolean) => void;
}
const CloudflareSettings: React.FC<CloudflareSettingsProps> = ({ enabled, token, onChange }) => {
const CloudflareSettings: React.FC<CloudflareSettingsProps> = ({ enabled, token, visitorMode, onChange }) => {
const { t } = useLanguage();
const { showSnackbar } = useSnackbar();
const [showCopied, setShowCopied] = useState(false);
@@ -64,6 +65,7 @@ const CloudflareSettings: React.FC<CloudflareSettingsProps> = ({ enabled, token,
<Switch
checked={enabled ?? false}
onChange={(e) => onChange('cloudflaredTunnelEnabled', e.target.checked)}
disabled={visitorMode ?? false}
/>
}
label={t('enableCloudflaredTunnel')}
@@ -77,6 +79,7 @@ const CloudflareSettings: React.FC<CloudflareSettingsProps> = ({ enabled, token,
value={token || ''}
onChange={(e) => onChange('cloudflaredToken', e.target.value)}
margin="normal"
disabled={visitorMode ?? false}
helperText={t('cloudflaredTokenHelper') || "Paste your tunnel token here, or leave empty to use a random Quick Tunnel."}
/>
)}

View File

@@ -496,6 +496,7 @@ const SettingsPage: React.FC = () => {
<CloudflareSettings
enabled={settings.cloudflaredTunnelEnabled}
token={settings.cloudflaredToken}
visitorMode={visitorMode}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/>
</Grid>