添加删除文件

This commit is contained in:
2025-03-26 00:05:58 +08:00
parent 64c70ce527
commit 501a92eb88
10 changed files with 236 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
import { app } from '@/app.ts';
import { AppModel } from './module/app.ts';
import { AppDomainModel } from './module/app-domain.ts';
import { AppModel } from '../module/app.ts';
import { AppDomainModel } from '../module/app-domain.ts';
app
.route({
@@ -12,7 +12,7 @@ app
// const query = {
// }
const domainInfo = await AppDomainModel.findOne({ where: { domain } });
if (!domainInfo) {
if (!domainInfo || !domainInfo.appId) {
ctx.throw(404, 'app not found');
}
const app = await AppModel.findByPk(domainInfo.appId);

View File

@@ -0,0 +1,2 @@
import './domain-self.ts';
import './manager.ts';

View File

@@ -0,0 +1,125 @@
import { app } from '@/app.ts';
import { AppDomainModel } from '../module/app-domain.ts';
import { AppModel } from '../module/app.ts';
import { CustomError } from '@kevisual/router';
app
.route({
path: 'app.domain.manager',
key: 'list',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { page = 1, pageSize = 999 } = ctx.query.data || {};
const { count, rows } = await AppDomainModel.findAndCountAll({
offset: (page - 1) * pageSize,
limit: pageSize,
});
ctx.body = { count, list: rows, pagination: { page, pageSize } };
return ctx;
})
.addTo(app);
app
.route({
path: 'app.domain.manager',
key: 'update',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { domain, data, id, ...rest } = ctx.query.data || {};
if (!domain) {
ctx.throw(400, 'domain is required');
}
let domainInfo: AppDomainModel;
if (id) {
domainInfo = await AppDomainModel.findByPk(id);
} else {
domainInfo = await AppDomainModel.create({ domain });
}
const checkAppId = async () => {
const isUUID = (id: string) => {
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
};
if (rest.appId) {
if (!isUUID(rest.appId)) {
ctx.throw(400, 'appId is not valid');
}
const appInfo = await AppModel.findByPk(rest.appId);
if (!appInfo) {
ctx.throw(400, 'appId is not exist');
}
}
};
try {
if (!domainInfo) {
domainInfo = await AppDomainModel.create({ domain, data: {}, ...rest });
await checkAppId();
} else {
if (rest.status && domainInfo.status !== rest.status) {
await domainInfo.clearCache();
}
await checkAppId();
await domainInfo.update({
domain,
data: {
...domainInfo.data,
...data,
},
...rest,
});
}
ctx.body = domainInfo;
} catch (error) {
if (error.code) {
ctx.throw(error.code, error.message);
}
console.error(error);
ctx.throw(500, 'update domain failed, please check the data');
}
return ctx;
})
.addTo(app);
app
.route({
path: 'app.domain.manager',
key: 'delete',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { id, domain } = ctx.query.data || {};
if (!id && !domain) {
ctx.throw(400, 'id or domain is required');
}
if (id) {
await AppDomainModel.destroy({ where: { id }, force: true });
} else {
await AppDomainModel.destroy({ where: { domain }, force: true });
}
ctx.body = { message: 'delete domain success' };
return ctx;
})
.addTo(app);
app
.route({
path: 'app.domain.manager',
key: 'get',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { id, domain } = ctx.query.data || {};
if (!id && !domain) {
ctx.throw(400, 'id or domain is required');
}
const domainInfo = await AppDomainModel.findOne({ where: { id } });
if (!domainInfo) {
ctx.throw(404, 'domain not found');
}
ctx.body = domainInfo;
return ctx;
})
.addTo(app);

View File

@@ -2,6 +2,6 @@ import './list.ts';
import './user-app.ts';
import './public/index.ts';
import './domain.ts';
import './domain/index.ts';
export * from './module/index.ts';

View File

@@ -1,6 +1,7 @@
import { sequelize } from '../../../modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
export type DomainList = Partial<InstanceType<typeof AppDomainModel>>;
import { redis } from '../../../modules/redis.ts';
// 审核,通过,驳回
const appDomainStatus = ['audit', 'auditReject', 'auditPending', 'running', 'stop'] as const;
@@ -16,6 +17,7 @@ export class AppDomainModel extends Model {
// 状态,
declare status: AppDomainStatus;
declare uid: string;
declare data: Record<string, any>;
declare createdAt: Date;
declare updatedAt: Date;
@@ -28,6 +30,18 @@ export class AppDomainModel extends Model {
// 原本是审核状态,不能修改。
return false;
}
async clearCache() {
// 清除缓存
const cacheKey = `domain:${this.domain}`;
const checkHas = async () => {
const has = await redis.get(cacheKey);
return has;
};
const has = await checkHas();
if (has) {
await redis.set(cacheKey, '', 'EX', 1);
}
}
}
AppDomainModel.init(
@@ -43,13 +57,22 @@ AppDomainModel.init(
allowNull: false,
unique: true,
},
appId: {
data: {
type: DataTypes.JSONB,
allowNull: true,
},
status: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: 'running',
},
appId: {
type: DataTypes.STRING,
allowNull: true,
},
uid: {
type: DataTypes.STRING,
allowNull: false,
allowNull: true,
},
},
{

View File

@@ -1,5 +1,5 @@
import { app } from '@/app.ts';
import { getFileStat, getMinioList, deleteFile, updateFileStat } from './module/get-minio-list.ts';
import { getFileStat, getMinioList, deleteFile, updateFileStat, deleteFiles } from './module/get-minio-list.ts';
import path from 'path';
import { CustomError } from '@kevisual/router';
import { get } from 'http';
@@ -147,3 +147,37 @@ app
return ctx;
})
.addTo(app);
app
.route({
path: 'file',
key: 'delete-all',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
let directory = ctx.query.data?.directory as string;
if (!directory) {
ctx.throw(400, 'directory is required');
}
if (directory.startsWith('/')) {
ctx.throw(400, 'directory is invalid, cannot start with /');
}
if (directory.endsWith('/')) {
ctx.throw(400, 'directory is invalid, cannot end with /');
}
const prefix = tokenUser.username + '/' + directory + '/';
const list = await getMinioList<true>({ prefix, recursive: true });
if (list.length === 0) {
ctx.throw(400, 'directory is empty');
}
const res = await deleteFiles(list.map((item) => item.name));
if (!res) {
ctx.throw(500, 'delete all failed');
}
ctx.body = {
deleted: list.length,
message: 'delete all success',
};
})
.addTo(app);

View File

@@ -16,10 +16,10 @@ export type MinioDirectory = {
size: number;
};
export type MinioList = (MinioFile | MinioDirectory)[];
export const getMinioList = async (opts: MinioListOpt): Promise<MinioList> => {
export const getMinioList = async <IS_FILE extends boolean>(opts: MinioListOpt): Promise<IS_FILE extends true ? MinioFile[] : MinioDirectory[]> => {
const prefix = opts.prefix;
const recursive = opts.recursive ?? false;
return await new Promise((resolve, reject) => {
const res = await new Promise((resolve, reject) => {
let res: any[] = [];
let hasError = false;
minioClient
@@ -40,6 +40,7 @@ export const getMinioList = async (opts: MinioListOpt): Promise<MinioList> => {
}
});
});
return res as IS_FILE extends true ? MinioFile[] : MinioDirectory[];
};
export const getFileStat = async (prefix: string, isFile?: boolean): Promise<any> => {
try {