Skip to content

Commit 3b86554

Browse files
feat: improve client proxy UX
1 parent 9f240c1 commit 3b86554

13 files changed

Lines changed: 714 additions & 69 deletions

File tree

.env.sample

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
# PLGenesis Server Configuration
2-
SERVER_URL="http://localhost:8080"
3-
SERVER_HOST="localhost"
4-
SERVER_PORT=8080
1+
# Local Parity client proxy configuration
2+
SERVER_HOST="127.0.0.1"
3+
SERVER_PORT=8090
54
SERVER_ENDPOINT="/api/v1"
65

76
# Blockchain Network Configuration

README.md

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ cp .env.sample .env
105105
2. Edit `.env` with your settings:
106106

107107
```bash
108-
# Server Configuration
109-
SERVER_HOST="0.0.0.0"
110-
SERVER_PORT=3000
111-
SERVER_ENDPOINT="/api"
108+
# Local client proxy configuration
109+
SERVER_HOST="127.0.0.1"
110+
SERVER_PORT=8090
111+
SERVER_ENDPOINT="/api/v1"
112112

113113
# Blockchain Network Configuration
114114
BLOCKCHAIN_RPC=https://your-blockchain-node.com
@@ -351,7 +351,7 @@ parity-client llm list --limit 10
351351
Submit compute tasks to the network:
352352

353353
```bash
354-
curl -X POST http://localhost:3000/api/tasks \
354+
curl -X POST http://localhost:8090/api/v1/tasks \
355355
-H "Content-Type: application/json" \
356356
-d '{
357357
"image": "alpine:latest",
@@ -361,18 +361,39 @@ curl -X POST http://localhost:3000/api/tasks \
361361
}'
362362
```
363363

364+
If the image only exists on your local machine and needs to be uploaded first, use the dedicated local upload route instead:
365+
366+
```bash
367+
curl -X POST http://localhost:8090/api/v1/tasks/local \
368+
-H "Content-Type: application/json" \
369+
-d '{
370+
"image": "my-local-image:latest",
371+
"command": ["echo", "Hello from a local image"],
372+
"title": "Local Task",
373+
"description": "This image is uploaded by the client"
374+
}'
375+
```
376+
364377
### Health Monitoring
365378

366379
The parity client provides comprehensive health monitoring endpoints for operational visibility:
367380

381+
#### Local Proxy Guide
382+
383+
```bash
384+
curl http://localhost:8090/
385+
```
386+
387+
The root endpoint returns the local proxy routes, example task submissions, and the upstream server target so you can confirm whether you should use `/api/v1/tasks` or `/api/v1/tasks/local`.
388+
368389
#### Basic Health Check
369390

370391
```bash
371392
# Command line health check
372393
parity-client health
373394

374395
# HTTP health check
375-
curl http://localhost:3000/health
396+
curl http://localhost:8090/health
376397
```
377398

378399
#### Detailed Health Information
@@ -382,17 +403,17 @@ curl http://localhost:3000/health
382403
parity-client health --detailed
383404

384405
# HTTP detailed health check
385-
curl http://localhost:3000/health/detailed
406+
curl http://localhost:8090/health/detailed
386407
```
387408

388409
#### Kubernetes-style Probes
389410

390411
```bash
391412
# Readiness probe (for Kubernetes deployments)
392-
curl http://localhost:3000/health/ready
413+
curl http://localhost:8090/health/ready
393414

394415
# Liveness probe (for Kubernetes deployments)
395-
curl http://localhost:3000/health/live
416+
curl http://localhost:8090/health/live
396417
```
397418

