72 lines
2.6 KiB
JavaScript
72 lines
2.6 KiB
JavaScript
// Netlify Function: GET /api/latest-release
|
|
//
|
|
// The download page must not carry its own hard-coded version/hash. Forgejo is
|
|
// the release source of truth; this small same-origin proxy lets the static page
|
|
// read the newest release without relying on third-party CORS.
|
|
const OWNER = 'skatespots';
|
|
const REPO = 'SkateSpots';
|
|
const API = `https://git.skate-spots.org/api/v1/repos/${OWNER}/${REPO}/releases/latest`;
|
|
|
|
const headers = {
|
|
'content-type': 'application/json; charset=utf-8',
|
|
// Short cache means a new Forgejo release appears without a redeploy, while
|
|
// still shielding the Forgejo API from every page view.
|
|
'cache-control': 'public, max-age=300',
|
|
};
|
|
|
|
export default async (request) => {
|
|
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
return new Response(JSON.stringify({ error: 'method not allowed' }), {
|
|
status: 405,
|
|
headers: { ...headers, allow: 'GET, HEAD' },
|
|
});
|
|
}
|
|
|
|
try {
|
|
const releaseRes = await fetch(API, {
|
|
headers: { accept: 'application/json' },
|
|
signal: AbortSignal.timeout(8000),
|
|
});
|
|
if (!releaseRes.ok) throw new Error(`Forgejo release API returned ${releaseRes.status}`);
|
|
|
|
const release = await releaseRes.json();
|
|
const version = String(release.tag_name || '').replace(/^v/, '');
|
|
const assets = Array.isArray(release.assets) ? release.assets : [];
|
|
const apk = assets.find((asset) => /\.apk$/i.test(asset.name || ''));
|
|
const shaAsset = assets.find((asset) => /\.apk\.sha256$/i.test(asset.name || ''));
|
|
if (!version || !apk?.browser_download_url) throw new Error('Latest release has no APK asset');
|
|
|
|
let sha256 = '';
|
|
if (shaAsset?.browser_download_url) {
|
|
try {
|
|
const shaRes = await fetch(shaAsset.browser_download_url, {
|
|
signal: AbortSignal.timeout(5000),
|
|
});
|
|
if (shaRes.ok) {
|
|
const text = await shaRes.text();
|
|
sha256 = text.trim().split(/\s+/)[0]?.toLowerCase() || '';
|
|
}
|
|
} catch {
|
|
// The APK is still useful if the checksum asset is briefly unavailable.
|
|
}
|
|
}
|
|
|
|
return new Response(JSON.stringify({
|
|
version,
|
|
apkUrl: apk.browser_download_url,
|
|
sha256,
|
|
size: apk.size || 0,
|
|
publishedAt: release.published_at || release.created_at || null,
|
|
}), { headers });
|
|
} catch (error) {
|
|
return new Response(JSON.stringify({
|
|
error: 'latest release unavailable',
|
|
detail: error instanceof Error ? error.message : String(error),
|
|
}), {
|
|
status: 502,
|
|
headers: { ...headers, 'cache-control': 'no-store' },
|
|
});
|
|
}
|
|
};
|
|
|
|
export const config = { path: '/api/latest-release' };
|