mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-28 15:50:27 +00:00
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.
158 lines
5.1 KiB
TypeScript
158 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)
|
||
* 支持分页,懒加载:根据 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: '更新成功' });
|
||
};
|