337 lines
8.2 KiB
Go
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)
|
|
}
|