diff --git a/docSite/content/zh-cn/docs/development/upgrading/4111.md b/docSite/content/zh-cn/docs/development/upgrading/4111.md index cbb8afeb8..75dcd3fee 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4111.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4111.md @@ -11,6 +11,8 @@ weight: 782 ## 🚀 新增内容 +1. 账号注销 + ## ⚙️ 优化 1. 兑换码功能支持指定对公支付模式。 diff --git a/packages/global/support/wallet/sub/coupon/type.d.ts b/packages/global/support/wallet/sub/coupon/type.d.ts index eadbf28b7..07e347cd0 100644 --- a/packages/global/support/wallet/sub/coupon/type.d.ts +++ b/packages/global/support/wallet/sub/coupon/type.d.ts @@ -17,4 +17,5 @@ export type TeamCouponSchema = { redeemedTeamId?: string; type: CouponTypeEnum; price?: number; + description?: string; }; diff --git a/packages/service/core/app/controller.ts b/packages/service/core/app/controller.ts index dfb3d7f75..aa9ced399 100644 --- a/packages/service/core/app/controller.ts +++ b/packages/service/core/app/controller.ts @@ -5,6 +5,20 @@ import { MongoApp } from './schema'; import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { encryptSecretValue, storeSecretValue } from '../../common/secret/utils'; import { SystemToolInputTypeEnum } from '@fastgpt/global/core/app/systemTool/constants'; +import { type ClientSession } from '../../common/mongo'; +import { MongoEvaluation } from './evaluation/evalSchema'; +import { removeEvaluationJob } from './evaluation/mq'; +import { deleteChatFiles } from '../chat/controller'; +import { MongoChatItem } from '../chat/chatItemSchema'; +import { MongoChat } from '../chat/chatSchema'; +import { MongoOutLink } from '../../support/outLink/schema'; +import { MongoOpenApi } from '../../support/openapi/schema'; +import { MongoAppVersion } from './version/schema'; +import { MongoChatInputGuide } from '../chat/inputGuide/schema'; +import { MongoResourcePermission } from '../../support/permission/schema'; +import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; +import { removeImageByPath } from '../../common/file/image/controller'; +import { mongoSessionRun } from '../../common/mongo/sessionRun'; export const beforeUpdateAppFormat = ({ nodes }: { nodes?: StoreNodeItemType[] }) => { if (!nodes) return; @@ -110,3 +124,88 @@ export const getAppBasicInfoByIds = async ({ teamId, ids }: { teamId: string; id avatar: item.avatar })); }; + +export const onDelOneApp = async ({ + teamId, + appId, + session +}: { + teamId: string; + appId: string; + session?: ClientSession; +}) => { + const apps = await findAppAndAllChildren({ + teamId, + appId, + fields: '_id avatar' + }); + + // Remove eval job + const evalJobs = await MongoEvaluation.find( + { + appId: { $in: apps.map((app) => app._id) } + }, + '_id' + ).lean(); + await Promise.all(evalJobs.map((evalJob) => removeEvaluationJob(evalJob._id))); + + const del = async (session: ClientSession) => { + for await (const app of apps) { + const appId = app._id; + // Chats + await deleteChatFiles({ appId }); + await MongoChatItem.deleteMany( + { + appId + }, + { session } + ); + await MongoChat.deleteMany( + { + appId + }, + { session } + ); + + // 删除分享链接 + await MongoOutLink.deleteMany({ + appId + }).session(session); + // Openapi + await MongoOpenApi.deleteMany({ + appId + }).session(session); + + // delete version + await MongoAppVersion.deleteMany({ + appId + }).session(session); + + await MongoChatInputGuide.deleteMany({ + appId + }).session(session); + + await MongoResourcePermission.deleteMany({ + resourceType: PerResourceTypeEnum.app, + teamId, + resourceId: appId + }).session(session); + + // delete app + await MongoApp.deleteOne( + { + _id: appId + }, + { session } + ); + + await removeImageByPath(app.avatar, session); + } + }; + + if (session) { + return del(session); + } + + return mongoSessionRun(del); +}; diff --git a/packages/service/core/dataset/controller.ts b/packages/service/core/dataset/controller.ts index c8ab49910..095a7cfde 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -10,6 +10,10 @@ import { MongoDatasetDataText } from './data/dataTextSchema'; import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; import { retryFn } from '@fastgpt/global/common/system/utils'; import { clearDatasetImages } from './image/utils'; +import { MongoDatasetCollectionTags } from './tag/schema'; +import { removeDatasetSyncJobScheduler } from './datasetSync'; +import { mongoSessionRun } from '../../common/mongo/sessionRun'; +import { removeImageByPath } from '../../common/file/image/controller'; /* ============= dataset ========== */ /* find all datasetId by top datasetId */ @@ -118,3 +122,44 @@ export async function delDatasetRelevantData({ datasetId: { $in: datasetIds } }).session(session); } + +export const deleteDatasets = async ({ + teamId, + datasets +}: { + teamId: string; + datasets: DatasetSchemaType[]; +}) => { + const datasetIds = datasets.map((d) => d._id); + + // delete collection.tags + await MongoDatasetCollectionTags.deleteMany({ + teamId, + datasetId: { $in: datasetIds } + }); + + // Remove cron job + await Promise.all( + datasets.map((dataset) => { + return removeDatasetSyncJobScheduler(dataset._id); + }) + ); + + // delete all dataset.data and pg data + await mongoSessionRun(async (session) => { + // delete dataset data + await delDatasetRelevantData({ datasets, session }); + + // delete dataset + await MongoDataset.deleteMany( + { + _id: { $in: datasetIds } + }, + { session } + ); + + for await (const dataset of datasets) { + await removeImageByPath(dataset.avatar, session); + } + }); +}; diff --git a/packages/service/support/permission/schema.ts b/packages/service/support/permission/schema.ts index 10b4a0cdb..ae53c7da1 100644 --- a/packages/service/support/permission/schema.ts +++ b/packages/service/support/permission/schema.ts @@ -14,7 +14,8 @@ export const ResourcePermissionCollectionName = 'resource_permissions'; export const ResourcePermissionSchema = new Schema({ teamId: { type: Schema.Types.ObjectId, - ref: TeamCollectionName + ref: TeamCollectionName, + required: true }, tmbId: { type: Schema.Types.ObjectId, diff --git a/packages/service/support/user/session.ts b/packages/service/support/user/session.ts index 8507e56ec..97cc3dbbe 100644 --- a/packages/service/support/user/session.ts +++ b/packages/service/support/user/session.ts @@ -86,7 +86,7 @@ const getSession = async (key: string): Promise => { export const delUserAllSession = async (userId: string, whiteList?: (string | undefined)[]) => { const formatWhiteList = whiteList?.map((item) => item && getSessionKey(item)); const redis = getGlobalRedisConnection(); - const keys = (await getAllKeysByPrefix(`${redisPrefix}${userId}`)).filter( + const keys = (await getAllKeysByPrefix(`${redisPrefix}${String(userId)}`)).filter( (item) => !formatWhiteList?.includes(item) ); diff --git a/packages/service/support/wallet/coupon/schema.ts b/packages/service/support/wallet/coupon/schema.ts index 8be723654..27a9ceab3 100644 --- a/packages/service/support/wallet/coupon/schema.ts +++ b/packages/service/support/wallet/coupon/schema.ts @@ -17,6 +17,7 @@ const CouponSchema = new Schema({ enum: Object.values(CouponTypeEnum) }, price: Number, + description: String, subscriptions: { type: [Object], required: true diff --git a/projects/app/src/pages/api/core/app/del.ts b/projects/app/src/pages/api/core/app/del.ts index 52cc5672d..3d2db7e02 100644 --- a/projects/app/src/pages/api/core/app/del.ts +++ b/projects/app/src/pages/api/core/app/del.ts @@ -1,35 +1,18 @@ import type { NextApiRequest, NextApiResponse } from 'next'; -import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; -import { MongoApp } from '@fastgpt/service/core/app/schema'; -import { MongoOutLink } from '@fastgpt/service/support/outLink/schema'; import { authApp } from '@fastgpt/service/support/permission/app/auth'; -import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; -import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; -import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { NextAPI } from '@/service/middleware/entry'; -import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema'; -import { - OwnerPermissionVal, - PerResourceTypeEnum -} from '@fastgpt/global/support/permission/constant'; -import { findAppAndAllChildren } from '@fastgpt/service/core/app/controller'; -import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; -import { type ClientSession } from '@fastgpt/service/common/mongo'; -import { deleteChatFiles } from '@fastgpt/service/core/chat/controller'; +import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'; +import { onDelOneApp } from '@fastgpt/service/core/app/controller'; import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils'; -import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema'; -import { removeImageByPath } from '@fastgpt/service/common/file/image/controller'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { getI18nAppType } from '@fastgpt/service/support/user/audit/util'; -import { removeEvaluationJob } from '@fastgpt/service/core/app/evaluation/mq'; -import { MongoEvaluation } from '@fastgpt/service/core/app/evaluation/evalSchema'; async function handler(req: NextApiRequest, res: NextApiResponse) { const { appId } = req.query as { appId: string }; if (!appId) { - throw new Error('参数错误'); + return Promise.reject('参数错误'); } // Auth owner (folder owner, can delete all apps in the folder) @@ -44,6 +27,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { teamId, appId }); + (async () => { addAuditLog({ tmbId, @@ -61,88 +45,3 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } export default NextAPI(handler); - -export const onDelOneApp = async ({ - teamId, - appId, - session -}: { - teamId: string; - appId: string; - session?: ClientSession; -}) => { - const apps = await findAppAndAllChildren({ - teamId, - appId, - fields: '_id avatar' - }); - - // Remove eval job - const evalJobs = await MongoEvaluation.find( - { - appId: { $in: apps.map((app) => app._id) } - }, - '_id' - ).lean(); - await Promise.all(evalJobs.map((evalJob) => removeEvaluationJob(evalJob._id))); - - const del = async (session: ClientSession) => { - for await (const app of apps) { - const appId = app._id; - // Chats - await deleteChatFiles({ appId }); - await MongoChatItem.deleteMany( - { - appId - }, - { session } - ); - await MongoChat.deleteMany( - { - appId - }, - { session } - ); - - // 删除分享链接 - await MongoOutLink.deleteMany({ - appId - }).session(session); - // Openapi - await MongoOpenApi.deleteMany({ - appId - }).session(session); - - // delete version - await MongoAppVersion.deleteMany({ - appId - }).session(session); - - await MongoChatInputGuide.deleteMany({ - appId - }).session(session); - - await MongoResourcePermission.deleteMany({ - resourceType: PerResourceTypeEnum.app, - teamId, - resourceId: appId - }).session(session); - - // delete app - await MongoApp.deleteOne( - { - _id: appId - }, - { session } - ); - - await removeImageByPath(app.avatar, session); - } - }; - - if (session) { - return del(session); - } - - return mongoSessionRun(del); -}; diff --git a/projects/app/src/pages/api/core/app/httpPlugin/update.ts b/projects/app/src/pages/api/core/app/httpPlugin/update.ts index 0cf31c07a..6a4783180 100644 --- a/projects/app/src/pages/api/core/app/httpPlugin/update.ts +++ b/projects/app/src/pages/api/core/app/httpPlugin/update.ts @@ -10,7 +10,7 @@ import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant import { MongoApp } from '@fastgpt/service/core/app/schema'; import { isEqual } from 'lodash'; import { onCreateApp } from '../create'; -import { onDelOneApp } from '../del'; +import { onDelOneApp } from '@fastgpt/service/core/app/controller'; import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller'; export type UpdateHttpPluginBody = { diff --git a/projects/app/src/pages/api/core/app/mcpTools/update.ts b/projects/app/src/pages/api/core/app/mcpTools/update.ts index ffddf99c8..89ecc3068 100644 --- a/projects/app/src/pages/api/core/app/mcpTools/update.ts +++ b/projects/app/src/pages/api/core/app/mcpTools/update.ts @@ -8,7 +8,7 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { isEqual } from 'lodash'; import { type ClientSession } from 'mongoose'; import { MongoApp } from '@fastgpt/service/core/app/schema'; -import { onDelOneApp } from '../del'; +import { onDelOneApp } from '@fastgpt/service/core/app/controller'; import { onCreateApp } from '../create'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; diff --git a/projects/app/src/pages/api/core/dataset/delete.ts b/projects/app/src/pages/api/core/dataset/delete.ts index fa5474234..7efef6eda 100644 --- a/projects/app/src/pages/api/core/dataset/delete.ts +++ b/projects/app/src/pages/api/core/dataset/delete.ts @@ -1,16 +1,10 @@ import type { NextApiRequest } from 'next'; import { authDataset } from '@fastgpt/service/support/permission/dataset/auth'; -import { delDatasetRelevantData } from '@fastgpt/service/core/dataset/controller'; +import { deleteDatasets } from '@fastgpt/service/core/dataset/controller'; import { findDatasetAndAllChildren } from '@fastgpt/service/core/dataset/controller'; -import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; -import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { NextAPI } from '@/service/middleware/entry'; import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; -import { MongoDatasetCollectionTags } from '@fastgpt/service/core/dataset/tag/schema'; -import { removeImageByPath } from '@fastgpt/service/common/file/image/controller'; -import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; -import { removeDatasetSyncJobScheduler } from '@fastgpt/service/core/dataset/datasetSync'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { getI18nDatasetType } from '@fastgpt/service/support/user/audit/util'; @@ -37,37 +31,10 @@ async function handler(req: NextApiRequest) { teamId, datasetId }); - const datasetIds = datasets.map((d) => d._id); - // delete collection.tags - await MongoDatasetCollectionTags.deleteMany({ + await deleteDatasets({ teamId, - datasetId: { $in: datasetIds } - }); - - // Remove cron job - await Promise.all( - datasets.map((dataset) => { - return removeDatasetSyncJobScheduler(dataset._id); - }) - ); - - // delete all dataset.data and pg data - await mongoSessionRun(async (session) => { - // delete dataset data - await delDatasetRelevantData({ datasets, session }); - - // delete dataset - await MongoDataset.deleteMany( - { - _id: { $in: datasetIds } - }, - { session } - ); - - for await (const dataset of datasets) { - await removeImageByPath(dataset.avatar, session); - } + datasets }); (async () => {