Преглед изворни кода

implement pagination for recent threads and add timestamps to thread relations

Shun Miyazawa пре 10 месеци
родитељ
комит
4441ff73c8

+ 6 - 0
apps/app/src/features/openai/server/models/thread-relation.ts

@@ -1,10 +1,12 @@
 import { addDays } from 'date-fns';
 import { addDays } from 'date-fns';
 import { type Model, type Document, Schema } from 'mongoose';
 import { type Model, type Document, Schema } from 'mongoose';
+import mongoosePaginate from 'mongoose-paginate-v2';
 
 
 import { getOrCreateModel } from '~/server/util/mongoose-utils';
 import { getOrCreateModel } from '~/server/util/mongoose-utils';
 
 
 import { type IThreadRelation, ThreadType } from '../../interfaces/thread-relation';
 import { type IThreadRelation, ThreadType } from '../../interfaces/thread-relation';
 
 
+
 const DAYS_UNTIL_EXPIRATION = 3;
 const DAYS_UNTIL_EXPIRATION = 3;
 
 
 const generateExpirationDate = (): Date => {
 const generateExpirationDate = (): Date => {
@@ -47,8 +49,12 @@ const schema = new Schema<ThreadRelationDocument, ThreadRelationModel>({
     default: generateExpirationDate,
     default: generateExpirationDate,
     required: true,
     required: true,
   },
   },
+}, {
+  timestamps: { createdAt: true, updatedAt: true },
 });
 });
 
 
+schema.plugin(mongoosePaginate);
+
 schema.statics.getExpiredThreadRelations = async function(limit?: number): Promise<ThreadRelationDocument[] | undefined> {
 schema.statics.getExpiredThreadRelations = async function(limit?: number): Promise<ThreadRelationDocument[] | undefined> {
   const currentDate = new Date();
   const currentDate = new Date();
   const expiredThreadRelations = await this.find({ expiredAt: { $lte: currentDate } }).limit(limit ?? 100).exec();
   const expiredThreadRelations = await this.find({ expiredAt: { $lte: currentDate } }).limit(limit ?? 100).exec();

+ 23 - 7
apps/app/src/features/openai/server/routes/get-recent-threads.ts

@@ -1,7 +1,8 @@
 import { type IUserHasId } from '@growi/core';
 import { type IUserHasId } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { ErrorV3 } from '@growi/core/dist/models';
 import type { Request, RequestHandler } from 'express';
 import type { Request, RequestHandler } from 'express';
-import { type ValidationChain, param } from 'express-validator';
+import { type ValidationChain, query } from 'express-validator';
+
 
 
 import type Crowi from '~/server/crowi';
 import type Crowi from '~/server/crowi';
 import { accessTokenParser } from '~/server/middlewares/access-token-parser';
 import { accessTokenParser } from '~/server/middlewares/access-token-parser';
@@ -9,6 +10,7 @@ import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
+import ThreadRelationModel from '../models/thread-relation';
 import { getOpenaiService } from '../services/openai';
 import { getOpenaiService } from '../services/openai';
 
 
 import { certifyAiService } from './middlewares/certify-ai-service';
 import { certifyAiService } from './middlewares/certify-ai-service';
@@ -17,11 +19,13 @@ const logger = loggerFactory('growi:routes:apiv3:openai:get-recent-threads');
 
 
 type GetRecentThreadsFactory = (crowi: Crowi) => RequestHandler[];
 type GetRecentThreadsFactory = (crowi: Crowi) => RequestHandler[];
 
 
-type ReqParams = {
-  //
+type ReqQuery = {
+  page?: number,
+  limit?: number,
+  sort?: string, // e.g. 'updatedAt', '-updatedAt'
 }
 }
 
 
-type Req = Request<ReqParams, Response, undefined> & {
+type Req = Request<undefined, Response, undefined, ReqQuery> & {
   user: IUserHasId,
   user: IUserHasId,
 }
 }
 
 
@@ -29,7 +33,11 @@ export const getRecentThreadsFactory: GetRecentThreadsFactory = (crowi) => {
   const loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi);
   const loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi);
 
 
   const validator: ValidationChain[] = [
   const validator: ValidationChain[] = [
-    //
+    query('page').optional().isInt().withMessage('page must be a positive integer'),
+    query('page').toInt(),
+    query('limit').optional().isInt({ min: 1, max: 10 }).withMessage('limit must be an integer between 1 and 100'),
+    query('limit').toInt(),
+    query('sort').optional().isString().withMessage('sort must be a string'),
   ];
   ];
 
 
   return [
   return [
@@ -41,8 +49,16 @@ export const getRecentThreadsFactory: GetRecentThreadsFactory = (crowi) => {
       }
       }
 
 
       try {
       try {
-        const threads = await openaiService.getRecentThreads(req.user);
-        return res.apiv3({ threads });
+        const paginateResult = await (ThreadRelationModel as any).paginate(
+          { userId: req.user._id },
+          {
+            page: req.query.page ?? 1,
+            limit: req.query.limit ?? 10,
+            sort: req.query.sort ?? '-updatedAt',
+          },
+        );
+
+        return res.apiv3({ paginateResult });
       }
       }
       catch (err) {
       catch (err) {
         logger.error(err);
         logger.error(err);

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

@@ -74,7 +74,6 @@ const convertPathPatternsToRegExp = (pagePathPatterns: string[]): Array<string |
 export interface IOpenaiService {
 export interface IOpenaiService {
   createThread(userId: string, type: ThreadType, aiAssistantId?: string, initialUserMessage?: string): Promise<ThreadRelationDocument>;
   createThread(userId: string, type: ThreadType, aiAssistantId?: string, initialUserMessage?: string): Promise<ThreadRelationDocument>;
   getThreadsByAiAssistantId(aiAssistantId: string): Promise<ThreadRelationDocument[]>
   getThreadsByAiAssistantId(aiAssistantId: string): Promise<ThreadRelationDocument[]>
-  getRecentThreads(user: IUserHasId): Promise<ThreadRelationDocument[]>;
   deleteThread(threadRelationId: string): Promise<ThreadRelationDocument>;
   deleteThread(threadRelationId: string): Promise<ThreadRelationDocument>;
   deleteExpiredThreads(limit: number, apiCallInterval: number): Promise<void>; // for CronJob
   deleteExpiredThreads(limit: number, apiCallInterval: number): Promise<void>; // for CronJob
   deleteObsoletedVectorStoreRelations(): Promise<void> // for CronJob
   deleteObsoletedVectorStoreRelations(): Promise<void> // for CronJob
@@ -188,11 +187,6 @@ class OpenaiService implements IOpenaiService {
     return threadRelations;
     return threadRelations;
   }
   }
 
 
-  async getRecentThreads(user: IUserHasId): Promise<ThreadRelationDocument[]> {
-    const threadRelations = await ThreadRelationModel.find({ userId: user._id });
-    return threadRelations;
-  }
-
   async deleteThread(threadRelationId: string): Promise<ThreadRelationDocument> {
   async deleteThread(threadRelationId: string): Promise<ThreadRelationDocument> {
     const threadRelation = await ThreadRelationModel.findById(threadRelationId);
     const threadRelation = await ThreadRelationModel.findById(threadRelationId);
     if (threadRelation == null) {
     if (threadRelation == null) {