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
|
// 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() };
|
||||||
@@ -2407,4 +2502,106 @@ ipcMain.handle('test-gitea-connection', async (event, data) => {
|
|||||||
console.error('test-gitea-connection error', e);
|
console.error('test-gitea-connection error', e);
|
||||||
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) };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user