Update from Git Manager GUI

This commit is contained in:
2026-03-24 16:34:42 +01:00
parent c64d40fbda
commit 658b29368b

View File

@@ -3,10 +3,96 @@
// getGiteaRepoContents, getGiteaFileContent, uploadGiteaFile
const axios = require('axios');
const http = require('http');
const https = require('https');
// IPv4 bevorzugen verhindert ETIMEDOUT wenn der Hostname nur per IPv6 erreichbar wäre
// oder Node.js fälschlicherweise IPv6 vorranging versucht.
const ipv4HttpAgent = new http.Agent({ family: 4, keepAlive: true });
const ipv4HttpsAgent = new https.Agent({ family: 4, keepAlive: true });
const axiosInstance = axios.create({
httpAgent: ipv4HttpAgent,
httpsAgent: ipv4HttpsAgent,
});
function normalizeAndValidateBaseUrl(rawUrl) {
const value = (rawUrl || '').trim();
if (!value) {
throw new Error('Gitea URL fehlt. Bitte tragen Sie eine URL ein.');
}
let parsed;
try {
parsed = new URL(value);
} catch (_) {
throw new Error('Ungueltige Gitea URL. Beispiel fuer IPv6: http://[2001:db8::1]:3000');
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
throw new Error('Gitea URL muss mit http:// oder https:// beginnen.');
}
return value.replace(/\/$/, '');
}
function normalizeBase(url) {
if (!url) return null;
return url.replace(/\/$/, '');
return normalizeAndValidateBaseUrl(url);
}
async function checkGiteaConnection({ token, url, timeout = 8000 }) {
const base = normalizeAndValidateBaseUrl(url);
const started = Date.now();
const versionRes = await axiosInstance.get(`${base}/api/v1/version`, {
timeout,
validateStatus: () => true,
headers: {
'User-Agent': 'Git-Manager-GUI'
}
});
const latencyMs = Math.max(1, Date.now() - started);
const apiReachable = versionRes.status >= 200 && versionRes.status < 500;
let authStatus = null;
let authOk = false;
if (token) {
const userRes = await axiosInstance.get(`${base}/api/v1/user`, {
timeout,
validateStatus: () => true,
headers: {
Authorization: `token ${token}`,
'User-Agent': 'Git-Manager-GUI'
}
});
authStatus = userRes.status;
authOk = userRes.status >= 200 && userRes.status < 300;
}
const serverVersion =
(versionRes.data && (versionRes.data.version || versionRes.data.Version || versionRes.data.tag)) ||
null;
return {
ok: apiReachable && (!!token ? authOk : true),
base,
checks: {
urlValid: true,
apiReachable,
authProvided: !!token,
authOk
},
metrics: {
latencyMs,
versionStatus: versionRes.status,
authStatus
},
server: {
version: serverVersion
}
};
}
function buildContentsUrl(base, owner, repo, p) {
@@ -17,7 +103,7 @@ function buildContentsUrl(base, owner, repo, p) {
async function tryRequest(url, token, opts = {}) {
try {
const res = await axios.get(url, {
const res = await axiosInstance.get(url, {
headers: token ? { Authorization: `token ${token}` } : {},
timeout: opts.timeout || 10000
});
@@ -40,7 +126,7 @@ async function createRepoGitHub({ name, token, auto_init = true, license = '', p
}
try {
const response = await axios.post('https://api.github.com/user/repos', body, {
const response = await axiosInstance.post('https://api.github.com/user/repos', body, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -99,7 +185,7 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
console.log('Request body:', JSON.stringify(body, null, 2));
try {
const response = await axios.post(endpoint, body, {
const response = await axiosInstance.post(endpoint, body, {
headers: { Authorization: `token ${token}` },
timeout: 15000 // 15 Sekunden Timeout
});
@@ -122,7 +208,7 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
};
try {
const retryResponse = await axios.post(endpoint, bodyWithoutLicense, {
const retryResponse = await axiosInstance.post(endpoint, bodyWithoutLicense, {
headers: { Authorization: `token ${token}` },
timeout: 15000
});
@@ -168,7 +254,7 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
async function listGiteaRepos({ token, url }) {
const endpoint = normalizeBase(url) + '/api/v1/user/repos';
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -335,8 +421,9 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
const fetchSha = async () => {
try {
const existing = await getGiteaRepoContents({ token, url: base, owner, repo, path, ref: branchName });
if (existing && existing.length > 0 && existing[0].sha) {
return existing[0].sha;
const items = existing && existing.items ? existing.items : (Array.isArray(existing) ? existing : []);
if (items.length > 0 && items[0].sha) {
return items[0].sha;
}
return null;
} catch (e) {
@@ -350,11 +437,10 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
const fileName = pathParts.pop();
const dirPath = pathParts.join('/');
const list = await getGiteaRepoContents({ token, url: base, owner, repo, path: dirPath, ref: branchName });
if (Array.isArray(list)) {
const item = list.find(i => i.name === fileName);
if (item && item.sha) return item.sha;
}
const result = await getGiteaRepoContents({ token, url: base, owner, repo, path: dirPath, ref: branchName });
const list = result && result.items ? result.items : (Array.isArray(result) ? result : []);
const item = list.find(i => i.name === fileName);
if (item && item.sha) return item.sha;
return null;
} catch (e) {
return null;
@@ -385,7 +471,7 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
console.log(`[Upload Debug] Datei: ${path}, Branch: ${branchName}, SHA: ${sha ? sha.substring(0, 8) : 'keine'}`);
try {
const res = await axios.put(endpoint, body, {
const res = await axiosInstance.put(endpoint, body, {
headers: { Authorization: `token ${token}` },
timeout: 30000 // 30 Sekunden Timeout für größere Dateien
});
@@ -478,7 +564,7 @@ async function getGiteaCommits({ token, url, owner, repo, branch = 'HEAD', page
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` },
params: {
sha: branch,
@@ -503,7 +589,7 @@ async function getGiteaCommit({ token, url, owner, repo, sha }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/commits/${sha}`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -524,7 +610,7 @@ async function getGiteaCommitDiff({ token, url, owner, repo, sha }) {
const endpoint = `${base}/${owner}/${repo}/commit/${sha}.diff`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -544,7 +630,7 @@ async function getGiteaCommitFiles({ token, url, owner, repo, sha }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/commits/${sha}`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
@@ -581,7 +667,7 @@ async function searchGiteaCommits({ token, url, owner, repo, query, branch = 'HE
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` },
params: {
sha: branch,
@@ -614,7 +700,7 @@ async function getGiteaBranches({ token, url, owner, repo }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -638,7 +724,7 @@ async function listGiteaReleases({ token, url, owner, repo }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -658,7 +744,7 @@ async function getGiteaRelease({ token, url, owner, repo, tag }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`;
try {
const response = await axios.get(endpoint, {
const response = await axiosInstance.get(endpoint, {
headers: { Authorization: `token ${token}` }
});
return response.data;
@@ -687,7 +773,7 @@ async function createGiteaRelease({ token, url, owner, repo, data }) {
};
try {
const response = await axios.post(endpoint, body, {
const response = await axiosInstance.post(endpoint, body, {
headers: {
Authorization: `token ${token}`,
'Content-Type': 'application/json'
@@ -747,7 +833,7 @@ async function editGiteaRelease({ token, url, owner, repo, releaseId, data }) {
if (data.tag_name !== undefined) body.tag_name = data.tag_name;
try {
const response = await axios.patch(endpoint, body, {
const response = await axiosInstance.patch(endpoint, body, {
headers: {
Authorization: `token ${token}`,
'Content-Type': 'application/json'
@@ -770,7 +856,7 @@ async function deleteGiteaRelease({ token, url, owner, repo, releaseId }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${releaseId}`;
try {
await axios.delete(endpoint, {
await axiosInstance.delete(endpoint, {
headers: { Authorization: `token ${token}` }
});
return { ok: true };
@@ -799,7 +885,7 @@ async function uploadReleaseAsset({ token, url, owner, repo, releaseId, filePath
});
try {
const response = await axios.post(endpoint, formData, {
const response = await axiosInstance.post(endpoint, formData, {
headers: {
Authorization: `token ${token}`,
...formData.getHeaders()
@@ -824,7 +910,7 @@ async function deleteReleaseAsset({ token, url, owner, repo, assetId }) {
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/assets/${assetId}`;
try {
await axios.delete(endpoint, {
await axiosInstance.delete(endpoint, {
headers: { Authorization: `token ${token}` }
});
return { ok: true };
@@ -835,8 +921,10 @@ async function deleteReleaseAsset({ token, url, owner, repo, assetId }) {
}
module.exports = {
normalizeAndValidateBaseUrl,
createRepoGitHub,
createRepoGitea,
checkGiteaConnection,
listGiteaRepos,
getGiteaRepoContents,
getGiteaFileContent,