feat: 添加发布应用目录功能,增强应用版本管理和权限校验

This commit is contained in:
2026-02-26 14:13:16 +08:00
parent 79e07d6689
commit 0084f9878b

View File

@@ -1,8 +1,8 @@
import { App as AppType, AppList, AppData } from './module/app-drizzle.ts'; import { App as AppType, AppList, AppData } from './module/app-drizzle.ts';
import { app, db, schema } from '@/app.ts'; import { app, db, oss, schema } from '@/app.ts';
import { uniqBy } from 'es-toolkit'; import { uniqBy } from 'es-toolkit';
import { getUidByUsername, prefixFix } from './util.ts'; import { getUidByUsername, prefixFix } from './util.ts';
import { deleteFiles, getMinioListAndSetToAppList } from '../file/index.ts'; import { deleteFiles, getMinioList, getMinioListAndSetToAppList } from '../file/index.ts';
import { setExpire } from './revoke.ts'; import { setExpire } from './revoke.ts';
import { User } from '@/models/user.ts'; import { User } from '@/models/user.ts';
import { callDetectAppVersion } from './export.ts'; import { callDetectAppVersion } from './export.ts';
@@ -47,6 +47,16 @@ app
key: 'get', key: 'get',
middleware: ['auth'], middleware: ['auth'],
description: '获取应用详情可以通过id或者key+version来获取', description: '获取应用详情可以通过id或者key+version来获取',
metadata: {
args: {
data: z.object({
id: z.string().optional(),
version: z.string().optional(),
key: z.string().optional(),
create: z.boolean().optional().describe('如果应用版本不存在是否创建应用版本记录默认false'),
})
}
}
}) })
.define(async (ctx) => { .define(async (ctx) => {
console.log('get app manager called'); console.log('get app manager called');
@@ -376,11 +386,88 @@ app
}) })
.addTo(app); .addTo(app);
app.route({
path: 'app',
key: 'publishDirectory',
middleware: ['auth'],
description: '发布应用目录,将某个版本的应用目录设置为当前应用的版本',
metadata: {
args: {
data: z.object({
key: z.string().describe('应用的唯一标识'),
version: z.string().describe('应用版本'),
directory: z.string().describe('应用目录'),
})
}
}
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { key, version, directory } = ctx.query.data;
if (!key || !version || !directory) {
ctx.throw('key, version and directory are required');
}
const username = tokenUser.username;
const chunks = directory.split('/').filter((item) => item);
const [_username, appWay, ...rest] = chunks;
if (username !== _username) {
ctx.throw('没有权限');
}
let newChunks = [];
if (appWay === 'resources') {
newChunks = [username, ...rest];
} else if (appWay === 'ai') {
newChunks = [username, 'ai', '1.0.0', ...rest];
}
const [_, originAppKey, originVersion] = newChunks.filter((item) => item);
if (!originAppKey || !originVersion) {
ctx.throw('目录不合法');
}
const pub = async () => {
return await app.run({
path: 'app',
key: 'publish',
payload: {
data: {
appKey: key,
version,
detect: true,
},
token: ctx.query.token,
},
})
}
// 如果发布的版本和当前版本不一致,则将目录下的文件复制到新的目录下
if (originAppKey !== key || originVersion !== version) {
const oldPrefix = newChunks.join('/') + '/';
const newPrefix = `${username}/${key}/${version}/`;
const listSource = await getMinioList<true>({ prefix: oldPrefix, recursive: true });
for (const item of listSource) {
const newName = item.name.slice(oldPrefix.length);
await oss.copyObject(item.name, `${newPrefix}${newName}`);
}
}
const appRes = await app.run({ path: 'app', key: 'get', payload: { data: { key, version, create: true }, token: ctx.query.token } });
if (appRes.code !== 200) {
ctx.throw(appRes.message || '获取应用信息失败');
}
const res = await pub();
ctx.forward(res);
}).addTo(app);
app app
.route({ .route({
path: 'app', path: 'app',
key: 'getApp', key: 'getApp',
description: '获取应用信息可以通过id或者key+version来获取, 参数在data中传入', description: '获取应用信息可以通过id或者key+version来获取, 参数在data中传入',
metadata: {
args: {
data: z.object({
id: z.string().optional(),
key: z.string().optional(),
version: z.string().optional(),
})
}
}
}) })
.define(async (ctx) => { .define(async (ctx) => {
const { user, key, id } = ctx.query.data; const { user, key, id } = ctx.query.data;