Skip to content

Commit

Permalink
feat: added an option to trigger a manual rescan
Browse files Browse the repository at this point in the history
  • Loading branch information
MontaGhanmy committed Nov 12, 2024
1 parent 2197472 commit 8ca7c15
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 10 deletions.
67 changes: 67 additions & 0 deletions tdrive/backend/node/src/services/documents/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,73 @@ export class DocumentsService {
}
};

/**
* Triggers an AV Rescan for the document.
*
* @param {string} id - the Drive item id to rescan.
* @param {DriveExecutionContext} context - the company execution context
* @returns {Promise<DriveFile>} - the DriveFile after the rescan has been triggered
*/
reScan = async (id: string, context: DriveExecutionContext): Promise<DriveFile> => {
if (!context) {
this.logger.error("invalid execution context");
return null;
}

try {
const hasAccess = await checkAccess(id, null, "write", this.repository, context);
if (!hasAccess) {
this.logger.error("user does not have access drive item ", id);
throw Error("user does not have access to this item");
}

const item = await this.repository.findOne(
{
id,
company_id: context.company.id,
},
{},
context,
);

if (!item) {
throw Error("Drive item not found");
}

if (item.is_directory) {
throw Error("cannot create version for a directory");
}

// If AV feature is enabled, scan the file
if (globalResolver.services.av?.avEnabled) {
try {
item.av_status = await globalResolver.services.av.scanDocument(
item,
item.last_version_cache,
async (av_status: AVStatus) => {
// Update the AV status of the file
await this.handleAVStatusUpdate(item, av_status, context);
},
context,
);
await this.repository.save(item);
if (item.av_status === "skipped") {
// Notify the user that the document has been skipped
await this.notifyAVScanAlert(item, context);
}
} catch (error) {
this.logger.error(
`Error scanning file ${item.last_version_cache.file_metadata.external_id}`,
);
}
}
return item;
} catch (error) {
logger.error({ error: `${error}` }, "Failed to create Drive item version");
CrudException.throwMe(error, new CrudException("Failed to create Drive item version", 500));
}
};

/**
* If not already in an editing session, uses the `editing_session_key` of the
* `DriveFile` entity to store a unique new value to expect an update later
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,35 @@ export class DocumentsController {
}
};

/**
* Triggers an AV Rescan for the document.
*
* @param {FastifyRequest} request
* @returns {Promise<DriveFile>}
*/
reScan = async (
request: FastifyRequest<{
Params: ItemRequestParams;
Body: Partial<any>;
Querystring: { public_token?: string };
}>,
): Promise<DriveFile | any> => {
try {
const context = getDriveExecutionContext(request);
const { id } = request.params;

if (!id) throw new CrudException("Missing id", 400);

return await globalResolver.services.documents.documents.reScan(id, context);
} catch (error) {
logger.error({ error: `${error}` }, "Failed to trigger AV rescan for Drive item");
CrudException.throwMe(
error,
new CrudException("Failed to trigger AV rescan for Drive item", 500),
);
}
};

