fix(gui,android,api): fix Wayland icon, add Android launcher mipmaps and add CORS middleware
@ -13,6 +13,8 @@
|
|||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:label="BenzCloud Server"
|
android:label="BenzCloud Server"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
|
|
||||||
|
|||||||
BIN
android/app/src/main/res/drawable/icon.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
139
gui_linux.go
@ -4,21 +4,75 @@ package main
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo pkg-config: gtk+-3.0 webkit2gtk-4.1
|
#cgo pkg-config: gtk+-3.0 webkit2gtk-4.1
|
||||||
|
#include <stdlib.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <webkit2/webkit2.h>
|
#include <webkit2/webkit2.h>
|
||||||
|
|
||||||
static void activate_gtk_app(const char* title, const char* url, int width, int height) {
|
static int check_display() {
|
||||||
gtk_init(NULL, NULL);
|
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);
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
gtk_window_set_title(GTK_WINDOW(window), title);
|
gtk_window_set_title(GTK_WINDOW(window), title);
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
|
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
|
||||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||||
|
|
||||||
GtkWidget *web_view = webkit_web_view_new();
|
if (icon_buf && icon_len > 0) {
|
||||||
gtk_container_add(GTK_CONTAINER(window), web_view);
|
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);
|
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), url);
|
||||||
gtk_widget_show_all(window);
|
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 "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.
|
// LaunchGUI launches native WebKitGTK desktop shell on Linux.
|
||||||
func LaunchGUI(title, url string, width, height int) {
|
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)
|
cTitle := C.CString(title)
|
||||||
cURL := C.CString(url)
|
cURL := C.CString(url)
|
||||||
defer C.free(unsafe.Pointer(cTitle))
|
defer C.free(unsafe.Pointer(cTitle))
|
||||||
defer C.free(unsafe.Pointer(cURL))
|
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)))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,16 @@ func NewServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Handler() http.Handler {
|
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() {
|
func (s *Server) registerRoutes() {
|
||||||
|
|||||||
BIN
web/icon.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
86
web/icon.svg
Normal 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 |