mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-18 17:16:26 +00:00
Merge pull request #1200 from sv-dispatcharr/feat/plugin-hub-disclaimer
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
This commit is contained in:
commit
3586dba677
4 changed files with 244 additions and 36 deletions
120
frontend/src/components/PluginWarnings.jsx
Normal file
120
frontend/src/components/PluginWarnings.jsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import React from 'react';
|
||||
import { Box, Text } from '@mantine/core';
|
||||
import { AlertTriangle, Info, OctagonAlert } from 'lucide-react';
|
||||
import dispatcharrLogo from '../images/logo.png';
|
||||
|
||||
export const PluginSecurityWarning = ({ children }) => (
|
||||
<Box
|
||||
style={{
|
||||
background: 'rgba(239, 68, 68, 0.1)',
|
||||
border: '1px solid rgba(239, 68, 68, 0.35)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
padding: '10px 14px',
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box style={{ color: '#ef4444', flexShrink: 0, paddingTop: 1 }}>
|
||||
<OctagonAlert size={16} />
|
||||
</Box>
|
||||
<Text size="xs" style={{ color: '#f87171' }}>
|
||||
{children}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const PluginSupportDisclaimer = () => (
|
||||
<Box
|
||||
style={{
|
||||
background: 'rgba(20, 145, 126, 0.1)',
|
||||
border: '1px solid rgba(20, 145, 126, 0.35)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
padding: '10px 14px',
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box style={{ flexShrink: 0, paddingTop: 1 }}>
|
||||
<img
|
||||
src={dispatcharrLogo}
|
||||
alt="Dispatcharr"
|
||||
width={16}
|
||||
height={16}
|
||||
draggable={false}
|
||||
style={{ display: 'block', objectFit: 'contain' }}
|
||||
/>
|
||||
</Box>
|
||||
<Text size="xs" style={{ color: '#4db8a8' }}>
|
||||
Dispatcharr community support cannot assist with third-party plugin
|
||||
issues. For help, use the plugin's Discord thread or submit an issue
|
||||
on the plugin's repository.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const PluginDowngradeWarning = ({ children }) => (
|
||||
<Box
|
||||
style={{
|
||||
background: 'rgba(249, 115, 22, 0.1)',
|
||||
border: '1px solid rgba(249, 115, 22, 0.35)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
padding: '10px 14px',
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box style={{ color: '#f97316', flexShrink: 0, paddingTop: 1 }}>
|
||||
<AlertTriangle size={16} />
|
||||
</Box>
|
||||
<Text size="xs" style={{ color: '#fb923c' }}>
|
||||
{children}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const PluginInfoNote = ({ children }) => (
|
||||
<Box
|
||||
style={{
|
||||
background: 'rgba(148, 163, 184, 0.08)',
|
||||
border: '1px solid rgba(148, 163, 184, 0.25)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
padding: '10px 14px',
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box style={{ color: '#94a3b8', flexShrink: 0, paddingTop: 1 }}>
|
||||
<Info size={16} />
|
||||
</Box>
|
||||
<Text size="xs" style={{ color: '#cbd5e1' }}>
|
||||
{children}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const PluginRestartWarning = () => (
|
||||
<Box
|
||||
style={{
|
||||
background: 'rgba(234, 179, 8, 0.1)',
|
||||
border: '1px solid rgba(234, 179, 8, 0.35)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
padding: '10px 14px',
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box style={{ color: '#eab308', flexShrink: 0, paddingTop: 1 }}>
|
||||
<AlertTriangle size={16} />
|
||||
</Box>
|
||||
<Text size="xs" style={{ color: '#ca8a04' }}>
|
||||
Importing a plugin may briefly restart the backend (you might see a
|
||||
temporary disconnect). Please wait a few seconds and the app will
|
||||
reconnect automatically.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
|
@ -27,6 +27,12 @@ import {
|
|||
ShieldCheck,
|
||||
Trash2,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
PluginDowngradeWarning,
|
||||
PluginInfoNote,
|
||||
PluginSecurityWarning,
|
||||
PluginSupportDisclaimer,
|
||||
} from '../PluginWarnings.jsx';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import API from '../../api';
|
||||
import { usePluginStore } from '../../store/plugins';
|
||||
|
|
@ -730,6 +736,13 @@ const AvailablePluginCard = ({
|
|||
stop working with future versions of Dispatcharr. It is recommended
|
||||
to look for an alternative.
|
||||
</Text>
|
||||
<PluginSecurityWarning>
|
||||
Plugins run server-side code with full access to your Dispatcharr
|
||||
instance and its data. Only install plugins from developers you
|
||||
trust. Malicious plugins could read or modify data, call internal
|
||||
APIs, or perform unwanted actions.
|
||||
</PluginSecurityWarning>
|
||||
<PluginSupportDisclaimer />
|
||||
<Text size="sm" fw={500}>
|
||||
Do you still want to proceed?
|
||||
</Text>
|
||||
|
|
@ -830,38 +843,37 @@ const AvailablePluginCard = ({
|
|||
)}
|
||||
.
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
<PluginSecurityWarning>
|
||||
Plugins run server-side code with full access to your
|
||||
Dispatcharr instance and its data. Only install plugins from
|
||||
developers you trust. Malicious plugins could read or modify
|
||||
data, call internal APIs, or perform unwanted actions.
|
||||
</Text>
|
||||
</PluginSecurityWarning>
|
||||
<PluginSupportDisclaimer />
|
||||
{isDowngrade && (
|
||||
<Text size="sm" c="orange">
|
||||
<b>Warning:</b> Downgrading may cause issues with saved
|
||||
settings or data.
|
||||
</Text>
|
||||
<PluginDowngradeWarning>
|
||||
Downgrading may cause issues with saved settings or data.
|
||||
</PluginDowngradeWarning>
|
||||
)}
|
||||
{isBadSig && (
|
||||
<Text size="sm" c="red">
|
||||
<b>Warning:</b> This repository has an invalid or unverified
|
||||
signature. Installing plugins from unverified sources may be
|
||||
risky.
|
||||
</Text>
|
||||
<PluginSecurityWarning>
|
||||
This repository has an invalid or unverified signature.
|
||||
Installing plugins from unverified sources may be risky.
|
||||
</PluginSecurityWarning>
|
||||
)}
|
||||
{plugin.install_status === 'unmanaged' && (
|
||||
<Text size="sm" c="orange">
|
||||
<b>Note:</b> This plugin was installed manually. Installing
|
||||
from this repo will bring it under repo management and enable
|
||||
future update checks.
|
||||
</Text>
|
||||
<PluginInfoNote>
|
||||
This plugin was installed manually. Installing from this repo
|
||||
will bring it under repo management and enable future update
|
||||
checks.
|
||||
</PluginInfoNote>
|
||||
)}
|
||||
{plugin.install_status === 'different_repo' && (
|
||||
<Text size="sm" c="orange">
|
||||
<b>Note:</b> This plugin is currently managed by{' '}
|
||||
<PluginInfoNote>
|
||||
This plugin is currently managed by{' '}
|
||||
<b>{plugin.installed_source_repo_name || 'another repo'}</b>.
|
||||
Installing will transfer management to this repo.
|
||||
</Text>
|
||||
</PluginInfoNote>
|
||||
)}
|
||||
<Text size="sm" fw={500}>
|
||||
Are you sure you want to proceed?
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
Text,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { Ban, Check, FlaskConical, Info, RefreshCw, Settings, Trash2, Zap } from 'lucide-react';
|
||||
import { Ban, Check, Download, FlaskConical, Info, RefreshCw, Settings, Trash2, Zap } from 'lucide-react';
|
||||
import { getConfirmationDetails } from '../../utils/cards/PluginCardUtils.js';
|
||||
import { SUBSCRIPTION_EVENTS } from '../../constants.js';
|
||||
import useSettingsStore from '../../store/settings.jsx';
|
||||
|
|
@ -25,6 +25,11 @@ import { usePluginStore } from '../../store/plugins.jsx';
|
|||
import API from '../../api';
|
||||
import PluginDetailPanel from '../PluginDetailPanel.jsx';
|
||||
import { compareVersions } from '../pluginUtils.js';
|
||||
import {
|
||||
PluginDowngradeWarning,
|
||||
PluginSecurityWarning,
|
||||
PluginSupportDisclaimer,
|
||||
} from '../PluginWarnings.jsx';
|
||||
|
||||
const PluginFieldList = ({ plugin, settings, updateField }) => {
|
||||
return plugin.fields.map((f) => (
|
||||
|
|
@ -128,6 +133,8 @@ const PluginCard = ({
|
|||
const [selectedVersion, setSelectedVersion] = useState(null);
|
||||
const [installing, setInstalling] = useState(false);
|
||||
const [uninstalling] = useState(false);
|
||||
const [installConfirmOpen, setInstallConfirmOpen] = useState(false);
|
||||
const [pendingInstallParams, setPendingInstallParams] = useState(null);
|
||||
|
||||
const installPlugin = usePluginStore((s) => s.installPlugin);
|
||||
|
||||
|
|
@ -311,14 +318,18 @@ const PluginCard = ({
|
|||
};
|
||||
|
||||
const handleDetailInstall = async (params) => {
|
||||
setPendingInstallParams(params);
|
||||
setInstallConfirmOpen(true);
|
||||
};
|
||||
|
||||
const confirmAndInstall = async () => {
|
||||
if (!pendingInstallParams) return;
|
||||
const params = pendingInstallParams;
|
||||
const selVer = params.version;
|
||||
const isDown = plugin.version && compareVersions(selVer, plugin.version) < 0;
|
||||
const action = isDown ? 'downgrade' : 'update';
|
||||
const confirmed = await onRequestConfirm(
|
||||
`${isDown ? 'Downgrade' : 'Update'} ${plugin.name}?`,
|
||||
`${isDown ? 'Downgrade' : 'Update'} from v${plugin.version} to v${selVer}?`
|
||||
);
|
||||
if (!confirmed) return;
|
||||
setInstallConfirmOpen(false);
|
||||
setPendingInstallParams(null);
|
||||
setInstalling(true);
|
||||
try {
|
||||
const result = await installPlugin(params);
|
||||
|
|
@ -616,6 +627,71 @@ const PluginCard = ({
|
|||
)}
|
||||
</Tabs>
|
||||
</Modal>
|
||||
|
||||
{/* Install confirmation modal */}
|
||||
{(() => {
|
||||
const selVer = pendingInstallParams?.version;
|
||||
const isDown = plugin.version && selVer && compareVersions(selVer, plugin.version) < 0;
|
||||
const actionLabel = isDown ? 'Downgrade' : 'Update';
|
||||
return (
|
||||
<Modal
|
||||
opened={installConfirmOpen}
|
||||
onClose={() => {
|
||||
setInstallConfirmOpen(false);
|
||||
setPendingInstallParams(null);
|
||||
}}
|
||||
zIndex={300}
|
||||
title={
|
||||
<Group gap="xs" align="center">
|
||||
{isDown
|
||||
? <Download size={18} color="var(--mantine-color-orange-6)" />
|
||||
: <Download size={18} />}
|
||||
<Text fw={600}>Confirm {actionLabel}</Text>
|
||||
</Group>
|
||||
}
|
||||
size="sm"
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Text size="sm">
|
||||
You are about to {actionLabel.toLowerCase()} <b>{plugin.name}</b>{' '}
|
||||
from <b>v{plugin.version}</b> to <b>v{selVer}</b>.
|
||||
</Text>
|
||||
<PluginSecurityWarning>
|
||||
Plugins run server-side code with full access to your Dispatcharr
|
||||
instance and its data. Only install plugins from developers you
|
||||
trust. Malicious plugins could read or modify data, call internal
|
||||
APIs, or perform unwanted actions.
|
||||
</PluginSecurityWarning>
|
||||
<PluginSupportDisclaimer />
|
||||
{isDown && (
|
||||
<PluginDowngradeWarning>
|
||||
Downgrading may cause issues with saved settings or data.
|
||||
</PluginDowngradeWarning>
|
||||
)}
|
||||
<Text size="sm" fw={500}>Are you sure you want to proceed?</Text>
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button
|
||||
size="xs"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
setInstallConfirmOpen(false);
|
||||
setPendingInstallParams(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color={isDown ? 'orange' : undefined}
|
||||
onClick={confirmAndInstall}
|
||||
>
|
||||
{actionLabel}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ import {
|
|||
} from '../utils/pages/PluginsUtils.js';
|
||||
import { RefreshCcw, Search } from 'lucide-react';
|
||||
import ErrorBoundary from '../components/ErrorBoundary.jsx';
|
||||
import {
|
||||
PluginRestartWarning,
|
||||
PluginSecurityWarning,
|
||||
PluginSupportDisclaimer,
|
||||
} from '../components/PluginWarnings.jsx';
|
||||
const PluginCard = React.lazy(
|
||||
() => import('../components/cards/PluginCard.jsx')
|
||||
);
|
||||
|
|
@ -426,11 +431,8 @@ export default function PluginsPage() {
|
|||
<Text size="sm" c="dimmed">
|
||||
Upload a ZIP containing your plugin folder or package.
|
||||
</Text>
|
||||
<Alert color="yellow" variant="light" title="Heads up">
|
||||
Importing a plugin may briefly restart the backend (you might see a
|
||||
temporary disconnect). Please wait a few seconds and the app will
|
||||
reconnect automatically.
|
||||
</Alert>
|
||||
<PluginRestartWarning />
|
||||
<PluginSupportDisclaimer />
|
||||
<Dropzone
|
||||
onDrop={(files) => files[0] && setImportFile(files[0])}
|
||||
onReject={() => {}}
|
||||
|
|
@ -536,16 +538,14 @@ export default function PluginsPage() {
|
|||
zIndex={300}
|
||||
>
|
||||
<Stack>
|
||||
<Text size="sm">
|
||||
<PluginSecurityWarning>
|
||||
Plugins run server-side code with full access to your Dispatcharr
|
||||
instance and its data. Only enable plugins from developers you
|
||||
trust.
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
Why: Malicious plugins could read or modify data, call internal
|
||||
trust. Malicious plugins could read or modify data, call internal
|
||||
APIs, or perform unwanted actions. Review the source or trust the
|
||||
author before enabling.
|
||||
</Text>
|
||||
</PluginSecurityWarning>
|
||||
<PluginSupportDisclaimer />
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
variant="default"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue