mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-01-11 06:39:00 +08:00
Extended artifact metadata to include workflow run ID and head SHA. Updated backend to filter artifacts by environment and provide additional metadata. Improved frontend to display new artifact details and adjusted UI for better clarity.
162 lines
5.1 KiB
TypeScript
162 lines
5.1 KiB
TypeScript
import { RequestHandler } from 'express';
|
||
import { WebUiDataRuntime } from '@/napcat-webui-backend/src/helper/Data';
|
||
|
||
import { sendSuccess } from '@/napcat-webui-backend/src/utils/response';
|
||
import { WebUiConfig } from '@/napcat-webui-backend/index';
|
||
import { getLatestTag, getAllTags, compareSemVer } from 'napcat-common/src/helper';
|
||
import { getLatestActionArtifacts } from '@/napcat-common/src/mirror';
|
||
import { NapCatCoreWorkingEnv } from '@/napcat-webui-backend/src/types';
|
||
|
||
export const GetNapCatVersion: RequestHandler = (_, res) => {
|
||
const data = WebUiDataRuntime.GetNapCatVersion();
|
||
sendSuccess(res, { version: data });
|
||
};
|
||
|
||
export const getLatestTagHandler: RequestHandler = async (_, res) => {
|
||
try {
|
||
const latestTag = await getLatestTag();
|
||
sendSuccess(res, latestTag);
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Failed to fetch latest tag', details: (error as Error).message });
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 版本信息接口
|
||
*/
|
||
export interface VersionInfo {
|
||
tag: string;
|
||
type: 'release' | 'prerelease' | 'action';
|
||
/** Action artifact 专用字段 */
|
||
artifactId?: number;
|
||
artifactName?: string;
|
||
createdAt?: string;
|
||
expiresAt?: string;
|
||
size?: number;
|
||
workflowRunId?: number;
|
||
headSha?: string;
|
||
}
|
||
|
||
/**
|
||
* 获取所有可用的版本(release + action artifacts)
|
||
* 支持分页
|
||
*/
|
||
export const getAllReleasesHandler: RequestHandler = async (req, res) => {
|
||
try {
|
||
const page = parseInt(req.query['page'] as string) || 1;
|
||
const pageSize = parseInt(req.query['pageSize'] as string) || 20;
|
||
const includeActions = req.query['includeActions'] !== 'false';
|
||
const typeFilter = req.query['type'] as string | undefined; // 'release' | 'action' | 'all'
|
||
const searchQuery = (req.query['search'] as string || '').toLowerCase().trim();
|
||
|
||
let tags: string[] = [];
|
||
let usedMirror = '';
|
||
try {
|
||
const result = await getAllTags();
|
||
tags = result.tags;
|
||
usedMirror = result.mirror;
|
||
} catch {
|
||
// 如果获取 tags 失败,返回空列表而不是抛出错误
|
||
tags = [];
|
||
}
|
||
|
||
// 解析版本信息
|
||
const versions: VersionInfo[] = tags.map(tag => {
|
||
// 检查是否是预发布版本
|
||
const isPrerelease = /-(alpha|beta|rc|dev|pre|snapshot)/i.test(tag);
|
||
return {
|
||
tag,
|
||
type: isPrerelease ? 'prerelease' : 'release',
|
||
};
|
||
});
|
||
|
||
// 使用语义化版本排序(最新的在前)
|
||
versions.sort((a, b) => -compareSemVer(a.tag, b.tag));
|
||
|
||
// 获取 Action Artifacts(如果请求)
|
||
let actionVersions: VersionInfo[] = [];
|
||
if (includeActions) {
|
||
try {
|
||
const artifacts = await getLatestActionArtifacts('NapNeko', 'NapCatQQ', 'build.yml', 'main');
|
||
|
||
// 根据当前工作环境自动过滤对应的 artifact 类型
|
||
const isFramework = WebUiDataRuntime.getWorkingEnv() === NapCatCoreWorkingEnv.Framework;
|
||
const targetArtifactName = isFramework ? 'NapCat.Framework' : 'NapCat.Shell';
|
||
|
||
actionVersions = artifacts
|
||
.filter(a => a.name === targetArtifactName)
|
||
.map(a => ({
|
||
tag: `action-${a.id}`,
|
||
type: 'action' as const,
|
||
artifactId: a.id,
|
||
artifactName: a.name,
|
||
createdAt: a.created_at,
|
||
expiresAt: a.expires_at,
|
||
size: a.size_in_bytes,
|
||
workflowRunId: a.workflow_run_id,
|
||
headSha: a.head_sha,
|
||
}));
|
||
} catch {
|
||
// 忽略 action artifacts 获取失败
|
||
}
|
||
}
|
||
|
||
// 合并版本列表(action 在最前面)
|
||
let allVersions = [...actionVersions, ...versions];
|
||
|
||
// 按类型过滤
|
||
if (typeFilter && typeFilter !== 'all') {
|
||
if (typeFilter === 'release') {
|
||
allVersions = allVersions.filter(v => v.type === 'release' || v.type === 'prerelease');
|
||
} else if (typeFilter === 'action') {
|
||
allVersions = allVersions.filter(v => v.type === 'action');
|
||
}
|
||
}
|
||
|
||
// 搜索过滤
|
||
if (searchQuery) {
|
||
allVersions = allVersions.filter(v => {
|
||
const tagMatch = v.tag.toLowerCase().includes(searchQuery);
|
||
const nameMatch = v.artifactName?.toLowerCase().includes(searchQuery);
|
||
return tagMatch || nameMatch;
|
||
});
|
||
}
|
||
|
||
// 分页
|
||
const total = allVersions.length;
|
||
const totalPages = Math.ceil(total / pageSize);
|
||
const start = (page - 1) * pageSize;
|
||
const end = start + pageSize;
|
||
const paginatedVersions = allVersions.slice(start, end);
|
||
|
||
sendSuccess(res, {
|
||
versions: paginatedVersions,
|
||
pagination: {
|
||
page,
|
||
pageSize,
|
||
total,
|
||
totalPages,
|
||
},
|
||
mirror: usedMirror
|
||
});
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Failed to fetch releases' });
|
||
}
|
||
};
|
||
|
||
export const QQVersionHandler: RequestHandler = (_, res) => {
|
||
const data = WebUiDataRuntime.getQQVersion();
|
||
sendSuccess(res, data);
|
||
};
|
||
|
||
export const GetThemeConfigHandler: RequestHandler = async (_, res) => {
|
||
const data = await WebUiConfig.GetTheme();
|
||
sendSuccess(res, data);
|
||
};
|
||
|
||
export const SetThemeConfigHandler: RequestHandler = async (req, res) => {
|
||
const { theme } = req.body;
|
||
await WebUiConfig.UpdateTheme(theme);
|
||
sendSuccess(res, { message: '更新成功' });
|
||
};
|