feat: add minio list detect

This commit is contained in:
xion 2025-03-13 02:32:28 +08:00
parent 1c820c3083
commit efef48a1b0
8 changed files with 208 additions and 15 deletions

View File

@ -12,9 +12,12 @@
"dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ", "dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ",
"build": "rimraf dist && rollup -c rollup.config.mjs", "build": "rimraf dist && rollup -c rollup.config.mjs",
"deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codecenter/dist", "deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codecenter/dist",
"deploy:sky": "rsync -avz --delete ./dist/ --exclude='app.config.json5' sky:~/kevisual/dist",
"clean": "rm -rf dist", "clean": "rm -rf dist",
"reload": "ssh light pm2 restart codecenter", "reload": "ssh light pm2 restart codecenter",
"reload:sky": "ssh sky pm2 restart codecenter",
"pub:me": "npm run build && npm run deploy && npm run reload", "pub:me": "npm run build && npm run deploy && npm run reload",
"pub:sky": "npm run build && npm run deploy:sky && npm run reload:sky",
"start": "pm2 start dist/app.mjs --name codecenter", "start": "pm2 start dist/app.mjs --name codecenter",
"release": "node ./config/release/index.mjs", "release": "node ./config/release/index.mjs",
"pub": "envision pack -p -u", "pub": "envision pack -p -u",

View File

