feat(desktop): add client release updater
Desktop Client Build / Resolve Build Metadata (push) Successful in 19s
Frontend CI / Frontend (push) Successful in 3m20s
Backend CI / Backend (push) Successful in 16m48s
Desktop Client Build / Build Desktop Client (push) Successful in 24m46s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 37s
Desktop Client Build / Resolve Build Metadata (push) Successful in 19s
Frontend CI / Frontend (push) Successful in 3m20s
Backend CI / Backend (push) Successful in 16m48s
Desktop Client Build / Build Desktop Client (push) Successful in 24m46s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 37s
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/bootstrap"
|
||||
opsapp "github.com/geo-platform/tenant-api/internal/ops/app"
|
||||
opsrepo "github.com/geo-platform/tenant-api/internal/ops/repository"
|
||||
"github.com/geo-platform/tenant-api/internal/shared/response"
|
||||
)
|
||||
|
||||
type DesktopReleaseHandler struct {
|
||||
svc *opsapp.DesktopClientReleaseService
|
||||
}
|
||||
|
||||
func NewDesktopReleaseHandler(a *bootstrap.App) *DesktopReleaseHandler {
|
||||
return &DesktopReleaseHandler{
|
||||
svc: opsapp.NewDesktopClientReleaseService(
|
||||
opsrepo.NewDesktopClientReleaseRepository(a.DB),
|
||||
a.ObjectStorage,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *DesktopReleaseHandler) Latest(c *gin.Context) {
|
||||
client := MustDesktopClient(c.Request.Context())
|
||||
data, err := h.svc.CheckLatest(c.Request.Context(), opsapp.DesktopClientReleaseCheckInput{
|
||||
Platform: c.Query("platform"),
|
||||
Arch: c.Query("arch"),
|
||||
Channel: defaultDesktopReleaseChannel(c.Query("channel"), client.Channel),
|
||||
CurrentVersion: c.Query("version"),
|
||||
})
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *DesktopReleaseHandler) DownloadURL(c *gin.Context) {
|
||||
client := MustDesktopClient(c.Request.Context())
|
||||
data, err := h.svc.ResolveLatestDownloadURL(c.Request.Context(), opsapp.DesktopClientReleaseCheckInput{
|
||||
Platform: c.Query("platform"),
|
||||
Arch: c.Query("arch"),
|
||||
Channel: defaultDesktopReleaseChannel(c.Query("channel"), client.Channel),
|
||||
CurrentVersion: c.Query("version"),
|
||||
})
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *DesktopReleaseHandler) LatestPublic(c *gin.Context) {
|
||||
data, err := h.svc.CheckLatest(c.Request.Context(), opsapp.DesktopClientReleaseCheckInput{
|
||||
Platform: c.Query("platform"),
|
||||
Arch: c.Query("arch"),
|
||||
Channel: c.DefaultQuery("channel", "stable"),
|
||||
CurrentVersion: c.Query("version"),
|
||||
})
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *DesktopReleaseHandler) DownloadURLPublic(c *gin.Context) {
|
||||
data, err := h.svc.ResolveLatestDownloadURL(c.Request.Context(), opsapp.DesktopClientReleaseCheckInput{
|
||||
Platform: c.Query("platform"),
|
||||
Arch: c.Query("arch"),
|
||||
Channel: c.DefaultQuery("channel", "stable"),
|
||||
CurrentVersion: c.Query("version"),
|
||||
})
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *DesktopReleaseHandler) UpdaterFeedPublic(c *gin.Context) {
|
||||
feed, err := h.svc.ResolveLatestUpdaterFeed(c.Request.Context(), opsapp.DesktopClientReleaseCheckInput{
|
||||
Platform: firstNonEmpty(c.Param("platform"), c.Query("platform")),
|
||||
Arch: firstNonEmpty(c.Param("arch"), c.Query("arch")),
|
||||
Channel: firstNonEmpty(c.Param("channel"), c.DefaultQuery("channel", "stable")),
|
||||
CurrentVersion: firstNonEmpty(c.Param("version"), c.Query("version")),
|
||||
}, c.Param("feed"))
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "application/x-yaml; charset=utf-8")
|
||||
c.Header("Cache-Control", "no-store")
|
||||
c.Header("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, sanitizeHeaderFileName(feed.FileName)))
|
||||
c.Data(http.StatusOK, "application/x-yaml; charset=utf-8", feed.Content)
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if strings.TrimSpace(value) != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func defaultDesktopReleaseChannel(queryValue string, clientChannel *string) string {
|
||||
if queryValue != "" {
|
||||
return queryValue
|
||||
}
|
||||
if clientChannel != nil && *clientChannel != "" {
|
||||
return *clientChannel
|
||||
}
|
||||
return "stable"
|
||||
}
|
||||
|
||||
func sanitizeHeaderFileName(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return "latest.yml"
|
||||
}
|
||||
return strings.Map(func(r rune) rune {
|
||||
if r == '"' || r == '\\' || r == '\r' || r == '\n' {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, value)
|
||||
}
|
||||
@@ -33,6 +33,7 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
protected.POST("/auth/logout", authHandler.Logout)
|
||||
|
||||
desktopClientHandler := NewDesktopClientHandler(app)
|
||||
desktopReleaseHandler := NewDesktopReleaseHandler(app)
|
||||
desktopAccountHandler := NewDesktopAccountHandler(app)
|
||||
desktopTaskHandler := NewDesktopTaskHandler(app)
|
||||
desktopDispatchHandler := NewDesktopDispatchHandler(app)
|
||||
@@ -40,6 +41,10 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
desktopContentHandler := NewDesktopContentHandler(app)
|
||||
publishJobHandler := NewPublishJobHandler(app)
|
||||
protected.POST("/desktop/clients/register", desktopClientHandler.Register)
|
||||
app.Engine.GET("/api/desktop/releases/latest", desktopReleaseHandler.LatestPublic)
|
||||
app.Engine.GET("/api/desktop/releases/download-url", desktopReleaseHandler.DownloadURLPublic)
|
||||
app.Engine.GET("/api/desktop/releases/updater/:platform/:arch/:channel/:version/:feed", desktopReleaseHandler.UpdaterFeedPublic)
|
||||
app.Engine.GET("/api/desktop/releases/updater/:platform/:arch/:channel/:version", desktopReleaseHandler.UpdaterFeedPublic)
|
||||
|
||||
desktopAuth := app.Engine.Group("/api/desktop")
|
||||
desktopAuth.Use(DesktopClientMiddleware(repository.NewDesktopClientRepository(app.DB)))
|
||||
@@ -47,6 +52,8 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
desktopAuth.POST("/clients/heartbeat", desktopClientHandler.Heartbeat)
|
||||
desktopAuth.POST("/clients/offline", desktopClientHandler.Offline)
|
||||
desktopAuth.POST("/clients/revoke", desktopClientHandler.Revoke)
|
||||
desktopAuth.GET("/clients/releases/latest", desktopReleaseHandler.Latest)
|
||||
desktopAuth.GET("/clients/releases/download-url", desktopReleaseHandler.DownloadURL)
|
||||
desktopAuth.GET("/dispatch", desktopDispatchHandler.Dispatch)
|
||||
desktopAuth.GET("/accounts", desktopAccountHandler.List)
|
||||
desktopAuth.GET("/content/articles/:id", desktopContentHandler.Article)
|
||||
|
||||
Reference in New Issue
Block a user