Gitea-Fix
Dieser Commit ist enthalten in:
@ -6,14 +6,49 @@
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const { getDb } = require('../database');
|
||||
const logger = require('../utils/logger');
|
||||
const gitService = require('../services/gitService');
|
||||
const giteaService = require('../services/giteaService');
|
||||
const multer = require('multer');
|
||||
|
||||
// Fester Pfad für Server-Modus (TaskMate Source-Code)
|
||||
const SERVER_SOURCE_PATH = '/app/taskmate-source';
|
||||
|
||||
// Temporäres Verzeichnis für Browser-Uploads
|
||||
const TEMP_UPLOAD_DIR = path.join(os.tmpdir(), 'taskmate-git-uploads');
|
||||
|
||||
// Multer-Konfiguration für Git-Uploads (beliebige Dateitypen)
|
||||
const gitUploadStorage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
// Erstelle eindeutiges temporäres Verzeichnis pro Upload-Session
|
||||
const sessionId = req.body.sessionId || Date.now().toString();
|
||||
const sessionDir = path.join(TEMP_UPLOAD_DIR, sessionId);
|
||||
|
||||
// Relativen Pfad aus dem Dateinamen extrahieren (wird vom Frontend gesendet)
|
||||
const relativePath = file.originalname;
|
||||
const fileDir = path.join(sessionDir, path.dirname(relativePath));
|
||||
|
||||
fs.mkdirSync(fileDir, { recursive: true });
|
||||
cb(null, fileDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
// Nur den Dateinamen ohne Pfad
|
||||
cb(null, path.basename(file.originalname));
|
||||
}
|
||||
});
|
||||
|
||||
const gitUpload = multer({
|
||||
storage: gitUploadStorage,
|
||||
limits: {
|
||||
fileSize: 50 * 1024 * 1024, // 50MB pro Datei
|
||||
files: 500 // Maximal 500 Dateien
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Anwendung für Projekt abrufen
|
||||
*/
|
||||
@ -757,4 +792,178 @@ router.get('/server/info', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// BROWSER-UPLOAD ENDPOINTS
|
||||
// Für lokale Verzeichnis-Uploads vom Browser
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Verzeichnis rekursiv löschen
|
||||
*/
|
||||
function deleteFolderRecursive(dirPath) {
|
||||
if (fs.existsSync(dirPath)) {
|
||||
fs.readdirSync(dirPath).forEach((file) => {
|
||||
const curPath = path.join(dirPath, file);
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteFolderRecursive(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/git/browser-upload
|
||||
* Empfängt Dateien vom Browser und pusht sie ins Gitea
|
||||
*
|
||||
* Body (multipart/form-data):
|
||||
* - files: Die hochgeladenen Dateien (originalname enthält relativen Pfad)
|
||||
* - repoUrl: Die Gitea-Repository-URL
|
||||
* - branch: Der Ziel-Branch (default: main)
|
||||
* - commitMessage: Die Commit-Nachricht
|
||||
* - sessionId: Eindeutige Session-ID für den Upload
|
||||
*/
|
||||
router.post('/browser-upload', gitUpload.array('files', 500), async (req, res) => {
|
||||
const sessionId = req.body.sessionId || Date.now().toString();
|
||||
const sessionDir = path.join(TEMP_UPLOAD_DIR, sessionId);
|
||||
|
||||
try {
|
||||
const { repoUrl, branch = 'main', commitMessage } = req.body;
|
||||
const files = req.files;
|
||||
|
||||
// Validierung
|
||||
if (!repoUrl) {
|
||||
return res.status(400).json({ error: 'Repository-URL ist erforderlich' });
|
||||
}
|
||||
|
||||
if (!commitMessage) {
|
||||
return res.status(400).json({ error: 'Commit-Nachricht ist erforderlich' });
|
||||
}
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
return res.status(400).json({ error: 'Keine Dateien hochgeladen' });
|
||||
}
|
||||
|
||||
logger.info(`[Browser-Upload] ${files.length} Dateien empfangen für ${repoUrl}`);
|
||||
|
||||
// Git-Repository initialisieren
|
||||
const initResult = gitService.initRepository(sessionDir);
|
||||
if (!initResult.success) {
|
||||
throw new Error('Git-Initialisierung fehlgeschlagen: ' + initResult.error);
|
||||
}
|
||||
|
||||
// Remote hinzufügen
|
||||
const remoteResult = gitService.addRemote(sessionDir, repoUrl, 'origin');
|
||||
if (!remoteResult.success) {
|
||||
throw new Error('Remote hinzufügen fehlgeschlagen: ' + remoteResult.error);
|
||||
}
|
||||
|
||||
// Autor aus eingeloggtem Benutzer
|
||||
const author = req.user ? {
|
||||
name: req.user.display_name || req.user.username,
|
||||
email: req.user.email || `${req.user.username.toLowerCase()}@taskmate.local`
|
||||
} : null;
|
||||
|
||||
// Alle Dateien stagen
|
||||
const stageResult = gitService.stageAll(sessionDir);
|
||||
if (!stageResult.success) {
|
||||
throw new Error('Staging fehlgeschlagen: ' + stageResult.error);
|
||||
}
|
||||
|
||||
// Commit erstellen
|
||||
const commitResult = gitService.commit(sessionDir, commitMessage, author);
|
||||
if (!commitResult.success) {
|
||||
throw new Error('Commit fehlgeschlagen: ' + commitResult.error);
|
||||
}
|
||||
|
||||
// Push mit Upstream
|
||||
const pushResult = gitService.pushWithUpstream(sessionDir, branch, 'origin', false);
|
||||
if (!pushResult.success) {
|
||||
// Bei Fehler: Versuche Force-Push falls Branch existiert
|
||||
if (pushResult.error && pushResult.error.includes('rejected')) {
|
||||
logger.warn('[Browser-Upload] Normaler Push abgelehnt, versuche mit Force...');
|
||||
const forcePushResult = gitService.pushWithUpstream(sessionDir, branch, 'origin', true);
|
||||
if (!forcePushResult.success) {
|
||||
throw new Error('Push fehlgeschlagen: ' + forcePushResult.error);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Push fehlgeschlagen: ' + pushResult.error);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`[Browser-Upload] Erfolgreich nach ${repoUrl}/${branch} gepusht`);
|
||||
|
||||
// Erfolgreich - Aufräumen
|
||||
deleteFolderRecursive(sessionDir);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `${files.length} Dateien erfolgreich nach ${branch} gepusht`,
|
||||
filesCount: files.length,
|
||||
branch: branch,
|
||||
commit: commitResult.hash || null
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[Browser-Upload] Fehler:', error);
|
||||
|
||||
// Bei Fehler aufräumen
|
||||
try {
|
||||
deleteFolderRecursive(sessionDir);
|
||||
} catch (cleanupError) {
|
||||
logger.warn('[Browser-Upload] Aufräumen fehlgeschlagen:', cleanupError);
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message || 'Upload fehlgeschlagen'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/git/browser-upload-prepare
|
||||
* Bereitet einen Upload vor und gibt eine Session-ID zurück
|
||||
*/
|
||||
router.post('/browser-upload-prepare', (req, res) => {
|
||||
const sessionId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
const sessionDir = path.join(TEMP_UPLOAD_DIR, sessionId);
|
||||
|
||||
try {
|
||||
fs.mkdirSync(sessionDir, { recursive: true });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
sessionId: sessionId,
|
||||
maxFileSize: 50 * 1024 * 1024, // 50MB
|
||||
maxFiles: 500
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[Browser-Upload] Prepare fehlgeschlagen:', error);
|
||||
res.status(500).json({ error: 'Vorbereitung fehlgeschlagen' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /api/git/browser-upload/:sessionId
|
||||
* Löscht eine Upload-Session (für Abbruch)
|
||||
*/
|
||||
router.delete('/browser-upload/:sessionId', (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
const sessionDir = path.join(TEMP_UPLOAD_DIR, sessionId);
|
||||
|
||||
try {
|
||||
if (fs.existsSync(sessionDir)) {
|
||||
deleteFolderRecursive(sessionDir);
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
logger.error('[Browser-Upload] Löschen fehlgeschlagen:', error);
|
||||
res.status(500).json({ error: 'Löschen fehlgeschlagen' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren