|
@@ -1,7 +1,7 @@
|
|
|
import type { Readable } from 'stream';
|
|
import type { Readable } from 'stream';
|
|
|
import { Writable, pipeline } from 'stream';
|
|
import { Writable, pipeline } from 'stream';
|
|
|
|
|
|
|
|
-import { type IPage, isPopulated } from '@growi/core';
|
|
|
|
|
|
|
+import { type IPage, isPopulated, SubscriptionStatusType } from '@growi/core';
|
|
|
import { normalizePath } from '@growi/core/dist/utils/path-utils';
|
|
import { normalizePath } from '@growi/core/dist/utils/path-utils';
|
|
|
import type { Archiver } from 'archiver';
|
|
import type { Archiver } from 'archiver';
|
|
|
import archiver from 'archiver';
|
|
import archiver from 'archiver';
|
|
@@ -11,7 +11,9 @@ import mongoose from 'mongoose';
|
|
|
|
|
|
|
|
import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
|
|
import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
|
|
|
import type { PageModel, PageDocument } from '~/server/models/page';
|
|
import type { PageModel, PageDocument } from '~/server/models/page';
|
|
|
|
|
+import Subscription from '~/server/models/subscription';
|
|
|
import type { IAwsMultipartUploader } from '~/server/service/file-uploader/aws/multipart-upload';
|
|
import type { IAwsMultipartUploader } from '~/server/service/file-uploader/aws/multipart-upload';
|
|
|
|
|
+import { preNotifyService } from '~/server/service/pre-notify';
|
|
|
import { getBufferToFixedSizeTransform } from '~/server/util/stream';
|
|
import { getBufferToFixedSizeTransform } from '~/server/util/stream';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
|
@@ -26,6 +28,11 @@ interface ArchiverWithQueue extends Archiver {
|
|
|
_queue?: QueueObject<any>;
|
|
_queue?: QueueObject<any>;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+type ActivityParameters ={
|
|
|
|
|
+ ip: string;
|
|
|
|
|
+ endpoint: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
class PageBulkExportService {
|
|
class PageBulkExportService {
|
|
|
|
|
|
|
|
crowi: any;
|
|
crowi: any;
|
|
@@ -42,7 +49,7 @@ class PageBulkExportService {
|
|
|
this.activityEvent = crowi.event('activity');
|
|
this.activityEvent = crowi.event('activity');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async bulkExportWithBasePagePath(basePagePath: string, currentUser): Promise<void> {
|
|
|
|
|
|
|
+ async bulkExportWithBasePagePath(basePagePath: string, currentUser, activityParameters: ActivityParameters): Promise<void> {
|
|
|
const Page = mongoose.model<IPage, PageModel>('Page');
|
|
const Page = mongoose.model<IPage, PageModel>('Page');
|
|
|
const basePage = await Page.findByPathAndViewer(basePagePath, currentUser, null, true);
|
|
const basePage = await Page.findByPathAndViewer(basePagePath, currentUser, null, true);
|
|
|
|
|
|
|
@@ -58,6 +65,7 @@ class PageBulkExportService {
|
|
|
const zipArchiver = this.setUpZipArchiver();
|
|
const zipArchiver = this.setUpZipArchiver();
|
|
|
const pagesWritable = this.getPageWritable(zipArchiver);
|
|
const pagesWritable = this.getPageWritable(zipArchiver);
|
|
|
const bufferToPartSizeTransform = getBufferToFixedSizeTransform(this.partSize);
|
|
const bufferToPartSizeTransform = getBufferToFixedSizeTransform(this.partSize);
|
|
|
|
|
+ let multipartUploadWritable: Writable;
|
|
|
|
|
|
|
|
// init multipart upload
|
|
// init multipart upload
|
|
|
// TODO: Create abstract interface IMultipartUploader in https://redmine.weseek.co.jp/issues/135775
|
|
// TODO: Create abstract interface IMultipartUploader in https://redmine.weseek.co.jp/issues/135775
|
|
@@ -67,21 +75,22 @@ class PageBulkExportService {
|
|
|
throw Error('Multipart upload not available for configured file upload type');
|
|
throw Error('Multipart upload not available for configured file upload type');
|
|
|
}
|
|
}
|
|
|
await multipartUploader.initUpload();
|
|
await multipartUploader.initUpload();
|
|
|
|
|
+
|
|
|
|
|
+ const pageBulkExportJob = await PageBulkExportJob.create({
|
|
|
|
|
+ user: currentUser._id,
|
|
|
|
|
+ page: basePage._id,
|
|
|
|
|
+ uploadId: multipartUploader.uploadId,
|
|
|
|
|
+ format: PageBulkExportFormat.markdown,
|
|
|
|
|
+ });
|
|
|
|
|
+ await Subscription.upsertSubscription(currentUser, SupportedTargetModel.MODEL_PAGE_BULK_EXPORT_JOB, pageBulkExportJob, SubscriptionStatusType.SUBSCRIBE);
|
|
|
|
|
+
|
|
|
|
|
+ multipartUploadWritable = this.getMultipartUploadWritable(multipartUploader, pageBulkExportJob, currentUser, activityParameters);
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
await this.handleExportError(err, multipartUploader);
|
|
await this.handleExportError(err, multipartUploader);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const pageBulkExportJob = await PageBulkExportJob.create({
|
|
|
|
|
- user: currentUser._id,
|
|
|
|
|
- page: basePage._id,
|
|
|
|
|
- uploadId: multipartUploader.uploadId,
|
|
|
|
|
- format: PageBulkExportFormat.markdown,
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const multipartUploadWritable = this.getMultipartUploadWritable(multipartUploader, pageBulkExportJob, currentUser);
|
|
|
|
|
-
|
|
|
|
|
// Cannot directly pipe from pagesWritable to zipArchiver due to how the 'append' method works.
|
|
// Cannot directly pipe from pagesWritable to zipArchiver due to how the 'append' method works.
|
|
|
// Hence, execution of two pipelines is required.
|
|
// Hence, execution of two pipelines is required.
|
|
|
pipeline(pagesReadable, pagesWritable, err => this.handleExportError(err, multipartUploader));
|
|
pipeline(pagesReadable, pagesWritable, err => this.handleExportError(err, multipartUploader));
|
|
@@ -166,7 +175,9 @@ class PageBulkExportService {
|
|
|
return zipArchiver;
|
|
return zipArchiver;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private getMultipartUploadWritable(multipartUploader: IAwsMultipartUploader, pageBulkExportJob: PageBulkExportJobDocument, user): Writable {
|
|
|
|
|
|
|
+ private getMultipartUploadWritable(
|
|
|
|
|
+ multipartUploader: IAwsMultipartUploader, pageBulkExportJob: PageBulkExportJobDocument, user, activityParameters: ActivityParameters,
|
|
|
|
|
+ ): Writable {
|
|
|
let partNumber = 1;
|
|
let partNumber = 1;
|
|
|
|
|
|
|
|
return new Writable({
|
|
return new Writable({
|
|
@@ -191,6 +202,7 @@ class PageBulkExportService {
|
|
|
await pageBulkExportJob.save();
|
|
await pageBulkExportJob.save();
|
|
|
|
|
|
|
|
const activity = await this.crowi.activityService.createActivity({
|
|
const activity = await this.crowi.activityService.createActivity({
|
|
|
|
|
+ ...activityParameters,
|
|
|
action: SupportedAction.ACTION_PAGE_BULK_EXPORT_COMPLETED,
|
|
action: SupportedAction.ACTION_PAGE_BULK_EXPORT_COMPLETED,
|
|
|
user,
|
|
user,
|
|
|
targetModel: SupportedTargetModel.MODEL_PAGE_BULK_EXPORT_JOB,
|
|
targetModel: SupportedTargetModel.MODEL_PAGE_BULK_EXPORT_JOB,
|
|
@@ -199,7 +211,8 @@ class PageBulkExportService {
|
|
|
username: user.username,
|
|
username: user.username,
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
- this.activityEvent.emit('updated', activity, page, preNotify);
|
|
|
|
|
|
|
+ const preNotify = preNotifyService.generatePreNotify(activity);
|
|
|
|
|
+ this.activityEvent.emit('updated', activity, pageBulkExportJob, preNotify);
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
callback(err);
|
|
callback(err);
|