1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
package main
import (
"context"
"fmt"
"net/http"
"github.com/wailsapp/wails/v2/pkg/runtime"
"rickub.com/bots-garden/ori-desktop/internal/health"
"rickub.com/bots-garden/ori-desktop/internal/settings"
)
// App holds the state shared by the Go-bound methods exposed to the frontend.
// Every exported method on App is callable from JavaScript as
// window.go.main.App.<Method>(...) and returns a Promise.
type App struct {
ctx context.Context
settingsPath string
client *http.Client
}
// NewApp creates the application state. The settings location is resolved
// once here; if the platform exposes no user config directory the path is
// left empty and SaveConfig reports the problem instead of guessing.
func NewApp() *App {
path, err := settings.DefaultPath()
if err != nil {
path = ""
}
return &App{
settingsPath: path,
client: &http.Client{Timeout: health.DefaultTimeout},
}
}
// startup is the Wails OnStartup hook; it keeps the runtime context needed by
// runtime.* calls such as BrowserOpenURL.
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// GetConfig returns the remembered settings (defaults on a first run).
func (a *App) GetConfig() (settings.Settings, error) {
if a.settingsPath == "" {
return settings.Default(), nil
}
return settings.Load(a.settingsPath)
}
// SaveConfig normalises the server URL and persists the settings.
func (a *App) SaveConfig(s settings.Settings) error {
if a.settingsPath == "" {
return fmt.Errorf("no user configuration directory available on this platform")
}
normalized, err := settings.NormalizeURL(s.ServerURL)
if err != nil {
return err
}
s.ServerURL = normalized
return settings.Save(a.settingsPath, s)
}
// CheckHealth probes GET <url>/healthz and reports the outcome. It never
// fails the Promise: a malformed URL or an unreachable server both come back
// as a Report with OK=false and an explanatory Detail.
func (a *App) CheckHealth(rawURL string) health.Report {
base, err := settings.NormalizeURL(rawURL)
if err != nil {
return health.Report{URL: rawURL, Detail: err.Error()}
}
return health.Check(a.context(), a.client, base)
}
// Connect is the one-call happy path used by the connection screen: it
// normalises the URL, checks /healthz, remembers the URL on success and
// returns the normalised base URL the frontend should load. On failure the
// Promise rejects with the health Detail so the screen can show it.
func (a *App) Connect(rawURL string) (string, error) {
base, err := settings.NormalizeURL(rawURL)
if err != nil {
return "", err
}
rep := health.Check(a.context(), a.client, base)
if !rep.OK {
return "", fmt.Errorf("%s", rep.Detail)
}
if a.settingsPath != "" {
if err := settings.Save(a.settingsPath, settings.Settings{ServerURL: base}); err != nil {
// Not fatal for the session: the user is connected, only the
// memory of the URL is lost. Surface it in the returned error?
// No — the frontend treats an error as "not connected"; log it.
fmt.Println("ori-desktop: warning:", err)
}
}
return base, nil
}
// OpenInBrowser opens the given URL (normalised first) in the system's
// default browser — the fallback when embedding is not wanted.
func (a *App) OpenInBrowser(rawURL string) error {
base, err := settings.NormalizeURL(rawURL)
if err != nil {
return err
}
if a.ctx == nil {
return fmt.Errorf("application runtime not ready")
}
runtime.BrowserOpenURL(a.ctx, base)
return nil
}
// SettingsPath tells the frontend where the settings live, for display.
func (a *App) SettingsPath() string {
return a.settingsPath
}
// context returns the runtime context, or a background one before startup
// (bound methods are only reachable after startup, this is belt and braces).
func (a *App) context() context.Context {
if a.ctx != nil {
return a.ctx
}
return context.Background()
}
|