Chapter-Daten auf separaten data-Branch ausgelagert

chapters/ lag bisher im main-Branch, aus dem sich jede lokale
Installation bedient (z.B. per ZIP-Download) — dadurch bekam jedes
Chapter beim Einrichten die Mitgliederdaten aller anderen Chapter mit,
obwohl die App diese Daten ohnehin nie lokal liest, sondern immer live
über die Gitea-API holt.

Alle Lese-/Schreibzugriffe auf chapters/* laufen jetzt gegen den neuen
data-Branch (giteaDataUrl / giteaAuthUrl(..., 'data') inkl. branch:
'data' in den Schreib-Bodies). App-Code (index.html, admin.html,
version.json) bleibt weiterhin auf main. chapters/ ist aus main
entfernt (lokal weiterhin vorhanden, per .gitignore ausgeschlossen)
und liegt vollständig auf dem neuen data-Branch.
This commit is contained in:
Christian Heubaum
2026-08-31 17:16:29 +02:00
parent 8127f87e03
commit 08340b5aec
28 changed files with 43 additions and 2031 deletions

View File

@@ -1947,7 +1947,8 @@ const APP_VERSION = '2.1';
const GITEA_RAW = 'http://72.60.134.73:3000/api/v1/repos/cheubaum/BNI-Vorstellungsrunde/raw';
const GITEA_API = 'http://72.60.134.73:3000/api/v1/repos/cheubaum/BNI-Vorstellungsrunde';
function giteaUrl(path) { return `${GITEA_RAW}/${path}?ref=main`; }
function giteaAuthUrl(path, token) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}`; }
function giteaDataUrl(path) { return `${GITEA_RAW}/${path}?ref=data`; }
function giteaAuthUrl(path, token, ref) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}${ref ? '&ref=' + ref : ''}`; }
const CHAPTER_CONFIG_KEY = 'bni-active-chapter';
function loadChapterConfig() {
@@ -2509,7 +2510,7 @@ function renderDisplay() {
imgEl.src = '';
} else if (it.imagePath) {
const src = it.imagePath.startsWith('http') ? it.imagePath
: giteaUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
: giteaDataUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
imgEl.onerror = () => {
imgEl.style.display = 'none';
if (errEl) errEl.style.display = 'block';
@@ -3234,7 +3235,7 @@ async function fetchChapterAgendaFromGitea() {
if (!state.agendaCatalog) state.agendaCatalog = [];
ensureAgendaOrderDefaults();
try {
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
const items = res.ok ? (await res.json()).items : null;
if (Array.isArray(items)) {
// Mergen statt Überschreiben: lokale, noch nicht hochgeladene Punkte
@@ -5337,7 +5338,7 @@ async function activateChapter() {
status.textContent = 'Überprüfe Lizenzcode…';
status.style.color = 'rgba(255,255,255,0.6)';
try {
const res = await fetch(giteaUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
const res = await fetch(giteaDataUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
if (!res.ok) {
status.textContent = 'Ungültiger oder abgelaufener Lizenzcode.';
status.style.color = '#f87171';
@@ -5379,7 +5380,7 @@ function applyChapterUI(config) {
if (ctrlName) ctrlName.textContent = `${name} – Steuerung`;
if (config.logoUrl && activeChapter) {
const src = config.logoUrl.startsWith('http') ? config.logoUrl
: giteaUrl(`chapters/${activeChapter.code}/${config.logoUrl}`);
: giteaDataUrl(`chapters/${activeChapter.code}/${config.logoUrl}`);
['display-logo','display-logo-empty','ctrl-logo'].forEach(id => {
const img = document.getElementById(id);
if (img) img.src = src;
@@ -5397,7 +5398,7 @@ function applyChapterUI(config) {
async function checkLicenseAndUpdates() {
if (!activeChapter) return;
try {
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
if (!res.ok) {
clearChapterData();
state = defaultState();
@@ -5441,7 +5442,7 @@ function showUpdateBanner(version, notes) {
async function downloadUpdate(version) {
try {
const licRes = await fetch(giteaUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
const licRes = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
if (!licRes.ok) {
clearChapterData();
state = defaultState();
@@ -5502,7 +5503,7 @@ async function downloadUpdate(version) {
async function fetchChapterMembersFromGitea() {
if (!activeChapter) return;
try {
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
if (!res.ok) return;
const data = await res.json();
if (!data.members || !data.members.length) return;
@@ -5563,13 +5564,13 @@ async function uploadMembersToGitea() {
try {
// Get current SHA
let sha = null;
const shaRes = await fetch(giteaAuthUrl(path, token));
const shaRes = await fetch(giteaAuthUrl(path, token, 'data'));
if (shaRes.ok) sha = (await shaRes.json()).sha;
const body = { message: `Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content };
const body = { message: `Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content, branch: 'data' };
if (sha) body.sha = sha;
const res = await fetch(giteaAuthUrl(path, token), {
const res = await fetch(giteaAuthUrl(path, token, 'data'), {
method: sha ? 'PUT' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -5821,7 +5822,7 @@ async function showCoordinatorReport() {
];
const datas = await Promise.all(candidates.map(async f => {
try {
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/${f}`), { cache: 'no-store' });
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/${f}`), { cache: 'no-store' });
if (!res.ok) return null;
return await res.json();
} catch(e) { return null; }