Upload main.js via GUI
This commit is contained in:
199
main.js
199
main.js
@@ -1,5 +1,5 @@
|
||||
// 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 fs = require('fs');
|
||||
const os = require('os');
|
||||
@@ -15,6 +15,7 @@ const {
|
||||
createRepoGitea,
|
||||
checkGiteaConnection,
|
||||
listGiteaRepos,
|
||||
getGiteaUserHeatmap,
|
||||
getGiteaRepoContents,
|
||||
getGiteaFileContent,
|
||||
uploadGiteaFile,
|
||||
@@ -254,13 +255,32 @@ function getSafeTmpDir(baseName) {
|
||||
/* -----------------------------
|
||||
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() {
|
||||
// Entfernt das Menü (File, Edit, View...) komplett
|
||||
Menu.setApplicationMenu(null);
|
||||
|
||||
const startHidden = process.argv.includes('--hidden');
|
||||
|
||||
const win = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 820,
|
||||
frame: false,
|
||||
show: !startHidden,
|
||||
webPreferences: {
|
||||
preload: ppath.join(__dirname, 'preload.js'),
|
||||
nodeIntegration: false,
|
||||
@@ -269,6 +289,17 @@ function createWindow() {
|
||||
});
|
||||
win.loadFile(ppath.join(__dirname, 'renderer', 'index.html'));
|
||||
// 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(() => {
|
||||
@@ -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) => {
|
||||
try {
|
||||
const credentials = readCredentials();
|
||||
@@ -2349,6 +2395,55 @@ ipcMain.handle('save-recent', async (event, recent) => {
|
||||
|
||||
// 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
|
||||
ipcMain.handle('get-app-version', async () => {
|
||||
return { ok: true, version: app.getVersion() };
|
||||
@@ -2407,4 +2502,106 @@ ipcMain.handle('test-gitea-connection', async (event, data) => {
|
||||
console.error('test-gitea-connection error', 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) };
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user