@ -2,8 +2,8 @@ import { App, CustomError } from '@kevisual/router';
import { AppModel, AppListModel } from './module/index.ts'; import { AppModel, AppListModel } from './module/index.ts';
import { app, redis } from '@/app.ts'; import { app, redis } from '@/app.ts';
import _ from 'lodash'; import _ from 'lodash';
import { prefixFix } from './util.ts'; import { getUidByUsername, prefixFix } from './util.ts';
import { deleteFiles } from '../file/index.ts'; import { deleteFiles, 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';
app app
@ -67,7 +67,7 @@ app
const newData = { ...app.data, ...data }; const newData = { ...app.data, ...data };
const newApp = await app.update({ data: newData, ...rest }); const newApp = await app.update({ data: newData, ...rest });
ctx.body = newApp; ctx.body = newApp;
setExpire(newApp.id, tokenUser.username); setExpire(newApp.id, 'test');
} else { } else {
throw new CustomError('app not found'); throw new CustomError('app not found');
} }
@ -194,7 +194,7 @@ app
const dataFiles = app.data.files || []; const dataFiles = app.data.files || [];
const newFiles = _.uniqBy([...dataFiles, ...files], 'name'); const newFiles = _.uniqBy([...dataFiles, ...files], 'name');
const res = await app.update({ data: { ...app.data, files: newFiles } }); const res = await app.update({ data: { ...app.data, files: newFiles } });
setExpire(app.id, userPrefix); setExpire(app.id, 'test');
ctx.body = prefixFix(res, userPrefix); ctx.body = prefixFix(res, userPrefix);
} catch (e) { } catch (e) {
console.log('update error', e); console.log('update error', e);
@ -211,24 +211,26 @@ app
}) })
.define(async (ctx) => { .define(async (ctx) => {
const tokenUser = ctx.state.tokenUser; const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query.data; const { id, username } = ctx.query.data;
if (!id) { if (!id) {
throw new CustomError('id is required'); throw new CustomError('id is required');
} }
const app = await AppListModel.findByPk(id);
if (!app) { const uid = await getUidByUsername(app, ctx, username);
const appList = await AppListModel.findByPk(id);
if (!appList) {
throw new CustomError('app not found'); throw new CustomError('app not found');
} }
const files = app.data.files || []; const files = appList.data.files || [];
const am = await AppModel.findOne({ where: { key: app.key, uid: tokenUser.id } }); const am = await AppModel.findOne({ where: { key: appList.key, uid: uid } });
if (!am) { if (!am) {
throw new CustomError('app not found'); throw new CustomError('app not found');
} }
await am.update({ data: { ...am.data, files }, version: app.version }); await am.update({ data: { ...am.data, files }, version: appList.version });
setExpire(app.key, am.user); setExpire(appList.key, am.user);
ctx.body = { ctx.body = {
key: app.key, key: appList.key,
version: app.version, version: appList.version,
appManager: am, appManager: am,
user: am.user, user: am.user,
}; };
@ -274,3 +276,66 @@ app
return ctx; return ctx;
}) })
.addTo(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: 'detect-version-list',
description: '检测版本列表minio中的数据自己上传后根据版本信息进行替换',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
let { key, version, username } = ctx.query?.data || {};
if (!key || !version) {
throw new CustomError('key and version are required');
}
const uid = await getUidByUsername(app, ctx, username);
const appList = await AppListModel.findOne({ where: { key, version, uid: uid } });
if (!appList) {
throw new CustomError('app not found');
}
const checkUsername = username || tokenUser.username;
const files = await getMinioListAndSetToAppList({ username: checkUsername, appKey: key, version });
const newFiles = files.map((item) => {
return {
name: item.name.replace(`${checkUsername}/${key}/${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.path === item.path) {
return findFile;
}
return item;
});
await appList.update({ data: { files: needAddFiles } });
setExpire(appList.id, 'test');
const appModel = await AppModel.findOne({ where: { key, version, uid: uid } });
if (appModel) {
await appModel.update({ data: { files: needAddFiles } });
setExpire(appModel.key, appModel.user);
}
ctx.body = appList;
})
.addTo(app);

View File

@ -2,6 +2,8 @@ import { CustomError } from '@kevisual/router';
import { AppModel, AppListModel } from './module/index.ts'; import { AppModel, AppListModel } from './module/index.ts';
import { app } from '@/app.ts'; import { app } from '@/app.ts';
import { setExpire } from './revoke.ts'; import { setExpire } from './revoke.ts';
import { getMinioListAndSetToAppList } from '../file/index.ts';
import { getUidByUsername } from './util.ts';
app app
.route({ .route({
@ -111,6 +113,9 @@ app
if (!am) { if (!am) {
throw new CustomError('app not found'); throw new CustomError('app not found');
} }
if (am.uid !== tokenUser.id) {
throw new CustomError('app not found');
}
const list = await AppListModel.findAll({ where: { key: am.key, uid: tokenUser.id } }); const list = await AppListModel.findAll({ where: { key: am.key, uid: tokenUser.id } });
await am.destroy({ force: true }); await am.destroy({ force: true });
await Promise.all(list.map((item) => item.destroy({ force: true }))); await Promise.all(list.map((item) => item.destroy({ force: true })));
@ -123,6 +128,7 @@ app
.route({ .route({
path: 'user-app', path: 'user-app',
key: 'test', key: 'test',
description: '对user-app的数据进行测试, 获取版本的信息',
}) })
.define(async (ctx) => { .define(async (ctx) => {
const id = ctx.query.id; const id = ctx.query.id;
@ -133,6 +139,12 @@ app
if (!am) { if (!am) {
throw new CustomError('app not found'); throw new CustomError('app not found');
} }
ctx.body = am; const amJson = am.toJSON();
ctx.body = {
...amJson,
proxy: true,
};
}) })
.addTo(app); .addTo(app);

View File

@ -1,3 +1,5 @@
import { App } from '@kevisual/router';
type Opts = { type Opts = {
prefix: string; prefix: string;
}; };
@ -13,3 +15,34 @@ export const prefixFix = (data: any, prefix: string, opts?: Opts) => {
} }
return data; return data;
}; };
/**
* username获取uid
* @param app
* @param ctx
* @param username
* @returns
*/
export const getUidByUsername = async (app: App, ctx: any, username?: string) => {
const tokenUser = ctx.state.tokenUser;
let uid = tokenUser.id; // TODO: 需要根据username来判断
if (username) {
// 这个用户的数据tokenUser具有这个org的权限才行。真实用户的那个org
const res = await app.call({
path: 'org',
key: 'hasUser',
payload: {
data: {
username,
},
token: ctx.query.token,
},
});
if (res.code === 200 && res.body?.uid) {
uid = res.body.uid;
} else {
ctx.throw(500, 'user not found');
}
}
return uid;
};

View File

@ -12,6 +12,12 @@ const handlePrefix = (prefix: string) => {
} }
return prefix; return prefix;
}; };
/**
* prefix
* @param data
* @param tokenUser
* @returns
*/
const getPrefixByUser = (data: { prefix: string }, tokenUser: { username: string }) => { const getPrefixByUser = (data: { prefix: string }, tokenUser: { username: string }) => {
const prefixBase = '/' + tokenUser.username; const prefixBase = '/' + tokenUser.username;
const _prefix = handlePrefix(data.prefix); const _prefix = handlePrefix(data.prefix);
@ -63,3 +69,28 @@ app
return ctx; return ctx;
}) })
.addTo(app); .addTo(app);
app
.route({
path: 'file',
key: 'me-all-file-stat',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const list = await getMinioList({ prefix: '' + tokenUser.username, recursive: true });
const size = list.reduce((acc, item) => {
if ('size' in item) {
return acc + item.size;
}
return acc;
}, 0);
const sizeMb = size / 1024 / 1024;
ctx.body = {
list,
total: list.length,
size,
sizeMb,
};
})
.addTo(app);

View File

@ -77,3 +77,16 @@ export const deleteFiles = async (prefixs: string[]): Promise<any> => {
return false; return false;
} }
}; };
type GetMinioListAndSetToAppListOpts = {
username: string;
appKey: string;
version: string;
};
// 批量列出文件并设置到appList的files中
export const getMinioListAndSetToAppList = async (opts: GetMinioListAndSetToAppListOpts) => {
const { username, appKey, version } = opts;
const minioList = await getMinioList({ prefix: `${username}/${appKey}/${version}`, recursive: true });
const files = minioList;
return files as MinioFile[];
};

View File

@ -12,6 +12,6 @@ import './app-manager/index.ts';
import './file/index.ts'; import './file/index.ts';
import './packages/index.ts'; // import './packages/index.ts';
import './micro-app/index.ts'; import './micro-app/index.ts';

View File

@ -64,6 +64,7 @@ app
if (!user) { if (!user) {
ctx.throw('user not found'); ctx.throw('user not found');
} }
user.setTokenUser(tokenUser);
const orgs = await user.getOrgs(); const orgs = await user.getOrgs();
if (!orgs.includes('admin')) { if (!orgs.includes('admin')) {
ctx.throw('Permission denied'); ctx.throw('Permission denied');
@ -136,3 +137,38 @@ app
}; };
}) })
.addTo(app); .addTo(app);
app
.route({
path: 'org',
key: 'hasUser',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { username } = ctx.query.data;
const user = await User.findByPk(tokenUser.id);
if (!user) {
ctx.throw('user not found');
}
user.setTokenUser(tokenUser);
const userOrgs = await user.hasUser(username, true);
if (!userOrgs) {
ctx.body = {
uid: null,
};
return;
}
const usernameUser = await User.findOne({ where: { username } });
if (!usernameUser) {
ctx.body = {
uid: null,
};
return;
}
ctx.body = {
uid: usernameUser.id,
user: usernameUser,
};
})
.addTo(app);