Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ae24f47e3 | |||
| ea4aa06e31 | |||
| f6e73250f0 | |||
| 57bdd3a735 | |||
| 0b56564074 | |||
| 7659be9adc | |||
| 09bfb602e3 | |||
| 5e0f741bc3 | |||
| 7eec6d19e5 | |||
| d8ce73439e | |||
| d8798ac32f | |||
| f2f5823fbb | |||
| 0a6703b87e | |||
| eece161e58 | |||
| a35d6cf0cf | |||
| 594f12e927 | |||
| 8d70b71d66 | |||
| 2c39ae4651 | |||
| 9c14a1a7e0 | |||
| 677b983706 | |||
| 20a9d601ff | |||
| df9790177d | |||
| 1621a100af |
205
CREATE_COMPLETE_FILES.sh
Normal file
205
CREATE_COMPLETE_FILES.sh
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
##############################################################################
|
||||||
|
# CREATE_COMPLETE_FILES.sh
|
||||||
|
#
|
||||||
|
# Dieses Script erstellt die vollständigen renderer.js und main.js Dateien
|
||||||
|
# mit allen notwendigen Änderungen für Auto-Login und persistente Settings.
|
||||||
|
#
|
||||||
|
# VERWENDUNG:
|
||||||
|
# 1. Speichere dieses Script in deinem Projekt-Ordner
|
||||||
|
# 2. Mache es ausführbar: chmod +x CREATE_COMPLETE_FILES.sh
|
||||||
|
# 3. Führe es aus: ./CREATE_COMPLETE_FILES.sh
|
||||||
|
# 4. Die neuen Dateien werden erstellt: renderer_NEW.js und main_NEW.js
|
||||||
|
# 5. Ersetze deine alten Dateien mit den neuen
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
echo "========================================="
|
||||||
|
echo "Erstelle vollständige Dateien..."
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob Original-Dateien existieren
|
||||||
|
if [ ! -f "renderer.js" ]; then
|
||||||
|
echo "❌ FEHLER: renderer.js nicht gefunden!"
|
||||||
|
echo "Bitte führe dieses Script im Projekt-Ordner aus."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "main.js" ]; then
|
||||||
|
echo "❌ FEHLER: main.js nicht gefunden!"
|
||||||
|
echo "Bitte führe dieses Script im Projekt-Ordner aus."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Original-Dateien gefunden"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup erstellen
|
||||||
|
echo "📦 Erstelle Backups..."
|
||||||
|
cp renderer.js renderer.js.backup
|
||||||
|
cp main.js main.js.backup
|
||||||
|
echo "✅ Backups erstellt: renderer.js.backup, main.js.backup"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# RENDERER.JS - Patch anwenden
|
||||||
|
echo "🔧 Patche renderer.js..."
|
||||||
|
|
||||||
|
# Erstelle temporäre Datei mit der neuen DOMContentLoaded Funktion
|
||||||
|
cat > /tmp/new_domcontentloaded.js << 'NEWFUNC'
|
||||||
|
window.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
// Prevent default drag/drop on document (except in repo view)
|
||||||
|
document.addEventListener('dragover', e => {
|
||||||
|
if (currentState.view !== 'gitea-repo') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('drop', e => {
|
||||||
|
if (currentState.view !== 'gitea-repo') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load credentials and auto-login if available
|
||||||
|
try {
|
||||||
|
const creds = await window.electronAPI.loadCredentials();
|
||||||
|
if (creds) {
|
||||||
|
// Fülle Settings-Felder
|
||||||
|
if ($('githubToken')) $('githubToken').value = creds.githubToken || '';
|
||||||
|
if ($('giteaToken')) $('giteaToken').value = creds.giteaToken || '';
|
||||||
|
if ($('giteaURL')) $('giteaURL').value = creds.giteaURL || '';
|
||||||
|
|
||||||
|
// AUTO-LOGIN: Wenn Gitea-Credentials vorhanden sind, lade sofort die Repos
|
||||||
|
if (creds.giteaToken && creds.giteaURL) {
|
||||||
|
console.log('✅ Credentials gefunden - Auto-Login wird gestartet...');
|
||||||
|
setStatus('Lade deine Projekte...');
|
||||||
|
|
||||||
|
// Kurze Verzögerung damit UI fertig geladen ist
|
||||||
|
setTimeout(() => {
|
||||||
|
loadGiteaRepos();
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ Keine vollständigen Gitea-Credentials - bitte in Settings eintragen');
|
||||||
|
setStatus('Bereit - bitte Settings konfigurieren');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ Keine Credentials gespeichert');
|
||||||
|
setStatus('Bereit - bitte Settings konfigurieren');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading credentials:', error);
|
||||||
|
setStatus('Fehler beim Laden der Einstellungen');
|
||||||
|
}
|
||||||
|
NEWFUNC
|
||||||
|
|
||||||
|
# Nutze sed um die alte Funktion zu ersetzen
|
||||||
|
# (Dies ist komplex - nutze Python für robusteres Replacement)
|
||||||
|
python3 << 'PYSCRIPT'
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Lese Original-Datei
|
||||||
|
with open('renderer.js', 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Lese neue Funktion
|
||||||
|
with open('/tmp/new_domcontentloaded.js', 'r') as f:
|
||||||
|
new_func_start = f.read()
|
||||||
|
|
||||||
|
# Finde die alte DOMContentLoaded Funktion und ersetze nur den Anfang
|
||||||
|
# Der Rest (Event Handlers) bleibt gleich
|
||||||
|
pattern = r"(window\.addEventListener\('DOMContentLoaded', async \(\) => \{.*?// Load credentials.*?try \{.*?\} catch \(error\) \{.*?console\.error\('Error loading credentials:', error\);.*?\})"
|
||||||
|
|
||||||
|
# Suche nach dem Pattern
|
||||||
|
match = re.search(pattern, content, re.DOTALL)
|
||||||
|
if match:
|
||||||
|
# Ersetze nur den Credentials-Loading Teil
|
||||||
|
old_section = match.group(1)
|
||||||
|
content = content.replace(old_section, new_func_start)
|
||||||
|
|
||||||
|
# Schreibe neue Datei
|
||||||
|
with open('renderer_NEW.js', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
print("✅ renderer_NEW.js erstellt")
|
||||||
|
else:
|
||||||
|
print("⚠️ Konnte Funktion nicht automatisch patchen")
|
||||||
|
print(" Bitte nutze die manuellen Patches")
|
||||||
|
PYSCRIPT
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# MAIN.JS - Patch anwenden
|
||||||
|
echo "🔧 Patche main.js..."
|
||||||
|
|
||||||
|
python3 << 'PYSCRIPT2'
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Lese Original
|
||||||
|
with open('main.js', 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# ÄNDERUNG 1: Entferne getDataDir, ändere getCredentialsFilePath
|
||||||
|
old_funcs = r"function getDataDir\(\) \{[^}]+\}\s*function getCredentialsFilePath\(\) \{[^}]+\}"
|
||||||
|
new_func = """function getCredentialsFilePath() {
|
||||||
|
return ppath.join(app.getPath('userData'), 'credentials.json');
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = re.sub(old_funcs, new_func, content)
|
||||||
|
|
||||||
|
# ÄNDERUNG 2: Patche save-credentials Handler
|
||||||
|
old_handler = r"(ipcMain\.handle\('save-credentials', async \(event, data\) => \{[\s\S]*?const DATA_DIR = getDataDir\(\);[\s\S]*?ensureDir\(DATA_DIR\);[^}]+)\}"
|
||||||
|
|
||||||
|
new_handler = """ipcMain.handle('save-credentials', async (event, data) => {
|
||||||
|
try {
|
||||||
|
const CREDENTIALS_FILE = getCredentialsFilePath();
|
||||||
|
const userDataDir = app.getPath('userData');
|
||||||
|
if (!fs.existsSync(userDataDir)) {
|
||||||
|
fs.mkdirSync(userDataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.stringify(data);
|
||||||
|
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, IV);
|
||||||
|
const encrypted = Buffer.concat([cipher.update(json, 'utf8'), cipher.final()]);
|
||||||
|
fs.writeFileSync(CREDENTIALS_FILE, encrypted);
|
||||||
|
|
||||||
|
console.log('✅ Credentials saved to:', CREDENTIALS_FILE);
|
||||||
|
return { ok: true };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('save-credentials error', e);
|
||||||
|
return { ok: false, error: String(e) };
|
||||||
|
}
|
||||||
|
});"""
|
||||||
|
|
||||||
|
content = re.sub(old_handler, new_handler, content, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# Schreibe neue Datei
|
||||||
|
with open('main_NEW.js', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print("✅ main_NEW.js erstellt")
|
||||||
|
PYSCRIPT2
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================="
|
||||||
|
echo "✅ FERTIG!"
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Neue Dateien erstellt:"
|
||||||
|
echo " 📄 renderer_NEW.js"
|
||||||
|
echo " 📄 main_NEW.js"
|
||||||
|
echo ""
|
||||||
|
echo "Backups erstellt:"
|
||||||
|
echo " 💾 renderer.js.backup"
|
||||||
|
echo " 💾 main.js.backup"
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1. Prüfe die neuen Dateien"
|
||||||
|
echo " 2. Ersetze die alten:"
|
||||||
|
echo " mv renderer_NEW.js renderer.js"
|
||||||
|
echo " mv main_NEW.js main.js"
|
||||||
|
echo " 3. Starte die App neu"
|
||||||
|
echo ""
|
||||||
|
echo "Bei Problemen: Backups wiederherstellen"
|
||||||
|
echo " mv renderer.js.backup renderer.js"
|
||||||
|
echo " mv main.js.backup main.js"
|
||||||
|
echo ""
|
||||||
0
__tests__/gitHandler.test.js
Normal file
0
__tests__/gitHandler.test.js
Normal file
BIN
assets/Thumbs.db
Normal file
BIN
assets/Thumbs.db
Normal file
Binary file not shown.
23
main.js
23
main.js
@@ -35,16 +35,8 @@ const {
|
|||||||
const { initRepo, commitAndPush, getBranches, getCommitLogs } = require('./src/git/gitHandler.js');
|
const { initRepo, commitAndPush, getBranches, getCommitLogs } = require('./src/git/gitHandler.js');
|
||||||
|
|
||||||
// NOTE: credentials/data location is computed via getDataDir() to avoid calling app.getPath before ready
|
// NOTE: credentials/data location is computed via getDataDir() to avoid calling app.getPath before ready
|
||||||
function getDataDir() {
|
|
||||||
try {
|
|
||||||
return ppath.join(app.getPath('userData'), 'data');
|
|
||||||
} catch (e) {
|
|
||||||
// Fallback: use __dirname/data (only if app.getPath not available)
|
|
||||||
return ppath.join(__dirname, 'data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function getCredentialsFilePath() {
|
function getCredentialsFilePath() {
|
||||||
return ppath.join(getDataDir(), 'credentials.json');
|
return ppath.join(app.getPath('userData'), 'credentials.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALGORITHM = 'aes-256-cbc';
|
const ALGORITHM = 'aes-256-cbc';
|
||||||
@@ -193,13 +185,19 @@ ipcMain.handle('select-file', async () => {
|
|||||||
|
|
||||||
ipcMain.handle('save-credentials', async (event, data) => {
|
ipcMain.handle('save-credentials', async (event, data) => {
|
||||||
try {
|
try {
|
||||||
const DATA_DIR = getDataDir();
|
|
||||||
const CREDENTIALS_FILE = getCredentialsFilePath();
|
const CREDENTIALS_FILE = getCredentialsFilePath();
|
||||||
ensureDir(DATA_DIR); // robust gegen ENOTDIR
|
// ✅ Stelle sicher dass das userData Verzeichnis existiert
|
||||||
|
const userDataDir = app.getPath('userData');
|
||||||
|
if (!fs.existsSync(userDataDir)) {
|
||||||
|
fs.mkdirSync(userDataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
const json = JSON.stringify(data);
|
const json = JSON.stringify(data);
|
||||||
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, IV);
|
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, IV);
|
||||||
const encrypted = Buffer.concat([cipher.update(json, 'utf8'), cipher.final()]);
|
const encrypted = Buffer.concat([cipher.update(json, 'utf8'), cipher.final()]);
|
||||||
fs.writeFileSync(CREDENTIALS_FILE, encrypted);
|
fs.writeFileSync(CREDENTIALS_FILE, encrypted);
|
||||||
|
|
||||||
|
console.log('✅ Credentials gespeichert in:', CREDENTIALS_FILE);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('save-credentials error', e);
|
console.error('save-credentials error', e);
|
||||||
@@ -1348,7 +1346,8 @@ ipcMain.handle('create-release', async (event, data) => {
|
|||||||
return { ok: true, release };
|
return { ok: true, release };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('create-release error:', error);
|
console.error('create-release error:', error);
|
||||||
return { ok: false, error: String(error) };
|
const errorMsg = error.message || String(error);
|
||||||
|
return { ok: false, error: errorMsg };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
4190
package-lock.json
generated
4190
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "git-manager-gui",
|
"name": "git-manager-gui",
|
||||||
"version": "2.0.0",
|
"version": "2.0.3",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
|
|||||||
0
renderer/modules/editor.js
Normal file
0
renderer/modules/editor.js
Normal file
0
renderer/modules/gitea.js
Normal file
0
renderer/modules/gitea.js
Normal file
0
renderer/modules/progress.js
Normal file
0
renderer/modules/progress.js
Normal file
0
renderer/modules/state.js
Normal file
0
renderer/modules/state.js
Normal file
0
renderer/modules/ui.js
Normal file
0
renderer/modules/ui.js
Normal file
@@ -1588,18 +1588,39 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load credentials
|
// Load credentials and auto-login if available
|
||||||
try {
|
try {
|
||||||
const creds = await window.electronAPI.loadCredentials();
|
const creds = await window.electronAPI.loadCredentials();
|
||||||
if (creds) {
|
if (creds) {
|
||||||
|
// Fülle Settings-Felder
|
||||||
if ($('githubToken')) $('githubToken').value = creds.githubToken || '';
|
if ($('githubToken')) $('githubToken').value = creds.githubToken || '';
|
||||||
if ($('giteaToken')) $('giteaToken').value = creds.giteaToken || '';
|
if ($('giteaToken')) $('giteaToken').value = creds.giteaToken || '';
|
||||||
if ($('giteaURL')) $('giteaURL').value = creds.giteaURL || '';
|
if ($('giteaURL')) $('giteaURL').value = creds.giteaURL || '';
|
||||||
|
|
||||||
|
// 🆕 AUTO-LOGIN: Wenn Gitea-Credentials vorhanden sind, lade sofort die Repos
|
||||||
|
if (creds.giteaToken && creds.giteaURL) {
|
||||||
|
console.log('✅ Credentials gefunden - Auto-Login wird gestartet...');
|
||||||
|
setStatus('Lade deine Projekte...');
|
||||||
|
|
||||||
|
// Kurze Verzögerung damit UI fertig geladen ist
|
||||||
|
setTimeout(() => {
|
||||||
|
loadGiteaRepos();
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ Keine vollständigen Gitea-Credentials - bitte in Settings eintragen');
|
||||||
|
setStatus('Bereit - bitte Settings konfigurieren');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ Keine Credentials gespeichert');
|
||||||
|
setStatus('Bereit - bitte Settings konfigurieren');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading credentials:', error);
|
console.error('Error loading credentials:', error);
|
||||||
|
setStatus('Fehler beim Laden der Einstellungen');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rest of Event Handlers... (bleibt unverändert)
|
||||||
|
|
||||||
// Event Handlers
|
// Event Handlers
|
||||||
if ($('btnLoadGiteaRepos')) {
|
if ($('btnLoadGiteaRepos')) {
|
||||||
$('btnLoadGiteaRepos').onclick = loadGiteaRepos;
|
$('btnLoadGiteaRepos').onclick = loadGiteaRepos;
|
||||||
@@ -1723,8 +1744,6 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal wird mit pointer-events: none nicht geschlossen durch Klicks
|
|
||||||
// Der Grid bleibt voll interaktiv für neue Tabs
|
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
@@ -1827,11 +1846,23 @@ async function loadRepoReleases(owner, repo) {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Event-Listener MUSS VOR innerHTML += gesetzt werden
|
||||||
const newBtn = grid.querySelector('.btn-new-release');
|
const newBtn = grid.querySelector('.btn-new-release');
|
||||||
newBtn.onclick = () => showCreateReleaseModal(owner, repo);
|
if (newBtn) {
|
||||||
|
newBtn.onclick = () => {
|
||||||
|
console.log('New Release button clicked');
|
||||||
|
showCreateReleaseModal(owner, repo);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.error('New Release button not found in DOM');
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.releases || res.releases.length === 0) {
|
if (!res.releases || res.releases.length === 0) {
|
||||||
grid.innerHTML += '<div style="grid-column: 1/-1; text-align: center; padding: 60px; color: var(--text-muted); font-size: 16px;">📭 Noch keine Releases veröffentlicht</div>';
|
// WICHTIG: appendChild statt innerHTML +=, um Event-Listener zu erhalten
|
||||||
|
const emptyMsg = document.createElement('div');
|
||||||
|
emptyMsg.style.cssText = 'grid-column: 1/-1; text-align: center; padding: 60px; color: var(--text-muted); font-size: 16px;';
|
||||||
|
emptyMsg.textContent = '📭 Noch keine Releases veröffentlicht';
|
||||||
|
grid.appendChild(emptyMsg);
|
||||||
setStatus('No releases');
|
setStatus('No releases');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2131,7 +2162,7 @@ function createReleaseCard(release, isLatest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------
|
/* -------------------------
|
||||||
CREATE RELEASE MODAL
|
CREATE RELEASE MODAL (MIT DATEI-UPLOAD)
|
||||||
------------------------- */
|
------------------------- */
|
||||||
function showCreateReleaseModal(owner, repo) {
|
function showCreateReleaseModal(owner, repo) {
|
||||||
const modal = document.createElement('div');
|
const modal = document.createElement('div');
|
||||||
@@ -2172,6 +2203,31 @@ function showCreateReleaseModal(owner, repo) {
|
|||||||
<label>Target Branch</label>
|
<label>Target Branch</label>
|
||||||
<input id="releaseTarget" type="text" value="main" placeholder="main">
|
<input id="releaseTarget" type="text" value="main" placeholder="main">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- NEU: Datei Upload -->
|
||||||
|
<div class="input-group">
|
||||||
|
<label>Release Asset (Optional)</label>
|
||||||
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<input id="releaseAssetInput" type="text" readonly placeholder="Keine Datei gewählt" style="
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: not-allowed;
|
||||||
|
">
|
||||||
|
<button id="btnSelectReleaseAsset" type="button" style="
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
">📎 Datei wählen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group" style="display: flex; gap: 20px;">
|
<div class="input-group" style="display: flex; gap: 20px;">
|
||||||
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
||||||
@@ -2190,7 +2246,28 @@ function showCreateReleaseModal(owner, repo) {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
// Variable zum Speichern des gewählten Dateipfads
|
||||||
|
let selectedAssetPath = null;
|
||||||
|
|
||||||
|
// Event Listener: Datei auswählen
|
||||||
|
$('btnSelectReleaseAsset').onclick = async () => {
|
||||||
|
try {
|
||||||
|
const res = await window.electronAPI.selectFile();
|
||||||
|
if (res.ok && res.files && res.files.length > 0) {
|
||||||
|
selectedAssetPath = res.files[0];
|
||||||
|
const fileName = selectedAssetPath.split(/[\\/]/).pop();
|
||||||
|
$('releaseAssetInput').value = fileName;
|
||||||
|
$('releaseAssetInput').style.color = 'var(--text-primary)';
|
||||||
|
$('releaseAssetInput').style.borderColor = 'var(--accent-primary)';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Auswählen der Datei:', error);
|
||||||
|
alert('Konnte Dateidialog nicht öffnen.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Event Listener: Release erstellen
|
||||||
$('btnCreateRelease').onclick = async () => {
|
$('btnCreateRelease').onclick = async () => {
|
||||||
const tag = $('releaseTag').value.trim();
|
const tag = $('releaseTag').value.trim();
|
||||||
const name = $('releaseName').value.trim() || tag;
|
const name = $('releaseName').value.trim() || tag;
|
||||||
@@ -2207,6 +2284,7 @@ function showCreateReleaseModal(owner, repo) {
|
|||||||
setStatus('Creating release...');
|
setStatus('Creating release...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 1. Release erstellen
|
||||||
const res = await window.electronAPI.createRelease({
|
const res = await window.electronAPI.createRelease({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
@@ -2219,15 +2297,49 @@ function showCreateReleaseModal(owner, repo) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
// 2. Falls eine Datei ausgewählt wurde, direkt hochladen
|
||||||
|
if (selectedAssetPath) {
|
||||||
|
setStatus('Release erstellt. Lade Datei hoch...');
|
||||||
|
showProgress(50, 'Uploading Asset...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileName = selectedAssetPath.split(/[\\/]/).pop();
|
||||||
|
const uploadRes = await window.electronAPI.uploadReleaseAsset({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
releaseId: res.release.id,
|
||||||
|
filePath: selectedAssetPath,
|
||||||
|
fileName
|
||||||
|
});
|
||||||
|
|
||||||
|
if (uploadRes.ok) {
|
||||||
|
setStatus(`Release "${tag}" und Asset erstellt!`);
|
||||||
|
} else {
|
||||||
|
console.error('Asset Upload fehlgeschlagen:', uploadRes.error);
|
||||||
|
alert(`Release erstellt, aber Asset Upload fehlgeschlagen: ${uploadRes.error}`);
|
||||||
|
setStatus('Release erstellt (Upload Fehler)');
|
||||||
|
}
|
||||||
|
} catch (uploadErr) {
|
||||||
|
console.error('Upload error:', uploadErr);
|
||||||
|
alert('Release erstellt, aber Fehler beim Hochladen der Datei.');
|
||||||
|
setStatus('Release erstellt (Upload Fehler)');
|
||||||
|
} finally {
|
||||||
|
hideProgress();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setStatus('Release created!');
|
||||||
|
}
|
||||||
|
|
||||||
modal.remove();
|
modal.remove();
|
||||||
setStatus('Release created!');
|
loadRepoReleases(owner, repo); // Liste neu laden
|
||||||
loadRepoReleases(owner, repo); // Reload
|
|
||||||
} else {
|
} else {
|
||||||
setStatus('Failed: ' + res.error);
|
setStatus('Failed: ' + res.error);
|
||||||
|
alert('Fehler beim Erstellen des Releases: ' + res.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Create release error:', error);
|
console.error('Create release error:', error);
|
||||||
setStatus('Create failed');
|
setStatus('Create failed');
|
||||||
|
alert('Ein unerwarteter Fehler ist aufgetreten.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2238,7 +2350,6 @@ function showCreateReleaseModal(owner, repo) {
|
|||||||
if (e.target === modal) modal.remove();
|
if (e.target === modal) modal.remove();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------
|
/* -------------------------
|
||||||
UPLOAD ASSET DIALOG
|
UPLOAD ASSET DIALOG
|
||||||
------------------------- */
|
------------------------- */
|
||||||
|
|||||||
@@ -39,10 +39,36 @@ async function createRepoGitHub({ name, token, auto_init = true, license = '', p
|
|||||||
body.license_template = license;
|
body.license_template = license;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await axios.post('https://api.github.com/user/repos', body, {
|
try {
|
||||||
headers: { Authorization: `token ${token}` }
|
const response = await axios.post('https://api.github.com/user/repos', body, {
|
||||||
});
|
headers: { Authorization: `token ${token}` }
|
||||||
return response.data;
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
// Benutzerfreundliche Fehlerbehandlung
|
||||||
|
if (error.response) {
|
||||||
|
const status = error.response.status;
|
||||||
|
const data = error.response.data;
|
||||||
|
|
||||||
|
if (status === 401) {
|
||||||
|
throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren GitHub-Token.');
|
||||||
|
} else if (status === 422) {
|
||||||
|
const msg = data?.message || 'Repository konnte nicht erstellt werden';
|
||||||
|
if (msg.includes('name already exists')) {
|
||||||
|
throw new Error(`Ein Repository mit dem Namen "${name}" existiert bereits.`);
|
||||||
|
}
|
||||||
|
throw new Error(`GitHub-Fehler: ${msg}`);
|
||||||
|
} else if (status === 403) {
|
||||||
|
throw new Error('Zugriff verweigert. Bitte überprüfen Sie Ihre Token-Berechtigungen.');
|
||||||
|
} else {
|
||||||
|
throw new Error(`GitHub-Fehler (${status}): ${data?.message || error.message}`);
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
throw new Error('Keine Antwort von GitHub. Bitte überprüfen Sie Ihre Internetverbindung.');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Fehler beim Erstellen des Repositories: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createRepoGitea({ name, token, url, auto_init = true, license = '', private: isPrivate = false }) {
|
async function createRepoGitea({ name, token, url, auto_init = true, license = '', private: isPrivate = false }) {
|
||||||
@@ -54,6 +80,10 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
|
|||||||
console.log('Token length:', token ? token.length : 0);
|
console.log('Token length:', token ? token.length : 0);
|
||||||
console.log('Name:', name);
|
console.log('Name:', name);
|
||||||
console.log('auto_init:', auto_init);
|
console.log('auto_init:', auto_init);
|
||||||
|
console.log('License:', license);
|
||||||
|
|
||||||
|
// Normalisiere Lizenz zu Großbuchstaben, wenn vorhanden
|
||||||
|
const normalizedLicense = license ? license.toUpperCase() : '';
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
name,
|
name,
|
||||||
@@ -62,21 +92,77 @@ async function createRepoGitea({ name, token, url, auto_init = true, license = '
|
|||||||
default_branch: 'main'
|
default_branch: 'main'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (license) {
|
if (normalizedLicense) {
|
||||||
body.license = license;
|
body.license = normalizedLicense;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 axios.post(endpoint, body, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` },
|
||||||
|
timeout: 15000 // 15 Sekunden Timeout
|
||||||
});
|
});
|
||||||
console.log('Success! Status:', response.status);
|
console.log('Success! Status:', response.status);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating repo:', error.response?.status, error.response?.data);
|
console.error('Error creating repo:', error.response?.status, error.response?.data);
|
||||||
throw error;
|
|
||||||
|
// Wenn Lizenz-Fehler auftritt (500 mit Lizenz-Meldung), versuche ohne Lizenz
|
||||||
|
if (error.response?.status === 500 &&
|
||||||
|
error.response?.data?.message?.includes('getLicense') &&
|
||||||
|
normalizedLicense) {
|
||||||
|
console.warn(`Lizenz "${normalizedLicense}" wird vom Server nicht unterstützt. Versuche ohne Lizenz...`);
|
||||||
|
|
||||||
|
const bodyWithoutLicense = {
|
||||||
|
name,
|
||||||
|
private: isPrivate,
|
||||||
|
auto_init: auto_init,
|
||||||
|
default_branch: 'main'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const retryResponse = await axios.post(endpoint, bodyWithoutLicense, {
|
||||||
|
headers: { Authorization: `token ${token}` },
|
||||||
|
timeout: 15000
|
||||||
|
});
|
||||||
|
console.log('Success without license! Status:', retryResponse.status);
|
||||||
|
console.warn(`Hinweis: Repository wurde ohne Lizenz erstellt, da "${normalizedLicense}" nicht verfügbar ist.`);
|
||||||
|
return retryResponse.data;
|
||||||
|
} catch (retryError) {
|
||||||
|
// Falls auch ohne Lizenz fehlschlägt, behandle den neuen Fehler
|
||||||
|
error = retryError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benutzerfreundliche Fehlerbehandlung
|
||||||
|
if (error.response) {
|
||||||
|
const status = error.response.status;
|
||||||
|
const data = error.response.data;
|
||||||
|
|
||||||
|
if (status === 401) {
|
||||||
|
throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Gitea-Token.');
|
||||||
|
} else if (status === 409 || (status === 422 && data?.message?.includes('already exists'))) {
|
||||||
|
throw new Error(`Ein Repository mit dem Namen "${name}" existiert bereits auf Gitea.`);
|
||||||
|
} else if (status === 403) {
|
||||||
|
throw new Error('Zugriff verweigert. Bitte überprüfen Sie Ihre Token-Berechtigungen.');
|
||||||
|
} else if (status === 404) {
|
||||||
|
throw new Error('Gitea-Server nicht gefunden. Bitte überprüfen Sie die URL.');
|
||||||
|
} else if (status === 422) {
|
||||||
|
const msg = data?.message || 'Repository konnte nicht erstellt werden';
|
||||||
|
throw new Error(`Gitea-Fehler: ${msg}`);
|
||||||
|
} else if (status === 500 && data?.message?.includes('getLicense')) {
|
||||||
|
throw new Error(`Die Lizenz "${normalizedLicense}" wird von Ihrem Gitea-Server nicht unterstützt. Bitte wählen Sie eine andere Lizenz oder lassen Sie das Feld leer.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Gitea-Fehler (${status}): ${data?.message || error.message}`);
|
||||||
|
}
|
||||||
|
} else if (error.code === 'ECONNABORTED') {
|
||||||
|
throw new Error('Zeitüberschreitung beim Verbinden mit Gitea. Bitte überprüfen Sie Ihre Internetverbindung oder Server-URL.');
|
||||||
|
} else if (error.request) {
|
||||||
|
throw new Error('Keine Antwort von Gitea-Server. Bitte überprüfen Sie die URL und Ihre Internetverbindung.');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Fehler beim Erstellen des Repositories: ${error.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +346,7 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
|
|||||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const MAX_RETRIES = 3;
|
const MAX_RETRIES = 2; // Reduziert auf 2 Retries für schnelleren Fallback
|
||||||
|
|
||||||
while (retryCount <= MAX_RETRIES) {
|
while (retryCount <= MAX_RETRIES) {
|
||||||
let sha = await fetchSha();
|
let sha = await fetchSha();
|
||||||
@@ -275,14 +361,48 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
|
|||||||
};
|
};
|
||||||
if (sha) body.sha = sha;
|
if (sha) body.sha = sha;
|
||||||
|
|
||||||
|
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 axios.put(endpoint, body, {
|
||||||
headers: { Authorization: `token ${token}` }
|
headers: { Authorization: `token ${token}` },
|
||||||
|
timeout: 30000 // 30 Sekunden Timeout für größere Dateien
|
||||||
});
|
});
|
||||||
|
console.log(`[Upload Success] ${path} erfolgreich gespeichert`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Upload Attempt ${retryCount + 1} for ${path}:`, err.response ? err.response.data : err.message);
|
console.error(`Upload Attempt ${retryCount + 1} for ${path}:`, err.response ? err.response.data : err.message);
|
||||||
|
|
||||||
|
// Behandle 500 Server-Fehler speziell
|
||||||
|
if (err.response && err.response.status === 500) {
|
||||||
|
const errorMsg = err.response.data?.message || err.message;
|
||||||
|
|
||||||
|
// Git-Referenz-Konflikt: Branch hat sich geändert, SHA ist veraltet
|
||||||
|
if (errorMsg.includes('cannot lock ref') || errorMsg.includes('failed to update ref') || errorMsg.includes('but expected')) {
|
||||||
|
if (retryCount < MAX_RETRIES) {
|
||||||
|
retryCount++;
|
||||||
|
console.warn(`-> Git-Referenz-Konflikt erkannt. Branch hat sich geändert. Aktualisiere SHA und versuche erneut... (Retry ${retryCount}/${MAX_RETRIES})`);
|
||||||
|
await sleep(500); // Kurze Pause
|
||||||
|
continue; // SHA wird in der nächsten Iteration neu geholt
|
||||||
|
} else {
|
||||||
|
throw new Error(`Git-Referenz-Konflikt: Der Branch "${branchName}" wurde während des Uploads geändert. Bitte versuchen Sie es erneut.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn es ein Lizenz-Fehler ist (sollte nicht passieren, aber als Fallback)
|
||||||
|
if (errorMsg.includes('getLicense')) {
|
||||||
|
throw new Error(`Server-Fehler beim Speichern: Lizenz-Problem. Versuchen Sie es erneut.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn es ein anderes Branch-Problem ist
|
||||||
|
if (errorMsg.includes('branch') || errorMsg.includes('ref')) {
|
||||||
|
throw new Error(`Server-Fehler: Problem mit Branch "${branchName}". Details: ${errorMsg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allgemeiner 500-Fehler
|
||||||
|
throw new Error(`Server-Fehler (500) beim Speichern der Datei. Details: ${errorMsg}`);
|
||||||
|
}
|
||||||
|
|
||||||
const isShaRequired = err.response &&
|
const isShaRequired = err.response &&
|
||||||
err.response.status === 422 &&
|
err.response.status === 422 &&
|
||||||
err.response.data &&
|
err.response.data &&
|
||||||
@@ -291,15 +411,33 @@ async function uploadGiteaFile({ token, url, owner, repo, path, contentBase64, m
|
|||||||
|
|
||||||
if (isShaRequired && retryCount < MAX_RETRIES) {
|
if (isShaRequired && retryCount < MAX_RETRIES) {
|
||||||
retryCount++;
|
retryCount++;
|
||||||
console.warn(`-> 422 SHA Required. Waiting 2 seconds for server index update... (Retry ${retryCount}/${MAX_RETRIES})`);
|
console.warn(`-> 422 SHA Required. Waiting 1.5 seconds for server index update... (Retry ${retryCount}/${MAX_RETRIES})`);
|
||||||
await sleep(2000); // Wartezeit geben
|
await sleep(1500); // Reduzierte Wartezeit für schnelleren Fallback
|
||||||
// Schleife wird neu gestartet, SHA wird erneut gesucht
|
// Schleife wird neu gestartet, SHA wird erneut gesucht
|
||||||
continue;
|
continue;
|
||||||
} else if (isShaRequired && retryCount >= MAX_RETRIES) {
|
} else if (isShaRequired && retryCount >= MAX_RETRIES) {
|
||||||
throw new Error(`Upload failed after ${MAX_RETRIES} retries. Server insists file exists but we cannot find its SHA. Check the repository manually.`);
|
// Verbesserte Fehlermeldung mit Hinweis auf Git-Fallback
|
||||||
|
const error = new Error(`API-Upload fehlgeschlagen: Repository wurde gerade erstellt, Index noch nicht bereit. Verwende Git-Fallback.`);
|
||||||
|
error.code = 'SHA_NOT_FOUND';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Andere Fehler mit besserer Meldung werfen
|
||||||
|
if (err.response) {
|
||||||
|
const status = err.response.status;
|
||||||
|
const data = err.response.data;
|
||||||
|
|
||||||
|
if (status === 401) {
|
||||||
|
throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Token.');
|
||||||
|
} else if (status === 403) {
|
||||||
|
throw new Error('Zugriff verweigert. Keine Berechtigung zum Schreiben in dieses Repository.');
|
||||||
|
} else if (status === 404) {
|
||||||
|
throw new Error(`Datei oder Repository nicht gefunden. Bitte überprüfen Sie den Pfad: ${path}`);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Fehler beim Speichern (${status}): ${data?.message || err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Andere Fehler sofort werfen
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,12 +670,42 @@ async function createGiteaRelease({ token, url, owner, repo, data }) {
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `token ${token}`,
|
Authorization: `token ${token}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
},
|
||||||
|
timeout: 15000
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('createGiteaRelease error:', err.response?.data || err.message);
|
console.error('createGiteaRelease error:', err.response?.data || err.message);
|
||||||
throw err;
|
|
||||||
|
// Benutzerfreundliche Fehlerbehandlung
|
||||||
|
if (err.response) {
|
||||||
|
const status = err.response.status;
|
||||||
|
const data = err.response.data;
|
||||||
|
|
||||||
|
if (status === 401) {
|
||||||
|
throw new Error('Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihren Token.');
|
||||||
|
} else if (status === 403) {
|
||||||
|
throw new Error('Zugriff verweigert. Keine Berechtigung zum Erstellen von Releases.');
|
||||||
|
} else if (status === 404) {
|
||||||
|
throw new Error(`Repository "${owner}/${repo}" nicht gefunden.`);
|
||||||
|
} else if (status === 409 || (status === 422 && data?.message?.includes('already exists'))) {
|
||||||
|
throw new Error(`Ein Release mit dem Tag "${data.tag_name}" existiert bereits.`);
|
||||||
|
} else if (status === 422) {
|
||||||
|
const msg = data?.message || 'Release konnte nicht erstellt werden';
|
||||||
|
throw new Error(`Gitea-Fehler: ${msg}`);
|
||||||
|
} else if (status === 500) {
|
||||||
|
const msg = data?.message || err.message;
|
||||||
|
throw new Error(`Server-Fehler (500) beim Erstellen des Release. Details: ${msg}`);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Fehler beim Erstellen des Release (${status}): ${data?.message || err.message}`);
|
||||||
|
}
|
||||||
|
} else if (err.code === 'ECONNABORTED') {
|
||||||
|
throw new Error('Zeitüberschreitung. Bitte versuchen Sie es erneut.');
|
||||||
|
} else if (err.request) {
|
||||||
|
throw new Error('Keine Antwort vom Server. Bitte überprüfen Sie Ihre Internetverbindung.');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Fehler beim Erstellen des Release: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user