69 lines
3.1 KiB
JavaScript
69 lines
3.1 KiB
JavaScript
export class Gitea {
|
|
constructor(repositoryUrl, token, fetchImpl = fetch) {
|
|
const url = new URL(repositoryUrl);
|
|
this.origin = url.origin;
|
|
this.base = `${url.origin}/api/v1/repos${url.pathname.replace(/\/$/, '')}`;
|
|
this.token = token;
|
|
this.fetch = fetchImpl;
|
|
}
|
|
|
|
async request(path, { method = 'GET', body, missing = false, raw = false } = {}) {
|
|
const response = await this.fetch(`${this.base}${path}`, {
|
|
method, redirect: 'error', signal: AbortSignal.timeout(60_000),
|
|
headers: { Authorization: `token ${this.token}`, ...(body && !(body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}) },
|
|
body: body instanceof FormData ? body : body ? JSON.stringify(body) : undefined,
|
|
});
|
|
if (missing && response.status === 404) return null;
|
|
if (!response.ok) throw new Error(`Gitea ${method} ${path}: HTTP ${response.status}`);
|
|
if (raw) return Buffer.from(await response.arrayBuffer());
|
|
return response.status === 204 ? null : response.json();
|
|
}
|
|
|
|
release(tag) { return this.request(`/releases/tags/${encodeURIComponent(tag)}`, { missing: true }); }
|
|
tag(tag) { return this.request(`/tags/${encodeURIComponent(tag)}`, { missing: true }); }
|
|
|
|
async releases() {
|
|
const releases = [];
|
|
for (let page = 1; ; page++) {
|
|
const batch = await this.request(`/releases?limit=50&page=${page}`);
|
|
releases.push(...batch);
|
|
if (batch.length < 50) return releases;
|
|
}
|
|
}
|
|
|
|
async asset(release, name) {
|
|
const assets = await this.request(`/releases/${release.id}/assets`);
|
|
const asset = assets.find(item => item.name === name);
|
|
if (!asset) return null;
|
|
let url = new URL(asset.browser_download_url);
|
|
if (url.origin !== this.origin) throw new Error('Unerwartete Herkunft der Release-Datei.');
|
|
for (let redirects = 0; redirects < 5; redirects++) {
|
|
const response = await this.fetch(url.href, {
|
|
redirect: 'manual', signal: AbortSignal.timeout(60_000),
|
|
headers: url.origin === this.origin ? { Authorization: `token ${this.token}` } : {},
|
|
});
|
|
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
url = new URL(response.headers.get('location'), url);
|
|
if (url.protocol !== 'https:') throw new Error('Unsicheres Download-Redirect.');
|
|
continue;
|
|
}
|
|
if (!response.ok) throw new Error(`Release-Download: HTTP ${response.status}`);
|
|
return Buffer.from(await response.arrayBuffer());
|
|
}
|
|
throw new Error('Zu viele Download-Redirects.');
|
|
}
|
|
|
|
async upload(release, name, data) {
|
|
const assets = await this.request(`/releases/${release.id}/assets`);
|
|
const existing = assets.find(item => item.name === name);
|
|
if (existing) {
|
|
const saved = await this.asset(release, name);
|
|
if (!saved.equals(Buffer.from(data))) throw new Error(`Release-Datei ${name} existiert bereits mit anderem Inhalt.`);
|
|
return;
|
|
}
|
|
const form = new FormData();
|
|
form.append('attachment', new Blob([data]), name);
|
|
await this.request(`/releases/${release.id}/assets?name=${encodeURIComponent(name)}`, { method: 'POST', body: form });
|
|
}
|
|
}
|