Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
package websocket
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"fiskerinc.com/modules/grpc/kafka_grpc"
"fiskerinc.com/modules/common"
"fiskerinc.com/modules/kafka"
"fiskerinc.com/modules/logger"
"fiskerinc.com/modules/security"
"github.com/gobwas/ws"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
// NewHMISession serves as the constructor for HMI sessions
func NewHMISession(w http.ResponseWriter, r *http.Request, vin string, version string) (SessionInterface, error) {
var s SessionInterface
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
return s, errors.WithStack(err)
}
return &SessionHMI{
Session: &Session{
Websocket: conn,
Type: common.HMI,
Version: version,
ID: vin,
epoch: time.Now().UnixNano(),
},
InAuthentication: false,
}, nil
}
// SessionHMI contains websocket info
type SessionHMI struct {
*Session
SessionID string
InAuthentication bool
}
// Authenticate returns id if proper authentication, else returns error
//
// validates VIN inputted through "key" field of message
func (s *SessionHMI) Authenticate() error {
s.InAuthentication = true
defer func() { s.InAuthentication = false }()
err := s.authenticate()
data, _ := json.Marshal(AuthResponse{
Handler: "verify",
Data: AuthResponseData{
Authenticated: err == nil,
},
})
s.SendMsgToClient(data)
if err != nil {
errors.Errorf("Unable to send to session %s", s.SessionID)
return errors.WithStack(err)
}
return nil
}
func (s *SessionHMI) SendMsgToClient(message []byte) error {
if !s.InAuthentication && len(s.SessionID) == 0 {
return fmt.Errorf("session is not authorized, %s", s.SessionID)
}
return s.Session.SendMsgToClient(message)
}
func (s *SessionHMI) authenticate() error {
s.InAuthentication = true
defer func() { s.InAuthentication = false }()
msg, _, err := s.Receive()
if err != nil {
errors.Errorf("unable to read socket %s", s.ID)
return err
}
var m common.HMISessionMessage
err = json.Unmarshal(msg, &m)
if err != nil {
return errors.WithStack(err)
} else if m.Handler != "verify" {
return errors.Errorf("incorrect auth handler specified %v", m.Handler)
}
switch {
case m.Data.SessionID != "":
salter, err := security.NewSalter(s.ID)
if err != nil {
errors.Errorf("unable to generate salt %s", s.ID)
return err
}
err = salter.ValidateSessionID(m.Data.SessionID)
if err != nil {
errors.Errorf("unable to validate session %s", m.Data.SessionID)
return err
}
s.SessionID = m.Data.SessionID
case m.Data.Salt != "":
s.SessionID = m.Data.Salt
default:
return ErrFailedAuthentication
}
return nil
}
// Listen to websocket session and use handler upon message received
func (s *SessionHMI) Listen(ctx context.Context, producer kafka.ProducerInterface) error {
span, _ := tracer.StartSpanFromContext(ctx, "listen")
defer span.Finish()
key := s.Key()
for {
msg, op, err := s.Receive()
if op == ws.OpClose {
// logger.At(logger.Info(), key, "conn").Msg("OpClose") //commented out because reported by the call in websocket.go
return nil
} else if err != nil {
// logger.At(logger.Info(), key, "conn").Err(err).Send() //commented out because reported by the call in websocket.go
return err
}
err = s.Route(producer, msg)
if err != nil {
logger.At(logger.Warn(), key, "route").Err(err).Send()
}
}
}
// Route HMI messages
func (s *SessionHMI) Route(producer kafka.ProducerInterface, data []byte) error {
var m common.MessageRawJSON
err := m.Unmarshal(data)
if err != nil {
return err
}
if m.Verify == "ack"{
// Throw out ACK messages
return nil
}
key := s.Key()
logger.Debug().
Str("id", key).
Str("handler", m.Handler).
Int("size", len(data)).
Msgf("received from %v %s", key, string(data))
topic, ok := kafka.HMIHandlerTopics[m.Handler]
if !ok {
err = ErrInvalidHandler(m.Handler)
logger.Err(err).Str("Byte Data", string(data)).Str("Parsed Data", string(m.Data)).Msg("No Handler Insights")
return err
}
switch topic {
case kafka.AttendantServiceGRPCKafka:
valetData := common.AttendantRouteHMIGRPC(m)
grpcData, err := proto.Marshal(valetData)
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "unable to marshal trexlogs GRPC "+topic)).Send()
return errors.WithStack(err)
}
err = producer.ProduceBinary(kafka.AttendantServiceGRPCKafka, key, grpcData, nil)
grpcData = nil
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "Producer for trex logs failed")).Send()
return err
}
case kafka.DepotServiceGRPCKafka:
valetData := common.DepotRouteHMIToGRPC(m)
grpcData, err := proto.Marshal(valetData)
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "unable to marshal trexlogs GRPC "+topic)).Send()
return errors.WithStack(err)
}
err = producer.ProduceBinary(kafka.DepotServiceGRPCKafka, key, grpcData, nil)
grpcData = nil
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "Producer for trex logs failed")).Send()
return err
}
case kafka.ValetServiceGRPCKafka:
valetData := common.ValetRouteHMIPayloadGRPC(m)
grpcData, err := proto.Marshal(valetData)
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "unable to marshal trexlogs GRPC "+topic)).Send()
return errors.WithStack(err)
}
err = producer.ProduceBinary(kafka.ValetServiceGRPCKafka, key, grpcData, nil)
grpcData = nil
if err != nil {
logger.At(logger.Error(), key, "route").
Err(errors.Wrap(err, "Producer for trex logs failed")).Send()
return err
}
default:
err = producer.Produce(topic, key, m, nil)
}
if err != nil {
return err
}
return nil
}
func (s *SessionHMI) Load(producer kafka.ProducerInterface) error {
key := s.Key()
hmiSession := &kafka_grpc.GRPC_DepotPayload_HmiSession{
HmiSession: &kafka_grpc.HMISessionData{
SessionId: s.SessionID,
},
}
payload := kafka_grpc.GRPC_DepotPayload{
Handler: "init",
Data: hmiSession,
}
binaryPayload, _ := proto.Marshal(&payload)
err := producer.ProduceBinary(kafka.DepotServiceGRPCKafka, key, binaryPayload, nil)
return err
}