添加删除文件
This commit is contained in:
parent
64c70ce527
commit
501a92eb88
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "submodules/oss"]
|
||||
path = submodules/oss
|
||||
url = git@git.xiongxiao.me:kevisual/kevisual-oss.git
|
||||
[submodule "submodules/pay-center-code"]
|
||||
path = submodules/pay-center-code
|
||||
url = git@git.xiongxiao.me:kevisual/pay-center-code.git
|
||||
|
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
@ -301,6 +301,18 @@ importers:
|
||||
specifier: ^8.4.0
|
||||
version: 8.4.0(tsx@4.19.3)(typescript@5.8.2)
|
||||
|
||||
submodules/pay-center-code:
|
||||
devDependencies:
|
||||
'@kevisual/router':
|
||||
specifier: 0.0.10-beta.1
|
||||
version: 0.0.10-beta.1
|
||||
'@kevisual/use-config':
|
||||
specifier: ^1.0.10
|
||||
version: 1.0.10(dotenv@16.4.7)
|
||||
tsup:
|
||||
specifier: ^8.4.0
|
||||
version: 8.4.0(tsx@4.19.3)(typescript@5.8.2)
|
||||
|
||||
submodules/permission:
|
||||
devDependencies:
|
||||
tsup:
|
||||
@ -510,6 +522,9 @@ packages:
|
||||
'@kevisual/use-config': ^1.0.5
|
||||
pm2: ^5.4.3
|
||||
|
||||
'@kevisual/router@0.0.10-beta.1':
|
||||
resolution: {integrity: sha512-jl3f6HMdEd0B/6y14w437NatvpOKQ7Gkkr9vFNXvJ3tnYk7ozwjtavLSP3k4MWr5Er9SCT0KBX7+FjnvslCsSw==}
|
||||
|
||||
'@kevisual/router@0.0.9':
|
||||
resolution: {integrity: sha512-qPyC2GVJ7iOIdJCCKNDsWMAKOQeSJW9HBpL5ZWKHTbi+t4jJBGTzIlXmjKeMHRd0lr/Qq1imQvlkSh4hlrbodA==}
|
||||
|
||||
@ -2739,6 +2754,18 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.18.1:
|
||||
resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xml2js@0.5.0:
|
||||
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@ -2909,6 +2936,15 @@ snapshots:
|
||||
'@kevisual/use-config': 1.0.10(dotenv@16.4.7)
|
||||
pm2: 6.0.5
|
||||
|
||||
'@kevisual/router@0.0.10-beta.1':
|
||||
dependencies:
|
||||
path-to-regexp: 8.2.0
|
||||
selfsigned: 2.4.1
|
||||
ws: 8.18.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@kevisual/router@0.0.9':
|
||||
dependencies:
|
||||
path-to-regexp: 8.2.0
|
||||
@ -5343,6 +5379,8 @@ snapshots:
|
||||
|
||||
ws@8.18.0: {}
|
||||
|
||||
ws@8.18.1: {}
|
||||
|
||||
xml2js@0.5.0:
|
||||
dependencies:
|
||||
sax: 1.4.1
|
||||
|
@ -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);
|
2
src/routes/app-manager/domain/index.ts
Normal file
2
src/routes/app-manager/domain/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import './domain-self.ts';
|
||||
import './manager.ts';
|
125
src/routes/app-manager/domain/manager.ts
Normal file
125
src/routes/app-manager/domain/manager.ts
Normal 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);
|
@ -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';
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
1
submodules/pay-center-code
Submodule
1
submodules/pay-center-code
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 951280f0975f9c46a465d45919250fd2d49503fd
|
Loading…
x
Reference in New Issue
Block a user