page-operation.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { addSeconds } from 'date-fns';
  2. import mongoose, {
  3. Schema, Model, Document, QueryOptions, FilterQuery,
  4. } from 'mongoose';
  5. import { PageActionType, PageActionStage } from '~/interfaces/page-operation';
  6. import {
  7. IPageForResuming, IUserForResuming, IOptionsForResuming,
  8. } from '~/server/models/interfaces/page-operation';
  9. import loggerFactory from '../../utils/logger';
  10. import { ObjectIdLike } from '../interfaces/mongoose-utils';
  11. import { getOrCreateModel } from '../util/mongoose-utils';
  12. const TIME_TO_ADD_SEC = 10;
  13. const logger = loggerFactory('growi:models:page-operation');
  14. const ObjectId = mongoose.Schema.Types.ObjectId;
  15. /*
  16. * Main Schema
  17. */
  18. export interface IPageOperation {
  19. actionType: PageActionType,
  20. actionStage: PageActionStage,
  21. fromPath: string,
  22. toPath?: string,
  23. page: IPageForResuming,
  24. user: IUserForResuming,
  25. options?: IOptionsForResuming,
  26. incForUpdatingDescendantCount?: number,
  27. unprocessableExpiryDate: Date,
  28. exPage?: IPageForResuming,
  29. isProcessable(): boolean
  30. }
  31. export interface PageOperationDocument extends IPageOperation, Document {}
  32. export type PageOperationDocumentHasId = PageOperationDocument & { _id: ObjectIdLike };
  33. export interface PageOperationModel extends Model<PageOperationDocument> {
  34. findByIdAndUpdatePageActionStage(pageOpId: ObjectIdLike, stage: PageActionStage): Promise<PageOperationDocumentHasId | null>
  35. findMainOps(filter?: FilterQuery<PageOperationDocument>, projection?: any, options?: QueryOptions): Promise<PageOperationDocumentHasId[]>
  36. deleteByActionTypes(deleteTypeList: PageActionType[]): Promise<void>
  37. extendExpiryDate(operationId: ObjectIdLike): Promise<void>
  38. }
  39. const pageSchemaForResuming = new Schema<IPageForResuming>({
  40. _id: { type: ObjectId, ref: 'Page', index: true },
  41. parent: { type: ObjectId, ref: 'Page' },
  42. descendantCount: { type: Number },
  43. isEmpty: { type: Boolean },
  44. path: { type: String, required: true, index: true },
  45. revision: { type: ObjectId, ref: 'Revision' },
  46. status: { type: String },
  47. grant: { type: Number },
  48. grantedUsers: [{ type: ObjectId, ref: 'User' }],
  49. grantedGroup: { type: ObjectId, ref: 'UserGroup' },
  50. creator: { type: ObjectId, ref: 'User' },
  51. lastUpdateUser: { type: ObjectId, ref: 'User' },
  52. });
  53. const userSchemaForResuming = new Schema<IUserForResuming>({
  54. _id: { type: ObjectId, ref: 'User', required: true },
  55. });
  56. const optionsSchemaForResuming = new Schema<IOptionsForResuming>({
  57. createRedirectPage: { type: Boolean },
  58. updateMetadata: { type: Boolean },
  59. prevDescendantCount: { type: Number },
  60. grant: { type: Number },
  61. grantUserGroupId: { type: ObjectId, ref: 'UserGroup' },
  62. format: { type: String },
  63. overwriteScopesOfDescendants: { type: Boolean },
  64. }, { _id: false });
  65. const schema = new Schema<PageOperationDocument, PageOperationModel>({
  66. actionType: {
  67. type: String,
  68. enum: PageActionType,
  69. required: true,
  70. index: true,
  71. },
  72. actionStage: {
  73. type: String,
  74. enum: PageActionStage,
  75. required: true,
  76. index: true,
  77. },
  78. fromPath: { type: String, required: true, index: true },
  79. toPath: { type: String, index: true },
  80. page: { type: pageSchemaForResuming, required: true },
  81. exPage: { type: pageSchemaForResuming, required: false },
  82. user: { type: userSchemaForResuming, required: true },
  83. options: { type: optionsSchemaForResuming },
  84. incForUpdatingDescendantCount: { type: Number },
  85. unprocessableExpiryDate: { type: Date, default: () => addSeconds(new Date(), 10) },
  86. });
  87. schema.statics.findByIdAndUpdatePageActionStage = async function(
  88. pageOpId: ObjectIdLike, stage: PageActionStage,
  89. ): Promise<PageOperationDocumentHasId | null> {
  90. return this.findByIdAndUpdate(pageOpId, {
  91. $set: { actionStage: stage },
  92. }, { new: true });
  93. };
  94. schema.statics.findMainOps = async function(
  95. filter?: FilterQuery<PageOperationDocument>, projection?: any, options?: QueryOptions,
  96. ): Promise<PageOperationDocumentHasId[]> {
  97. return this.find(
  98. { ...filter, actionStage: PageActionStage.Main },
  99. projection,
  100. options,
  101. );
  102. };
  103. schema.statics.deleteByActionTypes = async function(
  104. actionTypes: PageActionType[],
  105. ): Promise<void> {
  106. await this.deleteMany({ actionType: { $in: actionTypes } });
  107. logger.info(`Deleted all PageOperation documents with actionType: [${actionTypes}]`);
  108. };
  109. /**
  110. * add TIME_TO_ADD_SEC to current time and update unprocessableExpiryDate with it
  111. */
  112. schema.statics.extendExpiryDate = async function(operationId: ObjectIdLike): Promise<void> {
  113. const date = addSeconds(new Date(), TIME_TO_ADD_SEC);
  114. await this.findByIdAndUpdate(operationId, { unprocessableExpiryDate: date });
  115. };
  116. schema.methods.isProcessable = function(): boolean {
  117. const { unprocessableExpiryDate } = this;
  118. return unprocessableExpiryDate == null || (unprocessableExpiryDate != null && new Date() > unprocessableExpiryDate);
  119. };
  120. export default getOrCreateModel<PageOperationDocument, PageOperationModel>('PageOperation', schema);