Upload file main.js via GUI
This commit is contained in:
463
main.js
463
main.js
@@ -252,19 +252,13 @@ ipcMain.handle('push-project', async (event, data) => {
|
||||
try {
|
||||
if (!data.folder || !fs.existsSync(data.folder)) return { ok: false, error: 'folder-not-found' };
|
||||
|
||||
// Prüfen, ob der lokale Branch 'master' heißt und in 'main' umbenennen
|
||||
// Aktuellen Branch ermitteln (NICHT umbenennen!)
|
||||
let currentBranch = data.branch || null;
|
||||
try {
|
||||
const currentBranch = execSync('git branch --show-current', { cwd: data.folder, stdio: 'pipe', encoding: 'utf-8' }).trim();
|
||||
console.log('Current local branch:', currentBranch);
|
||||
|
||||
if (currentBranch === 'master') {
|
||||
console.log('Attempting to rename master to main...');
|
||||
try {
|
||||
execSync('git branch -m master main', { cwd: data.folder, stdio: 'inherit' });
|
||||
console.log('Successfully renamed local branch master to main');
|
||||
} catch (e) {
|
||||
console.warn('Failed to rename branch (maybe main already exists)', e.message);
|
||||
}
|
||||
const detected = execSync('git branch --show-current', { cwd: data.folder, stdio: 'pipe', encoding: 'utf-8' }).trim();
|
||||
if (detected) {
|
||||
currentBranch = currentBranch || detected;
|
||||
console.log('Current local branch:', detected);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Could not check local branch (maybe not a git repo yet)', e.message);
|
||||
@@ -310,9 +304,11 @@ ipcMain.handle('push-project', async (event, data) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Pushen (nutze 'main')
|
||||
// 3. Pushen (nutze den tatsächlichen Branch - main ODER master)
|
||||
const progressCb = percent => { try { event.sender.send('push-progress', percent); } catch (_) {} };
|
||||
await commitAndPush(data.folder, 'main', 'Update from Git Manager GUI', progressCb);
|
||||
const commitMsg = data.commitMessage || 'Update from Git Manager GUI';
|
||||
const pushBranch = currentBranch || 'main';
|
||||
await commitAndPush(data.folder, pushBranch, commitMsg, progressCb);
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
console.error('push-project error', e);
|
||||
@@ -323,8 +319,20 @@ ipcMain.handle('push-project', async (event, data) => {
|
||||
ipcMain.handle('getBranches', async (event, data) => {
|
||||
try {
|
||||
const branches = await getBranches(data.folder);
|
||||
// Sortieren, damit 'main' oben steht
|
||||
branches.sort((a, b) => (a === 'main' ? -1 : b === 'main' ?1 : 0));
|
||||
// Aktuellen Branch ermitteln und nach oben sortieren
|
||||
let currentBranch = null;
|
||||
try {
|
||||
currentBranch = execSync('git branch --show-current', { cwd: data.folder, stdio: 'pipe', encoding: 'utf-8' }).trim();
|
||||
} catch (_) {}
|
||||
branches.sort((a, b) => {
|
||||
if (a === currentBranch) return -1;
|
||||
if (b === currentBranch) return 1;
|
||||
if (a === 'main') return -1;
|
||||
if (b === 'main') return 1;
|
||||
if (a === 'master') return -1;
|
||||
if (b === 'master') return 1;
|
||||
return 0;
|
||||
});
|
||||
return { ok: true, branches };
|
||||
} catch (e) {
|
||||
console.error('getBranches error', e);
|
||||
@@ -463,6 +471,121 @@ ipcMain.handle('writeFile', async (event, data) => {
|
||||
|
||||
ipcMain.handle('deleteFile', async (event, data) => {
|
||||
try {
|
||||
// --- GITEA DELETION ---
|
||||
if (data && data.isGitea) {
|
||||
const credentials = readCredentials();
|
||||
const token = (data.token) || (credentials && credentials.giteaToken);
|
||||
const giteaUrl = (data.url) || (credentials && credentials.giteaURL);
|
||||
if (!token || !giteaUrl) return { ok: false, error: 'missing-token-or-url' };
|
||||
|
||||
const owner = data.owner;
|
||||
const repo = data.repo;
|
||||
const filePath = data.path;
|
||||
if (!owner || !repo || !filePath) return { ok: false, error: 'missing-owner-repo-or-path' };
|
||||
|
||||
const urlObj = new URL(giteaUrl);
|
||||
const protocol = urlObj.protocol === 'https:' ? https : http;
|
||||
const port = urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80);
|
||||
|
||||
// Helper: GET contents from Gitea API
|
||||
function giteaGet(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = protocol.request({
|
||||
hostname: urlObj.hostname, port,
|
||||
path: `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${path.split('/').map(encodeURIComponent).join('/')}?ref=${data.ref || 'HEAD'}`,
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': `token ${token}`, 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
try { resolve(JSON.parse(body)); } catch (e) { reject(e); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// Helper: DELETE a single file by path + sha
|
||||
function giteaDeleteFile(filePath, sha) {
|
||||
return new Promise((resolve) => {
|
||||
const body = JSON.stringify({
|
||||
message: `Delete ${filePath} via Git Manager GUI`,
|
||||
sha,
|
||||
branch: data.ref || 'HEAD'
|
||||
});
|
||||
const req = protocol.request({
|
||||
hostname: urlObj.hostname, port,
|
||||
path: `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${filePath.split('/').map(encodeURIComponent).join('/')}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `token ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(body)
|
||||
}
|
||||
}, (res) => {
|
||||
let respBody = '';
|
||||
res.on('data', chunk => respBody += chunk);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) resolve({ ok: true });
|
||||
else resolve({ ok: false, error: `HTTP ${res.statusCode}: ${respBody}` });
|
||||
});
|
||||
});
|
||||
req.on('error', (e) => resolve({ ok: false, error: String(e) }));
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// Helper: Recursively collect all files in a folder
|
||||
async function collectAllFiles(path) {
|
||||
const contents = await giteaGet(path);
|
||||
const files = [];
|
||||
if (Array.isArray(contents)) {
|
||||
// It's a folder — recurse into it
|
||||
for (const item of contents) {
|
||||
if (item.type === 'dir') {
|
||||
const sub = await collectAllFiles(item.path);
|
||||
files.push(...sub);
|
||||
} else if (item.type === 'file') {
|
||||
files.push({ path: item.path, sha: item.sha });
|
||||
}
|
||||
}
|
||||
} else if (contents && contents.sha) {
|
||||
// It's a single file
|
||||
files.push({ path: contents.path, sha: contents.sha });
|
||||
} else {
|
||||
throw new Error(`Unbekannte Antwort: ${JSON.stringify(contents)}`);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
// Collect all files to delete (handles both file and folder)
|
||||
const filesToDelete = await collectAllFiles(filePath);
|
||||
|
||||
if (filesToDelete.length === 0) {
|
||||
return { ok: false, error: 'Keine Dateien zum Löschen gefunden' };
|
||||
}
|
||||
|
||||
// Delete all files sequentially
|
||||
let failed = 0;
|
||||
for (const f of filesToDelete) {
|
||||
const res = await giteaDeleteFile(f.path, f.sha);
|
||||
if (!res.ok) {
|
||||
console.error(`Fehler beim Löschen von ${f.path}:`, res.error);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed > 0) {
|
||||
return { ok: false, error: `${failed} von ${filesToDelete.length} Dateien konnten nicht gelöscht werden` };
|
||||
}
|
||||
|
||||
return { ok: true, deleted: filesToDelete.length };
|
||||
}
|
||||
|
||||
// --- LOCAL DELETION ---
|
||||
if (!data || !data.path || !fs.existsSync(data.path)) return { ok: false, error: 'file-not-found' };
|
||||
fs.rmSync(data.path, { recursive: true, force: true });
|
||||
return { ok: true };
|
||||
@@ -503,8 +626,8 @@ ipcMain.handle('get-gitea-repo-contents', async (event, data) => {
|
||||
// This allows apiHandler.js to try ['main', 'master'] if no ref is passed
|
||||
const ref = data.ref;
|
||||
|
||||
const items = await getGiteaRepoContents({ token, url, owner, repo, path: p, ref });
|
||||
return { ok: true, items };
|
||||
const result = await getGiteaRepoContents({ token, url, owner, repo, path: p, ref });
|
||||
return { ok: true, items: result.items || result, empty: result.empty || false };
|
||||
} catch (e) {
|
||||
console.error('get-gitea-repo-contents error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
@@ -546,7 +669,7 @@ ipcMain.handle('read-gitea-file', async (event, data) => {
|
||||
const owner = data.owner;
|
||||
const repo = data.repo;
|
||||
const p = data.path;
|
||||
const ref = data.ref || 'main';
|
||||
const ref = data.ref || 'HEAD';
|
||||
|
||||
console.log(`read-gitea-file: ${owner}/${repo}/${p} (ref: ${ref})`);
|
||||
|
||||
@@ -619,9 +742,8 @@ ipcMain.handle('upload-gitea-file', async (event, data) => {
|
||||
const repo = data.repo;
|
||||
// destPath is the target folder in the repo
|
||||
const destPath = (data.destPath || '').replace(/^\//, '').replace(/\/$/, '');
|
||||
// FIXED: Konvertiere 'master' zu 'main' (Upload should generally target main)
|
||||
let branch = data.branch || 'main';
|
||||
if (branch === 'master') branch = 'main';
|
||||
// Branch wird unverändert übernommen (main UND master werden unterstützt)
|
||||
let branch = data.branch || 'HEAD';
|
||||
const message = data.message || 'Upload via Git Manager GUI';
|
||||
const localFiles = Array.isArray(data.localPath) ? data.localPath : (data.localPath ? [data.localPath] : []);
|
||||
const results = [];
|
||||
@@ -678,7 +800,7 @@ ipcMain.handle('write-gitea-file', async (event, data) => {
|
||||
const repo = data.repo;
|
||||
const path = data.path;
|
||||
const content = data.content || '';
|
||||
const ref = data.ref || 'main';
|
||||
const ref = data.ref || 'HEAD';
|
||||
|
||||
// Konvertiere Content zu Base64
|
||||
const base64 = Buffer.from(content, 'utf8').toString('base64');
|
||||
@@ -712,9 +834,8 @@ ipcMain.handle('upload-local-folder-to-gitea', async (event, data) => {
|
||||
const repo = data.repo;
|
||||
// destPath is the target directory in the repo
|
||||
const destPath = (data.destPath || '').replace(/^\//, '').replace(/\/$/, '');
|
||||
// FIXED: Konvertiere 'master' zu 'main'
|
||||
let branch = data.branch || 'main';
|
||||
if (branch === 'master') branch = 'main';
|
||||
// Branch wird unverändert übernommen (main UND master werden unterstützt)
|
||||
let branch = data.branch || 'HEAD';
|
||||
const messagePrefix = data.messagePrefix || 'Upload folder via GUI';
|
||||
const concurrency = data.concurrency || DEFAULT_CONCURRENCY;
|
||||
if (!localFolder || !fs.existsSync(localFolder)) return { ok: false, error: 'local-folder-not-found' };
|
||||
@@ -792,7 +913,7 @@ ipcMain.handle('download-gitea-file', async (event, data) => {
|
||||
const repo = data.repo;
|
||||
const filePath = data.path;
|
||||
if (!owner || !repo || !filePath) return { ok: false, error: 'missing-owner-repo-or-path' };
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: filePath, ref: 'main' });
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: filePath, ref: data.ref || 'HEAD' });
|
||||
const save = await dialog.showSaveDialog({ defaultPath: ppath.basename(filePath) });
|
||||
if (save.canceled || !save.filePath) return { ok: false, error: 'save-canceled' };
|
||||
fs.writeFileSync(save.filePath, content, 'utf8');
|
||||
@@ -820,7 +941,8 @@ ipcMain.handle('download-gitea-folder', async (event, data) => {
|
||||
|
||||
const allFiles = [];
|
||||
async function gather(pathInRepo) {
|
||||
const items = await getGiteaRepoContents({ token, url, owner, repo, path: pathInRepo, ref: 'main' });
|
||||
const _r = await getGiteaRepoContents({ token, url, owner, repo, path: pathInRepo, ref: data.ref || 'HEAD' });
|
||||
const items = _r.items || _r;
|
||||
for (const item of items) {
|
||||
if (item.type === 'dir') await gather(item.path);
|
||||
else if (item.type === 'file') allFiles.push(item.path);
|
||||
@@ -832,7 +954,7 @@ ipcMain.handle('download-gitea-folder', async (event, data) => {
|
||||
if (total === 0) return { ok: true, savedTo: destBase, files: [] };
|
||||
|
||||
const tasks = allFiles.map(remoteFile => async () => {
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: remoteFile, ref: 'main' });
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: remoteFile, ref: data.ref || 'HEAD' });
|
||||
const localPath = ppath.join(destBase, remoteFile);
|
||||
fs.mkdirSync(ppath.dirname(localPath), { recursive: true });
|
||||
fs.writeFileSync(localPath, content, 'utf8');
|
||||
@@ -892,8 +1014,9 @@ ipcMain.handle('prepare-download-drag', async (event, data) => {
|
||||
// Gather list of files (recursive)
|
||||
const allFiles = [];
|
||||
async function gather(pathInRepo) {
|
||||
const items = await getGiteaRepoContents({ token, url, owner, repo, path: pathInRepo, ref: data.ref || 'main' });
|
||||
for (const item of items || []) {
|
||||
const _r2 = await getGiteaRepoContents({ token, url, owner, repo, path: pathInRepo, ref: data.ref || 'HEAD' });
|
||||
const items = (_r2.items || _r2) || [];
|
||||
for (const item of items) {
|
||||
if (item.type === 'dir') await gather(item.path);
|
||||
else if (item.type === 'file') allFiles.push(item.path);
|
||||
}
|
||||
@@ -909,7 +1032,7 @@ ipcMain.handle('prepare-download-drag', async (event, data) => {
|
||||
|
||||
// Download files sequentially or with limited concurrency:
|
||||
const tasks = allFiles.map(remoteFile => async () => {
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: remoteFile, ref: data.ref || 'main' });
|
||||
const content = await getGiteaFileContent({ token, url, owner, repo, path: remoteFile, ref: data.ref || 'HEAD' });
|
||||
const localPath = ppath.join(tmpBase, remoteFile);
|
||||
ensureDir(ppath.dirname(localPath));
|
||||
|
||||
@@ -1030,9 +1153,8 @@ ipcMain.handle('upload-and-push', async (event, data) => {
|
||||
const giteaUrl = (data && data.url) || (credentials && credentials.giteaURL);
|
||||
const owner = data.owner;
|
||||
const repo = data.repo;
|
||||
// FIXED: Konvertiere 'master' zu 'main'
|
||||
let branch = data.branch || 'main';
|
||||
if (branch === 'master') branch = 'main';
|
||||
// Branch wird unverändert übernommen (main UND master werden unterstützt)
|
||||
let branch = data.branch || 'HEAD';
|
||||
const cloneUrl = data.cloneUrl || null;
|
||||
const destPath = (data.destPath || '').replace(/^\//, '').replace(/\/$/, '');
|
||||
if (!owner || !repo) return { ok: false, error: 'missing-owner-or-repo' };
|
||||
@@ -1275,6 +1397,226 @@ ipcMain.handle('upload-and-push', async (event, data) => {
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
/* ================================
|
||||
RENAME / CREATE / MOVE HANDLERS
|
||||
================================ */
|
||||
|
||||
// Gitea: Datei/Ordner umbenennen (= alle Dateien kopieren + alte löschen)
|
||||
ipcMain.handle('rename-gitea-item', async (event, data) => {
|
||||
try {
|
||||
const credentials = readCredentials();
|
||||
const token = credentials?.giteaToken;
|
||||
const giteaUrl = credentials?.giteaURL;
|
||||
if (!token || !giteaUrl) return { ok: false, error: 'missing-token-or-url' };
|
||||
|
||||
const { owner, repo, oldPath, newPath, isDir } = data;
|
||||
const urlObj = new URL(giteaUrl);
|
||||
const protocol = urlObj.protocol === 'https:' ? https : http;
|
||||
const port = urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80);
|
||||
|
||||
function giteaRequest(method, apiPath, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : null;
|
||||
const req = protocol.request({
|
||||
hostname: urlObj.hostname, port,
|
||||
path: apiPath, method,
|
||||
headers: {
|
||||
'Authorization': `token ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})
|
||||
}
|
||||
}, (res) => {
|
||||
let b = '';
|
||||
res.on('data', c => b += c);
|
||||
res.on('end', () => {
|
||||
try { resolve({ status: res.statusCode, body: JSON.parse(b) }); }
|
||||
catch (_) { resolve({ status: res.statusCode, body: b }); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function collectFiles(path) {
|
||||
const r = await giteaRequest('GET', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${path.split('/').map(encodeURIComponent).join('/')}?ref=HEAD`, null);
|
||||
const files = [];
|
||||
if (Array.isArray(r.body)) {
|
||||
for (const item of r.body) {
|
||||
if (item.type === 'dir') files.push(...await collectFiles(item.path));
|
||||
else files.push({ path: item.path, sha: item.sha });
|
||||
}
|
||||
} else if (r.body?.sha) {
|
||||
files.push({ path: r.body.path, sha: r.body.sha });
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function readFileContent(filePath) {
|
||||
const r = await giteaRequest('GET', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${filePath.split('/').map(encodeURIComponent).join('/')}?ref=HEAD`, null);
|
||||
return r.body?.content ? r.body.content.replace(/\n/g, '') : '';
|
||||
}
|
||||
|
||||
async function uploadFile(targetPath, contentBase64, message) {
|
||||
// Check if exists first (need SHA for update)
|
||||
const check = await giteaRequest('GET', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${targetPath.split('/').map(encodeURIComponent).join('/')}?ref=HEAD`, null);
|
||||
const body = { message, content: contentBase64, branch: 'HEAD' };
|
||||
if (check.body?.sha) body.sha = check.body.sha;
|
||||
return giteaRequest('POST', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${targetPath.split('/').map(encodeURIComponent).join('/')}`, body);
|
||||
}
|
||||
|
||||
async function deleteFile(filePath, sha) {
|
||||
return giteaRequest('DELETE', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${filePath.split('/').map(encodeURIComponent).join('/')}`, {
|
||||
message: `Delete ${filePath} (rename)`, sha, branch: 'HEAD'
|
||||
});
|
||||
}
|
||||
|
||||
// Collect all files under oldPath
|
||||
const files = await collectFiles(oldPath);
|
||||
|
||||
// For each file: read content, upload to newPath, delete from oldPath
|
||||
for (const f of files) {
|
||||
const content = await readFileContent(f.path);
|
||||
const relPath = isDir ? f.path.slice(oldPath.length + 1) : '';
|
||||
const targetPath = isDir ? `${newPath}/${relPath}` : newPath;
|
||||
await uploadFile(targetPath, content, `Rename: move ${f.path} to ${targetPath}`);
|
||||
await deleteFile(f.path, f.sha);
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
console.error('rename-gitea-item error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
// Gitea: Neue Datei oder Ordner (Ordner = Datei mit .gitkeep)
|
||||
ipcMain.handle('create-gitea-item', async (event, data) => {
|
||||
try {
|
||||
const credentials = readCredentials();
|
||||
const token = credentials?.giteaToken;
|
||||
const giteaUrl = credentials?.giteaURL;
|
||||
if (!token || !giteaUrl) return { ok: false, error: 'missing-token-or-url' };
|
||||
|
||||
const { owner, repo, path: itemPath, type } = data;
|
||||
const urlObj = new URL(giteaUrl);
|
||||
const protocol = urlObj.protocol === 'https:' ? https : http;
|
||||
const port = urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80);
|
||||
|
||||
// Für Ordner: .gitkeep Datei anlegen
|
||||
const targetPath = type === 'folder' ? `${itemPath}/.gitkeep` : itemPath;
|
||||
const content = Buffer.from('').toString('base64');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const body = JSON.stringify({ message: `Create ${itemPath}`, content, branch: data.branch || 'HEAD' });
|
||||
const req = protocol.request({
|
||||
hostname: urlObj.hostname, port,
|
||||
path: `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${targetPath.split('/').map(encodeURIComponent).join('/')}`,
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `token ${token}`, 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }
|
||||
}, (res) => {
|
||||
let b = '';
|
||||
res.on('data', c => b += c);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) resolve({ ok: true });
|
||||
else resolve({ ok: false, error: `HTTP ${res.statusCode}: ${b}` });
|
||||
});
|
||||
});
|
||||
req.on('error', e => resolve({ ok: false, error: String(e) }));
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('create-gitea-item error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
// Lokal: Umbenennen
|
||||
ipcMain.handle('rename-local-item', async (event, data) => {
|
||||
try {
|
||||
const { oldPath, newName } = data;
|
||||
if (!oldPath || !fs.existsSync(oldPath)) return { ok: false, error: 'path-not-found' };
|
||||
const dir = ppath.dirname(oldPath);
|
||||
const newPath = ppath.join(dir, newName);
|
||||
fs.renameSync(oldPath, newPath);
|
||||
return { ok: true, newPath };
|
||||
} catch (e) {
|
||||
console.error('rename-local-item error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
// Lokal: Neue Datei oder Ordner erstellen
|
||||
ipcMain.handle('create-local-item', async (event, data) => {
|
||||
try {
|
||||
const { parentDir, name, type } = data;
|
||||
const targetPath = ppath.join(parentDir, name);
|
||||
if (type === 'folder') {
|
||||
fs.mkdirSync(targetPath, { recursive: true });
|
||||
} else {
|
||||
// Sicherstellen dass Elternordner existiert
|
||||
fs.mkdirSync(ppath.dirname(targetPath), { recursive: true });
|
||||
fs.writeFileSync(targetPath, '', 'utf8');
|
||||
}
|
||||
return { ok: true, path: targetPath };
|
||||
} catch (e) {
|
||||
console.error('create-local-item error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
// Lokal: Verschieben (Cut & Paste)
|
||||
ipcMain.handle('move-local-item', async (event, data) => {
|
||||
try {
|
||||
const { srcPath, destDir } = data;
|
||||
if (!srcPath || !fs.existsSync(srcPath)) return { ok: false, error: 'source-not-found' };
|
||||
const name = ppath.basename(srcPath);
|
||||
const destPath = ppath.join(destDir, name);
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
fs.renameSync(srcPath, destPath);
|
||||
return { ok: true, destPath };
|
||||
} catch (e) {
|
||||
// renameSync kann über Laufwerke nicht funktionieren — dann cpSync + rmSync
|
||||
try {
|
||||
const { srcPath, destDir } = data;
|
||||
const name = ppath.basename(srcPath);
|
||||
const destPath = ppath.join(destDir, name);
|
||||
if (fs.statSync(srcPath).isDirectory()) {
|
||||
fs.cpSync(srcPath, destPath, { recursive: true });
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
fs.rmSync(srcPath, { recursive: true, force: true });
|
||||
return { ok: true, destPath };
|
||||
} catch (e2) {
|
||||
console.error('move-local-item error', e2);
|
||||
return { ok: false, error: String(e2) };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Lokal: Kopieren
|
||||
ipcMain.handle('copy-local-item', async (event, data) => {
|
||||
try {
|
||||
const { src, destDir } = data;
|
||||
if (!src || !fs.existsSync(src)) return { ok: false, error: 'source-not-found' };
|
||||
const name = ppath.basename(src);
|
||||
const dest = ppath.join(destDir, name);
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
fs.cpSync(src, dest, { recursive: true });
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
}
|
||||
return { ok: true, dest };
|
||||
} catch (e) {
|
||||
console.error('copy-local-item error', e);
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
/* ================================
|
||||
RELEASE MANAGEMENT IPC HANDLERS
|
||||
================================ */
|
||||
@@ -1592,6 +1934,57 @@ ipcMain.handle('get-commit-files', async (event, data) => {
|
||||
|
||||
|
||||
|
||||
/* ============================================
|
||||
FAVORITEN & ZULETZT GEÖFFNET - Persistenz
|
||||
============================================ */
|
||||
|
||||
function getFavoritesFilePath() {
|
||||
return ppath.join(app.getPath('userData'), 'favorites.json');
|
||||
}
|
||||
|
||||
function getRecentFilePath() {
|
||||
return ppath.join(app.getPath('userData'), 'recent.json');
|
||||
}
|
||||
|
||||
ipcMain.handle('load-favorites', async () => {
|
||||
try {
|
||||
const p = getFavoritesFilePath();
|
||||
if (!fs.existsSync(p)) return { ok: true, favorites: [] };
|
||||
return { ok: true, favorites: JSON.parse(fs.readFileSync(p, 'utf8')) || [] };
|
||||
} catch (e) {
|
||||
return { ok: true, favorites: [] };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('save-favorites', async (event, favorites) => {
|
||||
try {
|
||||
fs.writeFileSync(getFavoritesFilePath(), JSON.stringify(favorites || [], null, 2), 'utf8');
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('load-recent', async () => {
|
||||
try {
|
||||
const p = getRecentFilePath();
|
||||
if (!fs.existsSync(p)) return { ok: true, recent: [] };
|
||||
return { ok: true, recent: JSON.parse(fs.readFileSync(p, 'utf8')) || [] };
|
||||
} catch (e) {
|
||||
return { ok: true, recent: [] };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('save-recent', async (event, recent) => {
|
||||
try {
|
||||
const trimmed = (recent || []).slice(0, 20);
|
||||
fs.writeFileSync(getRecentFilePath(), JSON.stringify(trimmed, null, 2), 'utf8');
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e) };
|
||||
}
|
||||
});
|
||||
|
||||
// main.js - Updater IPC Handlers
|
||||
|
||||
// 1. Version abfragen
|
||||
|
||||
Reference in New Issue
Block a user