/**
* Begin an editing session if none exists, or return the existing one
* @returns The `editing_session_key` that was either set or already was there
Expand Down
7 changes: 7 additions & 0 deletions tdrive/backend/node/src/services/documents/web/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, _options, next)
handler: documentsController.beginEditing.bind(documentsController),
});

fastify.route({
method: "POST",
url: `${serviceUrl}/:id/rescan`,
preValidation: [fastify.authenticateOptional],
handler: documentsController.reScan.bind(documentsController),
});

fastify.route({
method: "GET",
url: editingSessionBase, //TODO NONONO check authenticate*Optional*
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"components.item_context_menu.move.modal_header": "Move",
"components.item_context_menu.move_multiple": "Move selected items",
"components.item_context_menu.move_multiple.modal_header": "Move selected items",
"components.item_context_menu.rescan_document": "Re-scan for viruses",
"components.item_context_menu.move_to_trash": "Delete",
"components.item_context_menu.open_new_window": "Open in new window",
"components.item_context_menu.preview": "Preview",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"components.item_context_menu.move.modal_header": "Déplacer",
"components.item_context_menu.move_multiple": "Déplacer",
"components.item_context_menu.move_multiple.modal_header": "Déplacer les éléments sélectionnés",
"components.item_context_menu.rescan_document": "Re-scanner pour les virus",
"components.item_context_menu.move_to_trash": "Supprimer",
"components.item_context_menu.open_new_window": "Ouvrir dans une nouvelle fenêtre",
"components.item_context_menu.preview": "Aperçu",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"components.item_context_menu.move.modal_header": "Переместить",
"components.item_context_menu.move_multiple": "Переместить все",
"components.item_context_menu.move_multiple.modal_header": "Переместить выбранные елементы",
"components.item_context_menu.rescan_document": "Повторное сканирование на вирусы",
"components.item_context_menu.move_to_trash": "Удалить",
"components.item_context_menu.open_new_window": "Открыть в новом окне",
"components.item_context_menu.preview": "Просмотр",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"components.item_context_menu.move.modal_header": "Di chuyển",
"components.item_context_menu.move_multiple": "Di chuyển các mục đã chọn",
"components.item_context_menu.move_multiple.modal_header": "Di chuyển các mục đã chọn",
"components.item_context_menu.rescan_document": "Quét lại để tìm virus",
"components.item_context_menu.move_to_trash": "Xóa",
"components.item_context_menu.open_new_window": "Mở trong cửa sổ mới",
"components.item_context_menu.preview": "Xem trước",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ export class DriveApiClient {
);
}

static async reScan(companyId: string, id: string) {
return await Api.post<any, DriveItem>(
`/internal/services/documents/v1/companies/${companyId}/item/${id}/rescan${appendTdriveToken()}`,
{},
);
}

static getDownloadUrl(companyId: string, id: string, versionId?: string) {
if (versionId)
return Api.route(`/internal/services/documents/v1/companies/${companyId}/item/${id}/download?version_id=${versionId}`);
Expand Down
49 changes: 44 additions & 5 deletions tdrive/frontend/src/app/features/drive/hooks/use-drive-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import useRouterCompany from '@features/router/hooks/use-router-company';
import { useCallback } from 'react';
import { useRecoilValue, useRecoilCallback, useRecoilState } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import { DriveItemAtom, DriveItemChildrenAtom, DriveItemPagination, DriveItemSort } from '../state/store';
import {
DriveItemAtom,
DriveItemChildrenAtom,
DriveItemPagination,
DriveItemSort,
} from '../state/store';
import { BrowseFilter, DriveItem, DriveItemVersion } from '../types';
import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
import Languages from 'features/global/services/languages-service';
Expand Down Expand Up @@ -35,7 +40,13 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
set(DriveItemPagination, pagination);
}
try {
const details = await DriveApiClient.browse(companyId, parentId, filter, sortItem, pagination);
const details = await DriveApiClient.browse(
companyId,
parentId,
filter,
sortItem,
pagination,
);
set(DriveItemChildrenAtom(parentId), details.children);
set(DriveItemAtom(parentId), details);
for (const child of details.children) {
Expand Down Expand Up @@ -142,7 +153,12 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
try {
const newItem = await DriveApiClient.update(companyId, id, update);
if (previousName && previousName !== newItem.name && !update.name)
ToasterService.warn(Languages.t('hooks.use-drive-actions.update_caused_a_rename', [previousName, newItem.name]));
ToasterService.warn(
Languages.t('hooks.use-drive-actions.update_caused_a_rename', [
previousName,
newItem.name,
]),
);
await refresh(id || '', true);
if (!inPublicSharing) await refresh(parentId || '', true);
if (update?.parent_id !== parentId) await refresh(update?.parent_id || '', true);
Expand Down Expand Up @@ -183,12 +199,35 @@ export const useDriveActions = (inPublicSharing?: boolean) => {
parentId,
filter,
sortItem,
pagination
pagination,
);
return details;
},
[paginateItem, refresh],
);

return { create, refresh, download, downloadZip, remove, restore, update, updateLevel, nextPage };
const reScan = useCallback(
async (item: Partial<DriveItem>) => {
try {
await DriveApiClient.reScan(companyId, item.id || '');
await refresh(item.parent_id || '', true);
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_rescan_file'));
}
},
[refresh],
);

return {
create,
refresh,
download,
downloadZip,
remove,
restore,
update,
updateLevel,
reScan,
nextPage,
};
};
23 changes: 19 additions & 4 deletions tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,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);
Expand Down Expand Up @@ -102,7 +102,22 @@ export const useOnBuildContextMenu = (
hide: hideManageAccessItem || notSafe,
onClick: () => setAccessModalState({ open: true, id: item.id }),
},
{ type: 'separator', hide: inTrash || (hideShareItem && hideManageAccessItem) || notSafe },
{
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',
Expand Down Expand Up @@ -185,7 +200,7 @@ export const useOnBuildContextMenu = (
hide: item.is_directory || inTrash || notSafe,
onClick: () => setVersionModal({ open: true, id: item.id }),
},
{ type: 'separator', hide: (access !== 'manage') || inTrash || notSafe },
{ type: 'separator', hide: access !== 'manage' || inTrash || notSafe },
{
type: 'menu',
icon: 'trash',
Expand Down Expand Up @@ -256,7 +271,7 @@ export const useOnBuildContextMenu = (
text: Languages.t('components.item_context_menu.clear_selection'),
onClick: () => setChecked({}),
},
{ type: 'separator', hide: (parent.access === 'read') || notSafe },
{ type: 'separator', hide: parent.access === 'read' || notSafe },
{
type: 'menu',
text: Languages.t('components.item_context_menu.delete_multiple'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
DotsHorizontalIcon,
ShieldCheckIcon,
ShieldExclamationIcon,
BanIcon
BanIcon,
} from '@heroicons/react/outline';
import { Button } from '@atoms/button/button';
import { Base, BaseSmall } from '@atoms/text';
Expand Down Expand Up @@ -97,6 +97,7 @@ export const DocumentRow = ({
<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>
)}
Expand Down

0 comments on commit 8ca7c15

Please sign in to comment.