feat: update agent button on server page
Server Deploy / deploy (push) Successful in 1m39s
Agent Release / build (push) Successful in 2m10s

This commit is contained in:
domrichardson
2026-06-24 14:33:17 +01:00
parent e6ef9bc536
commit aff6736b18
13 changed files with 331 additions and 45 deletions
+67
View File
@@ -1,7 +1,11 @@
package services
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"sync"
"github.com/google/uuid"
@@ -67,6 +71,69 @@ type KeyGenParams struct {
Comment string
}
// GetLatestAgentVersion queries the Gitea API for the latest agent/v* release tag
// and returns just the version number (e.g. "1.2.3").
func GetLatestAgentVersion() (string, error) {
giteaHost := os.Getenv("GITEA_HOST")
if giteaHost == "" {
giteaHost = "gitea.example.com"
}
url := fmt.Sprintf("https://%s/api/v1/repos/mrhid6/keymanager/releases?limit=20", giteaHost)
resp, err := http.Get(url) //nolint:gosec
if err != nil {
return "", fmt.Errorf("fetch releases: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Gitea API returned HTTP %d", resp.StatusCode)
}
var releases []struct {
TagName string `json:"tag_name"`
}
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
return "", fmt.Errorf("decode releases: %w", err)
}
for _, r := range releases {
if strings.HasPrefix(r.TagName, "agent/v") {
return strings.TrimPrefix(r.TagName, "agent/v"), nil
}
}
return "", fmt.Errorf("no agent release found")
}
// DispatchUpdateAgent sends an update command to the named server's agent.
// It fetches the latest version from Gitea and includes the download base URL.
func DispatchUpdateAgent(serverID string) (string, error) {
if !Dispatcher.IsConnected(serverID) {
return "", fmt.Errorf("agent is not connected to the command stream")
}
version, err := GetLatestAgentVersion()
if err != nil {
return "", fmt.Errorf("get latest version: %w", err)
}
giteaHost := os.Getenv("GITEA_HOST")
if giteaHost == "" {
giteaHost = "gitea.example.com"
}
cmdID := uuid.New().String()
cmd := &pb.ServerCommand{
CommandId: cmdID,
UpdateAgent: &pb.UpdateAgentCmd{
Version: version,
GiteaBaseURL: "https://" + giteaHost,
},
}
if err := Dispatcher.dispatch(serverID, cmd); err != nil {
return "", err
}
return version, nil
}
// DispatchDeleteKey sends a delete-key command to the named server's agent.
// It is best-effort: if the agent is offline the local files will remain until next connection.
func DispatchDeleteKey(serverID, label string) {
+6 -2
View File
@@ -134,14 +134,18 @@ func ValidateAgentToken(serverID, agentToken string) (*models.Server, error) {
return &s, nil
}
func UpdateServerLastSeen(serverID string) error {
func UpdateServerLastSeen(serverID, agentVersion string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
now := time.Now()
fields := bson.M{"last_seen": now, "status": "active"}
if agentVersion != "" {
fields["agent_version"] = agentVersion
}
_, err := db.Col("servers").UpdateOne(ctx,
bson.M{"server_id": serverID},
bson.M{"$set": bson.M{"last_seen": now, "status": "active"}},
bson.M{"$set": fields},
)
return err
}