🗑️ Clean up command related stuff
🚚 Move http package to web
This commit is contained in:
@ -1,22 +0,0 @@
|
||||
package directory
|
||||
|
||||
const (
|
||||
CommandMethodGet = "get"
|
||||
CommandMethodPut = "put"
|
||||
CommandMethodPatch = "patch"
|
||||
CommandMethodPost = "post"
|
||||
CommandMethodDelete = "delete"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
// The unique identifier of the command, different method command can hold the same command id
|
||||
ID string `json:"id"`
|
||||
// The method of the command, such as get, post, others; inspired by RESTful design
|
||||
Method string `json:"method"`
|
||||
// The tags of the command will be used to invoke the pre-command middlewares and post-command middlewares
|
||||
Tags []string `json:"tags"`
|
||||
// The implementation of the command, the handler is the service that will be invoked
|
||||
Handler []*ServiceInstance `json:"handler"`
|
||||
|
||||
RobinIndex uint `json:"robin_index"`
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
package directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/kv"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const CommandInfoKvPrefix = "nexus.command/"
|
||||
|
||||
func AddCommand(id, method string, tags []string, handler *ServiceInstance) error {
|
||||
if tags == nil {
|
||||
tags = make([]string, 0)
|
||||
}
|
||||
|
||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
||||
|
||||
command := &Command{
|
||||
ID: id,
|
||||
Method: method,
|
||||
Tags: tags,
|
||||
Handler: []*ServiceInstance{handler},
|
||||
}
|
||||
|
||||
command.Handler = lo.UniqBy(command.Handler, func(item *ServiceInstance) string {
|
||||
return item.ID
|
||||
})
|
||||
|
||||
commandJSON, err := json.Marshal(command)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling command: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = kv.Kv.Put(context.Background(), ky, string(commandJSON))
|
||||
return err
|
||||
}
|
||||
|
||||
func GetCommandHandler(id, method string) *ServiceInstance {
|
||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
||||
|
||||
resp, err := kv.Kv.Get(context.Background(), ky)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(resp.Kvs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var command Command
|
||||
if err := json.Unmarshal(resp.Kvs[0].Value, &command); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(command.Handler) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
idx := command.RobinIndex % uint(len(command.Handler))
|
||||
command.RobinIndex = idx + 1
|
||||
|
||||
raw, err := json.Marshal(&command)
|
||||
if err == nil {
|
||||
_, _ = kv.Kv.Put(context.Background(), ky, string(raw))
|
||||
}
|
||||
|
||||
return command.Handler[idx]
|
||||
}
|
||||
|
||||
func RemoveCommand(id, method string) error {
|
||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
||||
|
||||
_, err := kv.Kv.Delete(context.Background(), ky)
|
||||
return err
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CommandRpcServer struct {
|
||||
proto.UnimplementedCommandProviderServer
|
||||
}
|
||||
|
||||
func (c *CommandRpcServer) AddCommand(ctx context.Context, info *proto.CommandInfo) (*proto.AddCommandResponse, error) {
|
||||
clientId, err := GetClientId(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
service := GetServiceInstance(clientId)
|
||||
if service == nil {
|
||||
return nil, status.Errorf(codes.NotFound, "service not found")
|
||||
}
|
||||
|
||||
AddCommand(info.GetId(), info.GetMethod(), info.GetTags(), service)
|
||||
return &proto.AddCommandResponse{
|
||||
IsSuccess: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CommandRpcServer) RemoveCommand(ctx context.Context, request *proto.CommandLookupRequest) (*proto.RemoveCommandResponse, error) {
|
||||
RemoveCommand(request.GetId(), request.GetMethod())
|
||||
return &proto.RemoveCommandResponse{
|
||||
IsSuccess: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CommandRpcServer) SendCommand(ctx context.Context, argument *proto.CommandArgument) (*proto.CommandReturn, error) {
|
||||
id := argument.GetCommand()
|
||||
method := argument.GetMethod()
|
||||
|
||||
handler := GetCommandHandler(id, method)
|
||||
if handler == nil {
|
||||
return &proto.CommandReturn{
|
||||
IsDelivered: false,
|
||||
Status: http.StatusNotFound,
|
||||
ContentType: "text/plain+error",
|
||||
Payload: []byte("command not found"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
conn, err := handler.GetGrpcConn()
|
||||
if err != nil {
|
||||
return &proto.CommandReturn{
|
||||
IsDelivered: false,
|
||||
Status: http.StatusServiceUnavailable,
|
||||
ContentType: "text/plain+error",
|
||||
Payload: []byte("service unavailable"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, argument)
|
||||
if err != nil {
|
||||
return &proto.CommandReturn{
|
||||
IsDelivered: true,
|
||||
Status: http.StatusInternalServerError,
|
||||
ContentType: "text/plain+error",
|
||||
Payload: []byte(err.Error()),
|
||||
}, nil
|
||||
}
|
||||
out.IsDelivered = true
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *CommandRpcServer) SendStreamCommand(g proto.CommandProvider_SendStreamCommandServer) error {
|
||||
for {
|
||||
pck, err := g.Recv()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id := pck.GetCommand()
|
||||
method := pck.GetMethod()
|
||||
|
||||
handler := GetCommandHandler(id, method)
|
||||
if handler == nil {
|
||||
return status.Errorf(codes.NotFound, "command not found")
|
||||
}
|
||||
|
||||
conn, err := handler.GetGrpcConn()
|
||||
|
||||
ctx, cancel := context.WithTimeout(g.Context(), time.Second*10)
|
||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, pck)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
_ = g.Send(&proto.CommandReturn{
|
||||
IsDelivered: false,
|
||||
Status: http.StatusInternalServerError,
|
||||
ContentType: "text/plain+error",
|
||||
Payload: []byte(err.Error()),
|
||||
})
|
||||
} else {
|
||||
_ = g.Send(out)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||
"net"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||
|
||||
"google.golang.org/grpc/reflection"
|
||||
@ -29,7 +30,6 @@ func NewServer() *Server {
|
||||
}
|
||||
|
||||
proto.RegisterDirectoryServiceServer(server.srv, &directory.ServiceRpcServer{})
|
||||
proto.RegisterCommandProviderServer(server.srv, &directory.CommandRpcServer{})
|
||||
proto.RegisterDatabaseServiceServer(server.srv, server)
|
||||
proto.RegisterStreamServiceServer(server.srv, server)
|
||||
proto.RegisterAllocatorServiceServer(server.srv, server)
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/ws"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/ws"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||
|
@ -1,67 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func invokeCommand(c *fiber.Ctx) error {
|
||||
command := c.Params("command")
|
||||
method := strings.ToLower(c.Method())
|
||||
|
||||
handler := directory.GetCommandHandler(command, method)
|
||||
if handler == nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, "command not found")
|
||||
}
|
||||
|
||||
conn, err := handler.GetGrpcConn()
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusServiceUnavailable, "service unavailable")
|
||||
}
|
||||
|
||||
log.Debug().Str("id", command).Str("method", method).Msg("Invoking command from HTTP Gateway...")
|
||||
|
||||
var meta []string
|
||||
meta = append(meta, "client_id", "http-gateway")
|
||||
meta = append(meta, "net.ip", c.IP())
|
||||
meta = append(meta, "http.user_agent", c.Get(fiber.HeaderUserAgent))
|
||||
for k, v := range c.GetReqHeaders() {
|
||||
meta = append(
|
||||
meta,
|
||||
strings.ToLower(fmt.Sprintf("header.%s", strings.ReplaceAll(k, "-", "_"))),
|
||||
strings.Join(v, "\n"),
|
||||
)
|
||||
}
|
||||
|
||||
for k, v := range c.Queries() {
|
||||
meta = append(
|
||||
meta,
|
||||
strings.ToLower(fmt.Sprintf("query.%s", strings.ReplaceAll(k, "-", "_"))),
|
||||
v,
|
||||
)
|
||||
}
|
||||
|
||||
ctx := metadata.AppendToOutgoingContext(c.Context(), meta...)
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, &proto.CommandArgument{
|
||||
Command: command,
|
||||
Method: method,
|
||||
Payload: c.Body(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
c.Set(fiber.HeaderContentType, out.ContentType)
|
||||
return c.Status(int(out.Status)).Send(out.Payload)
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import (
|
||||
pkg "git.solsynth.dev/hypernet/nexus/pkg/internal"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/ws"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/ws"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||
"github.com/gofiber/contrib/websocket"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@ -12,6 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func MapAPIs(app *fiber.App) {
|
||||
app.Get("/check-ip", getClientIP)
|
||||
|
||||
// Some built-in public-accessible APIs
|
||||
wellKnown := app.Group("/.well-known").Name("Well Known")
|
||||
{
|
||||
@ -22,7 +24,6 @@ func MapAPIs(app *fiber.App) {
|
||||
"status": true,
|
||||
})
|
||||
})
|
||||
wellKnown.Get("/check-ip", getClientIP)
|
||||
wellKnown.Get("/directory/services", listExistsService)
|
||||
|
||||
wellKnown.Get("/openid-configuration", func(c *fiber.Ctx) error {
|
||||
@ -50,6 +51,5 @@ func MapAPIs(app *fiber.App) {
|
||||
// Common websocket gateway
|
||||
app.Get("/ws", auth.ValidatorMiddleware, websocket.New(ws.Listen))
|
||||
|
||||
app.All("/inv/:command", invokeCommand)
|
||||
app.All("/cgi/:service/*", forwardService)
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package server
|
||||
package web
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/api"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/api"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
@ -15,11 +15,11 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type HTTPApp struct {
|
||||
type WebApp struct {
|
||||
app *fiber.App
|
||||
}
|
||||
|
||||
func NewServer() *HTTPApp {
|
||||
func NewServer() *WebApp {
|
||||
app := fiber.New(fiber.Config{
|
||||
DisableStartupMessage: true,
|
||||
EnableIPValidation: true,
|
||||
@ -55,10 +55,10 @@ func NewServer() *HTTPApp {
|
||||
|
||||
api.MapAPIs(app)
|
||||
|
||||
return &HTTPApp{app}
|
||||
return &WebApp{app}
|
||||
}
|
||||
|
||||
func (v *HTTPApp) Listen() {
|
||||
func (v *WebApp) Listen() {
|
||||
if err := v.app.Listen(viper.GetString("bind")); err != nil {
|
||||
log.Fatal().Err(err).Msg("An error occurred when starting server...")
|
||||
}
|
Reference in New Issue
Block a user