398419
#### Health Check Options
@@ -451,7 +472,7 @@ Detailed health check response:
451472
},
452473
"config": {
453474
"server_host": "0.0.0.0",
454-
"server_port": 3000,
475+
"server_port": 8090,
455476
"blockchain_rpc": "https://your-blockchain-node.com",
456477
"ipfs_endpoint": "http://localhost:5001",
457478
"runner_url": "http://localhost:8080"
@@ -541,36 +562,37 @@ parity-client fl create-session \
541562

542563
| Method | Endpoint | Description |
543564
| ------ | ----------------------- | ---------------------------------- |
544-
| GET | `/api/llm/models` | List all available LLM models |
545-
| POST | `/api/llm/prompts` | Submit a prompt for LLM processing |
546-
| GET | `/api/llm/prompts/{id}` | Get prompt status and response |
547-
| GET | `/api/llm/prompts` | List recent prompts |
565+
| GET | `/api/v1/llm/models` | List all available LLM models |
566+
| POST | `/api/v1/llm/prompts` | Submit a prompt for LLM processing |
567+
| GET | `/api/v1/llm/prompts/{id}` | Get prompt status and response |
568+
| GET | `/api/v1/llm/prompts` | List recent prompts |
548569

549570
### Task Endpoints
550571

551572
| Method | Endpoint | Description |
552573
| ------ | ---------------------- | ---------------- |
553-
| POST | /api/tasks | Create task |
554-
| GET | /api/tasks | List all tasks |
555-
| GET | /api/tasks/{id} | Get task details |
556-
| GET | /api/tasks/{id}/status | Get task status |
557-
| GET | /api/tasks/{id}/logs | Get task logs |
574+
| POST | `/api/v1/tasks` | Create a registry-backed task |
575+
| POST | `/api/v1/tasks/local` | Upload a local Docker image and create a task |
576+
| GET | `/api/v1/tasks` | List all tasks |
577+
| GET | `/api/v1/tasks/{id}` | Get task details |
578+
| GET | `/api/v1/tasks/{id}/status` | Get task status |
579+
| GET | `/api/v1/tasks/{id}/logs` | Get task logs |
558580

559581
### Storage Endpoints
560582

561583
| Method | Endpoint | Description |
562584
| ------ | --------------------------- | ---------------------------- |
563-
| POST | /api/storage/upload | Upload file to IPFS |
564-
| GET | /api/storage/download/{cid} | Download file by CID |
565-
| GET | /api/storage/info/{cid} | Get file information |
566-
| POST | /api/storage/pin/{cid} | Pin file to IPFS |
585+
| POST | `/api/v1/storage/upload` | Upload file to IPFS |
586+
| GET | `/api/v1/storage/download/{cid}` | Download file by CID |
587+
| GET | `/api/v1/storage/info/{cid}` | Get file information |
588+
| POST | `/api/v1/storage/pin/{cid}` | Pin file to IPFS |
567589

568590
### Runner Endpoints
569591

570592
| Method | Endpoint | Description |
571593
| ------ | ---------------------- | --------------- |
572-
| POST | /api/runners | Register runner |
573-
| POST | /api/runners/heartbeat | Send heartbeat |
594+
| POST | `/api/v1/runners` | Register runner |
595+
| POST | `/api/v1/runners/heartbeat` | Send heartbeat |
574596

575597
### Health & Status Endpoints
576598

internal/commands/health.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7+
"strings"
78
"time"
89

910
"github.com/spf13/cobra"
1011
"github.com/theblitlabs/gologger"
12+
"github.com/theblitlabs/parity-client/internal/config"
13+
"github.com/theblitlabs/parity-client/internal/utils"
1114
)
1215

