Merge dev into master
This commit is contained in:
+51
-3
@@ -14,7 +14,6 @@ const __dirname = path.dirname(__filename);
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.get('*', (req, res, next) => {
|
||||
@@ -35,8 +34,10 @@ const axiosInstance = axios.create({
|
||||
const redeployingStacks = {};
|
||||
|
||||
const server = http.createServer(app);
|
||||
const io = new Server(server, { cors: { origin: "*" } });
|
||||
|
||||
const io = new Server(server, {
|
||||
path: "/socket.io",
|
||||
cors: { origin: "*" }
|
||||
});
|
||||
io.on("connection", (socket) => {
|
||||
console.log(`🔌 [Socket] Client verbunden: ${socket.id}`);
|
||||
});
|
||||
@@ -49,6 +50,7 @@ const broadcastRedeployStatus = (stackId, status) => {
|
||||
|
||||
// --- API Endpoints ---
|
||||
|
||||
// Stacks abrufen
|
||||
app.get('/api/stacks', async (req, res) => {
|
||||
console.log("ℹ️ [API] GET /api/stacks: Abruf gestartet");
|
||||
try {
|
||||
@@ -89,6 +91,7 @@ app.get('/api/stacks', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Einzel-Redeploy
|
||||
app.put('/api/stacks/:id/redeploy', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
console.log(`🔄 PUT /api/stacks/${id}/redeploy: Redeploy gestartet`);
|
||||
@@ -142,6 +145,51 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Redeploy ALL
|
||||
app.put('/api/stacks/redeploy-all', async (req, res) => {
|
||||
console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`);
|
||||
|
||||
try {
|
||||
const stacksRes = await axiosInstance.get('/api/stacks');
|
||||
const filteredStacks = stacksRes.data.filter(stack => stack.EndpointId === ENDPOINT_ID);
|
||||
|
||||
console.log("📦 Redeploy ALL für folgende Stacks:");
|
||||
filteredStacks.forEach(s => console.log(` - ${s.Name}`));
|
||||
|
||||
filteredStacks.forEach(async (stack) => {
|
||||
try {
|
||||
broadcastRedeployStatus(stack.Id, true);
|
||||
|
||||
if (stack.Type === 1) {
|
||||
console.log(`🔄 [Redeploy] Git Stack "${stack.Name}" (${stack.Id})`);
|
||||
await axiosInstance.put(`/api/stacks/${stack.Id}/git/redeploy?endpointId=${stack.EndpointId}`);
|
||||
} else if (stack.Type === 2) {
|
||||
console.log(`🔄 [Redeploy] Compose Stack "${stack.Name}" (${stack.Id})`);
|
||||
const fileRes = await axiosInstance.get(`/api/stacks/${stack.Id}/file`);
|
||||
const stackFileContent = fileRes.data?.StackFileContent;
|
||||
if (stackFileContent) {
|
||||
await axiosInstance.put(`/api/stacks/${stack.Id}`,
|
||||
{ StackFileContent: stackFileContent, Prune: false, PullImage: true },
|
||||
{ params: { endpointId: stack.EndpointId } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
broadcastRedeployStatus(stack.Id, false);
|
||||
console.log(`✅ Redeploy abgeschlossen: ${stack.Name}`);
|
||||
} catch (err) {
|
||||
broadcastRedeployStatus(stack.Id, false);
|
||||
console.error(`❌ Fehler beim Redeploy von Stack ${stack.Name}:`, err.message);
|
||||
}
|
||||
});
|
||||
|
||||
res.json({ success: true, message: 'Redeploy ALL gestartet' });
|
||||
} catch (err) {
|
||||
console.error(`❌ Fehler beim Redeploy ALL:`, err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`🚀 Backend läuft auf Port ${PORT}`);
|
||||
});
|
||||
|
||||
+59
-33
@@ -8,7 +8,10 @@ export default function Stacks() {
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const socket = io("/", { transports: ["websocket"] });
|
||||
const socket = io("/", {
|
||||
path: "/socket.io",
|
||||
transports: ["websocket"]
|
||||
});
|
||||
console.log("🔌 Socket connected");
|
||||
|
||||
socket.on("redeployStatus", async ({ stackId, status }) => {
|
||||
@@ -68,45 +71,68 @@ export default function Stacks() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRedeployAll = async () => {
|
||||
setStacks(prev => prev.map(stack => ({ ...stack, redeploying: true })));
|
||||
|
||||
try {
|
||||
await axios.put("/api/stacks/redeploy-all");
|
||||
// Statusupdates kommen über Socket.IO
|
||||
} catch (err) {
|
||||
console.error("❌ Fehler beim Redeploy ALL:", err);
|
||||
setStacks(prev => prev.map(stack => ({ ...stack, redeploying: false })));
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p className="text-gray-400">Lade Stacks...</p>;
|
||||
if (error) return <p className="text-red-400">{error}</p>;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 p-6">
|
||||
{stacks.map(stack => {
|
||||
const isRedeploying = stack.redeploying;
|
||||
<div className="p-6">
|
||||
<div className="flex justify-end mb-4">
|
||||
<button
|
||||
onClick={handleRedeployAll}
|
||||
className="px-5 py-2 rounded-lg font-medium transition bg-purple-500 hover:bg-purple-600"
|
||||
>
|
||||
Redeploy All
|
||||
</button>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div
|
||||
key={stack.Id}
|
||||
className={`flex justify-between items-center p-5 rounded-xl shadow-lg transition
|
||||
${isRedeploying ? "bg-gray-700 cursor-not-allowed" : "bg-gray-800 hover:bg-gray-700"}`}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-12 h-12 flex items-center justify-center rounded-full
|
||||
${stack.updateStatus === "✅" ? "bg-green-500" :
|
||||
stack.updateStatus === "⚠️" ? "bg-yellow-500" :
|
||||
"bg-red-500"}`}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-white">{stack.Name}</p>
|
||||
<p className="text-sm text-gray-400">ID: {stack.Id}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{stacks.map(stack => {
|
||||
const isRedeploying = stack.redeploying;
|
||||
|
||||
<button
|
||||
onClick={() => handleRedeploy(stack.Id)}
|
||||
disabled={isRedeploying}
|
||||
className={`px-5 py-2 rounded-lg font-medium transition
|
||||
${isRedeploying ? "bg-orange-500 cursor-not-allowed" :
|
||||
"bg-blue-500 hover:bg-blue-600"}`}
|
||||
return (
|
||||
<div
|
||||
key={stack.Id}
|
||||
className={`flex justify-between items-center p-5 rounded-xl shadow-lg transition
|
||||
${isRedeploying ? "bg-gray-700 cursor-not-allowed" : "bg-gray-800 hover:bg-gray-700"}`}
|
||||
>
|
||||
{isRedeploying ? "Redeploying" : "Redeploy"}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{stacks.length === 0 && <p className="text-gray-400">Keine Stacks gefunden.</p>}
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-12 h-12 flex items-center justify-center rounded-full
|
||||
${stack.updateStatus === "✅" ? "bg-green-500" :
|
||||
stack.updateStatus === "⚠️" ? "bg-yellow-500" :
|
||||
"bg-red-500"}`}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-white">{stack.Name}</p>
|
||||
<p className="text-sm text-gray-400">ID: {stack.Id}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => handleRedeploy(stack.Id)}
|
||||
disabled={isRedeploying}
|
||||
className={`px-5 py-2 rounded-lg font-medium transition
|
||||
${isRedeploying ? "bg-orange-500 cursor-not-allowed" :
|
||||
"bg-blue-500 hover:bg-blue-600"}`}
|
||||
>
|
||||
{isRedeploying ? "Redeploying" : "Redeploy"}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{stacks.length === 0 && <p className="text-gray-400">Keine Stacks gefunden.</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ===============================
|
||||
# Git Stash Manager mit Branch-Filter und Überschreiben
|
||||
# ===============================
|
||||
|
||||
# Aktuellen Branch ermitteln
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
echo "Aktueller Branch: $CURRENT_BRANCH"
|
||||
echo "Was möchtest du tun?"
|
||||
echo "1) Neuen Stash anlegen"
|
||||
echo "2) Vorhandenen Stash laden (apply)"
|
||||
echo "3) Vorhandenen Stash löschen"
|
||||
echo "4) Stash anwenden und löschen (pop)"
|
||||
read -p "Auswahl: " action
|
||||
|
||||
case $action in
|
||||
1)
|
||||
# ========== Neuen Stash anlegen ==========
|
||||
read -p "Gib einen Namen für den Stash ein: " USER_INPUT
|
||||
STASH_NAME="$CURRENT_BRANCH - $USER_INPUT"
|
||||
|
||||
# Prüfen, ob Stash-Name schon existiert
|
||||
EXISTING=$(git stash list | grep "$STASH_NAME")
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
echo "Ein Stash mit dem Namen '$STASH_NAME' existiert bereits."
|
||||
# Alle passenden Stashes löschen
|
||||
while IFS= read -r line; do
|
||||
STASH_REF=$(echo "$line" | awk -F: '{print $1}')
|
||||
echo "Lösche vorhandenen Stash: $STASH_REF"
|
||||
git stash drop "$STASH_REF"
|
||||
done <<< "$EXISTING"
|
||||
fi
|
||||
|
||||
# Neuen Stash anlegen (inkl. untracked Dateien)
|
||||
git stash push -u -m "$STASH_NAME"
|
||||
echo "Stash '$STASH_NAME' wurde angelegt."
|
||||
;;
|
||||
|
||||
2)
|
||||
# ========== Stash anwenden ==========
|
||||
echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':"
|
||||
STASHES=$(git stash list | grep "$CURRENT_BRANCH")
|
||||
|
||||
if [[ -z "$STASHES" ]]; then
|
||||
echo "Keine Stashes für diesen Branch vorhanden."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
i=1
|
||||
declare -A STASH_MAP
|
||||
while IFS= read -r line; do
|
||||
STASH_REF=$(echo "$line" | awk -F: '{print $1}')
|
||||
STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //')
|
||||
echo "$i) $STASH_REF -> $STASH_MSG"
|
||||
STASH_MAP[$i]=$STASH_REF
|
||||
((i++))
|
||||
done <<< "$STASHES"
|
||||
|
||||
read -p "Wähle einen Stash (Nummer): " choice
|
||||
if [[ -z "${STASH_MAP[$choice]}" ]]; then
|
||||
echo "Ungültige Auswahl!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_STASH=${STASH_MAP[$choice]}
|
||||
echo "Wende Stash an: $SELECTED_STASH"
|
||||
git stash apply "$SELECTED_STASH"
|
||||
;;
|
||||
|
||||
3)
|
||||
# ========== Stash löschen ==========
|
||||
echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':"
|
||||
STASHES=$(git stash list | grep "$CURRENT_BRANCH")
|
||||
|
||||
if [[ -z "$STASHES" ]]; then
|
||||
echo "Keine Stashes für diesen Branch vorhanden."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
i=1
|
||||
declare -A STASH_MAP
|
||||
while IFS= read -r line; do
|
||||
STASH_REF=$(echo "$line" | awk -F: '{print $1}')
|
||||
STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //')
|
||||
echo "$i) $STASH_REF -> $STASH_MSG"
|
||||
STASH_MAP[$i]=$STASH_REF
|
||||
((i++))
|
||||
done <<< "$STASHES"
|
||||
|
||||
read -p "Wähle einen Stash zum Löschen (Nummer): " choice
|
||||
if [[ -z "${STASH_MAP[$choice]}" ]]; then
|
||||
echo "Ungültige Auswahl!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_STASH=${STASH_MAP[$choice]}
|
||||
echo "Lösche Stash: $SELECTED_STASH"
|
||||
git stash drop "$SELECTED_STASH"
|
||||
;;
|
||||
|
||||
4)
|
||||
# ========== Stash anwenden und löschen (pop) ==========
|
||||
echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':"
|
||||
STASHES=$(git stash list | grep "$CURRENT_BRANCH")
|
||||
|
||||
if [[ -z "$STASHES" ]]; then
|
||||
echo "Keine Stashes für diesen Branch vorhanden."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
i=1
|
||||
declare -A STASH_MAP
|
||||
while IFS= read -r line; do
|
||||
STASH_REF=$(echo "$line" | awk -F: '{print $1}')
|
||||
STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //')
|
||||
echo "$i) $STASH_REF -> $STASH_MSG"
|
||||
STASH_MAP[$i]=$STASH_REF
|
||||
((i++))
|
||||
done <<< "$STASHES"
|
||||
|
||||
read -p "Wähle einen Stash zum Anwenden & Löschen (Nummer): " choice
|
||||
if [[ -z "${STASH_MAP[$choice]}" ]]; then
|
||||
echo "Ungültige Auswahl!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_STASH=${STASH_MAP[$choice]}
|
||||
echo "Wende Stash an und lösche ihn: $SELECTED_STASH"
|
||||
git stash pop "$SELECTED_STASH"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Ungültige Auswahl!"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user