Add native iOS app with App Store preparation
iOS app (ios/, SwiftUI, iOS 17+) in the web UI's styling with the Kamon logo. It covers everything the web interface does except password, API tokens and backups: dashboard, peers with search and filter, peer detail with traffic charts, add/edit peers, one-time config with QR code and share sheet, server settings with apply bar, app settings, data retention and log viewer. - Pairing by QR code or pasted pairing code; token kept in the keychain; self-signed certificates are pinned by SHA-256 fingerprint. - Colour providers and logo drawing are nonisolated: SwiftUI's background renderer calls them, and main-actor closures crashed there when the camera scanner was open. - App Store: version 1.0, export compliance, privacy manifest, app icon, release.sh (archive and upload), listing text, review notes and 6.9" screenshots in ios/AppStore. Server: - Full-access API tokens may use settings, logs and restart; password, tokens, backup/restore and the admin username stay admin-only. - Web pairing dialog gains "Copy pairing code". - The development simulator reports health checks in Linux wording. Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw
This commit is contained in:
@@ -125,14 +125,16 @@ func (a *App) routes() http.Handler {
|
||||
g("POST /api/v1/peers/{id}/issue-config", a.issueConfig)
|
||||
g("GET /api/v1/peers/{id}/stats", a.peerStats)
|
||||
|
||||
adm("GET /api/v1/settings", a.getSettings)
|
||||
adm("PATCH /api/v1/settings", a.patchSettings)
|
||||
adm("POST /api/v1/restart", a.restart)
|
||||
// Full-access tokens (the iOS app) may change app settings and read logs.
|
||||
// Password, tokens and backups stay with the admin account.
|
||||
g("GET /api/v1/settings", a.getSettings)
|
||||
g("PATCH /api/v1/settings", a.patchSettings)
|
||||
g("POST /api/v1/restart", a.restart)
|
||||
adm("GET /api/v1/tokens", a.listTokens)
|
||||
adm("POST /api/v1/tokens", a.createToken)
|
||||
adm("DELETE /api/v1/tokens/{id}", a.deleteToken)
|
||||
adm("GET /api/v1/logs", a.logs)
|
||||
adm("GET /api/v1/logs/download", a.downloadLog)
|
||||
g("GET /api/v1/logs", a.logs)
|
||||
g("GET /api/v1/logs/download", a.downloadLog)
|
||||
adm("GET /api/v1/backup", a.backup)
|
||||
adm("POST /api/v1/restore", a.restore)
|
||||
|
||||
@@ -816,6 +818,10 @@ func (a *App) patchSettings(w http.ResponseWriter, r *http.Request) {
|
||||
writeErr(w, err)
|
||||
return
|
||||
}
|
||||
if _, ok := m["adminUsername"]; ok && !who(r).IsAdmin {
|
||||
writeJSON(w, http.StatusForbidden, map[string]string{"error": "only the admin account can change the username"})
|
||||
return
|
||||
}
|
||||
var restart bool
|
||||
err = a.store.Update(func(c *Config) error {
|
||||
if err := field(m, "adminUsername", &c.Admin.Username); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user