Use default Wails asset handler + GetStoredConfig binding
- Remove custom asset handler — it broke Wails IPC routing - Use application.AssetFileServerFS(distFS) so Wails serves its own runtime - Add GetStoredConfig binding: SPA calls it on startup to retrieve keychain config - main.ts: loadDesktopConfig() fetches stored creds before mounting - Remove runtime.js embed (Wails serves it internally)
This commit is contained in:
@@ -31,17 +31,14 @@ var assets embed.FS
|
|||||||
//go:embed icon.png
|
//go:embed icon.png
|
||||||
var iconPNG []byte
|
var iconPNG []byte
|
||||||
|
|
||||||
//go:embed runtime.js
|
|
||||||
var runtimeJS []byte
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
keyringService = "com.hubris.oikos-desktop"
|
keyringService = "com.hubris.oikos-desktop"
|
||||||
keyringUser = "oikos"
|
keyringUser = "oikos"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
updateURL = "https://git.hubris.network/api/v1/repos/dtoro/oikos/releases"
|
updateURL = "https://git.hubris.network/api/v1/repos/dtoro/oikos/releases"
|
||||||
pollInterval = 30 * time.Second
|
pollInterval = 30 * time.Second
|
||||||
updateInterval = 6 * time.Hour
|
updateInterval = 6 * time.Hour
|
||||||
oidcCallbackPort = 18901
|
oidcCallbackPort = 18901
|
||||||
)
|
)
|
||||||
|
|
||||||
type OikosConfig struct {
|
type OikosConfig struct {
|
||||||
@@ -66,6 +63,10 @@ func (c *ConfigService) ClearConfig() error {
|
|||||||
return keyring.Delete(keyringService, keyringUser)
|
return keyring.Delete(keyringService, keyringUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfigService) GetStoredConfig() *OikosConfig {
|
||||||
|
return loadConfig()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ConfigService) EnableAutoStart() error {
|
func (c *ConfigService) EnableAutoStart() error {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
return fmt.Errorf("autostart not supported on %s", runtime.GOOS)
|
return fmt.Errorf("autostart not supported on %s", runtime.GOOS)
|
||||||
@@ -104,9 +105,6 @@ func (c *ConfigService) DisableAutoStart() error {
|
|||||||
return os.Remove(path)
|
return os.Remove(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartOIDCLogin opens the system browser for Authentik login and returns
|
|
||||||
// the access token. The desktop app hosts a local HTTP server on a fixed
|
|
||||||
// port to receive the OIDC callback directly (no copy-paste).
|
|
||||||
func (c *ConfigService) StartOIDCLogin(apiUrl string) (string, error) {
|
func (c *ConfigService) StartOIDCLogin(apiUrl string) (string, error) {
|
||||||
apiUrl = strings.TrimRight(apiUrl, "/")
|
apiUrl = strings.TrimRight(apiUrl, "/")
|
||||||
|
|
||||||
@@ -321,46 +319,6 @@ func loadConfig() *OikosConfig {
|
|||||||
return &cfg
|
return &cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Asset handler ----
|
|
||||||
|
|
||||||
func newAssetHandler(cfg *OikosConfig) http.Handler {
|
|
||||||
distFS, err := fs.Sub(assets, "frontend/dist")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("embedded assets: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fallback := http.FileServer(http.FS(distFS))
|
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
path := r.URL.Path
|
|
||||||
|
|
||||||
if path == "/wails/runtime.js" {
|
|
||||||
w.Header().Set("Content-Type", "application/javascript")
|
|
||||||
w.Write(runtimeJS)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if path == "/" || path == "/index.html" {
|
|
||||||
data, err := fs.ReadFile(distFS, "index.html")
|
|
||||||
if err != nil {
|
|
||||||
fallback.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
html := string(data)
|
|
||||||
if cfg != nil {
|
|
||||||
configJSON, _ := json.Marshal(cfg)
|
|
||||||
placeholder := `<script>window.__OIKOS_CONFIG__ = {};</script>`
|
|
||||||
injected := fmt.Sprintf(`<script>window.__OIKOS_CONFIG__ = %s;</script>`, configJSON)
|
|
||||||
html = strings.ReplaceAll(html, placeholder, injected)
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
||||||
w.Write([]byte(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallback.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Notifications ----
|
// ---- Notifications ----
|
||||||
|
|
||||||
type dashboardSummary struct {
|
type dashboardSummary struct {
|
||||||
@@ -483,7 +441,10 @@ func checkUpdates() {
|
|||||||
// ---- Main ----
|
// ---- Main ----
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cfg := loadConfig()
|
distFS, err := fs.Sub(assets, "frontend/dist")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("embedded assets: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
app := application.New(application.Options{
|
app := application.New(application.Options{
|
||||||
Name: "Oikos",
|
Name: "Oikos",
|
||||||
@@ -492,14 +453,13 @@ func main() {
|
|||||||
application.NewService(&ConfigService{}),
|
application.NewService(&ConfigService{}),
|
||||||
},
|
},
|
||||||
Assets: application.AssetOptions{
|
Assets: application.AssetOptions{
|
||||||
Handler: newAssetHandler(cfg),
|
Handler: application.AssetFileServerFS(distFS),
|
||||||
},
|
},
|
||||||
Mac: application.MacOptions{
|
Mac: application.MacOptions{
|
||||||
ApplicationShouldTerminateAfterLastWindowClosed: false,
|
ApplicationShouldTerminateAfterLastWindowClosed: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- System tray ---
|
|
||||||
systemTray := app.SystemTray.New()
|
systemTray := app.SystemTray.New()
|
||||||
systemTray.SetLabel("Oikos")
|
systemTray.SetLabel("Oikos")
|
||||||
systemTray.SetTooltip("Oikos")
|
systemTray.SetTooltip("Oikos")
|
||||||
@@ -523,7 +483,6 @@ func main() {
|
|||||||
})
|
})
|
||||||
systemTray.SetMenu(trayMenu)
|
systemTray.SetMenu(trayMenu)
|
||||||
|
|
||||||
// --- Main window ---
|
|
||||||
ws := loadWindowState()
|
ws := loadWindowState()
|
||||||
width, height := 1400, 900
|
width, height := 1400, 900
|
||||||
minWidth, minHeight := 1024, 700
|
minWidth, minHeight := 1024, 700
|
||||||
@@ -552,10 +511,10 @@ func main() {
|
|||||||
saveWindowState(window)
|
saveWindowState(window)
|
||||||
})
|
})
|
||||||
|
|
||||||
go pollDashboard(cfg)
|
go pollDashboard(loadConfig())
|
||||||
go checkUpdates()
|
go checkUpdates()
|
||||||
|
|
||||||
err := app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,11 +1,29 @@
|
|||||||
import { mount } from 'svelte'
|
import { mount } from 'svelte'
|
||||||
import App from './App.svelte'
|
import App from './App.svelte'
|
||||||
import './app.css'
|
import './app.css'
|
||||||
import { initConfig } from '$lib/config'
|
import { initConfig, setConfig } from '$lib/config'
|
||||||
|
|
||||||
initConfig()
|
async function loadDesktopConfig() {
|
||||||
|
const wails = (window as any).wails
|
||||||
|
if (!wails?.Call?.ByName) return
|
||||||
|
|
||||||
requestAnimationFrame(() => import('./lib/renderers'))
|
try {
|
||||||
|
const cfg = await wails.Call.ByName('GetStoredConfig')
|
||||||
|
if (cfg?.apiUrl && cfg?.token) {
|
||||||
|
setConfig({ apiUrl: cfg.apiUrl, token: cfg.token, isDesktop: true })
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// no stored config — Config page will handle it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const app = mount(App, { target: document.getElementById('app')! })
|
async function start() {
|
||||||
export default app
|
initConfig()
|
||||||
|
await loadDesktopConfig()
|
||||||
|
|
||||||
|
requestAnimationFrame(() => import('./lib/renderers'))
|
||||||
|
|
||||||
|
mount(App, { target: document.getElementById('app')! })
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|||||||
Reference in New Issue
Block a user