Files
gitea-sync-ai/app/templates/index.html
T
2026-08-03 20:18:16 +02:00

231 lines
8.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Gitea Sync AI 🧠</title>
<style>
body { font-family: sans-serif; padding: 20px; background: #f5f5f5; margin: 0; }
h1, h3 { color: #333; margin-top: 0; }
.repo-card, .project-card { background: white; margin: 10px 0; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,.1); }
.project-card { border-left: 4px solid #007bff; }
button { padding: 8px 16px; cursor: pointer; border: none; border-radius: 4px; margin-right: 5px; margin-top: 5px; font-size: 14px; }
.btn-subscribe { background: #007bff; color: white; }
.btn-unsubscribe { background: #dc3545; color: white; }
.btn-push { background: #28a745; color: white; }
.btn-changes { background: #ffc107; color: white; }
.status-ok { color: #28a745; font-size: 13px; }
.status-warn { color: #dc3545; font-size: 13px; }
hr { border: none; border-top: 1px solid #ccc; margin: 20px 0; }
</style>
</head>
<body style="padding: 20px; max-width: 900px; margin: 0 auto;">
<h1>🧠 Gitea Sync AI Dashboard</h1>
<section id="projects-section">
<h3>Deine abonnierten Projekte:</h3>
<div id="projects-list"></div>
</section>
<hr>
<section id="repos-section">
<h3>Gitea Repositories (nicht abonniert):</h3>
<div id="repos"></div>
</section>
<script>
//✅ WICHTIG: Nutze absolute URL für API-Calls!
const API_BASE = window.location.origin;
let subscribedNames = new Set(); // Cache für abonierte Namen!
async function loadProjects() {
const res = await fetch(`${API_BASE}/api/projects`);
const data = await res.json();
const container = document.getElementById("projects-list");
container.innerHTML = "";
if (!data.projects || data.projects.length === 0) {
container.innerHTML = "<p style='color:#666; font-style:italic'>Keine abonnierten Projekte vorhanden.</p>";
return [];
}
subscribedNames.clear();
data.projects.forEach(proj => subscribedNames.add(proj.name));
const titles = document.querySelectorAll("h3");
if (data.projects.length > 0) {
titles[0].innerHTML = `Deine abonnierten Projekte (${data.projects.length})`;
} else {
titles[0].textContent = "Deine abonnierten Projekte";
}
data.projects.forEach(proj => {
const card = document.createElement("div");
card.className = "project-card";
const writeStatus = proj.can_write ?
'<span class="status-ok">✅ Schreibfrei</span>' :
'<span class="status-warn">❌ Nur Lesen</span>';
// ✅ NEU: Zeige Changes an wenn vorhanden!
let changesHtml = '';
if (proj.has_changes && proj.uncommitted_files) {
const fileCount = proj.uncommitted_files.filter(f => f && !f.startsWith('??')).length;
if (fileCount > 0) {
changesHtml = `<span style="display:block;color:#dc3545;margin:5px 0;font-weight:bold">⚠️ ${fileCount} Datei(en) geändert (nicht committed)</span>`;
} else {
changesHtml = '<span style="display:block;color:#ffc107;margin:5px 0;">⚠️ Neuer Inhalt erkannt</span>';
}
}
card.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center;">
<strong>${proj.name}</strong>
<small style="color:#666">${writeStatus}</small>
</div>
${proj.has_git ? '<span style="font-size:12px;color:#007bff;margin-top:5px;display:block">📦 Git Repository</span>' : ''}
${changesHtml}
<div style="margin-top: 8px; font-size: 13px;">
${(proj.has_changes && proj.can_write) ? '<button class="btn-push" onclick="pushProject(\'' + proj.name + '\')">📤 Änderungen pushen</button>' : ''}
${!proj.can_write && !proj.has_changes ? '<small style="color:#999; font-size:12px;">Push nur bei Berechtigung & Changes möglich</small>' : ''}
<button class="btn-unsubscribe" onclick="unsubscribe(\'' + proj.name + '\')">🗑️ Deabonieren</button>
<a href="${API_BASE}/projects/${proj.name}" target="_blank"><button class="btn-check">📁 Öffnen</button></a>
</div>
`;
container.appendChild(card);
});
return data.projects;
}
async function pushProject(projectName) {
const commitMsg = prompt("Commit-Nachricht:", "AI-bearbeitete Änderungen");
if (!commitMsg) return; // Abgebrochen
try {
const res = await fetch(`${API_BASE}/api/push/${projectName}`, {
method: "POST",
headers: {"Content-Type": "application/json"}
});
const data = await res.json();
if (data.success) {
alert(`✅ ${projectName} erfolgreich gepusht!\n\n${data.output || ''}`);
loadProjects(); // Refresh
} else {
alert(`❌ Push fehlgeschlagen: ${data.error || 'Unbekannter Fehler'}`);
}
} catch (err) {
console.error(err);
alert("❌ Verbindungsfehler beim Pushen");
}
}
async function unsubscribe(name) {
if (!confirm(`Möchtest du '${name}' wirklich deabonnieren?\nDer komplette Ordner wird geloescht!`)) return;
const res = await fetch(`${API_BASE}/api/unsubscribe`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({project_name: name})
});
const data = await res.json();
if (data.success) {
alert(`✅ ${name} wurde geloescht!`);
subscribedNames.delete(name);
loadProjects();
} else {
alert(`❌ Fehler: ${data.error || "Unbekannt"}`);
}
}
let allRepos = [];
async function loadRepos() {
try {
const res = await fetch(`${API_BASE}/api/repos`);
allRepos = await res.json();
const container = document.getElementById("repos");
container.innerHTML = "";
if (!Array.isArray(allRepos)) {
container.innerHTML = `<p style='color:#dc3545'>Fehler: ${allRepos.error || 'Unbekannter Fehler'}</p>`;
return;
}
const titleEl = document.querySelector("#repos-section h3");
if (allRepos.length === 0) {
container.innerHTML = "<p style='color:#666; font-style:italic'>Keine Repositories verfügbar.</p>";
titleEl.textContent = "Gitea Repositories";
return;
}
titleEl.innerHTML = `Gitea Repositories (${allRepos.length})`;
const subscribed = allRepos.filter(r => subscribedNames.has(r.name));
const notSubscribed = allRepos.filter(r => !subscribedNames.has(r.name));
[...subscribed, ...notSubscribed].forEach((repo) => {
addRepoCard(repo, container);
});
} catch (err) {
console.error(err);
document.getElementById("repos").innerHTML = "<p style='color:#dc3545'>Fehler beim Laden der Repositories</p>";
}
}
function addRepoCard(repo, container) {
const card = document.createElement("div");
card.className = "repo-card";
const alreadySubscribed = subscribedNames.has(repo.name);
card.innerHTML = `
<strong>${repo.name}</strong> ${alreadySubscribed ? '✅ Schon abonniert' : ''}
<p style="margin: 5px 0; font-size: 13px">${repo.full_name || repo.id} - ${repo.owner?.login || "unbekannt"}</p>
`;
card.innerHTML += `
<button class="${alreadySubscribed ? 'btn-check' : 'btn-subscribe'}" onclick="subscribe(${JSON.stringify(repo).replace(/"/g, '&quot;')})" style="${alreadySubscribed ? 'background:#28a745;cursor:default;opacity:0.6' : ''}">
${alreadySubscribed ? '✅ Abonniert' : ' Abonnieren'}
</button>
`;
container.appendChild(card);
}
async function subscribe(repo) {
if (subscribedNames.has(repo.name)) return;
const target = prompt("Gewünschter Name (Standard: Repo-Name):") || repo.name;
try {
await fetch(`${API_BASE}/api/subscribe`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({repo_url: repo.clone_url, target_name: target})
});
subscribedNames.add(target);
alert(`📦 '${target}' wurde geklont!`);
loadProjects();
} catch (err) {
console.error(err);
alert("❌ Fehler beim Abonnieren");
}
}
loadProjects().then(() => loadRepos());
</script>
</body>
</html>