1316
var healthCmd = &cobra.Command{
@@ -25,18 +28,19 @@ var (
2528

2629
func init() {
2730
healthCmd.Flags().BoolVar(&healthDetailed, "detailed", false, "Get detailed health information")
28-
healthCmd.Flags().StringVar(&healthEndpoint, "endpoint", "http://localhost:3000", "Health check endpoint URL")
31+
healthCmd.Flags().StringVar(&healthEndpoint, "endpoint", "", "Health check endpoint URL (defaults to the configured local client address)")
2932
healthCmd.Flags().DurationVar(&healthTimeout, "timeout", 10*time.Second, "Timeout for health check request")
3033
}
3134

3235
func runHealthCheck(cmd *cobra.Command, args []string) {
3336
logger := gologger.Get().With().Str("component", "health-cmd").Logger()
37+
endpoint := resolveHealthEndpoint(cmd)
3438

3539
var url string
3640
if healthDetailed {
37-
url = fmt.Sprintf("%s/health/detailed", healthEndpoint)
41+
url = fmt.Sprintf("%s/health/detailed", endpoint)
3842
} else {
39-
url = fmt.Sprintf("%s/health", healthEndpoint)
43+
url = fmt.Sprintf("%s/health", endpoint)
4044
}
4145

4246
logger.Info().Str("url", url).Msg("Checking health status")
@@ -75,6 +79,7 @@ func runHealthCheck(cmd *cobra.Command, args []string) {
7579
}
7680

7781
fmt.Printf("✅ Health check successful\n")
82+
fmt.Printf("Endpoint: %s\n", endpoint)
7883
fmt.Printf("Status: %s\n", result["status"])
7984
fmt.Printf("Timestamp: %s\n", result["timestamp"])
8085
fmt.Printf("Version: %s\n", result["version"])
@@ -84,3 +89,41 @@ func runHealthCheck(cmd *cobra.Command, args []string) {
8489
fmt.Println(string(prettyJSON))
8590
}
8691
}
92+
93+
func resolveHealthEndpoint(cmd *cobra.Command) string {
94+
if cmd != nil && cmd.Flags().Changed("endpoint") && strings.TrimSpace(healthEndpoint) != "" {
95+
return strings.TrimRight(strings.TrimSpace(healthEndpoint), "/")
96+
}
97+
98+
configPath := ""
99+
if cmd != nil {
100+
if flagValue, err := cmd.Flags().GetString("config-path"); err == nil {
101+
configPath = flagValue
102+
}
103+
}
104+
if configPath == "" {
105+
configPath = utils.GetDefaultConfigPath()
106+
}
107+
108+
cfg, err := config.NewConfigManager(configPath).GetConfig()
109+
if err != nil || cfg == nil {
110+
return "http://127.0.0.1:8090"
111+
}
112+
113+
return strings.TrimRight(localHealthBaseURL(cfg), "/")
114+
}
115+
116+
func localHealthBaseURL(cfg *config.Config) string {
117+
host := cfg.Server.Host
118+
switch host {
119+
case "", "0.0.0.0", "::":
120+
host = "127.0.0.1"
121+
}
122+
123+
port := cfg.Server.Port
124+
if port == 0 {
125+
port = 8090
126+
}
127+
128+
return fmt.Sprintf("http://%s:%d", host, port)
129+
}

internal/commands/health_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package commands
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func TestResolveHealthEndpointUsesExplicitFlag(t *testing.T) {
12+
t.Cleanup(func() {
13+
healthEndpoint = ""
14+
})
15+
16+
cmd := &cobra.Command{Use: "health"}
17+
cmd.Flags().String("endpoint", "", "")
18+
if err := cmd.Flags().Set("endpoint", "http://example.com:9000/"); err != nil {
19+
t.Fatalf("Set() error = %v", err)
20+
}
21+
22+
healthEndpoint = "http://example.com:9000/"
23+
24+
if got := resolveHealthEndpoint(cmd); got != "http://example.com:9000" {
25+
t.Fatalf("resolveHealthEndpoint() = %q, want %q", got, "http://example.com:9000")
26+
}
27+
}
28+
29+
func TestResolveHealthEndpointUsesConfigPath(t *testing.T) {
30+
t.Cleanup(func() {
31+
healthEndpoint = ""
32+
})
33+
34+
tempDir := t.TempDir()
35+
configPath := filepath.Join(tempDir, "config.yaml")
36+
configBody := []byte("SERVER:\n HOST: 0.0.0.0\n PORT: 8123\n")
37+
if err := os.WriteFile(configPath, configBody, 0o600); err != nil {
38+
t.Fatalf("WriteFile() error = %v", err)
39+
}
40+
41+
cmd := &cobra.Command{Use: "health"}
42+
cmd.Flags().String("endpoint", "", "")
43+
cmd.Flags().String("config-path", "", "")
44+
if err := cmd.Flags().Set("config-path", configPath); err != nil {
45+
t.Fatalf("Set() error = %v", err)
46+
}
47+
48+
if got := resolveHealthEndpoint(cmd); got != "http://127.0.0.1:8123" {
49+
t.Fatalf("resolveHealthEndpoint() = %q, want %q", got, "http://127.0.0.1:8123")
50+
}
51+
}
52+
53+
func TestResolveHealthEndpointFallsBackToLocalDefault(t *testing.T) {
54+
t.Cleanup(func() {
55+
healthEndpoint = ""
56+
})
57+
58+
cmd := &cobra.Command{Use: "health"}
59+
cmd.Flags().String("endpoint", "", "")
60+
cmd.Flags().String("config-path", "", "")
61+
62+
if got := resolveHealthEndpoint(cmd); got != "http://127.0.0.1:8090" {
63+
t.Fatalf("resolveHealthEndpoint() = %q, want %q", got, "http://127.0.0.1:8090")
64+
}
65+
}

0 commit comments

Comments
 (0)