🐞 Add antivirus inside Twake Drive (#725)

🐞 Add antivirus inside Twake Drive (#725)
This commit is contained in:
Montassar Ghanmy
2024-11-19 17:39:18 +01:00
committed by GitHub
parent cc53cd9a94
commit 00f819fb6d
49 changed files with 1341 additions and 257 deletions
@@ -44,7 +44,7 @@ export const useOnBuildContextMenu = (
DriveCurrentFolderAtom({ initialFolderId: initialParentId || 'root' }),
);
const { download, downloadZip, update, restore } = useDriveActions();
const { download, downloadZip, update, restore, reScan } = useDriveActions();
const setCreationModalState = useSetRecoilState(CreateModalAtom);
const setUploadModalState = useSetRecoilState(UploadModelAtom);
const setSelectorModalState = useSetRecoilState(SelectorModalAtom);
@@ -66,6 +66,8 @@ export const useOnBuildContextMenu = (
const inTrash = parent.path?.[0]?.id.includes('trash') || viewId?.includes('trash');
const isPersonal = item?.scope === 'personal';
const selectedCount = checked.length;
const notSafe =
!item?.is_directory && !['uploaded', 'safe'].includes(item?.av_status || '');
let menu: any[] = [];
@@ -75,26 +77,41 @@ export const useOnBuildContextMenu = (
const access = upToDateItem.access || 'none';
const hideShareItem = access === 'read' || getPublicLinkToken() || inTrash;
const hideManageAccessItem =
access === 'read'
|| getPublicLinkToken()
|| inTrash
|| !FeatureTogglesService.isActiveFeatureName(FeatureNames.COMPANY_MANAGE_ACCESS);
access === 'read' ||
getPublicLinkToken() ||
inTrash ||
!FeatureTogglesService.isActiveFeatureName(FeatureNames.COMPANY_MANAGE_ACCESS);
const newMenuActions = [
{
type: 'menu',
icon: 'share-alt',
text: Languages.t('components.item_context_menu.share'),
hide: hideShareItem,
hide: hideShareItem || notSafe,
onClick: () => setPublicLinkModalState({ open: true, id: item.id }),
},
{
type: 'menu',
icon: 'users-alt',
text: Languages.t('components.item_context_menu.manage_access'),
hide: hideManageAccessItem,
hide: hideManageAccessItem || notSafe,
onClick: () => setAccessModalState({ open: true, id: item.id }),
},
{ type: 'separator', hide: inTrash || (hideShareItem && hideManageAccessItem) },
{
type: 'menu',
icon: 'shield-check',
text: Languages.t('components.item_context_menu.rescan_document'),
hide: !(item.av_status === 'scan_failed'),
onClick: () => {
reScan(item);
},
},
{
type: 'separator',
hide:
inTrash ||
(hideShareItem && hideManageAccessItem) ||
(notSafe && !(item.av_status === 'scan_failed')),
},
{
type: 'menu',
icon: 'download-alt',
@@ -104,7 +121,7 @@ export const useOnBuildContextMenu = (
downloadZip([item!.id]);
console.log(item!.id);
} else {
download(item.id);
download(item.id, notSafe);
}
},
},
@@ -120,12 +137,12 @@ export const useOnBuildContextMenu = (
window.open(route, '_blank');
}
}, // */
{ type: 'separator' },
{ type: 'separator', hide: notSafe },
{
type: 'menu',
icon: 'folder-question',
text: Languages.t('components.item_context_menu.move'),
hide: access === 'read' || inTrash || inPublicSharing,
hide: access === 'read' || inTrash || inPublicSharing || notSafe,
onClick: () =>
setSelectorModalState({
open: true,
@@ -150,7 +167,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'file-edit-alt',
text: Languages.t('components.item_context_menu.rename'),
hide: access === 'read' || inTrash,
hide: access === 'read' || inTrash || notSafe,
onClick: () => setPropertiesModalState({ open: true, id: item.id, inPublicSharing }),
},
{
@@ -160,7 +177,8 @@ export const useOnBuildContextMenu = (
hide:
!item.access_info.public?.level ||
item.access_info.public?.level === 'none' ||
inTrash,
inTrash ||
notSafe,
onClick: () => {
copyToClipboard(getPublicLink(item || parent?.item));
ToasterService.success(
@@ -172,10 +190,10 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'history',
text: Languages.t('components.item_context_menu.versions'),
hide: item.is_directory || inTrash,
hide: item.is_directory || inTrash || notSafe,
onClick: () => setVersionModal({ open: true, id: item.id }),
},
{ type: 'separator', hide: access !== 'manage' || inTrash },
{ type: 'separator', hide: access !== 'manage' || inTrash || notSafe },
{
type: 'menu',
icon: 'trash',
@@ -238,15 +256,25 @@ export const useOnBuildContextMenu = (
type: 'menu',
text: Languages.t('components.item_context_menu.download_multiple'),
hide: inTrash,
onClick: () =>
selectedCount === 1 ? download(checked[0].id) : downloadZip(checked.map(c => c.id)),
onClick: () => {
const containsMalicious = checked.some(c => c.av_status === 'malicious');
if (selectedCount === 1) {
download(checked[0].id);
} else {
downloadZip(
checked.map(c => c.id),
false,
containsMalicious,
);
}
},
},
{
type: 'menu',
text: Languages.t('components.item_context_menu.clear_selection'),
onClick: () => setChecked({}),
},
{ type: 'separator', hide: parent.access === 'read' },
{ type: 'separator', hide: parent.access === 'read' || notSafe },
{
type: 'menu',
text: Languages.t('components.item_context_menu.delete_multiple'),
@@ -517,7 +545,9 @@ export const useOnBuildFileContextMenu = () => {
{
type: 'menu',
text: Languages.t('components.item_context_menu.download'),
onClick: () => download(item.id),
onClick: () => {
download(item.id);
},
},
];
return menuItems;
@@ -1,19 +1,29 @@
import { DotsHorizontalIcon } from '@heroicons/react/outline';
import {
DotsHorizontalIcon,
ShieldCheckIcon,
ShieldExclamationIcon,
BanIcon,
} from '@heroicons/react/outline';
import { Button } from '@atoms/button/button';
import { Base, BaseSmall } from '@atoms/text';
import Menu from '@components/menus/menu';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useDrivePreview } from '@features/drive/hooks/use-drive-preview';
import { formatBytes } from '@features/drive/utils';
import Languages from '@features/global/services/languages-service';
import { useState } from 'react';
import { PublicIcon } from '../components/public-icon';
import { CheckableIcon, DriveItemOverlayProps, DriveItemProps } from './common';
import './style.scss';
import { useHistory } from 'react-router-dom';
import RouterServices from '@features/router/services/router-service';
import { DocumentIcon } from './document-icon';
import { hasAnyPublicLinkAccess } from '@features/files/utils/access-info-helpers';
import { formatDateShort } from 'app/features/global/utils/Numbers';
import FeatureTogglesService, {
FeatureNames,
} from '@features/global/services/feature-toggles-service';
export const DocumentRow = ({
item,
@@ -27,6 +37,7 @@ export const DocumentRow = ({
const [hover, setHover] = useState(false);
const {open} = useDrivePreview();
const company = useRouterCompany();
const notSafe = ['malicious', 'skipped', 'scan_failed'].includes(item.av_status);
const preview = () => {
open(item);
@@ -39,7 +50,7 @@ export const DocumentRow = ({
className={
'flex flex-row items-center border border-zinc-200 dark:border-zinc-800 px-4 py-3 cursor-pointer ' +
(checked
? 'bg-blue-500 bg-opacity-10 hover:bg-opacity-25 '
? (notSafe ? 'bg-rose-500' : 'bg-blue-500') + ' bg-opacity-10 hover:bg-opacity-25'
: 'hover:bg-zinc-500 hover:bg-opacity-10 ') +
(className || '')
}
@@ -48,7 +59,9 @@ export const DocumentRow = ({
onClick={e => {
if (e.shiftKey || e.ctrlKey) onCheck(!checked);
else if (onClick) onClick();
else preview();
else {
if (!notSafe) preview();
}
}}
>
<div
@@ -76,6 +89,20 @@ export const DocumentRow = ({
<div className="shrink-0 ml-4 text-right lg:w-24 sm:w-20 ">
<BaseSmall>{formatBytes(item.size)}</BaseSmall>
</div>
{FeatureTogglesService.isActiveFeatureName(FeatureNames.COMPANY_AV_ENABLED) && (
<div className="shrink-0 ml-4 text-right lg:w-24 sm:w-20 ">
<BaseSmall title={Languages.t(`scenes.app.drive.document_row.av_${item?.av_status}`)}>
{item?.av_status === 'scanning' && (
<ShieldExclamationIcon className="w-5 text-yellow-400" />
)}
{item?.av_status === 'malicious' && (
<ShieldExclamationIcon className="w-5 text-rose-400" />
)}
{item?.av_status === 'skipped' && <BanIcon className="w-5 text-gray-400" />}
{item?.av_status === 'scan_failed' && <BanIcon className="w-5 text-gray-400" />}
</BaseSmall>
</div>
)}
<div className="shrink-0 ml-4">
<Menu menu={onBuildContextMenu}>
<Button
@@ -49,7 +49,7 @@ const VersionModalContent = ({ id }: { id: string }) => {
if (!item?.last_version_cache) return <></>;
return (
<ModalContent title={Languages.t('components.VersionModalContent_version') + " " + item?.name}>
<ModalContent title={Languages.t('components.VersionModalContent_version') + ' ' + item?.name}>
<UploadZone
overClassName={'!m-4'}
disableClick
@@ -66,12 +66,14 @@ const VersionModalContent = ({ id }: { id: string }) => {
}}
>
{access !== 'read' && (
<div className={'flex flex-row items-center bg-zinc-100 dark:bg-zinc-900 rounded-md mb-4 p-4'}>
<div
className={
'flex flex-row items-center bg-zinc-100 dark:bg-zinc-900 rounded-md mb-4 p-4'
}
>
<div className="flex flex-row">
<div className="grow flex items-center">
<Base>
{Languages.t('components.VersionModalContent_version_dec')}
</Base>
<Base>{Languages.t('components.VersionModalContent_version_dec')}</Base>
</div>
<div className="shrink-0 ml-4 flex items-center">
<Button
@@ -108,8 +110,11 @@ const VersionModalContent = ({ id }: { id: string }) => {
<BaseSmall>{formatBytes(version.file_metadata.size || 0)}</BaseSmall>
</div>
<div className="shrink-0 ml-4">
<Button theme="outline" onClick={() => download(id, version.id)}>
{Languages.t('components.VersionModalContent_donwload')}
<Button
theme="outline"
onClick={() => download(id, item.av_status === 'malicious', version.id)}
>
{Languages.t('components.VersionModalContent_donwload')}
</Button>
</div>
</div>
@@ -26,7 +26,7 @@ export default class ErrorBoundary extends React.Component<PropsType, { hasError
ErrorBoundary.lastError = {
error: {
name: error,
info: errorInfo.componentStack,
info: "",
},
};
RouterServices.replace(RouterServices.addRedirection(RouterServices.pathnames.ERROR));