fix: sanitize sync error messages, detect non-sync servers, add health check in TestAuth

This commit is contained in:
mirivlad 2026-06-20 03:20:25 +08:00
parent db67c370ab
commit 4de5a74a55
1 changed files with 47 additions and 6 deletions

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
) )
@ -124,12 +125,35 @@ func (c *Client) RevokeCurrent() error {
// TestAuth checks credentials without creating a device. // TestAuth checks credentials without creating a device.
func (c *Client) TestAuth(serverURL, username, password string) error { func (c *Client) TestAuth(serverURL, username, password string) error {
body := map[string]string{"username": username, "password": password} // First, check if this is a Verstak Sync server
healthURL := strings.TrimSuffix(serverURL, "/") + "/api/v1/health"
req, err := http.NewRequest("GET", healthURL, nil)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}
resp, err := c.HTTP.Do(req)
if err != nil {
return fmt.Errorf("connection failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("not a Verstak Sync server (HTTP %d)", resp.StatusCode)
}
data, _ := io.ReadAll(resp.Body)
body := string(data)
if !strings.Contains(body, "status") && !strings.Contains(body, "ok") {
return fmt.Errorf("not a Verstak Sync server (unexpected response)")
}
// Now test actual auth
authBody := map[string]string{"username": username, "password": password}
savedURL := c.ServerURL savedURL := c.ServerURL
savedKey := c.APIKey savedKey := c.APIKey
c.ServerURL = serverURL c.ServerURL = serverURL
c.APIKey = "" c.APIKey = ""
err := c.post("/api/auth/test", body, nil) err = c.post("/api/auth/test", authBody, nil)
c.ServerURL = savedURL c.ServerURL = savedURL
c.APIKey = savedKey c.APIKey = savedKey
return err return err
@ -273,6 +297,13 @@ func (c *Client) DownloadBlob(sha256, destPath string) error {
return err return err
} }
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
func (c *Client) bearerToken() string { func (c *Client) bearerToken() string {
if c.DeviceToken != "" { if c.DeviceToken != "" {
return c.DeviceToken return c.DeviceToken
@ -301,8 +332,7 @@ func (c *Client) post(path string, body, result interface{}) error {
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {
data, _ := io.ReadAll(resp.Body) return c.readErrorBody(resp, resp.StatusCode)
return fmt.Errorf("server %d: %s", resp.StatusCode, string(data))
} }
if result != nil { if result != nil {
@ -325,8 +355,7 @@ func (c *Client) get(path string, result interface{}) error {
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {
data, _ := io.ReadAll(resp.Body) return c.readErrorBody(resp, resp.StatusCode)
return fmt.Errorf("server %d: %s", resp.StatusCode, string(data))
} }
if result != nil { if result != nil {
@ -334,3 +363,15 @@ func (c *Client) get(path string, result interface{}) error {
} }
return nil return nil
} }
func (c *Client) readErrorBody(resp *http.Response, statusCode int) error {
buf := make([]byte, 4096)
n, _ := io.ReadFull(resp.Body, buf)
body := string(buf[:minInt(n, 500)])
lower := strings.ToLower(body)
if strings.Contains(lower, "<html") || strings.Contains(lower, "<!doctype") {
return fmt.Errorf("not a Verstak Sync server (HTTP %d)", statusCode)
}
return fmt.Errorf("server error (HTTP %d)", statusCode)
}