Upload main.js via GUI

This commit is contained in:
2026-03-24 18:18:31 +00:00
parent 8613720a0b
commit e3dbc6c663

199
main.js
View File

@@ -1,5 +1,5 @@
// main.js — Main-Process with concurrent folder upload/download, progress events, and temp-dir cleanup // main.js — Main-Process with concurrent folder upload/download, progress events, and temp-dir cleanup
const { app, BrowserWindow, ipcMain, dialog, Menu, nativeImage } = require('electron'); const { app, BrowserWindow, ipcMain, dialog, Menu, nativeImage, Tray, shell, clipboard } = require('electron');
const ppath = require('path'); const ppath = require('path');
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
@@ -15,6 +15,7 @@ const {
createRepoGitea, createRepoGitea,
checkGiteaConnection, checkGiteaConnection,
listGiteaRepos, listGiteaRepos,
getGiteaUserHeatmap,
getGiteaRepoContents, getGiteaRepoContents,
getGiteaFileContent, getGiteaFileContent,
uploadGiteaFile, uploadGiteaFile,
@@ -254,13 +255,32 @@ function getSafeTmpDir(baseName) {
/* ----------------------------- /* -----------------------------
app / window app / window
----------------------------- */ ----------------------------- */
let tray = null;
function createTray(win) {
const iconPath = ppath.join(__dirname, 'renderer', 'icon.png');
tray = new Tray(nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 }));
tray.setToolTip('Git Manager Explorer Pro');
const menu = Menu.buildFromTemplate([
{ label: 'Öffnen', click: () => { win.show(); win.focus(); } },
{ type: 'separator' },
{ label: 'Beenden', click: () => { app.quit(); } }
]);
tray.setContextMenu(menu);
tray.on('double-click', () => { win.show(); win.focus(); });
}
function createWindow() { function createWindow() {
// Entfernt das Menü (File, Edit, View...) komplett // Entfernt das Menü (File, Edit, View...) komplett
Menu.setApplicationMenu(null); Menu.setApplicationMenu(null);
const startHidden = process.argv.includes('--hidden');
const win = new BrowserWindow({ const win = new BrowserWindow({
width: 1200, width: 1200,
height: 820, height: 820,
frame: false,
show: !startHidden,
webPreferences: { webPreferences: {
preload: ppath.join(__dirname, 'preload.js'), preload: ppath.join(__dirname, 'preload.js'),
nodeIntegration: false, nodeIntegration: false,
@@ -269,6 +289,17 @@ function createWindow() {
}); });
win.loadFile(ppath.join(__dirname, 'renderer', 'index.html')); win.loadFile(ppath.join(__dirname, 'renderer', 'index.html'));
// win.webContents.openDevTools(); // win.webContents.openDevTools();
createTray(win);
// Schließen-Button -> Tray statt Beenden (nur wenn Autostart aktiv)
win.on('close', (e) => {
const { enabled } = app.getLoginItemSettings();
if (enabled) {
e.preventDefault();
win.hide();
}
});
} }
app.whenReady().then(() => { app.whenReady().then(() => {
@@ -804,6 +835,21 @@ ipcMain.handle('list-gitea-repos', async (event, data) => {
} }
}); });
ipcMain.handle('get-gitea-user-heatmap', async (event, data) => {
try {
const credentials = readCredentials();
const token = (data && data.token) || (credentials && credentials.giteaToken);
const url = (data && data.url) || (credentials && credentials.giteaURL);
if (!token || !url) return { ok: false, error: 'missing-token-or-url' };
const result = await getGiteaUserHeatmap({ token, url });
return { ok: true, ...result };
} catch (e) {
console.error('get-gitea-user-heatmap error', e);
return { ok: false, error: mapIpcError(e) };
}
});
ipcMain.handle('get-gitea-repo-contents', async (event, data) => { ipcMain.handle('get-gitea-repo-contents', async (event, data) => {
try { try {
const credentials = readCredentials(); const credentials = readCredentials();
@@ -2349,6 +2395,55 @@ ipcMain.handle('save-recent', async (event, recent) => {
// main.js - Updater IPC Handlers // main.js - Updater IPC Handlers
// Window Controls
ipcMain.on('window-minimize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) win.minimize();
});
ipcMain.on('window-maximize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) { win.isMaximized() ? win.unmaximize() : win.maximize(); }
});
ipcMain.on('window-close', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) win.close();
});
// Autostart
ipcMain.handle('set-autostart', (event, enable) => {
app.setLoginItemSettings({
openAtLogin: enable,
openAsHidden: true,
args: ['--hidden']
});
return { ok: true };
});
ipcMain.handle('get-autostart', () => {
const settings = app.getLoginItemSettings({ args: ['--hidden'] });
return { ok: true, enabled: settings.openAtLogin };
});
ipcMain.handle('copy-to-clipboard', async (_event, text) => {
try {
clipboard.writeText(String(text || ''));
return { ok: true };
} catch (e) {
return { ok: false, error: String(e) };
}
});
ipcMain.handle('open-external-url', async (_event, rawUrl) => {
try {
const url = String(rawUrl || '').trim();
if (!url) return { ok: false, error: 'Leere URL' };
await shell.openExternal(url);
return { ok: true };
} catch (e) {
return { ok: false, error: String(e) };
}
});
// 1. Version abfragen // 1. Version abfragen
ipcMain.handle('get-app-version', async () => { ipcMain.handle('get-app-version', async () => {
return { ok: true, version: app.getVersion() }; return { ok: true, version: app.getVersion() };
@@ -2408,3 +2503,105 @@ ipcMain.handle('test-gitea-connection', async (event, data) => {
return { ok: false, error: mapIpcError(e) }; return { ok: false, error: mapIpcError(e) };
} }
}); });
ipcMain.handle('validate-repo-name', async (_event, data) => {
try {
const name = String(data && data.name || '').trim();
const platform = String(data && data.platform || 'gitea').trim().toLowerCase();
const validPattern = /^[a-zA-Z0-9](?:[a-zA-Z0-9._-]{0,98}[a-zA-Z0-9])?$/;
const validFormat = validPattern.test(name);
if (!name) {
return { ok: true, validFormat: false, existsExact: false, similar: [], checked: false, reason: 'empty-name' };
}
if (!validFormat) {
return { ok: true, validFormat: false, existsExact: false, similar: [], checked: false, reason: 'invalid-format' };
}
if (platform !== 'gitea') {
return { ok: true, validFormat: true, existsExact: false, similar: [], checked: false, reason: 'platform-not-supported' };
}
const credentials = readCredentials();
if (!credentials || !credentials.giteaToken || !credentials.giteaURL) {
return { ok: true, validFormat: true, existsExact: false, similar: [], checked: false, reason: 'missing-credentials' };
}
const list = await listGiteaRepos({ token: credentials.giteaToken, url: credentials.giteaURL });
const repos = Array.isArray(list) ? list : [];
const allNames = repos
.map(r => String(r && r.name || '').trim())
.filter(Boolean);
const lower = name.toLowerCase();
const normalized = lower.replace(/[\s._-]+/g, '');
const existsExact = allNames.some(n => n.toLowerCase() === lower);
const similar = allNames
.filter(n => {
const nLower = n.toLowerCase();
const nNorm = nLower.replace(/[\s._-]+/g, '');
return nLower.includes(lower) || lower.includes(nLower) || nNorm.includes(normalized) || normalized.includes(nNorm);
})
.filter(n => n.toLowerCase() !== lower)
.slice(0, 8);
return {
ok: true,
validFormat: true,
existsExact,
similar,
checked: true,
totalKnown: allNames.length
};
} catch (e) {
return { ok: false, error: String(e) };
}
});
ipcMain.handle('check-clone-target-collisions', async (_event, data) => {
try {
const targetDir = String(data && data.targetDir || '').trim();
const rawRepos = Array.isArray(data && data.repos) ? data.repos : [];
const repos = rawRepos
.map(v => String(v || '').trim())
.filter(Boolean)
.map(v => {
const parts = v.split('/');
const repo = (parts[1] || '').trim();
return { input: v, repo };
})
.filter(r => r.repo);
const duplicateRepoNames = [];
const seen = new Map();
for (const item of repos) {
const key = item.repo.toLowerCase();
const count = (seen.get(key) || 0) + 1;
seen.set(key, count);
}
for (const [name, count] of seen.entries()) {
if (count > 1) duplicateRepoNames.push(name);
}
const existingTargets = [];
if (targetDir) {
for (const item of repos) {
const repoDir = ppath.join(targetDir, item.repo);
if (fs.existsSync(repoDir)) {
existingTargets.push(repoDir);
}
}
}
return {
ok: true,
duplicateRepoNames,
existingTargets,
hasCollisions: duplicateRepoNames.length > 0 || existingTargets.length > 0
};
} catch (e) {
return { ok: false, error: String(e) };
}
});