From 677b9837066d898eb80a60ff7483b52d3b28658c Mon Sep 17 00:00:00 2001 From: M_Viper Date: Tue, 3 Feb 2026 22:52:37 +0100 Subject: [PATCH] Update from Git Manager GUI --- src/git/apiHandler.js | 233 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 215 insertions(+), 18 deletions(-) diff --git a/src/git/apiHandler.js b/src/git/apiHandler.js index 3693cc5..75e4191 100644 --- a/src/git/apiHandler.js +++ b/src/git/apiHandler.js @@ -39,10 +39,36 @@ async function createRepoGitHub({ name, token, auto_init = true, license = '', p body.license_template = license; } - const response = await axios.post('https://api.github.com/user/repos', body, { - headers: { Authorization: `token ${token}` } - }); - return response.data; + try { + const response = await axios.post('https://api.github.com/user/repos', body, { + headers: { Authorization: `token ${token}` } + }); + return response.data; + } catch (error) { + // Benutzerfreundliche Fehlerbehandlung + if (error.response) { + const status = error.response.status; + const data = error.response.data; + + if (status === 401) { + throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren GitHub-Token.'); + } else if (status === 422) { + const msg = data?.message || 'Repository konnte nicht erstellt werden'; + if (msg.includes('name already exists')) { + throw new Error(`Ein Repository mit dem Namen "${name}" existiert bereits.`); + } + throw new Error(`GitHub-Fehler: ${msg}`); + } else if (status === 403) { + throw new Error('Zugriff verweigert. Bitte überprüfen Sie Ihre Token-Berechtigungen.'); + } else { + throw new Error(`GitHub-Fehler (${status}): ${data?.message || error.message}`); + } + } else if (error.request) { + throw new Error('Keine Antwort von GitHub. Bitte überprüfen Sie Ihre Internetverbindung.'); + } else { + throw new Error(`Fehler beim Erstellen des Repositories: ${error.message}`); + } + } } async function createRepoGitea({ name, token, url, auto_init = true, license = '', private: isPrivate = false }) { @@ -54,6 +80,10 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = ' console.log('Token length:', token ? token.length : 0); console.log('Name:', name); console.log('auto_init:', auto_init); + console.log('License:', license); + + // Normalisiere Lizenz zu Großbuchstaben, wenn vorhanden + const normalizedLicense = license ? license.toUpperCase() : ''; const body = { name, @@ -62,21 +92,77 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = ' default_branch: 'main' }; - if (license) { - body.license = license; + if (normalizedLicense) { + body.license = normalizedLicense; } console.log('Request body:', JSON.stringify(body, null, 2)); try { const response = await axios.post(endpoint, body, { - headers: { Authorization: `token ${token}` } + headers: { Authorization: `token ${token}` }, + timeout: 15000 // 15 Sekunden Timeout }); console.log('Success! Status:', response.status); return response.data; } catch (error) { console.error('Error creating repo:', error.response?.status, error.response?.data); - throw error; + + // Wenn Lizenz-Fehler auftritt (500 mit Lizenz-Meldung), versuche ohne Lizenz + if (error.response?.status === 500 && + error.response?.data?.message?.includes('getLicense') && + normalizedLicense) { + console.warn(`Lizenz "${normalizedLicense}" wird vom Server nicht unterstützt. Versuche ohne Lizenz...`); + + const bodyWithoutLicense = { + name, + private: isPrivate, + auto_init: auto_init, + default_branch: 'main' + }; + + try { + const retryResponse = await axios.post(endpoint, bodyWithoutLicense, { + headers: { Authorization: `token ${token}` }, + timeout: 15000 + }); + console.log('Success without license! Status:', retryResponse.status); + console.warn(`Hinweis: Repository wurde ohne Lizenz erstellt, da "${normalizedLicense}" nicht verfügbar ist.`); + return retryResponse.data; + } catch (retryError) { + // Falls auch ohne Lizenz fehlschlägt, behandle den neuen Fehler + error = retryError; + } + } + + // Benutzerfreundliche Fehlerbehandlung + if (error.response) { + const status = error.response.status; + const data = error.response.data; + + if (status === 401) { + throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Gitea-Token.'); + } else if (status === 409 || (status === 422 && data?.message?.includes('already exists'))) { + throw new Error(`Ein Repository mit dem Namen "${name}" existiert bereits auf Gitea.`); + } else if (status === 403) { + throw new Error('Zugriff verweigert. Bitte überprüfen Sie Ihre Token-Berechtigungen.'); + } else if (status === 404) { + throw new Error('Gitea-Server nicht gefunden. Bitte überprüfen Sie die URL.'); + } else if (status === 422) { + const msg = data?.message || 'Repository konnte nicht erstellt werden'; + throw new Error(`Gitea-Fehler: ${msg}`); + } else if (status === 500 && data?.message?.includes('getLicense')) { + throw new Error(`Die Lizenz "${normalizedLicense}" wird von Ihrem Gitea-Server nicht unterstützt. Bitte wählen Sie eine andere Lizenz oder lassen Sie das Feld leer.`); + } else { + throw new Error(`Gitea-Fehler (${status}): ${data?.message || error.message}`); + } + } else if (error.code === 'ECONNABORTED') { + throw new Error('Zeitüberschreitung beim Verbinden mit Gitea. Bitte überprüfen Sie Ihre Internetverbindung oder Server-URL.'); + } else if (error.request) { + throw new Error('Keine Antwort von Gitea-Server. Bitte überprüfen Sie die URL und Ihre Internetverbindung.'); + } else { + throw new Error(`Fehler beim Erstellen des Repositories: ${error.message}`); + } } } @@ -260,7 +346,7 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); let retryCount = 0; - const MAX_RETRIES = 3; + const MAX_RETRIES = 2; // Reduziert auf 2 Retries für schnelleren Fallback while (retryCount <= MAX_RETRIES) { let sha = await fetchSha(); @@ -275,14 +361,48 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m }; if (sha) body.sha = sha; + console.log(`[Upload Debug] Datei: ${path}, Branch: ${branchName}, SHA: ${sha ? sha.substring(0, 8) : 'keine'}`); + try { const res = await axios.put(endpoint, body, { - headers: { Authorization: `token ${token}` } + headers: { Authorization: `token ${token}` }, + timeout: 30000 // 30 Sekunden Timeout für größere Dateien }); + console.log(`[Upload Success] ${path} erfolgreich gespeichert`); return res.data; } catch (err) { console.error(`Upload Attempt ${retryCount + 1} for ${path}:`, err.response ? err.response.data : err.message); + // Behandle 500 Server-Fehler speziell + if (err.response && err.response.status === 500) { + const errorMsg = err.response.data?.message || err.message; + + // Git-Referenz-Konflikt: Branch hat sich geändert, SHA ist veraltet + if (errorMsg.includes('cannot lock ref') || errorMsg.includes('failed to update ref') || errorMsg.includes('but expected')) { + if (retryCount < MAX_RETRIES) { + retryCount++; + console.warn(`-> Git-Referenz-Konflikt erkannt. Branch hat sich geändert. Aktualisiere SHA und versuche erneut... (Retry ${retryCount}/${MAX_RETRIES})`); + await sleep(500); // Kurze Pause + continue; // SHA wird in der nächsten Iteration neu geholt + } else { + throw new Error(`Git-Referenz-Konflikt: Der Branch "${branchName}" wurde während des Uploads geändert. Bitte versuchen Sie es erneut.`); + } + } + + // Wenn es ein Lizenz-Fehler ist (sollte nicht passieren, aber als Fallback) + if (errorMsg.includes('getLicense')) { + throw new Error(`Server-Fehler beim Speichern: Lizenz-Problem. Versuchen Sie es erneut.`); + } + + // Wenn es ein anderes Branch-Problem ist + if (errorMsg.includes('branch') || errorMsg.includes('ref')) { + throw new Error(`Server-Fehler: Problem mit Branch "${branchName}". Details: ${errorMsg}`); + } + + // Allgemeiner 500-Fehler + throw new Error(`Server-Fehler (500) beim Speichern der Datei. Details: ${errorMsg}`); + } + const isShaRequired = err.response && err.response.status === 422 && err.response.data && @@ -291,15 +411,33 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m if (isShaRequired && retryCount < MAX_RETRIES) { retryCount++; - console.warn(`-> 422 SHA Required. Waiting 2 seconds for server index update... (Retry ${retryCount}/${MAX_RETRIES})`); - await sleep(2000); // Wartezeit geben + console.warn(`-> 422 SHA Required. Waiting 1.5 seconds for server index update... (Retry ${retryCount}/${MAX_RETRIES})`); + await sleep(1500); // Reduzierte Wartezeit für schnelleren Fallback // Schleife wird neu gestartet, SHA wird erneut gesucht continue; } else if (isShaRequired && retryCount >= MAX_RETRIES) { - throw new Error(`Upload failed after ${MAX_RETRIES} retries. Server insists file exists but we cannot find its SHA. Check the repository manually.`); + // Verbesserte Fehlermeldung mit Hinweis auf Git-Fallback + const error = new Error(`API-Upload fehlgeschlagen: Repository wurde gerade erstellt, Index noch nicht bereit. Verwende Git-Fallback.`); + error.code = 'SHA_NOT_FOUND'; + throw error; + } + + // Andere Fehler mit besserer Meldung werfen + if (err.response) { + const status = err.response.status; + const data = err.response.data; + + if (status === 401) { + throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Token.'); + } else if (status === 403) { + throw new Error('Zugriff verweigert. Keine Berechtigung zum Schreiben in dieses Repository.'); + } else if (status === 404) { + throw new Error(`Datei oder Repository nicht gefunden. Bitte überprüfen Sie den Pfad: ${path}`); + } else { + throw new Error(`Fehler beim Speichern (${status}): ${data?.message || err.message}`); + } } - // Andere Fehler sofort werfen throw err; } } @@ -532,12 +670,42 @@ async function createGiteaRelease({ token, url, owner, repo, data }) { headers: { Authorization: `token ${token}`, 'Content-Type': 'application/json' - } + }, + timeout: 15000 }); return response.data; } catch (err) { console.error('createGiteaRelease error:', err.response?.data || err.message); - throw err; + + // Benutzerfreundliche Fehlerbehandlung + if (err.response) { + const status = err.response.status; + const data = err.response.data; + + if (status === 401) { + throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Token.'); + } else if (status === 403) { + throw new Error('Zugriff verweigert. Keine Berechtigung zum Erstellen von Releases.'); + } else if (status === 404) { + throw new Error(`Repository "${owner}/${repo}" nicht gefunden.`); + } else if (status === 409 || (status === 422 && data?.message?.includes('already exists'))) { + throw new Error(`Ein Release mit dem Tag "${data.tag_name}" existiert bereits.`); + } else if (status === 422) { + const msg = data?.message || 'Release konnte nicht erstellt werden'; + throw new Error(`Gitea-Fehler: ${msg}`); + } else if (status === 500) { + const msg = data?.message || err.message; + throw new Error(`Server-Fehler (500) beim Erstellen des Release. Details: ${msg}`); + } else { + throw new Error(`Fehler beim Erstellen des Release (${status}): ${data?.message || err.message}`); + } + } else if (err.code === 'ECONNABORTED') { + throw new Error('Zeitüberschreitung. Bitte versuchen Sie es erneut.'); + } else if (err.request) { + throw new Error('Keine Antwort vom Server. Bitte überprüfen Sie Ihre Internetverbindung.'); + } else { + throw new Error(`Fehler beim Erstellen des Release: ${err.message}`); + } } } @@ -616,12 +784,41 @@ async function uploadReleaseAsset({ token, url, owner, repo, releaseId, filePath ...formData.getHeaders() }, maxContentLength: Infinity, - maxBodyLength: Infinity + maxBodyLength: Infinity, + timeout: 300000 // 5 Minuten für große Dateien }); return response.data; } catch (err) { console.error('uploadReleaseAsset error:', err.response?.data || err.message); - throw err; + + // Benutzerfreundliche Fehlerbehandlung + if (err.response) { + const status = err.response.status; + const data = err.response.data; + + if (status === 401) { + throw new Error('Authentifizierung fehlgeschlagen beim Upload.'); + } else if (status === 403) { + throw new Error('Zugriff verweigert. Keine Berechtigung zum Hochladen von Assets.'); + } else if (status === 404) { + throw new Error(`Release mit ID ${releaseId} nicht gefunden.`); + } else if (status === 413) { + throw new Error('Datei ist zu groß. Maximale Größe überschritten.'); + } else if (status === 500) { + const msg = data?.message || err.message; + throw new Error(`Server-Fehler beim Upload: ${msg}`); + } else { + throw new Error(`Fehler beim Upload (${status}): ${data?.message || err.message}`); + } + } else if (err.code === 'ECONNABORTED') { + throw new Error('Upload-Zeitüberschreitung. Datei ist möglicherweise zu groß.'); + } else if (err.code === 'ENOENT') { + throw new Error(`Datei nicht gefunden: ${filePath}`); + } else if (err.request) { + throw new Error('Keine Antwort vom Server beim Upload.'); + } else { + throw new Error(`Upload fehlgeschlagen: ${err.message}`); + } } }