Files
NapCatQQ/packages/napcat-webui-backend/src/api/BaseInfo.ts
手瓜一十雪 f1756c4d1c Optimize version fetching and update logic
Introduces lazy loading for release and action artifact versions, adds support for nightly.link mirrors, and improves artifact retrieval reliability. Removes unused loginService references, refactors update logic to handle action artifacts, and streamlines frontend/backend API parameters for version selection.
2026-01-04 12:41:21 +08:00

158 lines
5.1 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 { 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
* 支持分页,懒加载:根据 type 参数只获取需要的版本类型
*/
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 typeFilter = req.query['type'] as string | undefined; // 'release' | 'action' | 'all'
const searchQuery = (req.query['search'] as string || '').toLowerCase().trim();
let versions: VersionInfo[] = [];
let actionVersions: VersionInfo[] = [];
let usedMirror = '';
// 懒加载:只获取需要的版本类型
const needReleases = !typeFilter || typeFilter === 'all' || typeFilter === 'release';
const needActions = typeFilter === 'action' || typeFilter === 'all';
// 获取正式版本(仅当需要时)
if (needReleases) {
try {
const result = await getAllTags();
usedMirror = result.mirror;
versions = result.tags.map(tag => {
const isPrerelease = /-(alpha|beta|rc|dev|pre|snapshot)/i.test(tag);
return {
tag,
type: isPrerelease ? 'prerelease' : 'release',
} as VersionInfo;
});
// 使用语义化版本排序(最新的在前)
versions.sort((a, b) => -compareSemVer(a.tag, b.tag));
} catch {
// 如果获取 tags 失败,返回空列表而不是抛出错误
versions = [];
}
}
// 获取 Action Artifacts仅当需要时
if (needActions) {
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 {
// 获取失败时返回空列表
actionVersions = [];
}
}
// 合并版本列表action 在最前面)
let allVersions = [...actionVersions, ...versions];
// 搜索过滤
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: '更新成功' });
};