Skip to content

Commit

Permalink
馃洜 Fix zip download and few frontend issues
Browse files Browse the repository at this point in the history
  • Loading branch information
RomaricMourgues committed Mar 2, 2023
1 parent b2fa450 commit f7876c2
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 87 deletions.
Expand Up @@ -42,7 +42,7 @@ export class CompanyApplicationServiceImpl implements TwakeServiceProvider, Init

// TODO: remove logic from context
async get(
pk: CompanyApplicationPrimaryKey,
pk: Pick<CompanyApplicationPrimaryKey, "company_id" | "application_id"> & { id?: string },
context?: CompanyExecutionContext,
): Promise<CompanyApplicationWithApplication> {
const companyApplication = await this.repository.findOne(
Expand Down
54 changes: 24 additions & 30 deletions twake/backend/node/src/services/documents/services/index.ts
Expand Up @@ -236,11 +236,15 @@ export class DocumentsService {
driveItemVersion.file_metadata.mime = fileToProcess.metadata.mime;
driveItemVersion.file_metadata.size = fileToProcess.upload_data.size;
driveItemVersion.file_metadata.name = fileToProcess.metadata.name;
if (context.user.application_id) {
driveItemVersion.application_id = context.user.application_id;
}
}
}

driveItem.name = await getItemName(
driveItem.parent_id,
driveItem.id,
driveItem.name,
driveItem.is_directory,
this.repository,
Expand Down Expand Up @@ -335,6 +339,7 @@ export class DocumentsService {
if (key === "name") {
item.name = await getItemName(
content.parent_id || item.parent_id,
item.id,
content.name,
item.is_directory,
this.repository,
Expand Down Expand Up @@ -551,6 +556,9 @@ export class DocumentsService {
driveItemVersion.file_metadata.name = metadata.name;
driveItemVersion.file_metadata.mime = metadata.mime;
driveItemVersion.drive_item_id = item.id;
if (context.user.application_id) {
driveItemVersion.application_id = context.user.application_id;
}

await this.fileVersionRepository.save(driveItemVersion);

Expand Down Expand Up @@ -603,13 +611,13 @@ export class DocumentsService {
}>(token);
if (
ids.some(a => !(v?.ids || [])?.includes(a)) ||
(v?.version_id && v?.version_id !== versionId)
(v?.version_id && versionId && v?.version_id !== versionId)
) {
return;
}

context.company.id = v.company_id;
context.user.id = v.user_id;
context.company = { id: v.company_id };
context.user = { id: v.user_id };
} catch (e) {
if (token) throw new CrudException("Invalid token", 401);
}
Expand Down Expand Up @@ -666,36 +674,22 @@ export class DocumentsService {
zlib: { level: 9 },
});

let counter = ids.length;

await Promise.all(
ids.map(async id => {
if (!(await checkAccess(id, null, "read", this.repository, context))) {
this.logger.warn(`not enough permissions to download ${id}, skipping`);
counter--;
return;
}

try {
counter = await addDriveItemToArchive(
id,
null,
archive,
this.repository,
context,
counter,
);
} catch (error) {
this.logger.warn("failed to add item to archive", error);
throw new Error("Failed to add item to archive");
}
}),
);
for (const id of ids) {
if (!(await checkAccess(id, null, "read", this.repository, context))) {
this.logger.warn(`not enough permissions to download ${id}, skipping`);
return;
}

if (counter === 0) {
archive.finalize();
try {
await addDriveItemToArchive(id, null, archive, this.repository, context);
} catch (error) {
console.error(error);
this.logger.warn("failed to add item to archive", error);
}
}

archive.finalize();

return archive;
};

Expand Down
51 changes: 30 additions & 21 deletions twake/backend/node/src/services/documents/utils.ts
Expand Up @@ -294,7 +294,7 @@ export const getPath = async (
company_id: context.company.id,
});

if (!item || (!checkAccess(id, item, "read", repository, context) && !ignoreAccess)) {
if (!item || (!checkAccess(item.parent_id, null, "read", repository, context) && !ignoreAccess)) {
return [];
}

Expand Down Expand Up @@ -343,6 +343,18 @@ export const getAccessLevel = async (
repository: Repository<DriveFile>,
context: CompanyExecutionContext & { public_token?: string; twake_tab_token?: string },
): Promise<DriveFileAccessLevel | "none"> => {
if (
context.user?.application_id &&
(
await globalResolver.services.applications.companyApps.get({
company_id: context.company.id,
application_id: context.user.application_id,
})
)?.application?.access //TODO check precise access right
) {
return "manage";
}

if (!id || id === "root")
return !context?.user?.id ? "none" : (await isCompanyGuest(context)) ? "read" : "manage";
if (id === "trash")
Expand Down Expand Up @@ -520,9 +532,8 @@ export const addDriveItemToArchive = async (
archive: archiver.Archiver,
repository: Repository<DriveFile>,
context: CompanyExecutionContext,
counter: number,
prefix?: string,
): Promise<number> => {
): Promise<void> => {
const item = entity || (await repository.findOne({ id, company_id: context.company.id }));

if (!item) {
Expand All @@ -538,30 +549,25 @@ export const addDriveItemToArchive = async (
}

archive.append(file.file, { name: file.name, prefix: prefix ?? "" });
return counter - 1;
return;
} else {
const items = await repository.find({
parent_id: item.id,
company_id: context.company.id,
});

let currentCounter = counter;

await Promise.all(
items.getEntities().map(async child => {
currentCounter = await addDriveItemToArchive(
child.id,
child,
archive,
repository,
context,
currentCounter,
`${item.name}/`,
);
}),
);
for (const child of items.getEntities()) {
await addDriveItemToArchive(
child.id,
child,
archive,
repository,
context,
`${prefix || ""}${item.name}/`,
);
}

return currentCounter;
return;
}
};

Expand Down Expand Up @@ -686,6 +692,7 @@ export const getFileMetadata = async (
*/
export const getItemName = async (
parent_id: string,
id: string,
name: string,
is_directory: boolean,
repository: Repository<DriveFile>,
Expand All @@ -706,7 +713,9 @@ export const getItemName = async (
while (exists) {
exists = !!children
.getEntities()
.find(child => child.name === newName && child.is_directory === is_directory);
.find(
child => child.name === newName && child.is_directory === is_directory && child.id !== id,
);

if (exists) {
const ext = newName.split(".").pop();
Expand Down
Expand Up @@ -122,6 +122,8 @@ export class DocumentsController {
const context = getDriveExecutionContext(request);
const { id } = request.params;

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

return {
...(await globalResolver.services.documents.documents.get(id, context)),
websockets: request.currentUser?.id
Expand Down Expand Up @@ -150,6 +152,8 @@ export class DocumentsController {
const { id } = request.params;
const update = request.body;

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

return await globalResolver.services.documents.documents.update(id, update, context);
};

Expand All @@ -170,6 +174,8 @@ export class DocumentsController {
const { id } = request.params;
const version = request.body;

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

return await globalResolver.services.documents.documents.createVersion(id, version, context);
};

Expand Down Expand Up @@ -263,7 +269,7 @@ export class DocumentsController {
reply: FastifyReply,
): Promise<void> => {
const context = getDriveExecutionContext(request);
const ids = (request.query.items || "").split(",");
let ids = (request.query.items || "").split(",");
const token = request.query.token;

await globalResolver.services.documents.documents.applyDownloadTokenToContext(
Expand All @@ -273,6 +279,11 @@ export class DocumentsController {
context,
);

if (ids[0] === "root") {
const items = await globalResolver.services.documents.documents.get(ids[0], context);
ids = items.children.map(item => item.id);
}

try {
const archive = await globalResolver.services.documents.documents.createZip(ids, context);

Expand Down
Expand Up @@ -64,7 +64,7 @@ export default (): React.ReactElement => {
: Languages.t(
'components.invitation.title',
[workspace.workspace?.name],
`Invite people to ${workspace.workspace?.name}`,
`Invite users to ${workspace.workspace?.name}`,
)
}
>
Expand Down
22 changes: 12 additions & 10 deletions twake/frontend/src/app/features/drive/hooks/use-drive-actions.tsx
Expand Up @@ -17,17 +17,19 @@ export const useDriveActions = () => {
const refresh = useRecoilCallback(
({ set, snapshot }) =>
async (parentId: string) => {
try {
const details = await DriveApiClient.get(companyId, parentId);
set(DriveItemChildrenAtom(parentId), details.children);
set(DriveItemAtom(parentId), details);
for (const child of details.children) {
const currentValue = snapshot.getLoadable(DriveItemAtom(child.id)).contents;
set(DriveItemAtom(child.id), { ...currentValue, item: child });
if (parentId) {
try {
const details = await DriveApiClient.get(companyId, parentId);
set(DriveItemChildrenAtom(parentId), details.children);
set(DriveItemAtom(parentId), details);
for (const child of details.children) {
const currentValue = snapshot.getLoadable(DriveItemAtom(child.id)).contents;
set(DriveItemAtom(child.id), { ...currentValue, item: child });
}
return details;
} catch (e) {
ToasterService.error('Unable to load your files.');
}
return details;
} catch (e) {
ToasterService.error('Unable to load your files.');
}
},
[companyId],
Expand Down
12 changes: 7 additions & 5 deletions twake/frontend/src/app/views/applications/drive/browser.tsx
Expand Up @@ -134,15 +134,17 @@ export default memo(
(loading && !children?.length ? 'opacity-50 ' : '')
}
>
{document.location.origin.includes('canary') && access !== 'read' && (
{document.location.origin.includes('canary') && access !== 'read' && !inPublicSharing && (
<div className="bg-linear-purple w-full hidden sm:block px-4 py-2 rounded-md">
<Base className=" !text-white">
Welcome to the next version of Twake Drive.
<br />
Your documents are not migrated yet, you can switch back to{' '}
<a href="https://web.twake.app">https://web.twake.app</a> to see all your documents.
Documents added here will not be visible yet on production but will be kept after
the final migration.
<a className="text-white" href="https://web.twake.app">
https://web.twake.app
</a>{' '}
to see all your documents. Documents added here will not be visible yet on
production but will be kept after the final migration.
</Base>
</div>
)}
Expand Down Expand Up @@ -259,7 +261,7 @@ export default memo(
ToasterService.success('Public link copied to clipboard');
},
},
{ type: 'separator' },
{ type: 'separator', hide: inTrash || access === 'read' },
{
type: 'menu',
text: 'Go to trash',
Expand Down
@@ -1,10 +1,5 @@
import { CloudIcon } from '@heroicons/react/solid';
import Tooltip from 'app/components/tooltip/tooltip';

export const PublicIcon = ({ className }: { className?: string }) => {
return (
<Tooltip className={'flex ' + className} position="top" tooltip={'Available from public link'}>
<CloudIcon className={className} />
</Tooltip>
);
return <CloudIcon className={className} />;
};
Expand Up @@ -105,7 +105,7 @@ 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.drive_item_id)}>
<Button theme="outline" onClick={() => download(id, version.id)}>
Download
</Button>
</div>
Expand Down
Expand Up @@ -36,7 +36,7 @@ export default ({ context }: { context?: EmbedContext }) => {
}
}, [modalOpen]);

if (!item && !tab && (loading || itemLoading)) return <></>;
if (!item && !tab) return <></>;

// If configured then show the content of the tab and forward the fact that the access is done through a specific channel
return (
Expand All @@ -47,7 +47,7 @@ export default ({ context }: { context?: EmbedContext }) => {
twakeTabContextToken={context?.channelId + '+' + context?.tabId}
/>
)}
{!isConfigured && !loading && !itemLoading && (
{!isConfigured && !loading && !(tab?.item_id && itemLoading) && (
<div className="w-full h-full flex items-center justify-center">
<div className="text-center">
<Info>This Documents tabs is not configured yet.</Info>
Expand Down
Expand Up @@ -21,6 +21,7 @@ export const DrivePreview = (): React.ReactElement => {
const { isOpen, close, loading } = useDrivePreview();
const [modalLoading, setModalLoading] = useState(true);
const { loading: loadingData } = useDrivePreviewLoading();
const { type = '' } = useDrivePreviewDisplayData();
let animationTimeout: number = setTimeout(() => undefined);

useEffect(() => {
Expand Down Expand Up @@ -50,7 +51,7 @@ export const DrivePreview = (): React.ReactElement => {
positioned={false}
>
<XIcon
className="z-10 cursor-pointer absolute right-5 top-5 w-12 h-12 text-zinc-300 hover:text-white rounded-full p-1 hover:bg-black hover:bg-opacity-25"
className="z-10 cursor-pointer absolute right-5 top-5 w-12 h-12 text-zinc-300 hover:text-white rounded-full p-1 bg-black bg-opacity-25"
onClick={() => close()}
/>

Expand Down

0 comments on commit f7876c2

Please sign in to comment.