Files
code-center/src/routes/app-manager/list.ts
2025-12-30 00:46:07 +08:00

384 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { App, CustomError } from '@kevisual/router';
import { AppModel, AppListModel } from './module/index.ts';
import { app, redis } from '@/app.ts';
import { uniqBy } from 'lodash-es';
import { getUidByUsername, prefixFix } from './util.ts';
import { deleteFiles, getMinioListAndSetToAppList } from '../file/index.ts';
import { setExpire } from './revoke.ts';
import { User } from '@/models/user.ts';
app
.route({
path: 'app',
key: 'list',
middleware: ['auth'],
description: '获取应用列表根据key进行过滤',
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const data = ctx.query.data || {};
if (!data.key) {
throw new CustomError('key is required');
}
const list = await AppListModel.findAll({
order: [['updatedAt', 'DESC']],
where: {
uid: tokenUser.id,
key: data.key,
},
logging: false,
});
ctx.body = list.map((item) => prefixFix(item, tokenUser.username));
return ctx;
})
.addTo(app);
app
.route({
path: 'app',
key: 'get',
middleware: ['auth'],
description: '获取应用详情可以通过id或者key+version来获取',
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const id = ctx.query.id;
const { key, version } = ctx.query?.data || {};
if (!id && (!key || !version)) {
throw new CustomError('id is required');
}
let am: AppListModel;
if (id) {
am = await AppListModel.findByPk(id);
} else if (key && version) {
am = await AppListModel.findOne({
where: {
key,
version,
uid: tokenUser.id,
},
});
}
if (!am) {
throw new CustomError('app not found');
}
ctx.body = prefixFix(am, tokenUser.username);
})
.addTo(app);
app
.route({
path: 'app',
key: 'update',
middleware: ['auth'],
description: '创建或更新应用信息如果传入id则为更新否则为创建',
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { data, id, ...rest } = ctx.query.data;
if (id) {
const app = await AppListModel.findByPk(id);
if (app) {
const newData = { ...app.data, ...data };
const newApp = await app.update({ data: newData, ...rest });
ctx.body = newApp;
setExpire(newApp.id, 'test');
} else {
throw new CustomError('app not found');
}
return;
}
if (!rest.key) {
throw new CustomError('key is required');
}
const app = await AppListModel.create({ data, ...rest, uid: tokenUser.id });
ctx.body = app;
return ctx;
})
.addTo(app);
app
.route({
path: 'app',
key: 'delete',
middleware: ['auth'],
description: '删除应用信息,如果应用已发布,则不允许删除',
})
.define(async (ctx) => {
const id = ctx.query.id;
const deleteFile = !!ctx.query.deleteFile; // 是否删除文件, 默认不删除
if (!id) {
throw new CustomError('id is required');
}
const app = await AppListModel.findByPk(id);
if (!app) {
throw new CustomError('app not found');
}
const am = await AppModel.findOne({ where: { key: app.key, uid: app.uid } });
if (!am) {
throw new CustomError('app not found');
}
if (am.version === app.version) {
throw new CustomError('app is published');
}
const files = app.data.files || [];
if (deleteFile && files.length > 0) {
await deleteFiles(files.map((item) => item.path));
}
await app.destroy({
force: true,
});
ctx.body = 'success';
return ctx;
})
.addTo(app);
app
.route({
path: 'app',
key: 'canUploadFiles',
middleware: ['auth'],
description: '检查是否可以上传文件,如果当前版本已存在,则不允许上传',
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { appKey, version } = ctx.query.data;
if (!appKey) {
throw new CustomError('appKey is required');
}
const app = await AppListModel.findOne({ where: { version: version, key: appKey, uid: tokenUser.id } });
if (!app) {
throw new CustomError('app not found');
}
ctx.body = app;
})
.addTo(app);
app
.route({
path: 'app',
key: 'uploadFiles',
middleware: ['auth'],
isDebug: true,
description: '上传应用文件,如果应用版本不存在,则创建应用版本记录',
})
.define(async (ctx) => {
try {
const tokenUser = ctx.state.tokenUser;
const { appKey, files, version, username, description } = ctx.query.data;
if (!appKey) {
throw new CustomError('appKey is required');
}
if (!files || !files.length) {
throw new CustomError('files is required');
}
let uid = tokenUser.id;
let userPrefix = tokenUser.username;
if (username) {
try {
const _user = await User.getUserByToken(ctx.query.token);
if (_user.hasUser(username)) {
const upUser = await User.findOne({ where: { username } });
uid = upUser.id;
userPrefix = username;
}
} catch (e) {
console.log('getUserByToken error', e);
throw new CustomError('user not found');
}
}
let am = await AppModel.findOne({ where: { key: appKey, uid } });
let appIsNew = false;
if (!am) {
appIsNew = true;
am = await AppModel.create({
user: userPrefix,
key: appKey,
uid,
version: version || '0.0.0',
title: appKey,
proxy: appKey.includes('center') ? false : true,
description: description || '',
data: {
files: files || [],
},
});
}
let app = await AppListModel.findOne({ where: { version: version, key: appKey, uid: uid } });
if (!app) {
app = await AppListModel.create({
key: appKey,
version,
uid: uid,
data: {
files: [],
},
});
}
const dataFiles = app.data.files || [];
const newFiles = uniqBy([...dataFiles, ...files], 'name');
const res = await app.update({ data: { ...app.data, files: newFiles } });
if (version === am.version && !appIsNew) {
await am.update({ data: { ...am.data, files: newFiles } });
}
setExpire(app.id, 'test');
ctx.body = prefixFix(res, userPrefix);
} catch (e) {
console.log('update error', e);
throw new CustomError(e.message);
}
})
.addTo(app);
app
.route({
path: 'app',
key: 'publish',
middleware: ['auth'],
description: '发布应用,将某个版本的应用设置为当前应用的版本',
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, username, appKey, version } = ctx.query.data;
if (!id && !appKey) {
throw new CustomError('id or appKey is required');
}
const uid = await getUidByUsername(app, ctx, username);
let appList: AppListModel | null = null;
if (id) {
appList = await AppListModel.findByPk(id);
if (appList?.uid !== uid) {
throw new CustomError('no permission');
}
}
if (!appList && appKey) {
if (!version) {
throw new CustomError('version is required');
}
appList = await AppListModel.findOne({ where: { key: appKey, version, uid } });
}
if (!appList) {
throw new CustomError('app not found');
}
const files = appList.data.files || [];
const am = await AppModel.findOne({ where: { key: appList.key, uid: uid } });
if (!am) {
throw new CustomError('app not found');
}
await am.update({ data: { ...am.data, files }, version: appList.version });
setExpire(appList.key, am.user);
ctx.body = {
key: appList.key,
version: appList.version,
appManager: am,
user: am.user,
};
})
.addTo(app);
app
.route({
path: 'app',
key: 'getApp',
description: '获取应用信息可以通过id或者key+version来获取, 参数在data中传入',
})
.define(async (ctx) => {
const { user, key, id } = ctx.query.data;
let app;
if (id) {
app = await AppModel.findByPk(id);
} else if (user && key) {
app = await AppModel.findOne({ where: { user, key } });
} else {
throw new CustomError('user or key is required');
}
if (!app) {
throw new CustomError('app not found');
}
ctx.body = app;
})
.addTo(app);
app
.route({
path: 'app',
key: 'get-minio-list',
description: '获取minio列表',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { key, version } = ctx.query?.data || {};
if (!key || !version) {
throw new CustomError('key and version are required');
}
const files = await getMinioListAndSetToAppList({ username: tokenUser.username, appKey: key, version });
ctx.body = files;
})
.addTo(app);
app
.route({
path: 'app',
key: 'detectVersionList',
description: '检测版本列表minio中的数据自己上传后根据版本信息进行替换',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
let { appKey, version, username } = ctx.query?.data || {};
if (!appKey || !version) {
throw new CustomError('appKey and version are required');
}
const uid = await getUidByUsername(app, ctx, username);
let appList = await AppListModel.findOne({ where: { key: appKey, version, uid } });
if (!appList) {
appList = await AppListModel.create({
key: appKey,
version,
uid,
data: {
files: [],
},
});
}
const checkUsername = username || tokenUser.username;
const files = await getMinioListAndSetToAppList({ username: checkUsername, appKey, version });
const newFiles = files.map((item) => {
return {
name: item.name.replace(`${checkUsername}/${appKey}/${version}/`, ''),
path: item.name,
};
});
let appListFiles = appList.data?.files || [];
const needAddFiles = newFiles.map((item) => {
const findFile = appListFiles.find((appListFile) => appListFile.name === item.name);
if (findFile && findFile.name === item.name) {
return { ...findFile, ...item };
}
return item;
});
await appList.update({ data: { files: needAddFiles } });
setExpire(appList.id, 'test');
let am = await AppModel.findOne({ where: { key: appKey, uid } });
if (!am) {
am = await AppModel.create({
title: appKey,
key: appKey,
version: version || '0.0.0',
user: checkUsername,
uid,
data: { files: needAddFiles },
proxy: appKey.includes('center') ? false : true,
});
} else {
const appModel = await AppModel.findOne({ where: { key: appKey, version, uid } });
if (appModel) {
await appModel.update({ data: { files: needAddFiles } });
setExpire(appModel.key, appModel.user);
}
}
ctx.body = appList;
})
.addTo(app);