feat: initial commit - GitHub Stars Manager
This commit is contained in:
154
services/github.js
Normal file
154
services/github.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import Constants from 'expo-constants';
|
||||
|
||||
const GITHUB_API_URL = 'https://api.github.com';
|
||||
// 用于检查更新的仓库地址,从 app.json extra 中读取
|
||||
const APP_REPO = Constants.expoConfig?.extra?.appRepo || 'EchoZenith/GithubStarsManager-Android';
|
||||
// 当前版本号从 app.json 中读取(version 字段)
|
||||
const CURRENT_VERSION = Constants.expoConfig?.version || '1.0.0';
|
||||
|
||||
// 自定义错误类型:Token 过期或无效时抛出,供上层 UI 捕获后跳转 Token 输入页
|
||||
class TokenExpiredError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'TokenExpiredError';
|
||||
}
|
||||
}
|
||||
|
||||
export { TokenExpiredError };
|
||||
|
||||
// 构建 GitHub API 请求头
|
||||
function buildHeaders(token) {
|
||||
return {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
};
|
||||
}
|
||||
|
||||
// 分页获取用户所有星标仓库(每页 100 条,自动翻页直到取完)
|
||||
export async function fetchStarredRepos(token) {
|
||||
if (!token) {
|
||||
throw new TokenExpiredError('请先输入 GitHub Token');
|
||||
}
|
||||
|
||||
let allRepos = [];
|
||||
let page = 1;
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const response = await fetch(
|
||||
`${GITHUB_API_URL}/user/starred?per_page=100&page=${page}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: buildHeaders(token),
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new TokenExpiredError('GitHub Token 已过期或无效,请重新输入');
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API 错误!状态码: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.length === 0) {
|
||||
hasMore = false;
|
||||
} else {
|
||||
allRepos = allRepos.concat(data);
|
||||
page++;
|
||||
}
|
||||
}
|
||||
|
||||
return allRepos;
|
||||
}
|
||||
|
||||
// 获取仓库的 README 原始内容(raw 格式直接返回 markdown 文本)
|
||||
export async function fetchReadme(token, fullName) {
|
||||
if (!token) {
|
||||
throw new TokenExpiredError('请先输入 GitHub Token');
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${GITHUB_API_URL}/repos/${fullName}/readme`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...buildHeaders(token),
|
||||
Accept: 'application/vnd.github.raw',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new TokenExpiredError('GitHub Token 已过期或无效,请重新输入');
|
||||
}
|
||||
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`获取 README 失败!状态码: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
// 检查应用更新:对比当前版本与 GitHub Releases 最新版本号
|
||||
export async function checkUpdate(token) {
|
||||
try {
|
||||
const headers = {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
};
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${GITHUB_API_URL}/repos/${APP_REPO}/releases/latest`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers,
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 404) {
|
||||
return { hasUpdate: false, error: null, message: '未找到发布版本' };
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
return { hasUpdate: false, error: '检查更新失败', message: null };
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const latestVersion = data.tag_name.replace(/^v/, '');
|
||||
const currentParts = CURRENT_VERSION.split('.').map(Number);
|
||||
const latestParts = latestVersion.split('.').map(Number);
|
||||
|
||||
// 逐段比较版本号(major.minor.patch)
|
||||
let hasUpdate = false;
|
||||
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
||||
const cur = currentParts[i] || 0;
|
||||
const lat = latestParts[i] || 0;
|
||||
if (lat > cur) { hasUpdate = true; break; }
|
||||
if (lat < cur) break;
|
||||
}
|
||||
|
||||
return {
|
||||
hasUpdate,
|
||||
currentVersion: CURRENT_VERSION,
|
||||
latestVersion,
|
||||
releaseUrl: data.html_url,
|
||||
releaseName: data.name || data.tag_name,
|
||||
releaseBody: data.body ? data.body.split('\n').slice(0, 5).join('\n') : '',
|
||||
publishedAt: data.published_at,
|
||||
error: null,
|
||||
message: hasUpdate
|
||||
? `发现新版本 v${latestVersion}`
|
||||
: '已是最新版本',
|
||||
};
|
||||
} catch (e) {
|
||||
return { hasUpdate: false, error: e.message || '网络错误', message: null };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user