Ver código fonte

impl createConditionForCreateAiAssistant

Shun Miyazawa 1 ano atrás
pai
commit
47996e2fac

+ 51 - 6
apps/app/src/features/openai/server/services/openai.ts

@@ -2,7 +2,10 @@ import assert from 'node:assert';
 import { Readable, Transform } from 'stream';
 import { Readable, Transform } from 'stream';
 import { pipeline } from 'stream/promises';
 import { pipeline } from 'stream/promises';
 
 
-import { PageGrant, isPopulated } from '@growi/core';
+import {
+  IUser,
+  IUserGroup, PageGrant, Ref, getIdForRef, isPopulated,
+} from '@growi/core';
 import { isGrobPatternPath } from '@growi/core/dist/utils/page-path-utils';
 import { isGrobPatternPath } from '@growi/core/dist/utils/page-path-utils';
 import escapeStringRegexp from 'escape-string-regexp';
 import escapeStringRegexp from 'escape-string-regexp';
 import type { HydratedDocument, Types } from 'mongoose';
 import type { HydratedDocument, Types } from 'mongoose';
@@ -17,12 +20,13 @@ import VectorStoreFileRelationModel, {
   prepareVectorStoreFileRelations,
   prepareVectorStoreFileRelations,
 } from '~/features/openai/server/models/vector-store-file-relation';
 } from '~/features/openai/server/models/vector-store-file-relation';
 import type { PageDocument, PageModel } from '~/server/models/page';
 import type { PageDocument, PageModel } from '~/server/models/page';
+import userGroupRelation from '~/server/models/user-group-relation';
 import { configManager } from '~/server/service/config-manager';
 import { configManager } from '~/server/service/config-manager';
 import { createBatchStream } from '~/server/util/batch-stream';
 import { createBatchStream } from '~/server/util/batch-stream';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import { OpenaiServiceTypes } from '../../interfaces/ai';
 import { OpenaiServiceTypes } from '../../interfaces/ai';
-import { type AiAssistant } from '../../interfaces/ai-assistant';
+import { type AiAssistant, AiAssistantAccessScope, AiAssistantShareScope } from '../../interfaces/ai-assistant';
 import AiAssistantModel, { type AiAssistantDocument } from '../models/ai-assistant';
 import AiAssistantModel, { type AiAssistantDocument } from '../models/ai-assistant';
 import { convertMarkdownToHtml } from '../utils/convert-markdown-to-html';
 import { convertMarkdownToHtml } from '../utils/convert-markdown-to-html';
 
 
@@ -419,16 +423,57 @@ class OpenaiService implements IOpenaiService {
     await pipeline(pagesStream, batchStream, createVectorStoreFileStream);
     await pipeline(pagesStream, batchStream, createVectorStoreFileStream);
   }
   }
 
 
+  private async createConditionForCreateAiAssistant(data: AiAssistant): Promise<mongoose.FilterQuery<PageDocument>> {
+    const converterdPagePatgPatterns = convertPathPatternsToRegExp(data.pagePathPatterns);
+
+    if (data.accessScope === AiAssistantAccessScope.PUBLIC_ONLY) {
+      return {
+        grant: PageGrant.GRANT_PUBLIC,
+        path: { $in: converterdPagePatgPatterns },
+      };
+    }
+
+    if (data.accessScope === AiAssistantAccessScope.GROUPS) {
+      if (data.grantedGroups != null && data.grantedGroups.length > 0) {
+        const ownerMemberGroups = (await userGroupRelation.findAllUserGroupIdsRelatedToUser(data.owner)).map(group => group.toString());
+        const isValid = data.grantedGroups.every(group => ownerMemberGroups.includes(getIdForRef(group.item).toString()));
+        if (!isValid) {
+          throw new Error('A group to which the owner does not belong is specified.');
+        }
+      }
+
+      return {
+        grant: { $in: [PageGrant.GRANT_PUBLIC, PageGrant.GRANT_USER_GROUP] },
+        path: { $in: converterdPagePatgPatterns },
+        $or: [
+          { 'grantedGroups.item': { $in: data.grantedGroups?.map(group => getIdForRef(group.item)) } },
+          { grant: PageGrant.GRANT_PUBLIC },
+        ],
+      };
+    }
+
+    if (data.accessScope === AiAssistantAccessScope.OWNER) {
+      return {
+        grant: { $in: [PageGrant.GRANT_PUBLIC, PageGrant.GRANT_OWNER] },
+        path: { $in: converterdPagePatgPatterns },
+        $or: [
+          { grantedUsers: { $in: [getIdForRef(data.owner)] } },
+          { grant: PageGrant.GRANT_PUBLIC },
+        ],
+      };
+    }
+
+    throw new Error('Invalid accessScope value');
+  }
+
   async createAiAssistant(data: Omit<AiAssistant, 'vectorStore'>): Promise<AiAssistantDocument> {
   async createAiAssistant(data: Omit<AiAssistant, 'vectorStore'>): Promise<AiAssistantDocument> {
+    const conditions = await this.createConditionForCreateAiAssistant(data as AiAssistant);
+
     const vectorStoreRelation = await this.createVectorStore(data.name);
     const vectorStoreRelation = await this.createVectorStore(data.name);
     const aiAssistant = await AiAssistantModel.create({
     const aiAssistant = await AiAssistantModel.create({
       ...data, vectorStore: vectorStoreRelation,
       ...data, vectorStore: vectorStoreRelation,
     });
     });
 
 
-    const conditions = {
-      path: { $in: convertPathPatternsToRegExp(data.pagePathPatterns) },
-    };
-
     // VectorStore creation process does not await
     // VectorStore creation process does not await
     this.createVectorStoreFileWithStream(vectorStoreRelation, conditions);
     this.createVectorStoreFileWithStream(vectorStoreRelation, conditions);