v0.11.0 harden persistence and prepare game replays

This commit is contained in:
2026-09-07 14:39:00 -04:00
parent 21dea47694
commit 5d2be4abb2
42 changed files with 3591 additions and 693 deletions
+19 -6
View File
@@ -23,7 +23,7 @@ type Client struct {
func New(baseURL string) *Client {
return &Client{
BaseURL: baseURL,
BaseURL: strings.TrimRight(baseURL, "/"),
HTTPClient: &http.Client{
Timeout: HttpTimeout,
},
@@ -81,7 +81,7 @@ func (c *Client) doRequest(method, path string, body any, result any) error {
json.Unmarshal([]byte(bodyStr), &prettyBody)
prettyJSON, _ := json.MarshalIndent(prettyBody, "", " ")
display.Println(display.Cyan, "Request Body:")
display.Println(display.Reset, string(prettyJSON))
display.Println(display.Reset, "%s", string(prettyJSON))
} else {
display.Print(display.Blue, "%s\n", bodyStr)
}
@@ -114,10 +114,10 @@ func (c *Client) doRequest(method, path string, body any, result any) error {
if err := json.Unmarshal(respBody, &prettyResp); err == nil {
prettyJSON, _ := json.MarshalIndent(prettyResp, "", " ")
display.Println(display.Cyan, "Response Body:")
display.Println(display.Reset, string(prettyJSON))
display.Println(display.Reset, "%s", string(prettyJSON))
} else {
display.Println(display.Cyan, "Response:")
display.Println(display.Reset, string(respBody))
display.Println(display.Reset, "%s", string(respBody))
}
}
@@ -135,7 +135,7 @@ func (c *Client) doRequest(method, path string, body any, result any) error {
}
}
} else if !c.Verbose {
display.Println(display.Red, string(respBody))
display.Println(display.Red, "%s", string(respBody))
}
return fmt.Errorf("request failed with status %d", resp.StatusCode)
}
@@ -204,6 +204,19 @@ func (c *Client) GetBoard(gameID string) (*BoardResponse, error) {
return &resp, err
}
func (c *Client) GetGameHistory(gameID string) (*GameHistoryResponse, error) {
var resp GameHistoryResponse
err := c.doRequest("GET", "/api/v1/games/"+gameID+"/history", nil, &resp)
return &resp, err
}
func (c *Client) GetMyGames(limit, offset int) (*GameListResponse, error) {
var resp GameListResponse
path := fmt.Sprintf("/api/v1/users/me/games?limit=%d&offset=%d", limit, offset)
err := c.doRequest("GET", path, nil, &resp)
return &resp, err
}
func (c *Client) Register(username, password, email string) (*AuthResponse, error) {
req := &RegisterRequest{
Username: username,
@@ -246,4 +259,4 @@ func (c *Client) RawRequest(method, path string, body string) error {
}
return c.doRequest(method, path, bodyData, nil)
}
}
+38 -1
View File
@@ -52,9 +52,11 @@ type PlayersResponse struct {
type PlayerInfo struct {
ID string `json:"id"`
Color int `json:"color"`
Type int `json:"type"`
Level int `json:"level,omitempty"`
SearchTime int `json:"searchTime,omitempty"`
ClaimedBy string `json:"claimedBy,omitempty"`
}
type MoveInfo struct {
@@ -95,4 +97,39 @@ type HealthResponse struct {
Status string `json:"status"`
Time int64 `json:"time"`
Storage string `json:"storage,omitempty"`
}
}
type GameHistoryResponse struct {
GameID string `json:"gameId"`
InitialFEN string `json:"initialFen"`
Result string `json:"result,omitempty"`
StartTimeUTC time.Time `json:"startTimeUtc"`
EndTimeUTC *time.Time `json:"endTimeUtc,omitempty"`
Players PlayersResponse `json:"players"`
Moves []HistoryMove `json:"moves"`
}
type HistoryMove struct {
MoveNumber int `json:"moveNumber"`
MoveUCI string `json:"moveUci"`
FENAfterMove string `json:"fenAfterMove"`
PlayerColor string `json:"playerColor"`
MoveTimeUTC time.Time `json:"moveTimeUtc"`
}
type GameSummary struct {
GameID string `json:"gameId"`
InitialFEN string `json:"initialFen"`
Result string `json:"result,omitempty"`
StartTimeUTC time.Time `json:"startTimeUtc"`
EndTimeUTC *time.Time `json:"endTimeUtc,omitempty"`
MoveCount int `json:"moveCount"`
Players PlayersResponse `json:"players"`
}
type GameListResponse struct {
Games []GameSummary `json:"games"`
Limit int `json:"limit"`
Offset int `json:"offset"`
NextOffset *int `json:"nextOffset,omitempty"`
}