/** * TASKMATE - Git Route * ===================== * API-Endpoints für Git-Operationen */ const express = require('express'); const router = express.Router(); const { getDb } = require('../database'); const logger = require('../utils/logger'); const gitService = require('../services/gitService'); const giteaService = require('../services/giteaService'); /** * Hilfsfunktion: Anwendung für Projekt abrufen */ function getApplicationForProject(projectId) { const db = getDb(); return db.prepare('SELECT * FROM applications WHERE project_id = ?').get(projectId); } /** * POST /api/git/clone * Repository klonen */ router.post('/clone', async (req, res) => { try { const { projectId, repoUrl, localPath, branch } = req.body; if (!localPath) { return res.status(400).json({ error: 'localPath ist erforderlich' }); } if (!repoUrl) { return res.status(400).json({ error: 'repoUrl ist erforderlich' }); } // Clone ausführen const result = await gitService.cloneRepository(repoUrl, localPath, { branch }); if (result.success && projectId) { // Anwendung aktualisieren const db = getDb(); db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId); } res.json(result); } catch (error) { logger.error('Fehler beim Klonen:', error); res.status(500).json({ error: 'Serverfehler', details: error.message }); } }); /** * GET /api/git/status/:projectId * Git-Status für ein Projekt abrufen */ router.get('/status/:projectId', (req, res) => { try { const { projectId } = req.params; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.getStatus(application.local_path); res.json(result); } catch (error) { logger.error('Fehler beim Abrufen des Status:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/pull/:projectId * Pull für ein Projekt ausführen */ router.post('/pull/:projectId', (req, res) => { try { const { projectId } = req.params; const { branch } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } // Fetch zuerst gitService.fetchRemote(application.local_path); // Dann Pull const result = gitService.pullChanges(application.local_path, { branch }); if (result.success) { // Sync-Zeitpunkt aktualisieren const db = getDb(); db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId); } res.json(result); } catch (error) { logger.error('Fehler beim Pull:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/push/:projectId * Push für ein Projekt ausführen */ router.post('/push/:projectId', (req, res) => { try { const { projectId } = req.params; const { branch } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } // Prüfe ob Remote existiert if (!gitService.hasRemote(application.local_path)) { return res.json({ success: false, error: 'Kein Remote konfiguriert. Bitte Repository zuerst vorbereiten.' }); } // Versuche normalen Push, falls das fehlschlägt wegen fehlendem Upstream, push mit -u let result = gitService.pushChanges(application.local_path, { branch }); // Falls Push wegen fehlendem Upstream fehlschlägt, versuche mit -u if (!result.success && result.error && result.error.includes('no upstream')) { const currentBranch = branch || 'main'; result = gitService.pushWithUpstream(application.local_path, currentBranch); } if (result.success) { // Sync-Zeitpunkt aktualisieren const db = getDb(); db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId); } res.json(result); } catch (error) { logger.error('Fehler beim Push:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/commit/:projectId * Commit für ein Projekt erstellen */ router.post('/commit/:projectId', (req, res) => { try { const { projectId } = req.params; const { message, stageAll } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } if (!message) { return res.status(400).json({ error: 'Commit-Nachricht ist erforderlich' }); } // Optional: Alle Änderungen stagen if (stageAll !== false) { const stageResult = gitService.stageAll(application.local_path); if (!stageResult.success) { return res.json(stageResult); } } // Commit erstellen const result = gitService.commit(application.local_path, message); res.json(result); } catch (error) { logger.error('Fehler beim Commit:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * GET /api/git/commits/:projectId * Commit-Historie für ein Projekt abrufen */ router.get('/commits/:projectId', (req, res) => { try { const { projectId } = req.params; const limit = parseInt(req.query.limit) || 20; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.getCommitHistory(application.local_path, limit); res.json(result); } catch (error) { logger.error('Fehler beim Abrufen der Commits:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * GET /api/git/branches/:projectId * Branches für ein Projekt abrufen */ router.get('/branches/:projectId', (req, res) => { try { const { projectId } = req.params; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.getBranches(application.local_path); res.json(result); } catch (error) { logger.error('Fehler beim Abrufen der Branches:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/checkout/:projectId * Branch wechseln */ router.post('/checkout/:projectId', (req, res) => { try { const { projectId } = req.params; const { branch } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } if (!branch) { return res.status(400).json({ error: 'Branch ist erforderlich' }); } const result = gitService.checkoutBranch(application.local_path, branch); res.json(result); } catch (error) { logger.error('Fehler beim Branch-Wechsel:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/fetch/:projectId * Fetch von Remote ausführen */ router.post('/fetch/:projectId', (req, res) => { try { const { projectId } = req.params; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.fetchRemote(application.local_path); res.json(result); } catch (error) { logger.error('Fehler beim Fetch:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/stage/:projectId * Alle Änderungen stagen */ router.post('/stage/:projectId', (req, res) => { try { const { projectId } = req.params; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.stageAll(application.local_path); res.json(result); } catch (error) { logger.error('Fehler beim Stagen:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * GET /api/git/remote/:projectId * Remote-URL abrufen */ router.get('/remote/:projectId', (req, res) => { try { const { projectId } = req.params; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const result = gitService.getRemoteUrl(application.local_path); res.json(result); } catch (error) { logger.error('Fehler beim Abrufen der Remote-URL:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/validate-path * Pfad validieren */ router.post('/validate-path', (req, res) => { try { const { path } = req.body; if (!path) { return res.status(400).json({ error: 'Pfad ist erforderlich' }); } const isAccessible = gitService.isPathAccessible(path); const isRepo = isAccessible ? gitService.isGitRepository(path) : false; const hasRemote = isRepo ? gitService.hasRemote(path) : false; res.json({ valid: isAccessible, isRepository: isRepo, hasRemote: hasRemote, containerPath: gitService.windowsToContainerPath(path) }); } catch (error) { logger.error('Fehler bei der Pfad-Validierung:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/prepare/:projectId * Repository für Gitea vorbereiten (init, remote hinzufügen) */ router.post('/prepare/:projectId', (req, res) => { try { const { projectId } = req.params; const { repoUrl, branch } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } if (!repoUrl) { return res.status(400).json({ error: 'repoUrl ist erforderlich' }); } const result = gitService.prepareForGitea(application.local_path, repoUrl, { branch }); if (result.success) { logger.info(`Repository vorbereitet für Projekt ${projectId}: ${repoUrl}`); } res.json(result); } catch (error) { logger.error('Fehler beim Vorbereiten des Repositories:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/set-remote/:projectId * Remote für ein Projekt setzen/aktualisieren */ router.post('/set-remote/:projectId', (req, res) => { try { const { projectId } = req.params; const { repoUrl } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } if (!repoUrl) { return res.status(400).json({ error: 'repoUrl ist erforderlich' }); } // Prüfe ob Git-Repo existiert if (!gitService.isGitRepository(application.local_path)) { // Initialisiere Repository const initResult = gitService.initRepository(application.local_path); if (!initResult.success) { return res.json(initResult); } } // Remote setzen const result = gitService.setRemote(application.local_path, repoUrl); res.json(result); } catch (error) { logger.error('Fehler beim Setzen des Remotes:', error); res.status(500).json({ error: 'Serverfehler' }); } }); /** * POST /api/git/init-push/:projectId * Initialen Push mit Upstream-Tracking */ router.post('/init-push/:projectId', (req, res) => { try { const { projectId } = req.params; const { branch } = req.body; const application = getApplicationForProject(projectId); if (!application) { return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' }); } const currentBranch = branch || 'main'; const result = gitService.pushWithUpstream(application.local_path, currentBranch); if (result.success) { // Sync-Zeitpunkt aktualisieren const db = getDb(); db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId); } res.json(result); } catch (error) { logger.error('Fehler beim initialen Push:', error); res.status(500).json({ error: 'Serverfehler' }); } }); module.exports = router;