Update from Git Manager GUI
This commit is contained in:
@@ -260,6 +260,144 @@ async function listGiteaRepos({ token, url }) {
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toDateKey(value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const s = value.trim();
|
||||||
|
if (!s) return null;
|
||||||
|
const match = s.match(/^(\d{4}-\d{2}-\d{2})/);
|
||||||
|
if (match) return match[1];
|
||||||
|
if (/^\d+$/.test(s)) {
|
||||||
|
const n = Number(s);
|
||||||
|
if (!Number.isFinite(n)) return null;
|
||||||
|
const ms = n < 1e12 ? n * 1000 : n;
|
||||||
|
const d = new Date(ms);
|
||||||
|
if (Number.isNaN(d.getTime())) return null;
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
const d = new Date(s);
|
||||||
|
if (Number.isNaN(d.getTime())) return null;
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||||
|
const ms = value < 1e12 ? value * 1000 : value;
|
||||||
|
const d = new Date(ms);
|
||||||
|
if (Number.isNaN(d.getTime())) return null;
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
||||||
|
return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHeatmapEntries(payload) {
|
||||||
|
const acc = new Map();
|
||||||
|
|
||||||
|
const addEntry = (dateLike, countLike) => {
|
||||||
|
const key = toDateKey(dateLike);
|
||||||
|
if (!key) return;
|
||||||
|
const n = Number(countLike);
|
||||||
|
const count = Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 1;
|
||||||
|
acc.set(key, (acc.get(key) || 0) + count);
|
||||||
|
};
|
||||||
|
|
||||||
|
const walk = (node, depth = 0) => {
|
||||||
|
if (depth > 6 || node == null) return;
|
||||||
|
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
node.forEach(item => walk(item, depth + 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof node === 'number' || typeof node === 'string' || node instanceof Date) {
|
||||||
|
addEntry(node, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof node !== 'object') return;
|
||||||
|
|
||||||
|
const dateLike =
|
||||||
|
node.date ?? node.day ?? node.timestamp ?? node.time ?? node.ts ?? node.created_at ?? node.created ?? node.when;
|
||||||
|
const countLike =
|
||||||
|
node.count ?? node.contributions ?? node.value ?? node.total ?? node.commits ?? node.frequency;
|
||||||
|
|
||||||
|
if (dateLike != null) {
|
||||||
|
addEntry(dateLike, countLike);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.data != null) walk(node.data, depth + 1);
|
||||||
|
if (node.values != null) walk(node.values, depth + 1);
|
||||||
|
if (node.heatmap != null) walk(node.heatmap, depth + 1);
|
||||||
|
if (node.contributions != null) walk(node.contributions, depth + 1);
|
||||||
|
if (node.days != null) walk(node.days, depth + 1);
|
||||||
|
|
||||||
|
const keys = Object.keys(node);
|
||||||
|
for (const key of keys) {
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(key)) {
|
||||||
|
addEntry(key, node[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
walk(payload, 0);
|
||||||
|
|
||||||
|
return Array.from(acc.entries())
|
||||||
|
.map(([date, count]) => ({ date, count }))
|
||||||
|
.sort((a, b) => a.date.localeCompare(b.date));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGiteaUserHeatmap({ token, url }) {
|
||||||
|
const base = normalizeBase(url);
|
||||||
|
if (!base) throw new Error('Invalid Gitea base URL');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `token ${token}`,
|
||||||
|
'User-Agent': 'Git-Manager-GUI'
|
||||||
|
};
|
||||||
|
|
||||||
|
const meRes = await axiosInstance.get(`${base}/api/v1/user`, {
|
||||||
|
headers,
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
const username = meRes?.data?.login || meRes?.data?.username || meRes?.data?.name || null;
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
`${base}/api/v1/user/heatmap`,
|
||||||
|
username ? `${base}/api/v1/users/${encodeURIComponent(username)}/heatmap` : null
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
let lastError = null;
|
||||||
|
for (const endpoint of candidates) {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.get(endpoint, {
|
||||||
|
headers,
|
||||||
|
timeout: 12000,
|
||||||
|
validateStatus: () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return {
|
||||||
|
username,
|
||||||
|
endpoint,
|
||||||
|
entries: normalizeHeatmapEntries(response.data)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError = new Error(`Heatmap endpoint failed (${response.status}): ${endpoint}`);
|
||||||
|
} catch (e) {
|
||||||
|
lastError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError || new Error('No usable heatmap endpoint found');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of items for a directory or single item for file.
|
* Returns array of items for a directory or single item for file.
|
||||||
* Each item includes name, path, type, size, download_url, sha (if present).
|
* Each item includes name, path, type, size, download_url, sha (if present).
|
||||||
@@ -926,6 +1064,7 @@ module.exports = {
|
|||||||
createRepoGitea,
|
createRepoGitea,
|
||||||
checkGiteaConnection,
|
checkGiteaConnection,
|
||||||
listGiteaRepos,
|
listGiteaRepos,
|
||||||
|
getGiteaUserHeatmap,
|
||||||
getGiteaRepoContents,
|
getGiteaRepoContents,
|
||||||
getGiteaFileContent,
|
getGiteaFileContent,
|
||||||
uploadGiteaFile,
|
uploadGiteaFile,
|
||||||
|
|||||||
Reference in New Issue
Block a user