feat(vpn): VPN on-demand Tailscale + agent studioE5 standalone

- Agent studioE5 standalone en Go (console + systray)
- VPN on-demand via tailscaled + tailscale up (authkey Headscale)
- Resolver/serveur dans le tailnet studioe5
- Caddy on-demand TLS pour les instances
- Nouveaux endpoints serveur /api/internal/send-to-node
- Suppression des anciens binaires edubox-agent
- Suivi dans SUIVI_VPN_ONDEMAND.md
This commit is contained in:
EduBox Dev
2026-06-23 09:48:00 +00:00
parent dd49993157
commit 124543d658
40 changed files with 1303 additions and 485 deletions
+90
View File
@@ -0,0 +1,90 @@
package main
import (
_ "embed"
"fmt"
"log"
"os"
"os/exec"
"runtime"
"strings"
"time"
"fyne.io/systray"
)
//go:embed icon.png
var iconBytes []byte
const uiURL = "http://localhost:7070"
func runTray(appName string, shutdownCh chan struct{}) {
systray.Run(func() { onTrayReady(appName, shutdownCh) }, func() { onTrayExit(shutdownCh) })
}
func onTrayReady(appName string, shutdownCh chan struct{}) {
systray.SetIcon(iconBytes)
systray.SetTitle(appName)
systray.SetTooltip(fmt.Sprintf("%s Agent - Cliquez pour ouvrir l'interface", appName))
mOpen := systray.AddMenuItem("Ouvrir l'interface", "Ouvrir l'interface web locale")
mInstances := systray.AddMenuItem("Mes instances", "Afficher les instances")
mSettings := systray.AddMenuItem("Paramètres", "Ouvrir les paramètres")
systray.AddSeparator()
mQuit := systray.AddMenuItem("Quitter", "Arrêter l'agent")
go func() {
for {
select {
case <-mOpen.ClickedCh:
openBrowser(uiURL)
case <-mInstances.ClickedCh:
openBrowser(uiURL + "#instances")
case <-mSettings.ClickedCh:
openBrowser(uiURL + "#settings")
case <-mQuit.ClickedCh:
close(shutdownCh)
systray.Quit()
return
}
}
}()
}
func onTrayExit(shutdownCh chan struct{}) {
log.Printf("Tray exit requested")
// If the user did not already trigger shutdown via the menu, signal it now.
select {
case <-shutdownCh:
default:
close(shutdownCh)
}
// Give other goroutines a moment to clean up, then exit.
time.Sleep(200 * time.Millisecond)
os.Exit(0)
}
func openBrowser(url string) {
var cmd string
var args []string
switch runtime.GOOS {
case "windows":
cmd = "rundll32"
args = []string{"url.dll,FileProtocolHandler", url}
case "darwin":
cmd = "open"
args = []string{url}
default:
cmd = "xdg-open"
args = []string{url}
}
if err := exec.Command(cmd, args...).Start(); err != nil {
log.Printf("Failed to open browser: %v", err)
}
}
func normalizeName(name string) string {
return strings.ReplaceAll(name, " ", "")
}