添加删除文件
This commit is contained in:
parent
64c70ce527
commit
501a92eb88
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
|||||||
[submodule "submodules/oss"]
|
[submodule "submodules/oss"]
|
||||||
path = submodules/oss
|
path = submodules/oss
|
||||||
url = git@git.xiongxiao.me:kevisual/kevisual-oss.git
|
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
|
specifier: ^8.4.0
|
||||||
version: 8.4.0(tsx@4.19.3)(typescript@5.8.2)
|
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:
|
submodules/permission:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
tsup:
|
tsup:
|
||||||
@ -510,6 +522,9 @@ packages:
|
|||||||
'@kevisual/use-config': ^1.0.5
|
'@kevisual/use-config': ^1.0.5
|
||||||
pm2: ^5.4.3
|
pm2: ^5.4.3
|
||||||
|
|
||||||
|
'@kevisual/router@0.0.10-beta.1':
|
||||||
|
resolution: {integrity: sha512-jl3f6HMdEd0B/6y14w437NatvpOKQ7Gkkr9vFNXvJ3tnYk7ozwjtavLSP3k4MWr5Er9SCT0KBX7+FjnvslCsSw==}
|
||||||
|
|
||||||
'@kevisual/router@0.0.9':
|
'@kevisual/router@0.0.9':
|
||||||
resolution: {integrity: sha512-qPyC2GVJ7iOIdJCCKNDsWMAKOQeSJW9HBpL5ZWKHTbi+t4jJBGTzIlXmjKeMHRd0lr/Qq1imQvlkSh4hlrbodA==}
|
resolution: {integrity: sha512-qPyC2GVJ7iOIdJCCKNDsWMAKOQeSJW9HBpL5ZWKHTbi+t4jJBGTzIlXmjKeMHRd0lr/Qq1imQvlkSh4hlrbodA==}
|
||||||
|
|
||||||
@ -2739,6 +2754,18 @@ packages:
|
|||||||
utf-8-validate:
|
utf-8-validate:
|
||||||
optional: true
|
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:
|
xml2js@0.5.0:
|
||||||
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
|
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
@ -2909,6 +2936,15 @@ snapshots:
|
|||||||
'@kevisual/use-config': 1.0.10(dotenv@16.4.7)
|
'@kevisual/use-config': 1.0.10(dotenv@16.4.7)
|
||||||
pm2: 6.0.5
|
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':
|
'@kevisual/router@0.0.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
path-to-regexp: 8.2.0
|
path-to-regexp: 8.2.0
|
||||||
@ -5343,6 +5379,8 @@ snapshots:
|
|||||||
|
|
||||||
ws@8.18.0: {}
|
ws@8.18.0: {}
|
||||||
|
|
||||||
|
ws@8.18.1: {}
|
||||||
|
|
||||||
xml2js@0.5.0:
|
xml2js@0.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
sax: 1.4.1
|
sax: 1.4.1
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { app } from '@/app.ts';
|
import { app } from '@/app.ts';
|
||||||
import { AppModel } from './module/app.ts';
|
import { AppModel } from '../module/app.ts';
|
||||||
import { AppDomainModel } from './module/app-domain.ts';
|
import { AppDomainModel } from '../module/app-domain.ts';
|
||||||
|
|
||||||
app
|
app
|
||||||
.route({
|
.route({
|
||||||
@ -12,7 +12,7 @@ app
|
|||||||
// const query = {
|
// const query = {
|
||||||
// }
|
// }
|
||||||
const domainInfo = await AppDomainModel.findOne({ where: { domain } });
|
const domainInfo = await AppDomainModel.findOne({ where: { domain } });
|
||||||
if (!domainInfo) {
|
if (!domainInfo || !domainInfo.appId) {
|
||||||
ctx.throw(404, 'app not found');
|
ctx.throw(404, 'app not found');
|
||||||
}
|
}
|
||||||
const app = await AppModel.findByPk(domainInfo.appId);
|
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 './user-app.ts';
|
||||||
|
|
||||||
import './public/index.ts';
|
import './public/index.ts';
|
||||||
import './domain.ts';
|
import './domain/index.ts';
|
||||||
|
|
||||||
export * from './module/index.ts';
|
export * from './module/index.ts';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { sequelize } from '../../../modules/sequelize.ts';
|
import { sequelize } from '../../../modules/sequelize.ts';
|
||||||
import { DataTypes, Model } from 'sequelize';
|
import { DataTypes, Model } from 'sequelize';
|
||||||
export type DomainList = Partial<InstanceType<typeof AppDomainModel>>;
|
export type DomainList = Partial<InstanceType<typeof AppDomainModel>>;
|
||||||
|
import { redis } from '../../../modules/redis.ts';
|
||||||
|
|
||||||
// 审核,通过,驳回
|
// 审核,通过,驳回
|
||||||
const appDomainStatus = ['audit', 'auditReject', 'auditPending', 'running', 'stop'] as const;
|
const appDomainStatus = ['audit', 'auditReject', 'auditPending', 'running', 'stop'] as const;
|
||||||
@ -16,6 +17,7 @@ export class AppDomainModel extends Model {
|
|||||||
// 状态,
|
// 状态,
|
||||||
declare status: AppDomainStatus;
|
declare status: AppDomainStatus;
|
||||||
declare uid: string;
|
declare uid: string;
|
||||||
|
declare data: Record<string, any>;
|
||||||
|
|
||||||
declare createdAt: Date;
|
declare createdAt: Date;
|
||||||
declare updatedAt: Date;
|
declare updatedAt: Date;
|
||||||
@ -28,6 +30,18 @@ export class AppDomainModel extends Model {
|
|||||||
// 原本是审核状态,不能修改。
|
// 原本是审核状态,不能修改。
|
||||||
return false;
|
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(
|
AppDomainModel.init(
|
||||||
@ -43,13 +57,22 @@ AppDomainModel.init(
|
|||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
},
|
},
|
||||||
appId: {
|
data: {
|
||||||
|
type: DataTypes.JSONB,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
defaultValue: 'running',
|
||||||
|
},
|
||||||
|
appId: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
},
|
},
|
||||||
uid: {
|
uid: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { app } from '@/app.ts';
|
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 path from 'path';
|
||||||
import { CustomError } from '@kevisual/router';
|
import { CustomError } from '@kevisual/router';
|
||||||
import { get } from 'http';
|
import { get } from 'http';
|
||||||
@ -147,3 +147,37 @@ app
|
|||||||
return ctx;
|
return ctx;
|
||||||
})
|
})
|
||||||
.addTo(app);
|
.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;
|
size: number;
|
||||||
};
|
};
|
||||||
export type MinioList = (MinioFile | MinioDirectory)[];
|
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 prefix = opts.prefix;
|
||||||
const recursive = opts.recursive ?? false;
|
const recursive = opts.recursive ?? false;
|
||||||
return await new Promise((resolve, reject) => {
|
const res = await new Promise((resolve, reject) => {
|
||||||
let res: any[] = [];
|
let res: any[] = [];
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
minioClient
|
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> => {
|
export const getFileStat = async (prefix: string, isFile?: boolean): Promise<any> => {
|
||||||
try {
|
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