fix(gui,android,api): fix Wayland icon, add Android launcher mipmaps and add CORS middleware

This commit is contained in:
benzj 2026-09-12 21:51:28 +02:00
parent 72ee119a03
commit 3d15fef1d5
18 changed files with 230 additions and 8 deletions

View File

@ -13,6 +13,8 @@
<application
android:allowBackup="false"
android:label="BenzCloud Server"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

View File

@ -4,21 +4,75 @@ package main
/*
#cgo pkg-config: gtk+-3.0 webkit2gtk-4.1
#include <stdlib.h>
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>
static void activate_gtk_app(const char* title, const char* url, int width, int height) {
gtk_init(NULL, NULL);
static int check_display() {
int argc = 0;
char **argv = NULL;
return gtk_init_check(&argc, &argv) ? 1 : 0;
}
static void on_window_destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
static gboolean on_context_menu(WebKitWebView *web_view, WebKitContextMenu *context_menu, GdkEvent *event, WebKitHitTestResult *hit_test_result, gpointer user_data) {
return TRUE;
}
static void set_window_icon_from_memory(GtkWindow *window, const void *buf, gsize len) {
if (!buf || len == 0) return;
GError *err = NULL;
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
if (loader) {
if (gdk_pixbuf_loader_write(loader, (const guint8 *)buf, len, &err)) {
gdk_pixbuf_loader_close(loader, &err);
GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
if (pixbuf) {
gtk_window_set_icon(window, pixbuf);
gtk_window_set_default_icon(pixbuf);
}
}
g_object_unref(loader);
}
gtk_window_set_default_icon_name("benzcloud-server");
gtk_window_set_icon_name(window, "benzcloud-server");
}
static void activate_gtk_app(const char* title, const char* url, int width, int height, const void *icon_buf, int icon_len) {
int argc = 0;
char **argv = NULL;
if (!gtk_init_check(&argc, &argv)) {
return;
}
g_set_prgname("benzcloud-server");
g_set_application_name("BenzCloud Server");
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), title);
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
GtkWidget *web_view = webkit_web_view_new();
gtk_container_add(GTK_CONTAINER(window), web_view);
if (icon_buf && icon_len > 0) {
set_window_icon_from_memory(GTK_WINDOW(window), icon_buf, (gsize)icon_len);
}
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
GdkRGBA bg_color;
gdk_rgba_parse(&bg_color, "#0b0f19");
WebKitSettings *settings = webkit_settings_new();
webkit_settings_set_enable_developer_extras(settings, FALSE);
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS);
GtkWidget *web_view = webkit_web_view_new_with_settings(settings);
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(web_view), &bg_color);
g_signal_connect(web_view, "context-menu", G_CALLBACK(on_context_menu), NULL);
gtk_container_add(GTK_CONTAINER(window), web_view);
g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL);
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), url);
gtk_widget_show_all(window);
@ -27,14 +81,85 @@ static void activate_gtk_app(const char* title, const char* url, int width, int
}
*/
import "C"
import "unsafe"
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"unsafe"
)
func init() {
_ = os.Setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "1")
_ = os.Setenv("WEBKIT_FORCE_COMPOSITING_MODE", "1")
}
// installDesktopIntegration automatically installs icons and desktop file into user's XDG directories
func installDesktopIntegration() {
home, err := os.UserHomeDir()
if err != nil {
return
}
iconDir := filepath.Join(home, ".local", "share", "icons", "hicolor", "512x512", "apps")
pixmapDir := filepath.Join(home, ".local", "share", "pixmaps")
appDir := filepath.Join(home, ".local", "share", "applications")
_ = os.MkdirAll(iconDir, 0755)
_ = os.MkdirAll(pixmapDir, 0755)
_ = os.MkdirAll(appDir, 0755)
iconPng, _ := webFS.ReadFile("web/icon.png")
if len(iconPng) > 0 {
_ = os.WriteFile(filepath.Join(iconDir, "benzcloud-server.png"), iconPng, 0644)
_ = os.WriteFile(filepath.Join(pixmapDir, "benzcloud-server.png"), iconPng, 0644)
}
iconSvg, _ := webFS.ReadFile("web/icon.svg")
if len(iconSvg) > 0 {
svgDir := filepath.Join(home, ".local", "share", "icons", "hicolor", "scalable", "apps")
_ = os.MkdirAll(svgDir, 0755)
_ = os.WriteFile(filepath.Join(svgDir, "benzcloud-server.svg"), iconSvg, 0644)
}
desktopPath := filepath.Join(appDir, "benzcloud-server.desktop")
execPath, _ := os.Executable()
if execPath == "" {
execPath = "benzcloud-server"
}
content := fmt.Sprintf(`[Desktop Entry]
Name=BenzCloud Server
Comment=Decentralized Mesh Cloud & Self-Hosting Core
Exec=%s
Icon=benzcloud-server
Terminal=false
Type=Application
Categories=Network;Server;Utility;
StartupWMClass=benzcloud-server
X-Wayland-AppID=benzcloud-server
`, execPath)
_ = os.WriteFile(desktopPath, []byte(content), 0644)
}
// LaunchGUI launches native WebKitGTK desktop shell on Linux.
func LaunchGUI(title, url string, width, height int) {
installDesktopIntegration()
hasDisplay := os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != ""
if !hasDisplay || C.check_display() == 0 {
log.Println("[GUI] Kein Display gefunden, öffne Standard-Browser...")
_ = exec.Command("xdg-open", url).Start()
return
}
cTitle := C.CString(title)
cURL := C.CString(url)
defer C.free(unsafe.Pointer(cTitle))
defer C.free(unsafe.Pointer(cURL))
C.activate_gtk_app(cTitle, cURL, C.int(width), C.int(height))
iconBytes, _ := webFS.ReadFile("web/icon.png")
var iconPtr unsafe.Pointer
if len(iconBytes) > 0 {
iconPtr = unsafe.Pointer(&iconBytes[0])
}
C.activate_gtk_app(cTitle, cURL, C.int(width), C.int(height), iconPtr, C.int(len(iconBytes)))
}

