Update from Git Manager GUI
This commit is contained in:
@@ -3,10 +3,96 @@
|
|||||||
// getGiteaRepoContents, getGiteaFileContent, uploadGiteaFile
|
// getGiteaRepoContents, getGiteaFileContent, uploadGiteaFile
|
||||||
|
|
||||||
const axios = require('axios');
|
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) {
|
function normalizeBase(url) {
|
||||||
if (!url) return null;
|
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) {
|
function buildContentsUrl(base, owner, repo, p) {
|
||||||
@@ -17,7 +103,7 @@ function buildContentsUrl(base, owner, repo, p) {
|
|||||||
|
|
||||||
async function tryRequest(url, token, opts = {}) {
|
async function tryRequest(url, token, opts = {}) {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(url, {
|
const res = await axiosInstance.get(url, {
|
||||||
headers: token ? { Authorization: `token ${token}` } : {},
|
headers: token ? { Authorization: `token ${token}` } : {},
|
||||||
timeout: opts.timeout || 10000
|
timeout: opts.timeout || 10000
|
||||||
});
|
});
|
||||||
@@ -40,7 +126,7 @@ async function createRepoGitHub({ name, token, auto_init = true, license = '', p
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
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));
|
console.log('Request body:', JSON.stringify(body, null, 2));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(endpoint, body, {
|
const response = await axiosInstance.post(endpoint, body, {
|
||||||
headers: { Authorization: `token ${token}` },
|
headers: { Authorization: `token ${token}` },
|
||||||
timeout: 15000 // 15 Sekunden Timeout
|
timeout: 15000 // 15 Sekunden Timeout
|
||||||
});
|
});
|
||||||
@@ -122,7 +208,7 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const retryResponse = await axios.post(endpoint, bodyWithoutLicense, {
|
const retryResponse = await axiosInstance.post(endpoint, bodyWithoutLicense, {
|
||||||
headers: { Authorization: `token ${token}` },
|
headers: { Authorization: `token ${token}` },
|
||||||
timeout: 15000
|
timeout: 15000
|
||||||
});
|
});
|
||||||
@@ -168,7 +254,7 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
|
|||||||
|
|
||||||
async function listGiteaRepos({ token, url }) {
|
async function listGiteaRepos({ token, url }) {
|
||||||
const endpoint = normalizeBase(url) + '/api/v1/user/repos';
|
const endpoint = normalizeBase(url) + '/api/v1/user/repos';
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -335,8 +421,9 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
|
|||||||
const fetchSha = async () => {
|
const fetchSha = async () => {
|
||||||
try {
|
try {
|
||||||
const existing = await getGiteaRepoContents({ token, url: base, owner, repo, path, ref: branchName });
|
const existing = await getGiteaRepoContents({ token, url: base, owner, repo, path, ref: branchName });
|
||||||
if (existing && existing.length > 0 && existing[0].sha) {
|
const items = existing && existing.items ? existing.items : (Array.isArray(existing) ? existing : []);
|
||||||
return existing[0].sha;
|
if (items.length > 0 && items[0].sha) {
|
||||||
|
return items[0].sha;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -350,11 +437,10 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
|
|||||||
const fileName = pathParts.pop();
|
const fileName = pathParts.pop();
|
||||||
const dirPath = pathParts.join('/');
|
const dirPath = pathParts.join('/');
|
||||||
|
|
||||||
const list = await getGiteaRepoContents({ token, url: base, owner, repo, path: dirPath, ref: branchName });
|
const result = await getGiteaRepoContents({ token, url: base, owner, repo, path: dirPath, ref: branchName });
|
||||||
if (Array.isArray(list)) {
|
const list = result && result.items ? result.items : (Array.isArray(result) ? result : []);
|
||||||
const item = list.find(i => i.name === fileName);
|
const item = list.find(i => i.name === fileName);
|
||||||
if (item && item.sha) return item.sha;
|
if (item && item.sha) return item.sha;
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
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'}`);
|
console.log(`[Upload Debug] Datei: ${path}, Branch: ${branchName}, SHA: ${sha ? sha.substring(0, 8) : 'keine'}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.put(endpoint, body, {
|
const res = await axiosInstance.put(endpoint, body, {
|
||||||
headers: { Authorization: `token ${token}` },
|
headers: { Authorization: `token ${token}` },
|
||||||
timeout: 30000 // 30 Sekunden Timeout für größere Dateien
|
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`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` },
|
headers: { Authorization: `token ${token}` },
|
||||||
params: {
|
params: {
|
||||||
sha: branch,
|
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}`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/commits/${sha}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -524,7 +610,7 @@ async function getGiteaCommitDiff({ token, url, owner, repo, sha }) {
|
|||||||
const endpoint = `${base}/${owner}/${repo}/commit/${sha}.diff`;
|
const endpoint = `${base}/${owner}/${repo}/commit/${sha}.diff`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
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}`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/commits/${sha}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
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`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` },
|
headers: { Authorization: `token ${token}` },
|
||||||
params: {
|
params: {
|
||||||
sha: branch,
|
sha: branch,
|
||||||
@@ -614,7 +700,7 @@ async function getGiteaBranches({ token, url, owner, repo }) {
|
|||||||
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
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`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
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)}`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(endpoint, {
|
const response = await axiosInstance.get(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -687,7 +773,7 @@ async function createGiteaRelease({ token, url, owner, repo, data }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(endpoint, body, {
|
const response = await axiosInstance.post(endpoint, body, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `token ${token}`,
|
Authorization: `token ${token}`,
|
||||||
'Content-Type': 'application/json'
|
'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;
|
if (data.tag_name !== undefined) body.tag_name = data.tag_name;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.patch(endpoint, body, {
|
const response = await axiosInstance.patch(endpoint, body, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `token ${token}`,
|
Authorization: `token ${token}`,
|
||||||
'Content-Type': 'application/json'
|
'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}`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${releaseId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.delete(endpoint, {
|
await axiosInstance.delete(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
@@ -799,7 +885,7 @@ async function uploadReleaseAsset({ token, url, owner, repo, releaseId, filePath
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(endpoint, formData, {
|
const response = await axiosInstance.post(endpoint, formData, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `token ${token}`,
|
Authorization: `token ${token}`,
|
||||||
...formData.getHeaders()
|
...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}`;
|
const endpoint = `${base}/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/assets/${assetId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.delete(endpoint, {
|
await axiosInstance.delete(endpoint, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` }
|
||||||
});
|
});
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
@@ -835,8 +921,10 @@ async function deleteReleaseAsset({ token, url, owner, repo, assetId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
normalizeAndValidateBaseUrl,
|
||||||
createRepoGitHub,
|
createRepoGitHub,
|
||||||
createRepoGitea,
|
createRepoGitea,
|
||||||
|
checkGiteaConnection,
|
||||||
listGiteaRepos,
|
listGiteaRepos,
|
||||||
getGiteaRepoContents,
|
getGiteaRepoContents,
|
||||||
getGiteaFileContent,
|
getGiteaFileContent,
|
||||||
|
|||||||
Reference in New Issue
Block a user