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

194 lines
6.8 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; }
.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 [];
}
// Cache setzen:
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";
// ✅ NEU: Zeige Berechtigungs-Status!
const writeStatus = proj.can_write ?
'<span class="status-ok">✅ Schreibfrei</span>' :
'<span class="status-warn">❌ Nur Lesen</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>' : ''}
<div style="margin-top: 8px; font-size: 13px;">
<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 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 = []; // Globaler Cache für Repositories
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})`;
// Sortiere: Abonnierte zuerst, dann nicht abonnierte
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; // Schon abonniert!
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");
}
}
// Starte beim Laden: ✅ MIT ABSOLUTEN URLS!
loadProjects().then(() => loadRepos());</script>
</body>
</html>