feat(admin): add file delete action which delete minio file also and allow fuzzy match of objectName

pull/146/head
moonrailgun 2 years ago
parent 17464b0c6f
commit c98dc4714d

@ -23,15 +23,11 @@ import {
} from 'tushan/icon';
import { authHTTPClient, authProvider } from './auth';
import { Dashboard } from './components/Dashboard';
import {
discoverFields,
fileFields,
mailFields,
messageFields,
} from './fields';
import { discoverFields, mailFields, messageFields } from './fields';
import { i18n } from './i18n';
import { GroupList } from './resources/group';
import { UserList } from './resources/user';
import { FileList } from './resources/file';
import { TailchatAnalytics } from './routes/analytics';
import { CacheManager } from './routes/cache';
import { TailchatNetwork } from './routes/network';
@ -84,21 +80,7 @@ function App() {
<Resource name="groups" icon={<IconUserGroup />} list={<GroupList />} />
<Resource
name="file"
icon={<IconFile />}
list={
<ListTable
filter={[
createTextField('q', {
label: 'Search',
}),
]}
fields={fileFields}
action={{ detail: true }}
/>
}
/>
<Resource name="file" icon={<IconFile />} list={<FileList />} />
<Resource
name="mail"

@ -0,0 +1,19 @@
import React from 'react';
import { createTextField, ListTable } from 'tushan';
import { fileFields } from '../fields';
export const FileList: React.FC = React.memo(() => {
return (
<ListTable
filter={[
createTextField('q', {
label: 'Search',
}),
]}
fields={fileFields}
action={{ detail: true, delete: true }}
batchAction={{ delete: true }}
/>
);
});
FileList.displayName = 'FileList';

@ -8,6 +8,7 @@ import { fileRouter } from './file';
import dayjs from 'dayjs';
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 { cacheRouter } from './cache';
@ -269,11 +270,33 @@ router.use(
},
})
);
router.delete('/file/:id', auth(), async (req, res) => {
try {
const fileId = req.params.id;
const record = await fileModel.findById(fileId);
if (record) {
await callBrokerAction('file.delete', {
objectName: record.objectName,
});
}
res.json({ id: fileId });
} catch (err) {
console.error(err);
res.status(500).json({ message: (err as any).message });
}
});
router.use(
'/file',
auth(),
raExpressMongoose(require('../../../../models/file').default, {
raExpressMongoose(fileModel, {
q: ['objectName'],
allowedRegexFields: ['objectName'],
capabilities: {
delete: false,
},
})
);
router.use(

@ -59,6 +59,13 @@ class FileService extends TcService {
},
disableSocket: true,
});
this.registerAction('delete', this.delete, {
params: {
objectName: 'string',
},
disableSocket: true,
visibility: 'public',
});
}
async onInited() {
@ -307,19 +314,36 @@ class FileService extends TcService {
) {
const objectName = ctx.params.objectName;
const stream = await this.actions['getObject'](
{
bucketName: this.bucketName,
objectName,
},
{
parentCtx: ctx,
}
const stream = await this.minioClient.getObject(
this.bucketName,
objectName
);
return stream;
}
/**
*
*/
async delete(
ctx: TcContext<{
objectName: string;
}>
) {
const objectName = ctx.params.objectName;
try {
// 先删文件再删记录,确保文件被删除
await this.minioClient.removeObject(this.bucketName, objectName);
await this.adapter.model.deleteMany({
bucketName: this.bucketName,
objectName,
});
} catch (err) {
this.logger.warn('Delete file error:', objectName, err);
}
}
private randomName() {
return `unnamed_${this.broker.nodeID}_${Date.now()}`;
}

Loading…
Cancel
Save