Files

337 lines
8.2 KiB
Go

package handlers
import (
"encoding/json"
"io"
"net/http"
"strings"
"github.com/fiskerinc/cloud-services/services/depot/services"
"github.com/fiskerinc/cloud-services/pkg/cache"
"github.com/fiskerinc/cloud-services/pkg/carcommand"
"github.com/fiskerinc/cloud-services/pkg/common"
"github.com/fiskerinc/cloud-services/pkg/db/queries"
"github.com/fiskerinc/cloud-services/pkg/dbc/state"
"github.com/fiskerinc/cloud-services/pkg/logger"
"github.com/fiskerinc/cloud-services/pkg/mongo"
"github.com/fiskerinc/cloud-services/pkg/redis"
"github.com/fiskerinc/cloud-services/pkg/userconsent"
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
"github.com/fiskerinc/cloud-services/pkg/utils"
"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
"github.com/go-pg/pg/v10"
redigo "github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
mon "go.mongodb.org/mongo-driver/mongo"
)
var errInvalidICCID = errors.New("invalid iccid submitted")
var errBlocked = errors.New("unable to connect: car is blocked")
var logLevel = envtool.GetEnv("TREX_LOG_LEVEL", common.CriticalLabel)
var wakeUpVINS = envtool.GetEnv("WAKE_UP_VINS", "VCF1EBU2XPG001140")
var defaultFleet = envtool.GetEnv("DEFAULT_FLEET", "Default-Ocean")
// id is the vehicles VIN
func TRexInit(id string, vMap map[string]string) error {
carsDB := services.GetDB().GetCars()
blocked, err := insertIfNotExist(id, carsDB)
if err != nil {
return err
}
if blocked {
return errBlocked
}
if strings.Contains(wakeUpVINS, id) {
go wakeup(id)
}
client := services.RedisClientPool().GetFromPool()
defer client.Close()
err = addTRexSession(client, id)
if err != nil {
logger.Warn().Str("id", id).Err(err).Send()
}
m, err := services.GetMongoClient()
if err != nil {
logger.Warn().Str("id", id).Err(err).Send()
} else {
msg, err := retrieveTRexSettings(client, m, id)
if err != nil {
return err
}
// Log the config that will be sent to TREX
config, err := json.Marshal(msg)
if err != nil {
return err
}
logger.Info().Msgf("TREX Config sent for vin %s: \n%s", id, config)
err = sendTRexSettings(client, id, msg)
if err != nil {
return err
}
}
err = setTRexVersion(client, id, vMap["version"])
if err != nil {
return err
}
err = setDBCVersion(client, id, vMap["dbc_version"])
if err != nil {
return err
}
err = setTRexIP(client, id, vMap["ip"])
if err != nil {
return err
}
err = addICCID(carsDB, id, vMap["iccid"])
if err != nil {
logger.Warn().Err(err).Str("VIN", id).Str("ICCID", vMap["iccid"]).Msg("failed to add iccid to car")
}
uMsg, err := getUserConsentData(id)
if err != nil {
return err
}
err = sendTRexUserConsentData(client, id, uMsg)
if err != nil {
return err
}
err = checkAndRenewCertificate(id)
if err != nil {
return err
}
return nil
}
func checkAndRenewCertificate(id string) error {
logger.Debug().Msg("on trex init: checking TBOX cert for vin " + id)
cs := services.GetCertService()
certNeedsRenewal, err := cs.CheckCertificateNeedsRenewal(id, common.CertTBOX)
if err != nil {
return err
} else if certNeedsRenewal {
logger.Debug().Msg("TBOX cert for vin " + id + " is out of date")
cert, err := cs.RenewCertificate(id, common.CertTBOX)
if err != nil {
return err
}
client := services.RedisClientPool().GetFromPool()
defer client.Close()
err = client.SafeQueueMessage(
common.TRex.Key(id),
common.Message{
Handler: "update_cert",
Data: common.UpdateCert{
SSLCertBase64: cert.PublicKey,
},
},
)
if err != nil {
return err
}
}
logger.Debug().Msg("TBOX cert for vin " + id + " is not out of date")
return nil
}
func getUserConsentData(id string) ([]common.UserConsentDataTrexMsg, error) {
var ucdToSend = []common.UserConsentDataTrexMsg{}
resp, err := userconsent.GetUserConsentService().UserConsentByVehicleNumber(id)
if err != nil {
return nil, errors.WithStack(err)
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("call to user-consent/byVehicleNumber endpoint returned " + resp.Status)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.WithStack(err)
}
ucdResult := []common.UserConsentDataResponse{}
err = json.Unmarshal(respBody, &ucdResult)
if err != nil {
return nil, errors.WithStack(err)
}
if len(ucdResult) < 1 {
return ucdToSend, nil
}
// only send to TREX the consents that have the userID of the first consent returned
var userId = ucdResult[0].UserID
for _, ucd := range ucdResult {
if ucd.UserID == userId {
ucdToSend = append(ucdToSend, common.BuildUserConsentTrexMessage(ucd))
}
}
return ucdToSend, nil
}
func sendTRexUserConsentData(r redis.Client, id string, msg []common.UserConsentDataTrexMsg) error {
if len(msg) > 0 {
return r.SafePublishMessage(
common.TRex.Key(id),
common.Message{
Handler: "consent",
Data: msg,
},
)
}
return nil
}
func insertIfNotExist(id string, carsDB queries.CarsInterface) (bool, error) {
car, err := carsDB.SelectByVIN(id)
if err == nil && car != nil {
return car.Blocked, nil
}
if errors.Is(err, pg.ErrNoRows) {
err = nil
c, err := utils.ParseVIN(id)
if err != nil {
return false, errors.WithStack(err)
}
_, err = carsDB.Insert(c)
if err != nil {
return false, errors.WithStack(err)
}
client, err := services.GetMongoClient()
if err != nil {
return false, errors.WithStack(err)
}
v := &mongo.Vehicle{
VIN: id,
CANBus: common.CANBus{Enabled: true, DataLogger: true, DTCEnabled: elptr.ElPtr(false)},
LogLevel: common.UnmarshalLogLevelString(logLevel),
}
err = client.GetVehicles().AddVehicle(v)
if mon.IsDuplicateKeyError(err) {
err = client.GetVehicles().UpdateVehicle(v)
}
if err != nil {
return false, errors.WithStack(err)
}
err = client.GetFleets().AddVehiclesToFleet(defaultFleet, []string{id})
if err != nil {
return false, errors.WithStack(err)
}
return false, nil
}
return false, errors.WithStack(err)
}
func addICCID(db queries.CarsInterface, id, iccid string) error {
// Going to only update where an iccid is valid
if len(iccid) != 20 {
return errInvalidICCID
}
car := &common.Car{ICCID: iccid, VIN: id}
if ct, err := db.UpdateICCID(car); err != nil {
return errors.Wrapf(err, "failed to update car's iccid %s", id)
} else if ct.RowsAffected() == 1 {
logger.Info().Msgf("car %s has been updated with iccid %s", id, iccid)
}
return nil
}
func addTRexSession(client redis.Client, id string) error {
_, err := client.Execute("SADD", redis.CarSessionsKey(), id)
return err
}
func retrieveTRexSettings(r redis.Client, m mongo.Client, id string) (*common.TRexConfigResponse, error) {
return cache.RetrieveVehicleConfig(r, m, id)
}
func sendTRexSettings(r redis.Client, id string, msg *common.TRexConfigResponse) error {
err := r.SafePublishMessage(
common.TRex.Key(id),
common.Message{
Handler: "config",
Data: msg,
},
)
return err
}
func setTRexIP(r redis.Client, vin, ip string) error {
elms := strings.Split(ip, ":")
return r.SetObjectField(redis.CarStateHashKey(vin), state.TREX_IP, elms[0])
}
func setDBCVersion(r redis.Client, vin, version string) error {
return setVersion(r, vin, version, state.DBC_VERSION, common.DBCVersionSource)
}
func setTRexVersion(r redis.Client, vin, version string) error {
return setVersion(r, vin, version, state.TREX_VERSION, common.TREXVersionSource)
}
func setVersion(r redis.Client, vin, version, redisField string, versionSource common.VersionSource) error {
v, err := r.GetObjectField(redis.CarStateHashKey(vin), redisField)
if err != nil && !errors.Is(err, redigo.ErrNil) {
return errors.WithStack(err)
}
if v == version {
return nil
}
db := services.GetDB().GetCarVersionsLog()
err = r.SetObjectField(redis.CarStateHashKey(vin), redisField, version)
if err != nil {
return errors.WithStack(err)
}
_, err = db.LogVersionChange(&common.CarVersionLogs{
VIN: vin,
VersionSource: versionSource,
Version: version,
})
if err != nil {
return errors.WithStack(err)
}
return nil
}
func wakeup(vin string) {
wake := carcommand.NewCarWakeUp(services.GetDB().GetCars(), services.GetSMSClient())
err := wake.WakeUp(vin, false)
loggerdataresp.BadDataError(err)
}