v0.7.1 client readline removed for cross-platform compatibility with wasm, client logic fix fixes and refactor
This commit is contained in:
@ -3,17 +3,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"chess/internal/client/api"
|
"chess/internal/client/api"
|
||||||
"chess/internal/client/commands"
|
"chess/internal/client/command"
|
||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
"chess/internal/client/session"
|
"chess/internal/client/session"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -23,44 +21,37 @@ func main() {
|
|||||||
Verbose: false,
|
Verbose: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize readline
|
// Initialize simple input scanner
|
||||||
rl, err := readline.NewEx(&readline.Config{
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
Prompt: display.Prompt("chess"),
|
|
||||||
HistoryFile: ".chess_history",
|
|
||||||
InterruptPrompt: "^C",
|
|
||||||
EOFPrompt: "exit",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%s%s%s\n", display.Red, err.Error(), display.Reset)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer rl.Close()
|
|
||||||
|
|
||||||
fmt.Printf("%sChess Debug Client%s\n", display.Cyan, display.Reset)
|
display.Println(display.Cyan, "Chess Debug Client")
|
||||||
fmt.Printf("%sAPI: %s%s\n", display.Cyan, s.APIBaseURL, display.Reset)
|
display.Println(display.Cyan, "API: %s", s.APIBaseURL)
|
||||||
fmt.Printf("Type 'help' for commands\n\n")
|
fmt.Println("Type 'help' for commands\n")
|
||||||
|
|
||||||
registry := commands.NewRegistry(s)
|
registry := command.NewRegistry(s)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Build enhanced prompt
|
// Build enhanced prompt
|
||||||
prompt := buildPrompt(s)
|
prompt := buildPrompt(s)
|
||||||
rl.SetPrompt(prompt)
|
fmt.Print(prompt)
|
||||||
|
|
||||||
line, err := rl.Readline()
|
// Read input
|
||||||
if err == io.EOF {
|
if !scanner.Scan() {
|
||||||
|
// EOF or error
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
display.Println(display.Red, "\nError reading input: %s", err.Error())
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
line = strings.TrimSpace(line)
|
line := strings.TrimSpace(scanner.Text())
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for exit commands
|
||||||
if line == "exit" || line == "quit" || line == "x" {
|
if line == "exit" || line == "quit" || line == "x" {
|
||||||
|
display.Println(display.Cyan, "Goodbye!")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,63 +68,53 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildPrompt(s *session.Session) string {
|
func buildPrompt(s *session.Session) string {
|
||||||
parts := []string{}
|
var b display.Builder
|
||||||
|
b.Add("", "chess")
|
||||||
// Base
|
|
||||||
base := "chess"
|
|
||||||
|
|
||||||
// Add user/game context
|
// Add user/game context
|
||||||
if s.Username != "" {
|
if s.Username != "" {
|
||||||
parts = append(parts, fmt.Sprintf("%s%s%s", display.Magenta, s.Username, display.Reset))
|
b.Add("", " [").Add(display.Magenta, s.Username)
|
||||||
}
|
if s.CurrentGame != "" {
|
||||||
if s.Username != "" && s.CurrentGame != "" {
|
b.Add(display.Yellow, " - ")
|
||||||
parts = append(parts, fmt.Sprintf("%s - %s", display.Yellow, display.Reset))
|
} else {
|
||||||
|
b.Add("", "]")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.CurrentGame != "" {
|
if s.CurrentGame != "" {
|
||||||
parts = append(parts, fmt.Sprintf("%s%s%s", display.White, s.CurrentGame[:8], display.Reset))
|
if s.Username == "" {
|
||||||
|
b.Add("", " [")
|
||||||
|
}
|
||||||
|
b.Add(display.White, s.CurrentGame[:8])
|
||||||
|
b.Add("", "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add player color if in game
|
// Add player color if in game
|
||||||
if s.CurrentGameState != nil && s.PlayerColor != "" {
|
if s.CurrentGameState != nil && s.PlayerColor != "" {
|
||||||
colorText := ""
|
|
||||||
if s.PlayerColor == "w" {
|
if s.PlayerColor == "w" {
|
||||||
colorText = display.Blue + "White" + display.Reset
|
b.Add("", " ").Add(display.Blue, "White")
|
||||||
} else {
|
} else {
|
||||||
colorText = display.Red + "Black" + display.Reset
|
b.Add("", " ").Add(display.Red, "Black")
|
||||||
}
|
}
|
||||||
parts = append(parts, colorText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build first part
|
|
||||||
promptStr := base
|
|
||||||
if len(parts) > 0 {
|
|
||||||
promptStr += display.Yellow + " [" + display.Reset + strings.Join(parts, "") + display.Yellow + "]"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add game state if available
|
// Add game state if available
|
||||||
if s.CurrentGameState != nil {
|
if s.CurrentGameState != nil {
|
||||||
turnInfo := ""
|
turnInfo := " - Turn:"
|
||||||
if s.CurrentGameState.Turn == "w" {
|
if s.CurrentGameState.Turn == "w" {
|
||||||
turnPlayer := "White"
|
|
||||||
playerType := "h"
|
playerType := "h"
|
||||||
if s.CurrentGameState.Players.White.Type == 2 {
|
if s.CurrentGameState.Players.White.Type == 2 {
|
||||||
playerType = "c"
|
playerType = "c"
|
||||||
}
|
}
|
||||||
turnInfo = fmt.Sprintf(" - Turn:%s(%s)",
|
b.Add("", turnInfo).Add(display.Blue, "White").Add("", fmt.Sprintf("(%s)", playerType))
|
||||||
fmt.Sprintf("%s%s%s", display.Blue, turnPlayer, display.Reset),
|
|
||||||
playerType)
|
|
||||||
} else {
|
} else {
|
||||||
turnPlayer := "Black"
|
|
||||||
playerType := "h"
|
playerType := "h"
|
||||||
if s.CurrentGameState.Players.Black.Type == 2 {
|
if s.CurrentGameState.Players.Black.Type == 2 {
|
||||||
playerType = "c"
|
playerType = "c"
|
||||||
}
|
}
|
||||||
turnInfo = fmt.Sprintf(" - Turn:%s(%s)",
|
b.Add("", turnInfo).Add(display.Red, "Black").Add("", fmt.Sprintf("(%s)", playerType))
|
||||||
fmt.Sprintf("%s%s%s", display.Red, turnPlayer, display.Reset),
|
|
||||||
playerType)
|
|
||||||
}
|
}
|
||||||
promptStr += turnInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return display.Prompt(promptStr)
|
return display.Prompt(b.String())
|
||||||
}
|
}
|
||||||
1
go.mod
1
go.mod
@ -3,7 +3,6 @@ module chess
|
|||||||
go 1.25.4
|
go 1.25.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/chzyer/readline v1.5.1
|
|
||||||
github.com/go-playground/validator/v10 v10.28.0
|
github.com/go-playground/validator/v10 v10.28.0
|
||||||
github.com/gofiber/fiber/v2 v2.52.9
|
github.com/gofiber/fiber/v2 v2.52.9
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
|||||||
7
go.sum
7
go.sum
@ -1,11 +1,5 @@
|
|||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
|
||||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
|
||||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
|
||||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
|
||||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||||
@ -58,7 +52,6 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
|
|||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import (
|
|||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const HttpTimeout = 30 * time.Second
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
AuthToken string
|
AuthToken string
|
||||||
@ -24,7 +26,7 @@ func New(baseURL string) *Client {
|
|||||||
return &Client{
|
return &Client{
|
||||||
BaseURL: baseURL,
|
BaseURL: baseURL,
|
||||||
HTTPClient: &http.Client{
|
HTTPClient: &http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: HttpTimeout,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +44,7 @@ func (c *Client) SetToken(token string) {
|
|||||||
c.AuthToken = token
|
c.AuthToken = token
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doRequest(method, path string, body interface{}, result interface{}) error {
|
func (c *Client) doRequest(method, path string, body any, result any) error {
|
||||||
url := c.BaseURL + path
|
url := c.BaseURL + path
|
||||||
|
|
||||||
// Prepare body
|
// Prepare body
|
||||||
@ -72,23 +74,24 @@ func (c *Client) doRequest(method, path string, body interface{}, result interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display request
|
// Display request
|
||||||
fmt.Printf("\n%s[API] %s %s%s\n", display.Blue, method, path, display.Reset)
|
display.Print(display.Blue, "\n[API] %s %s\n", method, path)
|
||||||
if bodyStr != "" {
|
if bodyStr != "" {
|
||||||
if c.Verbose {
|
if c.Verbose {
|
||||||
// Display request body if verbose
|
// Display request body if verbose
|
||||||
var prettyBody interface{}
|
var prettyBody any
|
||||||
json.Unmarshal([]byte(bodyStr), &prettyBody)
|
json.Unmarshal([]byte(bodyStr), &prettyBody)
|
||||||
prettyJSON, _ := json.MarshalIndent(prettyBody, "", " ")
|
prettyJSON, _ := json.MarshalIndent(prettyBody, "", " ")
|
||||||
fmt.Printf("%sRequest Body:%s\n%s\n", display.Cyan, display.Reset, string(prettyJSON))
|
display.Println(display.Cyan, "Request Body:")
|
||||||
|
display.Println(display.Reset, string(prettyJSON))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s%s%s\n", display.Blue, bodyStr, display.Reset)
|
display.Print(display.Blue, "%s\n", bodyStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute request
|
// Execute request
|
||||||
resp, err := c.HTTPClient.Do(req)
|
resp, err := c.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s[ERROR] %s%s\n", display.Red, err.Error(), display.Reset)
|
display.Print(display.Red, "[ERROR] %s\n", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
@ -108,12 +111,14 @@ func (c *Client) doRequest(method, path string, body interface{}, result interfa
|
|||||||
|
|
||||||
// Display response body if verbose
|
// Display response body if verbose
|
||||||
if c.Verbose && len(respBody) > 0 {
|
if c.Verbose && len(respBody) > 0 {
|
||||||
var prettyResp interface{}
|
var prettyResp any
|
||||||
if err := json.Unmarshal(respBody, &prettyResp); err == nil {
|
if err := json.Unmarshal(respBody, &prettyResp); err == nil {
|
||||||
prettyJSON, _ := json.MarshalIndent(prettyResp, "", " ")
|
prettyJSON, _ := json.MarshalIndent(prettyResp, "", " ")
|
||||||
fmt.Printf("%sResponse Body:%s\n%s\n", display.Cyan, display.Reset, string(prettyJSON))
|
display.Println(display.Cyan, "Response Body:")
|
||||||
|
display.Println(display.Reset, string(prettyJSON))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%sResponse:%s\n%s\n", display.Cyan, display.Reset, string(respBody))
|
display.Println(display.Cyan, "Response:")
|
||||||
|
display.Println(display.Reset, string(respBody))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,16 +127,16 @@ func (c *Client) doRequest(method, path string, body interface{}, result interfa
|
|||||||
var errResp ErrorResponse
|
var errResp ErrorResponse
|
||||||
if err := json.Unmarshal(respBody, &errResp); err == nil {
|
if err := json.Unmarshal(respBody, &errResp); err == nil {
|
||||||
if !c.Verbose {
|
if !c.Verbose {
|
||||||
fmt.Printf("%sError: %s%s\n", display.Red, errResp.Error, display.Reset)
|
display.Print(display.Red, "Error: %s\n", errResp.Error)
|
||||||
if errResp.Code != "" {
|
if errResp.Code != "" {
|
||||||
fmt.Printf("%sCode: %s%s\n", display.Red, errResp.Code, display.Reset)
|
display.Print(display.Red, "Code: %s\n", errResp.Code)
|
||||||
}
|
}
|
||||||
if errResp.Details != "" {
|
if errResp.Details != "" {
|
||||||
fmt.Printf("%sDetails: %s%s\n", display.Red, errResp.Details, display.Reset)
|
display.Print(display.Red, "Details: %s\n", errResp.Details)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !c.Verbose {
|
} else if !c.Verbose {
|
||||||
fmt.Printf("%s%s%s\n", display.Red, string(respBody), display.Reset)
|
display.Println(display.Red, string(respBody))
|
||||||
}
|
}
|
||||||
return fmt.Errorf("request failed with status %d", resp.StatusCode)
|
return fmt.Errorf("request failed with status %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
@ -140,8 +145,8 @@ func (c *Client) doRequest(method, path string, body interface{}, result interfa
|
|||||||
if result != nil && len(respBody) > 0 {
|
if result != nil && len(respBody) > 0 {
|
||||||
if err := json.Unmarshal(respBody, result); err != nil {
|
if err := json.Unmarshal(respBody, result); err != nil {
|
||||||
// For debug, show raw response if parsing fails
|
// For debug, show raw response if parsing fails
|
||||||
fmt.Printf("%sResponse parse error: %s%s\n", display.Red, err.Error(), display.Reset)
|
display.Print(display.Red, "Response parse error: %s\n", err.Error())
|
||||||
fmt.Printf("%sRaw response: %s%s\n", display.Green, string(respBody), display.Reset)
|
display.Print(display.Green, "Raw response: %s\n", string(respBody))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,7 +234,7 @@ func (c *Client) GetCurrentUser() (*UserResponse, error) {
|
|||||||
|
|
||||||
// RawRequest performs a raw HTTP request for debugging purposes
|
// RawRequest performs a raw HTTP request for debugging purposes
|
||||||
func (c *Client) RawRequest(method, path string, body string) error {
|
func (c *Client) RawRequest(method, path string, body string) error {
|
||||||
var bodyData interface{}
|
var bodyData any
|
||||||
if body != "" {
|
if body != "" {
|
||||||
if err := json.Unmarshal([]byte(body), &bodyData); err != nil {
|
if err := json.Unmarshal([]byte(body), &bodyData); err != nil {
|
||||||
// Try as raw string
|
// Try as raw string
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
// FILE: lixenwraith/chess/internal/client/commands/auth.go
|
// FILE: lixenwraith/chess/internal/client/command/auth.go
|
||||||
package commands
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"chess/internal/client/api"
|
"chess/internal/client/api"
|
||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
|
"chess/internal/client/session"
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Registry) registerAuthCommands() {
|
func (r *Registry) registerAuthCommands() {
|
||||||
@ -56,21 +54,11 @@ func (r *Registry) registerAuthCommands() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPassword(prompt string) (string, error) {
|
func registerHandler(s *session.Session, args []string) error {
|
||||||
fmt.Print(prompt)
|
|
||||||
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(bytePassword), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerHandler(s Session, args []string) error {
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Username: " + display.Reset)
|
display.Print(display.Yellow, "Username: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
username := strings.TrimSpace(scanner.Text())
|
username := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
@ -79,7 +67,7 @@ func registerHandler(s Session, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Email (optional): " + display.Reset)
|
display.Print(display.Yellow, "Email (optional): ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
email := strings.TrimSpace(scanner.Text())
|
email := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
@ -93,18 +81,18 @@ func registerHandler(s Session, args []string) error {
|
|||||||
s.SetUsername(resp.Username)
|
s.SetUsername(resp.Username)
|
||||||
c.SetToken(resp.Token)
|
c.SetToken(resp.Token)
|
||||||
|
|
||||||
fmt.Printf("%sRegistered successfully%s\n", display.Green, display.Reset)
|
display.Println(display.Green, "Registered successfully")
|
||||||
fmt.Printf("User ID: %s\n", resp.UserID)
|
fmt.Printf("User ID: %s\n", resp.UserID)
|
||||||
fmt.Printf("Username: %s\n", resp.Username)
|
fmt.Printf("Username: %s\n", resp.Username)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginHandler(s Session, args []string) error {
|
func loginHandler(s *session.Session, args []string) error {
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Username or Email: " + display.Reset)
|
display.Print(display.Yellow, "Username or Email: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
identifier := strings.TrimSpace(scanner.Text())
|
identifier := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
@ -123,27 +111,27 @@ func loginHandler(s Session, args []string) error {
|
|||||||
s.SetUsername(resp.Username)
|
s.SetUsername(resp.Username)
|
||||||
c.SetToken(resp.Token)
|
c.SetToken(resp.Token)
|
||||||
|
|
||||||
fmt.Printf("%sLogged in successfully%s\n", display.Green, display.Reset)
|
display.Println(display.Green, "Logged in successfully")
|
||||||
fmt.Printf("User ID: %s\n", resp.UserID)
|
fmt.Printf("User ID: %s\n", resp.UserID)
|
||||||
fmt.Printf("Username: %s\n", resp.Username)
|
fmt.Printf("Username: %s\n", resp.Username)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logoutHandler(s Session, args []string) error {
|
func logoutHandler(s *session.Session, args []string) error {
|
||||||
s.SetAuthToken("")
|
s.SetAuthToken("")
|
||||||
s.SetCurrentUser("")
|
s.SetCurrentUser("")
|
||||||
s.SetUsername("")
|
s.SetUsername("")
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
c.SetToken("")
|
c.SetToken("")
|
||||||
|
|
||||||
fmt.Printf("%sLogged out%s\n", display.Green, display.Reset)
|
display.Println(display.Green, "Logged out")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func whoamiHandler(s Session, args []string) error {
|
func whoamiHandler(s *session.Session, args []string) error {
|
||||||
if s.GetAuthToken() == "" {
|
if s.GetAuthToken() == "" {
|
||||||
fmt.Printf("%sNot authenticated%s\n", display.Yellow, display.Reset)
|
display.Println(display.Yellow, "Not authenticated")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +141,7 @@ func whoamiHandler(s Session, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%sCurrent User:%s\n", display.Cyan, display.Reset)
|
display.Println(display.Cyan, "Current User:")
|
||||||
fmt.Printf(" User ID: %s\n", user.UserID)
|
fmt.Printf(" User ID: %s\n", user.UserID)
|
||||||
fmt.Printf(" Username: %s\n", user.Username)
|
fmt.Printf(" Username: %s\n", user.Username)
|
||||||
if user.Email != "" {
|
if user.Email != "" {
|
||||||
@ -167,14 +155,14 @@ func whoamiHandler(s Session, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUserHandler(s Session, args []string) error {
|
func setUserHandler(s *session.Session, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("usage: user <userId>")
|
return fmt.Errorf("usage: user <userId>")
|
||||||
}
|
}
|
||||||
|
|
||||||
userID := args[0]
|
userID := args[0]
|
||||||
s.SetCurrentUser(userID)
|
s.SetCurrentUser(userID)
|
||||||
fmt.Printf("%sUser ID set to: %s%s\n", display.Cyan, userID, display.Reset)
|
display.Println(display.Cyan, "User ID set to: %s", userID)
|
||||||
fmt.Println("Note: This doesn't authenticate, just sets the ID for display")
|
fmt.Println("Note: This doesn't authenticate, just sets the ID for display")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1,5 +1,5 @@
|
|||||||
// FILE: lixenwraith/chess/internal/client/commands/debug.go
|
// FILE: lixenwraith/chess/internal/client/command/debug.go
|
||||||
package commands
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"chess/internal/client/api"
|
"chess/internal/client/api"
|
||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
|
"chess/internal/client/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Registry) registerDebugCommands() {
|
func (r *Registry) registerDebugCommands() {
|
||||||
@ -46,14 +47,14 @@ func (r *Registry) registerDebugCommands() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func healthHandler(s Session, args []string) error {
|
func healthHandler(s *session.Session, args []string) error {
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
resp, err := c.Health()
|
resp, err := c.Health()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%sServer Health:%s\n", display.Cyan, display.Reset)
|
display.Println(display.Cyan, "%sServer Health:%s")
|
||||||
fmt.Printf(" Status: %s\n", resp.Status)
|
fmt.Printf(" Status: %s\n", resp.Status)
|
||||||
// Convert Unix timestamp to readable time
|
// Convert Unix timestamp to readable time
|
||||||
t := time.Unix(resp.Time, 0)
|
t := time.Unix(resp.Time, 0)
|
||||||
@ -65,7 +66,7 @@ func healthHandler(s Session, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func urlHandler(s Session, args []string) error {
|
func urlHandler(s *session.Session, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
fmt.Printf("Current API URL: %s\n", s.GetAPIBaseURL())
|
fmt.Printf("Current API URL: %s\n", s.GetAPIBaseURL())
|
||||||
return nil
|
return nil
|
||||||
@ -80,11 +81,11 @@ func urlHandler(s Session, args []string) error {
|
|||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
c.SetBaseURL(url)
|
c.SetBaseURL(url)
|
||||||
|
|
||||||
fmt.Printf("%sAPI URL set to: %s%s\n", display.Cyan, url, display.Reset)
|
display.Println(display.Cyan, "API URL set to: %s", url)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawRequestHandler(s Session, args []string) error {
|
func rawRequestHandler(s *session.Session, args []string) error {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return fmt.Errorf("usage: raw <method> <path> [json-body]")
|
return fmt.Errorf("usage: raw <method> <path> [json-body]")
|
||||||
}
|
}
|
||||||
@ -101,7 +102,7 @@ func rawRequestHandler(s Session, args []string) error {
|
|||||||
return c.RawRequest(method, path, body)
|
return c.RawRequest(method, path, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearHandler(s Session, args []string) error {
|
func clearHandler(s *session.Session, args []string) error {
|
||||||
cmd := exec.Command("clear")
|
cmd := exec.Command("clear")
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
@ -1,5 +1,5 @@
|
|||||||
// FILE: lixenwraith/chess/internal/client/commands/game.go
|
// FILE: lixenwraith/chess/internal/client/command/game.go
|
||||||
package commands
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"chess/internal/client/api"
|
"chess/internal/client/api"
|
||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
|
"chess/internal/client/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Registry) registerGameCommands() {
|
func (r *Registry) registerGameCommands() {
|
||||||
@ -87,14 +88,14 @@ func (r *Registry) registerGameCommands() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGameHandler(s Session, args []string) error {
|
func newGameHandler(s *session.Session, args []string) error {
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.Client
|
||||||
|
|
||||||
fmt.Println("\n" + display.Cyan + "Creating new game..." + display.Reset)
|
display.Println(display.Cyan, "\nCreating new game...")
|
||||||
|
|
||||||
// White player
|
// White player
|
||||||
fmt.Print(display.Yellow + "White player type (h/c) [h]: " + display.Reset)
|
display.Print(display.Yellow, "White player type (h/c) [h]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
whiteType := strings.ToLower(strings.TrimSpace(scanner.Text()))
|
whiteType := strings.ToLower(strings.TrimSpace(scanner.Text()))
|
||||||
if whiteType == "" {
|
if whiteType == "" {
|
||||||
@ -105,7 +106,7 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
if whiteType == "c" {
|
if whiteType == "c" {
|
||||||
white.Type = 2
|
white.Type = 2
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Computer level (0-20) [10]: " + display.Reset)
|
display.Print(display.Yellow, "Computer level (0-20) [10]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
levelStr := strings.TrimSpace(scanner.Text())
|
levelStr := strings.TrimSpace(scanner.Text())
|
||||||
if levelStr == "" {
|
if levelStr == "" {
|
||||||
@ -115,7 +116,7 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
white.Level = level
|
white.Level = level
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Search time (100-10000ms) [1000]: " + display.Reset)
|
display.Print(display.Yellow, "Search time (100-10000ms) [1000]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
timeStr := strings.TrimSpace(scanner.Text())
|
timeStr := strings.TrimSpace(scanner.Text())
|
||||||
if timeStr == "" {
|
if timeStr == "" {
|
||||||
@ -126,8 +127,8 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Black player
|
// Black player (same pattern)
|
||||||
fmt.Print(display.Yellow + "Black player type (h/c) [h]: " + display.Reset)
|
display.Print(display.Yellow, "Black player type (h/c) [h]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
blackType := strings.ToLower(strings.TrimSpace(scanner.Text()))
|
blackType := strings.ToLower(strings.TrimSpace(scanner.Text()))
|
||||||
if blackType == "" {
|
if blackType == "" {
|
||||||
@ -138,7 +139,7 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
if blackType == "c" {
|
if blackType == "c" {
|
||||||
black.Type = 2
|
black.Type = 2
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Computer level (0-20) [10]: " + display.Reset)
|
display.Print(display.Yellow, "Computer level (0-20) [10]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
levelStr := strings.TrimSpace(scanner.Text())
|
levelStr := strings.TrimSpace(scanner.Text())
|
||||||
if levelStr == "" {
|
if levelStr == "" {
|
||||||
@ -148,7 +149,7 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
black.Level = level
|
black.Level = level
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(display.Yellow + "Search time (100-10000ms) [1000]: " + display.Reset)
|
display.Print(display.Yellow, "Search time (100-10000ms) [1000]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
timeStr := strings.TrimSpace(scanner.Text())
|
timeStr := strings.TrimSpace(scanner.Text())
|
||||||
if timeStr == "" {
|
if timeStr == "" {
|
||||||
@ -160,7 +161,7 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Starting position
|
// Starting position
|
||||||
fmt.Print(display.Yellow + "Starting position (FEN) [default]: " + display.Reset)
|
display.Print(display.Yellow, "Starting position (FEN) [default]: ")
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
fen := strings.TrimSpace(scanner.Text())
|
fen := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
@ -175,36 +176,31 @@ func newGameHandler(s Session, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetCurrentGame(resp.GameID)
|
s.CurrentGame = resp.GameID
|
||||||
s.SetLastMoveCount(len(resp.Moves))
|
s.LastMoveCount = len(resp.Moves)
|
||||||
s.SetGameState(resp)
|
s.CurrentGameState = resp
|
||||||
|
|
||||||
// Determine player color if authenticated
|
// Determine player color if authenticated
|
||||||
if s.GetCurrentUser() != "" {
|
if s.CurrentUser != "" {
|
||||||
if resp.Players.White.ID == s.GetCurrentUser() {
|
if resp.Players.White.ID == s.CurrentUser {
|
||||||
s.SetPlayerColor("w")
|
s.PlayerColor = "w"
|
||||||
} else if resp.Players.Black.ID == s.GetCurrentUser() {
|
} else if resp.Players.Black.ID == s.CurrentUser {
|
||||||
s.SetPlayerColor("b")
|
s.PlayerColor = "b"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%sGame created: %s%s\n", display.Green, resp.GameID, display.Reset)
|
display.Println(display.Green, "Game created: %s", resp.GameID)
|
||||||
fmt.Printf("%sCurrent game set to: %s%s\n", display.Cyan, resp.GameID, display.Reset)
|
display.Println(display.Cyan, "Current game set to: %s", resp.GameID)
|
||||||
|
|
||||||
// If white is computer, trigger first move
|
// If white is computer, inform user to trigger move
|
||||||
if white.Type == 2 {
|
if white.Type == 2 {
|
||||||
fmt.Printf("\n%sTriggering white computer move...%s\n", display.Magenta, display.Reset)
|
display.Println(display.Magenta, "\nWhite is computer. Use 'computer' or 'c' to trigger first move.")
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
_, err = c.MakeMove(resp.GameID, "cccc")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%sFailed to trigger computer move: %s%s\n", display.Red, err.Error(), display.Reset)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinGameHandler(s Session, args []string) error {
|
func joinGameHandler(s *session.Session, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("usage: join <gameId>")
|
return fmt.Errorf("usage: join <gameId>")
|
||||||
}
|
}
|
||||||
@ -239,74 +235,65 @@ func joinGameHandler(s Session, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveHandler(s Session, args []string) error {
|
func moveHandler(s *session.Session, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("usage: move <uci-move>")
|
return fmt.Errorf("usage: move <uci-move>")
|
||||||
}
|
}
|
||||||
|
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.CurrentGame
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
}
|
}
|
||||||
|
|
||||||
move := args[0]
|
move := args[0]
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.Client
|
||||||
|
|
||||||
resp, err := c.MakeMove(gameID, move)
|
resp, err := c.MakeMove(gameID, move)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetLastMoveCount(len(resp.Moves))
|
s.LastMoveCount = len(resp.Moves)
|
||||||
s.SetGameState(resp)
|
s.CurrentGameState = resp
|
||||||
fmt.Printf("%sMove accepted%s\n", display.Green, display.Reset)
|
display.Println(display.Green, "Move accepted")
|
||||||
|
|
||||||
// Check if computer should move
|
// Check if game ended
|
||||||
currentTurn := resp.Turn
|
switch resp.State {
|
||||||
var computerPlayer *api.PlayerInfo
|
case "checkmate":
|
||||||
if currentTurn == "w" && resp.Players.White.Type == 2 {
|
winner := "Black"
|
||||||
computerPlayer = &resp.Players.White
|
if resp.Turn == "b" { // Turn switches after move, so if black's turn after checkmate, white won
|
||||||
} else if currentTurn == "b" && resp.Players.Black.Type == 2 {
|
winner = "White"
|
||||||
computerPlayer = &resp.Players.Black
|
}
|
||||||
}
|
display.Println(display.Green, "\nCHECKMATE! %s wins!", winner)
|
||||||
|
case "stalemate":
|
||||||
|
display.Println(display.Yellow, "\nSTALEMATE! Game drawn.")
|
||||||
|
case "draw":
|
||||||
|
display.Println(display.Yellow, "\nDRAW! Game drawn.")
|
||||||
|
case "ongoing":
|
||||||
|
// Check if computer needs to play
|
||||||
|
currentTurn := resp.Turn
|
||||||
|
var computerPlayer *api.PlayerInfo
|
||||||
|
if currentTurn == "w" && resp.Players.White.Type == 2 {
|
||||||
|
computerPlayer = &resp.Players.White
|
||||||
|
} else if currentTurn == "b" && resp.Players.Black.Type == 2 {
|
||||||
|
computerPlayer = &resp.Players.Black
|
||||||
|
}
|
||||||
|
|
||||||
if computerPlayer != nil && resp.State == "ongoing" {
|
if computerPlayer != nil {
|
||||||
fmt.Printf("\n%sComputer's turn, triggering move...%s\n", display.Magenta, display.Reset)
|
display.Println(display.Magenta, "\nComputer's turn. Use 'computer' or 'c' to trigger move.")
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
resp2, err := c.MakeMove(gameID, "cccc")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%sFailed to trigger computer move: %s%s\n", display.Red, err.Error(), display.Reset)
|
|
||||||
} else if resp2.State == "pending" {
|
|
||||||
fmt.Printf("%sComputer is thinking...%s\n", display.Magenta, display.Reset)
|
|
||||||
// Wait for completion
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
resp3, err := c.GetGame(gameID)
|
|
||||||
if err == nil && resp3.State != "pending" {
|
|
||||||
s.SetLastMoveCount(len(resp3.Moves))
|
|
||||||
if resp3.LastMove != nil {
|
|
||||||
fmt.Printf("%sComputer played: %s%s", display.Magenta, resp3.LastMove.Move, display.Reset)
|
|
||||||
if resp3.LastMove.Depth > 0 {
|
|
||||||
fmt.Printf(" (depth %d, score %d)", resp3.LastMove.Depth, resp3.LastMove.Score)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func computerMoveHandler(s Session, args []string) error {
|
func computerMoveHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.CurrentGame
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := s.GetClient().(*api.Client)
|
c := s.Client
|
||||||
|
|
||||||
resp, err := c.MakeMove(gameID, "cccc")
|
resp, err := c.MakeMove(gameID, "cccc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -314,34 +301,50 @@ func computerMoveHandler(s Session, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.State == "pending" {
|
if resp.State == "pending" {
|
||||||
fmt.Printf("%sComputer is thinking...%s\n", display.Magenta, display.Reset)
|
display.Println(display.Magenta, "Computer is thinking...")
|
||||||
|
|
||||||
// Poll for completion
|
// Poll for completion
|
||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
resp2, err := c.GetGame(gameID)
|
resp2, err := c.GetGame(gameID)
|
||||||
if err == nil && resp2.State != "pending" {
|
if err == nil && resp2.State != "pending" {
|
||||||
s.SetLastMoveCount(len(resp2.Moves))
|
s.LastMoveCount = len(resp2.Moves)
|
||||||
s.SetGameState(resp2)
|
s.CurrentGameState = resp2
|
||||||
if resp2.LastMove != nil {
|
if resp2.LastMove != nil {
|
||||||
fmt.Printf("%sComputer played: %s%s", display.Magenta, resp2.LastMove.Move, display.Reset)
|
display.Print(display.Magenta, "Computer played: %s", resp2.LastMove.Move)
|
||||||
if resp2.LastMove.Depth > 0 {
|
if resp2.LastMove.Depth > 0 {
|
||||||
fmt.Printf(" (depth %d, score %d)", resp2.LastMove.Depth, resp2.LastMove.Score)
|
fmt.Printf(" (depth %d, score %d)", resp2.LastMove.Depth, resp2.LastMove.Score)
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if game ended after computer move
|
||||||
|
switch resp2.State {
|
||||||
|
case "checkmate":
|
||||||
|
winner := "Black"
|
||||||
|
if resp2.Turn == "b" {
|
||||||
|
winner = "White"
|
||||||
|
}
|
||||||
|
display.Println(display.Green, "\nCHECKMATE! %s wins!", winner)
|
||||||
|
case "stalemate":
|
||||||
|
display.Println(display.Yellow, "\nSTALEMATE! Game drawn.")
|
||||||
|
case "draw":
|
||||||
|
display.Println(display.Yellow, "\nDRAW! Game drawn.")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("timeout waiting for computer move")
|
return fmt.Errorf("timeout waiting for computer move")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetLastMoveCount(len(resp.Moves))
|
s.LastMoveCount = len(resp.Moves)
|
||||||
fmt.Printf("%sMove triggered%s\n", display.Green, display.Reset)
|
s.CurrentGameState = resp
|
||||||
|
display.Println(display.Green, "Move triggered")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func undoHandler(s Session, args []string) error {
|
func undoHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.GetCurrentGame()
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
@ -363,11 +366,12 @@ func undoHandler(s Session, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.SetLastMoveCount(len(resp.Moves))
|
s.SetLastMoveCount(len(resp.Moves))
|
||||||
fmt.Printf("%sUndid %d move(s)%s\n", display.Green, count, display.Reset)
|
s.SetGameState(resp)
|
||||||
|
display.Println(display.Green, "Undid %d move(s)", count)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func showBoardHandler(s Session, args []string) error {
|
func showBoardHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.GetCurrentGame()
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
@ -431,7 +435,7 @@ func showBoardHandler(s Session, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gameStateHandler(s Session, args []string) error {
|
func gameStateHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.GetCurrentGame()
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
@ -446,13 +450,13 @@ func gameStateHandler(s Session, args []string) error {
|
|||||||
s.SetLastMoveCount(len(resp.Moves))
|
s.SetLastMoveCount(len(resp.Moves))
|
||||||
|
|
||||||
// Pretty print JSON
|
// Pretty print JSON
|
||||||
fmt.Printf("%sGame State:%s\n", display.Cyan, display.Reset)
|
display.Println(display.Cyan, "Game State:")
|
||||||
display.PrettyPrintJSON(resp)
|
display.PrettyPrintJSON(resp)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteGameHandler(s Session, args []string) error {
|
func deleteGameHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.GetCurrentGame()
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
gameID = args[0]
|
gameID = args[0]
|
||||||
@ -477,7 +481,7 @@ func deleteGameHandler(s Session, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pollHandler(s Session, args []string) error {
|
func pollHandler(s *session.Session, args []string) error {
|
||||||
gameID := s.GetCurrentGame()
|
gameID := s.GetCurrentGame()
|
||||||
if gameID == "" {
|
if gameID == "" {
|
||||||
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
return fmt.Errorf("no current game, use 'new' or 'join <gameId>'")
|
||||||
@ -486,9 +490,8 @@ func pollHandler(s Session, args []string) error {
|
|||||||
c := s.GetClient().(*api.Client)
|
c := s.GetClient().(*api.Client)
|
||||||
moveCount := s.GetLastMoveCount()
|
moveCount := s.GetLastMoveCount()
|
||||||
|
|
||||||
fmt.Printf("%sLong-polling for updates (move count: %d)...%s\n",
|
display.Println(display.Cyan, "Long-polling for updates (move count: %d)...", moveCount)
|
||||||
display.Cyan, moveCount, display.Reset)
|
display.Println(display.Cyan, "This may take up to 25 seconds")
|
||||||
fmt.Printf("%sThis may take up to 25 seconds%s\n", display.Cyan, display.Reset)
|
|
||||||
|
|
||||||
resp, err := c.GetGameWithPoll(gameID, moveCount)
|
resp, err := c.GetGameWithPoll(gameID, moveCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -499,12 +502,12 @@ func pollHandler(s Session, args []string) error {
|
|||||||
s.SetGameState(resp)
|
s.SetGameState(resp)
|
||||||
|
|
||||||
if len(resp.Moves) > moveCount {
|
if len(resp.Moves) > moveCount {
|
||||||
fmt.Printf("%sGame updated! New moves detected%s\n", display.Green, display.Reset)
|
display.Println(display.Green, "Game updated! New moves detected")
|
||||||
if resp.LastMove != nil {
|
if resp.LastMove != nil {
|
||||||
fmt.Printf("Last move: %s\n", resp.LastMove.Move)
|
fmt.Printf("Last move: %s\n", resp.LastMove.Move)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%sNo updates (timeout)%s\n", display.Yellow, display.Reset)
|
display.Println(display.Yellow, "No updates (timeout)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
20
internal/client/command/pass_unix.go
Normal file
20
internal/client/command/pass_unix.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// FILE: internal/client/command/auth_unix.go
|
||||||
|
//go:build !js && !wasm
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readPassword(prompt string) (string, error) {
|
||||||
|
fmt.Print(prompt)
|
||||||
|
bytePassword, err := term.ReadPassword(0) // 0 is stdin
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(bytePassword), nil
|
||||||
|
}
|
||||||
29
internal/client/command/pass_wasm.go
Normal file
29
internal/client/command/pass_wasm.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// FILE: lixenwraith/chess/internal/client/command/auth_wasm.go
|
||||||
|
//go:build js && wasm
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"chess/internal/client/display"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readPassword(prompt string) (string, error) {
|
||||||
|
display.Println(display.Red, "(warning: password visible in browser)")
|
||||||
|
display.Print(display.Yellow, prompt)
|
||||||
|
|
||||||
|
// In WASM/browser, password masking must be handled by JavaScript/xterm.js
|
||||||
|
// This is fallback with visible input
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
if scanner.Scan() {
|
||||||
|
return strings.TrimSpace(scanner.Text()), nil
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no input received")
|
||||||
|
}
|
||||||
@ -1,53 +1,33 @@
|
|||||||
// FILE: lixenwraith/chess/internal/client/commands/registry.go
|
// FILE: lixenwraith/chess/internal/client/command/registry.go
|
||||||
package commands
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"chess/internal/client/api"
|
"chess/internal/client/api"
|
||||||
"chess/internal/client/display"
|
"chess/internal/client/display"
|
||||||
|
"chess/internal/client/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session interface {
|
|
||||||
GetAPIBaseURL() string
|
|
||||||
SetAPIBaseURL(string)
|
|
||||||
GetCurrentGame() string
|
|
||||||
SetCurrentGame(string)
|
|
||||||
GetCurrentUser() string
|
|
||||||
SetCurrentUser(string)
|
|
||||||
GetAuthToken() string
|
|
||||||
SetAuthToken(string)
|
|
||||||
GetUsername() string
|
|
||||||
SetUsername(string)
|
|
||||||
GetLastMoveCount() int
|
|
||||||
SetLastMoveCount(int)
|
|
||||||
GetClient() interface{}
|
|
||||||
IsVerbose() bool
|
|
||||||
SetGameState(interface{})
|
|
||||||
SetPlayerColor(string)
|
|
||||||
GetPlayerColor() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command defines a client command with its handler
|
// Command defines a client command with its handler
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name string
|
Name string
|
||||||
ShortName string
|
ShortName string
|
||||||
Description string
|
Description string
|
||||||
Usage string
|
Usage string
|
||||||
Handler func(Session, []string) error
|
Handler func(*session.Session, []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
session Session
|
session *session.Session
|
||||||
commands map[string]*Command
|
commands map[string]*Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry manages command registration and execution
|
// Registry manages command registration and execution
|
||||||
func NewRegistry(session Session) *Registry {
|
func NewRegistry(s *session.Session) *Registry {
|
||||||
r := &Registry{
|
r := &Registry{
|
||||||
session: session,
|
session: s,
|
||||||
commands: make(map[string]*Command),
|
commands: make(map[string]*Command),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +45,7 @@ func NewRegistry(session Session) *Registry {
|
|||||||
Handler: r.helpHandler,
|
Handler: r.helpHandler,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Exit command
|
// Exit command (handled in main loop, but registered for help display)
|
||||||
r.Register(&Command{
|
r.Register(&Command{
|
||||||
Name: "exit",
|
Name: "exit",
|
||||||
ShortName: "x",
|
ShortName: "x",
|
||||||
@ -95,8 +75,8 @@ func (r *Registry) Execute(input string) {
|
|||||||
|
|
||||||
cmd, exists := r.commands[cmdName]
|
cmd, exists := r.commands[cmdName]
|
||||||
if !exists {
|
if !exists {
|
||||||
fmt.Printf("%sUnknown command: %s%s\n", display.Red, cmdName, display.Reset)
|
display.Println(display.Red, "Unknown command: %s", cmdName)
|
||||||
fmt.Printf("Type 'help' for available commands\n")
|
display.Println(display.Reset, "Type 'help' for available commands")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,27 +86,29 @@ func (r *Registry) Execute(input string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Handler(r.session, args); err != nil {
|
if err := cmd.Handler(r.session, args); err != nil {
|
||||||
fmt.Printf("%sError: %s%s\n", display.Red, err.Error(), display.Reset)
|
display.Println(display.Red, "Error: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) helpHandler(s Session, args []string) error {
|
func (r *Registry) helpHandler(s *session.Session, args []string) error {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
// Show help for specific command
|
// Show help for specific command
|
||||||
cmd, exists := r.commands[args[0]]
|
cmd, exists := r.commands[args[0]]
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("unknown command: %s", args[0])
|
return fmt.Errorf("unknown command: %s", args[0])
|
||||||
}
|
}
|
||||||
fmt.Printf("\n%s%s%s - %s\n", display.Cyan, cmd.Name, display.Reset, cmd.Description)
|
fmt.Println()
|
||||||
|
display.Print(display.Cyan, cmd.Name)
|
||||||
|
display.Println(display.Reset, " - %s", cmd.Description)
|
||||||
if cmd.ShortName != "" {
|
if cmd.ShortName != "" {
|
||||||
fmt.Printf("Short form: %s%s%s\n", display.Cyan, cmd.ShortName, display.Reset)
|
display.Println(display.Cyan, "Short form: %s", cmd.ShortName)
|
||||||
}
|
}
|
||||||
fmt.Printf("Usage: %s\n", cmd.Usage)
|
fmt.Printf("Usage: %s\n", cmd.Usage)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show all commands
|
// Show all commands
|
||||||
fmt.Printf("\n%sAvailable Commands:%s\n\n", display.Cyan, display.Reset)
|
display.Println(display.Cyan, "\nAvailable Commands:\n")
|
||||||
|
|
||||||
// Group commands
|
// Group commands
|
||||||
type cmdInfo struct {
|
type cmdInfo struct {
|
||||||
@ -165,7 +147,7 @@ func (r *Registry) helpHandler(s Session, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printCommandGroup := func(title string, cmds []cmdInfo) {
|
printCommandGroup := func(title string, cmds []cmdInfo) {
|
||||||
fmt.Printf("%s%s:%s\n", display.Yellow, title, display.Reset)
|
display.Println(display.Yellow, "%s:", title)
|
||||||
for _, info := range cmds {
|
for _, info := range cmds {
|
||||||
if cmd, exists := r.commands[info.name]; exists {
|
if cmd, exists := r.commands[info.name]; exists {
|
||||||
shortPart := ""
|
shortPart := ""
|
||||||
@ -175,20 +157,20 @@ func (r *Registry) helpHandler(s Session, args []string) error {
|
|||||||
fmt.Printf(" %s%-10s %s\n", shortPart, cmd.Name, cmd.Description)
|
fmt.Printf(" %s%-10s %s\n", shortPart, cmd.Name, cmd.Description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
printCommandGroup("Game Commands", gameCommands)
|
printCommandGroup("Game Commands", gameCommands)
|
||||||
fmt.Println()
|
|
||||||
printCommandGroup("Auth Commands", authCommands)
|
printCommandGroup("Auth Commands", authCommands)
|
||||||
fmt.Println()
|
|
||||||
printCommandGroup("Utility Commands", utilCommands)
|
printCommandGroup("Utility Commands", utilCommands)
|
||||||
|
|
||||||
fmt.Printf("\nType 'help <command>' for detailed usage\n")
|
display.Println(display.Reset, "Type 'help <command>' for detailed usage")
|
||||||
fmt.Printf("Add '-v' to any command for verbose output\n")
|
display.Println(display.Reset, "Add '-v' to any command for verbose output\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func exitHandler(s Session, args []string) error {
|
|
||||||
fmt.Printf("%sGoodbye!%s\n", display.Cyan, display.Reset)
|
func exitHandler(s *session.Session, args []string) error {
|
||||||
os.Exit(0)
|
// Exit is handled in main loop, this is just for consistency
|
||||||
|
display.Println(display.Cyan, "Goodbye!\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -22,23 +22,23 @@ func RenderBoard(asciiBoard string) {
|
|||||||
switch {
|
switch {
|
||||||
case char >= 'a' && char <= 'h' && isRankLine:
|
case char >= 'a' && char <= 'h' && isRankLine:
|
||||||
// File letters - Cyan
|
// File letters - Cyan
|
||||||
fmt.Printf("%s%c%s", Cyan, char, Reset)
|
Print(Cyan, "%c", char)
|
||||||
case char >= 'A' && char <= 'Z':
|
case char >= 'A' && char <= 'Z':
|
||||||
// White pieces - Blue
|
// White pieces - Blue
|
||||||
fmt.Printf("%s%c%s", Blue, char, Reset)
|
Print(Blue, "%c", char)
|
||||||
case char >= 'a' && char <= 'z' && !isRankLine:
|
case char >= 'a' && char <= 'z' && !isRankLine:
|
||||||
// Black pieces - Red
|
// Black pieces - Red
|
||||||
fmt.Printf("%s%c%s", Red, char, Reset)
|
Print(Red, "%c", char)
|
||||||
case char == '.':
|
case char == '.':
|
||||||
// Empty squares
|
// Empty squares
|
||||||
fmt.Printf(".")
|
Print(White, ".")
|
||||||
case char >= '1' && char <= '8':
|
case char >= '1' && char <= '8':
|
||||||
// Rank numbers - Cyan
|
// Rank numbers - Cyan
|
||||||
fmt.Printf("%s%c%s", Cyan, char, Reset)
|
Print(Cyan, "%c", char)
|
||||||
case char == ' ':
|
case char == ' ':
|
||||||
fmt.Printf(" ")
|
Print(Reset, " ")
|
||||||
default:
|
default:
|
||||||
fmt.Printf("%c", char)
|
Print(Reset, "%c", char)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
// FILE: lixenwraith/chess/internal/client/display/colors.go
|
// FILE: lixenwraith/chess/internal/client/display/colors.go
|
||||||
package display
|
package display
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// Terminal color codes
|
// Terminal color codes
|
||||||
const (
|
const (
|
||||||
Reset = "\033[0m"
|
Reset = "\033[0m"
|
||||||
@ -13,6 +18,35 @@ const (
|
|||||||
White = "\033[37m"
|
White = "\033[37m"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// C wraps text with color and reset codes
|
||||||
|
func C(color, text string) string {
|
||||||
|
return color + text + Reset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print outputs colored text immediately
|
||||||
|
func Print(color, format string, args ...any) {
|
||||||
|
fmt.Printf(C(color, format), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println outputs colored text with newline
|
||||||
|
func Println(color, format string, args ...any) {
|
||||||
|
fmt.Println(C(color, fmt.Sprintf(format, args...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build creates a multi-colored string
|
||||||
|
type Builder struct {
|
||||||
|
parts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Add(color, text string) *Builder {
|
||||||
|
b.parts = append(b.parts, C(color, text))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) String() string {
|
||||||
|
return strings.Join(b.parts, "")
|
||||||
|
}
|
||||||
|
|
||||||
// Prompt returns a colored prompt string
|
// Prompt returns a colored prompt string
|
||||||
func Prompt(text string) string {
|
func Prompt(text string) string {
|
||||||
return Yellow + text + Yellow + " > " + Reset
|
return Yellow + text + Yellow + " > " + Reset
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PrettyPrintJSON prints formatted JSON
|
// PrettyPrintJSON prints formatted JSON
|
||||||
func PrettyPrintJSON(v interface{}) {
|
func PrettyPrintJSON(v any) {
|
||||||
data, err := json.MarshalIndent(v, "", " ")
|
data, err := json.MarshalIndent(v, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%sError formatting JSON: %s%s\n", Red, err.Error(), Reset)
|
Print(Red, "Error formatting JSON: %s\n", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println(string(data))
|
fmt.Println(string(data))
|
||||||
|
|||||||
@ -33,9 +33,9 @@ func (s *Session) GetUsername() string { return s.Username }
|
|||||||
func (s *Session) SetUsername(name string) { s.Username = name }
|
func (s *Session) SetUsername(name string) { s.Username = name }
|
||||||
func (s *Session) GetLastMoveCount() int { return s.LastMoveCount }
|
func (s *Session) GetLastMoveCount() int { return s.LastMoveCount }
|
||||||
func (s *Session) SetLastMoveCount(count int) { s.LastMoveCount = count }
|
func (s *Session) SetLastMoveCount(count int) { s.LastMoveCount = count }
|
||||||
func (s *Session) GetClient() interface{} { return s.Client }
|
func (s *Session) GetClient() any { return s.Client }
|
||||||
func (s *Session) IsVerbose() bool { return s.Verbose }
|
func (s *Session) IsVerbose() bool { return s.Verbose }
|
||||||
func (s *Session) SetGameState(game interface{}) {
|
func (s *Session) SetGameState(game any) {
|
||||||
if g, ok := game.(*api.GameResponse); ok {
|
if g, ok := game.(*api.GameResponse); ok {
|
||||||
s.CurrentGameState = g
|
s.CurrentGameState = g
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,10 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"chess/internal/server/core"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"chess/internal/server/core"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,12 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"chess/internal/server/core"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"chess/internal/server/core"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -25,7 +26,7 @@ func validationMiddleware(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// Determine request type based on path
|
// Determine request type based on path
|
||||||
path := c.Path()
|
path := c.Path()
|
||||||
var requestType interface{}
|
var requestType any
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasSuffix(path, "/games") && method == fiber.MethodPost:
|
case strings.HasSuffix(path, "/games") && method == fiber.MethodPost:
|
||||||
|
|||||||
@ -22,15 +22,15 @@ const (
|
|||||||
type Command struct {
|
type Command struct {
|
||||||
Type CommandType
|
Type CommandType
|
||||||
UserID string
|
UserID string
|
||||||
GameID string // For game-specific commands
|
GameID string // For game-specific commands
|
||||||
Args interface{} // Command-specific arguments
|
Args any // Command-specific arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessorResponse wraps the response with metadata
|
// ProcessorResponse wraps the response with metadata
|
||||||
type ProcessorResponse struct {
|
type ProcessorResponse struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Pending bool `json:"pending,omitempty"` // For async operations
|
Pending bool `json:"pending,omitempty"` // For async operations
|
||||||
Data interface{} `json:"data,omitempty"`
|
Data any `json:"data,omitempty"`
|
||||||
Error *core.ErrorResponse `json:"error,omitempty"`
|
Error *core.ErrorResponse `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -93,7 +93,7 @@ func (s *Store) QueryGames(gameID, playerID string) ([]GameRecord, error) {
|
|||||||
start_time_utc
|
start_time_utc
|
||||||
FROM games WHERE 1=1`
|
FROM games WHERE 1=1`
|
||||||
|
|
||||||
var args []interface{}
|
var args []any
|
||||||
|
|
||||||
// Handle gameID filtering
|
// Handle gameID filtering
|
||||||
if gameID != "" && gameID != "*" {
|
if gameID != "" && gameID != "*" {
|
||||||
|
|||||||
@ -45,7 +45,7 @@ func (s *Store) CreateUser(record UserRecord) error {
|
|||||||
func (s *Store) userExists(tx *sql.Tx, username, email string) (bool, error) {
|
func (s *Store) userExists(tx *sql.Tx, username, email string) (bool, error) {
|
||||||
var count int
|
var count int
|
||||||
query := `SELECT COUNT(*) FROM users WHERE username = ? COLLATE NOCASE`
|
query := `SELECT COUNT(*) FROM users WHERE username = ? COLLATE NOCASE`
|
||||||
args := []interface{}{username}
|
args := []any{username}
|
||||||
|
|
||||||
if email != "" {
|
if email != "" {
|
||||||
query = `SELECT COUNT(*) FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE`
|
query = `SELECT COUNT(*) FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE`
|
||||||
|
|||||||
Reference in New Issue
Block a user