View File

@ -50,7 +50,16 @@ func NewServer(
}
func (s *Server) Handler() http.Handler {
return s.mux
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
s.mux.ServeHTTP(w, r)
})
}
func (s *Server) registerRoutes() {

BIN
web/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

86
web/icon.svg Normal file
View File

@ -0,0 +1,86 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#080e1a"/>
<stop offset="100%" stop-color="#121b2c"/>
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38bdf8"/>
<stop offset="100%" stop-color="#0284c7"/>
</linearGradient>
<linearGradient id="rackGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1e293b"/>
<stop offset="100%" stop-color="#0f172a"/>
</linearGradient>
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="10" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
<filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="6" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
</defs>
<!-- Background rounded squircle -->
<rect x="24" y="24" width="464" height="464" rx="108" fill="url(#bgGrad)" stroke="#1e293b" stroke-width="8"/>
<!-- Mesh VPN constellation lines & nodes -->
<g opacity="0.35" stroke="#38bdf8" stroke-width="2" stroke-dasharray="6,6">
<line x1="80" y1="120" x2="150" y2="150"/>
<line x1="432" y1="120" x2="362" y2="150"/>
<line x1="70" y1="360" x2="110" y2="310"/>
<line x1="442" y1="360" x2="402" y2="310"/>
<line x1="80" y1="120" x2="70" y2="360"/>
<line x1="432" y1="120" x2="442" y2="360"/>
</g>
<circle cx="80" cy="120" r="7" fill="#0284c7" stroke="#38bdf8" stroke-width="3"/>
<circle cx="432" cy="120" r="7" fill="#0284c7" stroke="#38bdf8" stroke-width="3"/>
<circle cx="70" cy="360" r="7" fill="#0284c7" stroke="#38bdf8" stroke-width="3"/>
<circle cx="442" cy="360" r="7" fill="#0284c7" stroke="#38bdf8" stroke-width="3"/>
<!-- Cloud Arch (Glowing Top) -->
<path d="M 160 190 C 135 190 115 168 120 142 C 126 112 154 96 182 104 C 198 74 236 60 270 74 C 304 54 350 68 362 104 C 392 104 416 128 410 158 C 404 186 382 190 355 190 Z"
fill="#0f1f38" stroke="url(#cyanGrad)" stroke-width="12" stroke-linejoin="round" filter="url(#glow)"/>
<!-- Server Rack Stack (3 Units) -->
<!-- Rack 1 -->
<rect x="106" y="212" width="300" height="48" rx="10" fill="url(#rackGrad)" stroke="#38bdf8" stroke-width="4"/>
<rect x="122" y="224" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="166" y="224" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="210" y="224" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="254" y="224" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<circle cx="340" cy="236" r="6" fill="#22c55e" filter="url(#softGlow)"/>
<circle cx="362" cy="236" r="6" fill="#38bdf8" filter="url(#softGlow)"/>
<circle cx="384" cy="236" r="6" fill="#38bdf8"/>
<!-- Rack 2 -->
<rect x="106" y="272" width="300" height="48" rx="10" fill="url(#rackGrad)" stroke="#0284c7" stroke-width="4"/>
<rect x="122" y="284" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="166" y="284" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="210" y="284" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="254" y="284" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<circle cx="340" cy="296" r="6" fill="#22c55e" filter="url(#softGlow)"/>
<circle cx="362" cy="296" r="6" fill="#f59e0b" filter="url(#softGlow)"/>
<circle cx="384" cy="296" r="6" fill="#38bdf8"/>
<!-- Rack 3 -->
<rect x="106" y="332" width="300" height="48" rx="10" fill="url(#rackGrad)" stroke="#0369a1" stroke-width="4"/>
<rect x="122" y="344" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="166" y="344" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="210" y="344" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<rect x="254" y="344" width="36" height="24" rx="4" fill="#09101d" stroke="#334155" stroke-width="2"/>
<circle cx="340" cy="356" r="6" fill="#22c55e" filter="url(#softGlow)"/>
<circle cx="362" cy="356" r="6" fill="#22c55e" filter="url(#softGlow)"/>
<circle cx="384" cy="356" r="6" fill="#38bdf8"/>
<!-- Cryptographic AES Vault Shield at Bottom Center -->
<g filter="url(#glow)">
<path d="M 256 320 L 296 345 L 296 395 C 296 422 278 444 256 454 C 234 444 216 422 216 395 L 216 345 Z"
fill="#0c192c" stroke="url(#cyanGrad)" stroke-width="8" stroke-linejoin="round"/>
<path d="M 246 384 L 246 372 C 246 366 250 362 256 362 C 262 362 266 366 266 372 L 266 384"
fill="none" stroke="#f0f6fc" stroke-width="5" stroke-linecap="round"/>
<rect x="240" y="384" width="32" height="24" rx="4" fill="#38bdf8"/>
<circle cx="256" cy="396" r="3.5" fill="#0c192c"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB