|
|
@@ -22,6 +22,7 @@ import streamToPromise from 'stream-to-promise';
|
|
|
import { Comment } from '~/features/comment/server';
|
|
|
import type { ExternalUserGroupDocument } from '~/features/external-user-group/server/models/external-user-group';
|
|
|
import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
|
|
|
+import { getOpenaiService } from '~/features/openai/server/services/openai';
|
|
|
import { SupportedAction } from '~/interfaces/activity';
|
|
|
import { V5ConversionErrCode } from '~/interfaces/errors/v5-conversion-error';
|
|
|
import type { IOptionsForCreate, IOptionsForUpdate } from '~/interfaces/page';
|
|
|
@@ -96,14 +97,10 @@ class PageCursorsForDescendantsFactory {
|
|
|
|
|
|
private initialCursor: Cursor<any> | never[]; // TODO: wait for mongoose update
|
|
|
|
|
|
- private Page: PageModel;
|
|
|
-
|
|
|
constructor(user: any, rootPage: any, shouldIncludeEmpty: boolean) {
|
|
|
this.user = user;
|
|
|
this.rootPage = rootPage;
|
|
|
this.shouldIncludeEmpty = shouldIncludeEmpty;
|
|
|
-
|
|
|
- this.Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
}
|
|
|
|
|
|
// prepare initial cursor
|
|
|
@@ -150,9 +147,10 @@ class PageCursorsForDescendantsFactory {
|
|
|
return [];
|
|
|
}
|
|
|
|
|
|
- const { PageQueryBuilder } = this.Page;
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
+ const { PageQueryBuilder } = Page;
|
|
|
|
|
|
- const builder = new PageQueryBuilder(this.Page.find(), this.shouldIncludeEmpty);
|
|
|
+ const builder = new PageQueryBuilder(Page.find(), this.shouldIncludeEmpty);
|
|
|
builder.addConditionToFilteringByParentId(page._id);
|
|
|
|
|
|
const cursor = builder.query.lean().cursor({ batchSize: BULK_REINDEX_SIZE }) as Cursor<any>;
|
|
|
@@ -1167,7 +1165,7 @@ class PageService implements IPageService {
|
|
|
grant,
|
|
|
grantUserGroupIds: grantedGroupIds,
|
|
|
};
|
|
|
- let duplicatedTarget;
|
|
|
+ let duplicatedTarget: HydratedDocument<PageDocument>;
|
|
|
if (page.isEmpty) {
|
|
|
const parent = await this.getParentAndFillAncestorsByUser(user, newPagePath);
|
|
|
duplicatedTarget = await Page.createEmptyPage(newPagePath, parent);
|
|
|
@@ -1177,6 +1175,10 @@ class PageService implements IPageService {
|
|
|
duplicatedTarget = await (this.create as CreateMethod)(
|
|
|
newPagePath, populatedPage?.revision?.body ?? '', user, options,
|
|
|
);
|
|
|
+
|
|
|
+ // Do not await because communication with OpenAI takes time
|
|
|
+ const openaiService = getOpenaiService();
|
|
|
+ openaiService?.createVectorStoreFile([duplicatedTarget]);
|
|
|
}
|
|
|
this.pageEvent.emit('duplicate', page, user);
|
|
|
|
|
|
@@ -1402,9 +1404,18 @@ class PageService implements IPageService {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- await Page.insertMany(newPages, { ordered: false });
|
|
|
+ const duplicatedPages = await Page.insertMany(newPages, { ordered: false });
|
|
|
+ const duplicatedPageIds = duplicatedPages.map(duplicatedPage => duplicatedPage._id);
|
|
|
+
|
|
|
await Revision.insertMany(newRevisions, { ordered: false });
|
|
|
await this.duplicateTags(pageIdMapping);
|
|
|
+
|
|
|
+ const duplicatedPagesWithPopulatedToShowRevison = await Page
|
|
|
+ .find({ _id: { $in: duplicatedPageIds }, grant: PageGrant.GRANT_PUBLIC }).populate('revision') as PageDocument[];
|
|
|
+
|
|
|
+ // Do not await because communication with OpenAI takes time
|
|
|
+ const openaiService = getOpenaiService();
|
|
|
+ openaiService?.createVectorStoreFile(duplicatedPagesWithPopulatedToShowRevison);
|
|
|
}
|
|
|
|
|
|
private async duplicateDescendantsV4(pages, user, oldPagePathPrefix, newPagePathPrefix) {
|
|
|
@@ -1890,6 +1901,10 @@ class PageService implements IPageService {
|
|
|
|
|
|
// Leave bookmarks without deleting -- 2024.05.17 Yuki Takei
|
|
|
]);
|
|
|
+
|
|
|
+ const openaiService = getOpenaiService();
|
|
|
+ const deleteVectorStoreFilePromises = pageIds.map(pageId => openaiService?.deleteVectorStoreFile(pageId));
|
|
|
+ await Promise.allSettled(deleteVectorStoreFilePromises);
|
|
|
}
|
|
|
|
|
|
// delete multiple pages
|
|
|
@@ -1902,7 +1917,6 @@ class PageService implements IPageService {
|
|
|
await this.deleteCompletelyOperation(ids, paths);
|
|
|
|
|
|
this.pageEvent.emit('syncDescendantsDelete', pages, user); // update as renamed page
|
|
|
-
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -3653,13 +3667,13 @@ class PageService implements IPageService {
|
|
|
|
|
|
// --------- Create ---------
|
|
|
|
|
|
- private async preparePageDocumentToCreate(path: string, shouldNew: boolean): Promise<PageDocument> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
+ private async preparePageDocumentToCreate(path: string, shouldNew: boolean): Promise<HydratedDocument<PageDocument>> {
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
|
|
|
const emptyPage = await Page.findOne({ path, isEmpty: true });
|
|
|
|
|
|
// Use empty page if exists, if not, create a new page
|
|
|
- let page;
|
|
|
+ let page: HydratedDocument<PageDocument>;
|
|
|
if (shouldNew) {
|
|
|
page = new Page();
|
|
|
}
|
|
|
@@ -3773,7 +3787,7 @@ class PageService implements IPageService {
|
|
|
* Create a page
|
|
|
* Set options.isSynchronously to true to await all process when you want to run this method multiple times at short intervals.
|
|
|
*/
|
|
|
- async create(_path: string, body: string, user: HasObjectId, options: IOptionsForCreate = {}): Promise<PageDocument> {
|
|
|
+ async create(_path: string, body: string, user: HasObjectId, options: IOptionsForCreate = {}): Promise<HydratedDocument<PageDocument>> {
|
|
|
// Switch method
|
|
|
const isV5Compatible = this.crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
|
|
|
if (!isV5Compatible) {
|
|
|
@@ -3784,7 +3798,7 @@ class PageService implements IPageService {
|
|
|
const path: string = generalXssFilter.process(_path); // sanitize path
|
|
|
|
|
|
// Retrieve closest ancestor document
|
|
|
- const Page = mongoose.model<PageDocument, PageModel>('Page');
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
const closestAncestor = await Page.findNonEmptyClosestAncestor(path);
|
|
|
|
|
|
// Determine grantData
|
|
|
@@ -3902,7 +3916,7 @@ class PageService implements IPageService {
|
|
|
* V4 compatible create method
|
|
|
*/
|
|
|
private async createV4(path, body, user, options: any = {}) {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
|
|
|
const format = options.format || 'markdown';
|
|
|
const grantUserGroupIds = options.grantUserGroupIds || null;
|
|
|
@@ -4078,7 +4092,7 @@ class PageService implements IPageService {
|
|
|
}
|
|
|
|
|
|
async updatePageSubOperation(page, user, exPage, options: IOptionsForUpdate, pageOpId: ObjectIdLike): Promise<void> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
|
|
|
const currentPage = page;
|
|
|
|
|
|
@@ -4109,7 +4123,7 @@ class PageService implements IPageService {
|
|
|
}
|
|
|
|
|
|
// 3. Update scopes for descendants
|
|
|
- if (options.overwriteScopesOfDescendants) {
|
|
|
+ if (options.overwriteScopesOfDescendants && shouldBeOnTree) {
|
|
|
await this.applyScopesToDescendantsWithStream(currentPage, user);
|
|
|
}
|
|
|
|
|
|
@@ -4143,13 +4157,13 @@ class PageService implements IPageService {
|
|
|
}
|
|
|
|
|
|
async updatePage(
|
|
|
- pageData: PageDocument,
|
|
|
+ pageData: HydratedDocument<PageDocument>,
|
|
|
body: string | null,
|
|
|
previousBody: string | null,
|
|
|
user: IUserHasId,
|
|
|
options: IOptionsForUpdate = {},
|
|
|
- ): Promise<PageDocument> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
+ ): Promise<HydratedDocument<PageDocument>> {
|
|
|
+ const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>('Page');
|
|
|
|
|
|
const wasOnTree = pageData.parent != null || isTopPage(pageData.path);
|
|
|
const isV5Compatible = this.crowi.configManager.getConfig('crowi', 'app:isV5Compatible');
|
|
|
@@ -4291,8 +4305,9 @@ class PageService implements IPageService {
|
|
|
}
|
|
|
|
|
|
|
|
|
- async updatePageV4(pageData: PageDocument, body, previousBody, user, options: IOptionsForUpdate = {}): Promise<PageDocument> {
|
|
|
- const Page = mongoose.model('Page') as unknown as PageModel;
|
|
|
+ async updatePageV4(
|
|
|
+ pageData: HydratedDocument<PageDocument>, body, previousBody, user, options: IOptionsForUpdate = {},
|
|
|
+ ): Promise<HydratedDocument<PageDocument>> {
|
|
|
|
|
|
// use the previous data if absent
|
|
|
const grant = options.grant || pageData.grant;
|