From 6363d38d035b8b64a35fd40586ab563f07d46601 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Mon, 18 Nov 2024 18:57:20 +0200 Subject: [PATCH] feat: gRPC client and fetch basic worker details --- cmd/emerwen-web/main.go | 5 ++- go.mod | 17 ++++--- go.sum | 32 +++++++++----- internal/client/client.go | 42 ++++++++++++++++++ internal/components/components.templ | 34 ++++++++++++++ internal/components/index.templ | 4 +- internal/components/partials.templ | 66 ++++++++++++++++++++++++++++ internal/config/config.go | 7 ++- internal/handlers/partials.go | 54 +++++++++++++++++++++++ internal/router/router.go | 8 +++- 10 files changed, 245 insertions(+), 24 deletions(-) create mode 100644 internal/client/client.go create mode 100644 internal/components/components.templ create mode 100644 internal/components/partials.templ create mode 100644 internal/handlers/partials.go diff --git a/cmd/emerwen-web/main.go b/cmd/emerwen-web/main.go index 53253e0..270fc9c 100644 --- a/cmd/emerwen-web/main.go +++ b/cmd/emerwen-web/main.go @@ -14,6 +14,7 @@ import ( "time" "git.src.quest/~liljamo/emerwen-web/internal/auth" + "git.src.quest/~liljamo/emerwen-web/internal/client" "git.src.quest/~liljamo/emerwen-web/internal/config" "git.src.quest/~liljamo/emerwen-web/internal/log" "git.src.quest/~liljamo/emerwen-web/internal/router" @@ -37,10 +38,12 @@ func main() { sm = scs.New() sm.Lifetime = 1 * time.Hour + client := client.NewClient(&c) + slog.Info("serving", slog.String("bindaddr", c.BindAddr)) s := &http.Server{ Addr: c.BindAddr, - Handler: sm.LoadAndSave(router.SetupRouter(l, a, sm)), + Handler: sm.LoadAndSave(router.SetupRouter(l, a, sm, client)), } g.Go(func() error { diff --git a/go.mod b/go.mod index 767ebd5..030f779 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,17 @@ module git.src.quest/~liljamo/emerwen-web -go 1.23.2 +go 1.23.3 require ( + git.src.quest/~liljamo/emerwen-proto v0.0.0-20241118080237-06cafd5cb729 github.com/a-h/templ v0.2.793 github.com/alexedwards/scs/v2 v2.8.0 github.com/coreos/go-oidc/v3 v3.11.0 github.com/gin-gonic/gin v1.10.0 github.com/samber/slog-gin v1.13.6 - golang.org/x/oauth2 v0.21.0 + golang.org/x/oauth2 v0.24.0 golang.org/x/sync v0.8.0 + google.golang.org/grpc v1.68.0 ) require ( @@ -37,10 +39,11 @@ require ( go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e2341d1..028ae64 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +git.src.quest/~liljamo/emerwen-proto v0.0.0-20241118080237-06cafd5cb729 h1:Zka776qZfycsq3f6VbDrbou3egXl2mCRFp0JkfH1VyY= +git.src.quest/~liljamo/emerwen-proto v0.0.0-20241118080237-06cafd5cb729/go.mod h1:TZkTqP3/rDTcJDTDU61QvV+4+TsZIvW0lt4hOiYCrr0= github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY= github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= @@ -33,6 +35,8 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4 github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -86,22 +90,26 @@ go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+M golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..214b1b7 --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 Jonni Liljamo + * + * This file is licensed under GPL-3.0-or-later, see NOTICE and LICENSE for + * more information. + */ + +// Package client provides a type wrapper for the emerwen gRPC WebToMasterClient. +package client + +import ( + "context" + "fmt" + "log/slog" + + "git.src.quest/~liljamo/emerwen-proto/go/proto" + "git.src.quest/~liljamo/emerwen-web/internal/config" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" +) + +// Client wraps the emerwen gRPC WebToMasterClient, along with a Context. +type Client struct { + Client proto.WebToMasterClient + Ctx context.Context +} + +// NewClient constructs a new Client. +func NewClient(c *config.Config) *Client { + conn, err := grpc.NewClient(fmt.Sprintf("unix://%s", c.SocketPath), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + slog.Error("yay") + } + + client := proto.NewWebToMasterClient(conn) + + // TODO: config read auth_token from file + ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer avain_perkele") + + return &Client{Client: client, Ctx: ctx} +} diff --git a/internal/components/components.templ b/internal/components/components.templ new file mode 100644 index 0000000..769f9d5 --- /dev/null +++ b/internal/components/components.templ @@ -0,0 +1,34 @@ +package components + +import "fmt" + +script toggleKeyVisibility(id string) { + var input = document.getElementById(id); + var eye_vis = document.getElementById("eye_visible_" + id); + var eye_hid = document.getElementById("eye_hidden_" + id); + if (input.type === "password") { + input.type = "text"; + } else { + input.type = "password"; + }; + eye_vis.classList.toggle("hidden"); + eye_hid.classList.toggle("hidden"); +} + +templ eyeToggle(t string) { +
+ +
+ + + + + +
+
+} diff --git a/internal/components/index.templ b/internal/components/index.templ index 5651572..a51330d 100644 --- a/internal/components/index.templ +++ b/internal/components/index.templ @@ -3,7 +3,9 @@ package components templ Index() { @Base("emerwen, counting sheep") {
- + if loggedIn(ctx) { +
+ }
} } diff --git a/internal/components/partials.templ b/internal/components/partials.templ new file mode 100644 index 0000000..a99d379 --- /dev/null +++ b/internal/components/partials.templ @@ -0,0 +1,66 @@ +package components + +import "git.src.quest/~liljamo/emerwen-proto/go/proto/shared" +import "fmt" + +func getMethodString(target *shared.Target) string { + switch target.Method.(type) { + case *shared.Target_Ping: + return "Ping" + case *shared.Target_Get: + return "Get" + default: + return "fuck" + } +} + +templ PartialWorkers(workers []*shared.Worker) { +
+ for _, worker := range workers { +
+
+ ID: { fmt.Sprintf("%d", worker.Id) } +
+
+
Auth Token:
+ + @eyeToggle(fmt.Sprintf("auth_key_%d", worker.Id)) +
+
+ Targets: +
+ for _, target := range worker.Targets { +
+
+ ID: { fmt.Sprintf("%d", target.Id) } +
+
+
Addr:
+ +
+
+
Interval (ms):
+ +
+
+
Method:
+ switch m := target.Method.(type) { + case *shared.Target_Ping: +
Ping
+ case *shared.Target_Get: +
Get
+ + default: +
fuck
+ } +
+
+ } +
+
+
+ } +
+} diff --git a/internal/config/config.go b/internal/config/config.go index 3fe56b4..10d2821 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -17,7 +17,8 @@ import ( // Config holds application configuration values. type Config struct { - BindAddr string + BindAddr string + SocketPath string OIDCClientID string OIDCClientSecret string @@ -31,6 +32,7 @@ type Config struct { // a file. func ParseFromArgs() Config { bindAddrPtr := flag.String("bind_address", "127.0.0.1:3000", "bind address") + socketPathPtr := flag.String("socket_path", "/tmp/emerwen/master.sock", "master socket path") oidcClientIDPtr := flag.String("oidc_client_id", "emerwen", "OIDC client ID") oidcClientIDFilePtr := flag.String("oidc_client_id_file", "", "OIDC client ID file") @@ -70,7 +72,8 @@ func ParseFromArgs() Config { } return Config{ - BindAddr: *bindAddrPtr, + BindAddr: *bindAddrPtr, + SocketPath: *socketPathPtr, OIDCClientID: oidcClientID, OIDCClientSecret: oidcClientSecret, diff --git a/internal/handlers/partials.go b/internal/handlers/partials.go new file mode 100644 index 0000000..efa7c01 --- /dev/null +++ b/internal/handlers/partials.go @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Jonni Liljamo + * + * This file is licensed under GPL-3.0-or-later, see NOTICE and LICENSE for + * more information. + */ + +package handlers + +import ( + "fmt" + "log/slog" + "net/http" + + "git.src.quest/~liljamo/emerwen-web/internal/client" + "git.src.quest/~liljamo/emerwen-web/internal/components" + "git.src.quest/~liljamo/emerwen-web/internal/renderer" + "github.com/gin-gonic/gin" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" +) + +func handleClientError(c *gin.Context, err error) { + if status, ok := status.FromError(err); ok { + slog.Error("RPC error", slog.String("err", err.Error())) + + if status.Code() == codes.Unavailable { + c.String(http.StatusInternalServerError, "RPC unavailable.") + return + } + + c.String(http.StatusInternalServerError, fmt.Sprintf("RPC error: %s", status.String())) + return + } + + slog.Error("Non-RPC error", slog.String("err", err.Error())) + + c.String(http.StatusInternalServerError, "Non-RPC error, see server logs.") +} + +// PartialWorkers returns a gin handler for the partial workers route. +func PartialWorkers(client *client.Client) gin.HandlerFunc { + return func(c *gin.Context) { + response, err := client.Client.GetWorkers(client.Ctx, &emptypb.Empty{}) + if err != nil { + handleClientError(c, err) + return + } + + r := renderer.New(BaseContext(c), http.StatusOK, components.PartialWorkers(response.Workers)) + c.Render(http.StatusOK, r) + } +} diff --git a/internal/router/router.go b/internal/router/router.go index 5de07a7..ae85776 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -13,6 +13,7 @@ import ( "log/slog" "git.src.quest/~liljamo/emerwen-web/internal/auth" + "git.src.quest/~liljamo/emerwen-web/internal/client" "git.src.quest/~liljamo/emerwen-web/internal/handlers" "git.src.quest/~liljamo/emerwen-web/internal/middlewares" "git.src.quest/~liljamo/emerwen-web/internal/renderer" @@ -22,7 +23,7 @@ import ( ) // SetupRouter returns a gin engine. -func SetupRouter(l *slog.Logger, a *auth.Auth, sm *scs.SessionManager) *gin.Engine { +func SetupRouter(l *slog.Logger, a *auth.Auth, sm *scs.SessionManager, client *client.Client) *gin.Engine { r := gin.New() r.Use(gin.Recovery()) r.Use(sloggin.New(l)) @@ -41,5 +42,10 @@ func SetupRouter(l *slog.Logger, a *auth.Auth, sm *scs.SessionManager) *gin.Engi r.GET("/logout", handlers.Logout(sm)) r.GET("/oauth2/oidc/callback", handlers.OAuth2OIDCCallback(a, sm)) + s := r.Group("/", middlewares.RequireSession(sm)) + { + s.GET("/partials/workers", handlers.PartialWorkers(client)) + } + return r } -- 2.44.1