diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6cc873ddffd..ac649e84751 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1653,8 +1653,8 @@ importers:
specifier: workspace:^
version: link:../packages/sdk
tushan:
- specifier: ^0.3.4
- version: 0.3.4(prop-types@15.8.1)(ts-node@10.9.1)
+ specifier: ^0.3.9
+ version: 0.3.9(prop-types@15.8.1)(ts-node@10.9.1)
vite-express:
specifier: 0.8.0
version: 0.8.0(patch_hash=u6touqej4dt3zxnslnszarl7vq)(express@4.18.2)(vite@4.2.0)
@@ -33333,8 +33333,8 @@ packages:
domino: 2.1.6
dev: false
- /tushan@0.3.4(prop-types@15.8.1)(ts-node@10.9.1):
- resolution: {integrity: sha512-y2z1wUaZTFO9MJj+sMJzn9xQ08VvdUwEWgVsRRiUKEaSkTOUHl2qqGDrE961X/gGnGcW/yHdzBYObwL44jUxiA==}
+ /tushan@0.3.9(prop-types@15.8.1)(ts-node@10.9.1):
+ resolution: {integrity: sha512-qKA6NdWkstPKYE0BaxLZFiPnWXCjpiO5voZxzqln2y0FRp1hG7Zb+gChBQWyrZulAmAVjBiABjOBajpuyu7bqw==}
dependencies:
'@arco-design/web-react': 2.51.0(@types/react@18.0.20)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query': 4.33.0(react-dom@18.2.0)(react@18.2.0)
@@ -33355,7 +33355,7 @@ packages:
immer: 9.0.21
jsonexport: 3.2.0
lodash-es: 4.17.21
- postcss: 8.4.21
+ postcss: 8.4.27
qs: 6.11.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
diff --git a/server/admin/package.json b/server/admin/package.json
index 440c3875fd5..35829b41f71 100644
--- a/server/admin/package.json
+++ b/server/admin/package.json
@@ -29,7 +29,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailchat-server-sdk": "workspace:^",
- "tushan": "^0.3.4",
+ "tushan": "^0.3.9",
"vite-express": "0.8.0"
},
"devDependencies": {
diff --git a/server/admin/src/client/fields.ts b/server/admin/src/client/fields.ts
index 99f8e18da10..f42a0075b77 100644
--- a/server/admin/src/client/fields.ts
+++ b/server/admin/src/client/fields.ts
@@ -159,6 +159,7 @@ export const fileFields = [
createFileSizeField('size', {
list: {
width: 120,
+ sort: true,
},
}),
createTextField('metaData.content-type'),
@@ -170,7 +171,11 @@ export const fileFields = [
width: 80,
},
}),
- createDateTimeField('createdAt'),
+ createDateTimeField('createdAt', {
+ list: {
+ sort: true,
+ },
+ }),
];
export const mailFields = [
diff --git a/server/admin/src/client/resources/file.tsx b/server/admin/src/client/resources/file.tsx
index 0a2541d65b5..dc66d93052f 100644
--- a/server/admin/src/client/resources/file.tsx
+++ b/server/admin/src/client/resources/file.tsx
@@ -1,17 +1,26 @@
import filesize from 'filesize';
-import React from 'react';
+import React, { useState } from 'react';
import {
createTextField,
ListTable,
useAsync,
useTranslation,
Typography,
+ styled,
+ Checkbox,
} from 'tushan';
import { fileFields } from '../fields';
import { request } from '../request';
+const Row = styled.div`
+ display: flex;
+ gap: 20px;
+ justify-content: end;
+`;
+
export const FileList: React.FC = React.memo(() => {
const { t } = useTranslation();
+ const [isOnlyChatFiles, setIsOnlyChatFiles] = useState(false);
const { value: totalSize = 0 } = useAsync(async () => {
const { data } = await request.get('/file/filesizeSum');
@@ -20,9 +29,19 @@ export const FileList: React.FC = React.memo(() => {
return (
<>
-
- {t('custom.file.fileTotalSize')}: {filesize(totalSize)}
-
+
+ {
+ setIsOnlyChatFiles(!isOnlyChatFiles);
+ }}
+ >
+ Only show chat files
+
+
+ {t('custom.file.fileTotalSize')}: {filesize(totalSize)}
+
+
{
action={{ detail: true, delete: true }}
batchAction={{ delete: true }}
showSizeChanger={true}
+ meta={isOnlyChatFiles ? 'onlyChat' : undefined}
/>
>
);
diff --git a/server/admin/src/server/router/api.ts b/server/admin/src/server/router/api.ts
index 59f9675db78..bbc2a3aea0f 100644
--- a/server/admin/src/server/router/api.ts
+++ b/server/admin/src/server/router/api.ts
@@ -10,10 +10,14 @@ import userModel from '../../../../models/user/user';
import messageModel from '../../../../models/chat/message';
import fileModel from '../../../../models/file';
import groupModel from '../../../../models/group/group';
-import { raExpressMongoose } from '../middleware/express-mongoose-ra-json-server';
+import {
+ raExpressMongoose,
+ virtualId,
+} from '../middleware/express-mongoose-ra-json-server';
import { cacheRouter } from './cache';
import discoverModel from '../../../../plugins/com.msgbyte.discover/models/discover';
import { analyticsRouter } from './analytics';
+import _ from 'lodash';
const router = Router();
@@ -292,6 +296,51 @@ router.delete('/file/:id', auth(), async (req, res) => {
router.use(
'/file',
auth(),
+ async (req, res, next) => {
+ const onlyChatFile = req.query.meta === 'onlyChat';
+
+ if (!onlyChatFile) {
+ return next();
+ }
+
+ // only return chatted file rather than all file
+ const result = await fileModel
+ .aggregate()
+ .lookup({
+ from: 'users',
+ localField: 'url',
+ foreignField: 'avatar',
+ as: 'avatarMatchedUser',
+ })
+ .match({
+ 'avatarMatchedUser.0': { $exists: false },
+ })
+ .project({
+ avatarMatchedUser: 0,
+ })
+ .facet({
+ metadata: [{ $count: 'total' }],
+ data: [
+ {
+ $sort: {
+ [typeof req.query._sort === 'string'
+ ? req.query._sort === 'id'
+ ? '_id'
+ : req.query._sort
+ : '_id']: req.query._order === 'ASC' ? 1 : -1,
+ },
+ },
+ { $skip: Number(req.query._start) },
+ { $limit: Number(req.query._end) - Number(req.query._start) },
+ ],
+ })
+ .exec();
+
+ const list = _.get(result, '0.data');
+ const total = _.get(result, '0.metadata.0.total');
+
+ return res.set('X-Total-Count', total).json(virtualId(list)).end();
+ },
raExpressMongoose(fileModel, {
q: ['objectName'],
allowedRegexFields: ['objectName'],
diff --git a/server/models/file.ts b/server/models/file.ts
index 3485ff07e40..b559625cbac 100644
--- a/server/models/file.ts
+++ b/server/models/file.ts
@@ -20,6 +20,7 @@ import { User } from './user/user';
},
})
@index({ bucketName: 1, objectName: 1 })
+@index({ url: 1 })
export class File extends TimeStamps implements Base {
_id: Types.ObjectId;
id: string;
diff --git a/server/models/user/user.ts b/server/models/user/user.ts
index 334e3a3f663..09e27ab5f6b 100644
--- a/server/models/user/user.ts
+++ b/server/models/user/user.ts
@@ -5,6 +5,7 @@ import {
ReturnModelType,
modelOptions,
Severity,
+ index,
} from '@typegoose/typegoose';
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';
import type { Types } from 'mongoose';
@@ -32,6 +33,7 @@ export interface UserLoginRes extends User {
allowMixed: Severity.ALLOW,
},
})
+@index({ avatar: 1 })
export class User extends TimeStamps implements Base {
_id: Types.ObjectId;
id: string;