All checks were successful
Build, Push and Run Container / build (push) Successful in 26s
Adds named HTTP client configuration with insecure settings to communicate with the tesla_command_proxy, which uses a self-signed certificate. Changes the base address to use HTTPS.
289 lines
20 KiB
C#
289 lines
20 KiB
C#
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
using Microsoft.IdentityModel.Protocols;
|
|
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
|
using ProofOfConcept.Models;
|
|
using ProofOfConcept.Services;
|
|
using ProofOfConcept.Utilities;
|
|
|
|
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
|
|
|
|
var builder = WebApplication.CreateSlimBuilder(args);
|
|
|
|
// Load static web assets manifest (referenced libs + your wwwroot)
|
|
builder.WebHost.UseStaticWebAssets();
|
|
|
|
// builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); });
|
|
|
|
// Add services
|
|
builder.Services.AddOpenApi();
|
|
builder.Services.AddMediator();
|
|
builder.Services.AddMemoryCache();
|
|
builder.Services.AddHybridCache();
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddHealthChecks()
|
|
.AddAsyncCheck("", cancellationToken => Task.FromResult(HealthCheckResult.Healthy()), ["ready"]); //TODO: Check tag
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddHttpClient().AddHttpClient("InsecureClient")
|
|
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
|
{
|
|
ServerCertificateCustomValidationCallback =
|
|
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
|
});
|
|
|
|
builder.Services
|
|
.AddAuthentication(o =>
|
|
{
|
|
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
|
})
|
|
.AddCookie()
|
|
.AddOpenIdConnect(o =>
|
|
{
|
|
// Point directly at the third-party metadata
|
|
// Metadata is wrong... it sets non-existing uris like: "jwks_uri": "https://fleet-auth.tesla.com/oauth2/v3/discovery/thirdparty/keys"
|
|
// o.MetadataAddress = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/thirdparty/.well-known/openid-configuration";
|
|
//
|
|
// // === Use Fleet-Auth third-party OIDC config ===
|
|
// o.Authority = "https://fleet-auth.tesla.com/oauth2/v3/nts";
|
|
//
|
|
// o.Configuration ??= new OpenIdConnectConfiguration();
|
|
// o.Configuration.AuthorizationEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
|
|
// o.Configuration.TokenEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
|
|
// o.Configuration.JwksUri = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/discovery/thirdparty/keys";
|
|
// o.Configuration.EndSessionEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/logout";
|
|
// o.Configuration.UserInfoEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/userinfo";
|
|
//
|
|
// o.Configuration.TokenEndpointAuthMethodsSupported.Clear();
|
|
// o.Configuration.TokenEndpointAuthMethodsSupported.Add("client_secret_post");
|
|
//
|
|
// o.Configuration.ResponseModesSupported.Clear();
|
|
// o.Configuration.ResponseModesSupported.Add("query");
|
|
//
|
|
// o.Configuration.GrantTypesSupported.Clear();
|
|
// o.Configuration.GrantTypesSupported.Add("authorization_code");
|
|
//
|
|
// o.Configuration.SubjectTypesSupported.Clear();
|
|
// o.Configuration.SubjectTypesSupported.Add("public");
|
|
//
|
|
// o.Configuration.ScopesSupported.Clear();
|
|
// o.Configuration.ScopesSupported.Add("openid");
|
|
// o.Configuration.ScopesSupported.Add("email");
|
|
// o.Configuration.ScopesSupported.Add("profile");
|
|
// o.Configuration.ScopesSupported.Add("metadata");
|
|
//
|
|
// o.Configuration.IdTokenSigningAlgValuesSupported.Clear();
|
|
// o.Configuration.IdTokenSigningAlgValuesSupported.Add("RS256");
|
|
//
|
|
// o.Configuration.TokenEndpointAuthSigningAlgValuesSupported.Clear();
|
|
// o.Configuration.TokenEndpointAuthSigningAlgValuesSupported.Add("RS256");
|
|
//
|
|
// o.Configuration.ClaimsSupported.Clear();
|
|
// o.Configuration.ClaimsSupported.Add("iss");
|
|
// o.Configuration.ClaimsSupported.Add("iat");
|
|
// o.Configuration.ClaimsSupported.Add("exp");
|
|
// o.Configuration.ClaimsSupported.Add("nonce");
|
|
// o.Configuration.ClaimsSupported.Add("sub");
|
|
// o.Configuration.ClaimsSupported.Add("aud");
|
|
|
|
o.ConfigurationManager = new TeslaOIDCConfigurationManager("https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/thirdparty/.well-known/openid-configuration");
|
|
|
|
// Standard OIDC web app settings
|
|
o.ResponseType = "code";
|
|
o.UsePkce = true;
|
|
o.SaveTokens = true;
|
|
|
|
o.ClientId = "b2240ee4-332a-4252-91aa-bbcc24f78fdb";
|
|
o.ClientSecret = "ta-secret.YG+XSdlvr6Lv8U-x";
|
|
|
|
// Must exactly match what you registered in Tesla portal
|
|
o.CallbackPath = new PathString("/token-exchange");
|
|
|
|
// Set scopes
|
|
o.Scope.Clear();
|
|
o.Scope.Add("openid");
|
|
o.Scope.Add("offline_access");
|
|
o.Scope.Add("vehicle_device_data");
|
|
o.Scope.Add("vehicle_location");
|
|
|
|
// Optional Tesla flags
|
|
o.AdditionalAuthorizationParameters.Add("require_requested_scopes", "true");
|
|
o.AdditionalAuthorizationParameters.Add("show_keypair_step", "true");
|
|
o.AdditionalAuthorizationParameters.Add("prompt_missing_scopes", "true");
|
|
|
|
o.TokenValidationParameters.ValidateIssuer = true;
|
|
o.TokenValidationParameters.ValidIssuer = "https://fleet-auth.tesla.com/oauth2/v3/nts";
|
|
|
|
// ✅ Add the Fleet API audience to the token POST
|
|
const string FleetApiAudience = "https://fleet-api.prd.eu.vn.cloud.tesla.com"; // set your region base
|
|
o.Events = new OpenIdConnectEvents
|
|
{
|
|
OnAuthorizationCodeReceived = ctx =>
|
|
{
|
|
ctx.TokenEndpointRequest.Parameters["audience"] = FleetApiAudience;
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
|
|
// Auto-refresh keys if Tesla rotates JWKS
|
|
o.RefreshOnIssuerKeyNotFound = true;
|
|
});
|
|
|
|
// Add own services
|
|
builder.Services.AddSingleton<IMessageProcessor, MessageProcessor>();
|
|
builder.Services.AddTransient<ITeslaAuthenticatorService, TeslaAuthenticatorService>();
|
|
|
|
// Add hosted services
|
|
builder.Services.AddHostedService<MQTTServer>();
|
|
builder.Services.AddHostedService<MQTTClient>();
|
|
|
|
//Build app
|
|
WebApplication app = builder.Build();
|
|
|
|
ForwardedHeadersOptions forwardedHeadersOptions = new ForwardedHeadersOptions() { ForwardedHeaders = ForwardedHeaders.All };
|
|
forwardedHeadersOptions.KnownNetworks.Clear();
|
|
forwardedHeadersOptions.KnownProxies.Clear();
|
|
app.UseForwardedHeaders(forwardedHeadersOptions);
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.MapOpenApi();
|
|
app.MapGet("/GetPartnerAuthenticationToken", ([FromServices] TeslaAuthenticatorService service) => service.GetPartnerAuthenticationTokenAsync());
|
|
app.MapGet("/PartnerToken", ([FromQueryAttribute] string json, [FromServices] IMemoryCache memoryCache) =>
|
|
{
|
|
var serializerOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
|
};
|
|
|
|
Token? token = JsonSerializer.Deserialize<Token>(json, serializerOptions);
|
|
if (token is not null)
|
|
memoryCache.Set(Keys.TeslaPartnerToken, token, token.Expires.Subtract(TimeSpan.FromSeconds(5)));
|
|
|
|
return JsonSerializer.Serialize(token, new JsonSerializerOptions() { WriteIndented = true });
|
|
});
|
|
app.MapGet("/CheckRegisteredApplication", ([FromServices] TeslaAuthenticatorService service) => service.CheckApplicationRegistrationAsync());
|
|
app.MapGet("/RegisterApplication", ([FromServices] TeslaAuthenticatorService service) => service.RegisterApplicationAsync());
|
|
app.MapGet("/Authorize", async (IHttpContextAccessor contextAccessor) => await (contextAccessor.HttpContext!).ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/Tesla" }));
|
|
app.MapGet("/KeyPairing", () => Results.Redirect("https://tesla.com/_ak/developer-domain.com"));
|
|
app.MapGet("/Tokens", async (IHttpContextAccessor httpContextAccessor) =>
|
|
{
|
|
var ctx = httpContextAccessor.HttpContext;
|
|
|
|
var accessToken = await ctx.GetTokenAsync("access_token");
|
|
var idToken = await ctx.GetTokenAsync("id_token");
|
|
var refreshToken = await ctx.GetTokenAsync("refresh_token");
|
|
var expiresAtRaw = await ctx.GetTokenAsync("expires_at"); // ISO 8601 string
|
|
|
|
return JsonSerializer.Serialize(new
|
|
{
|
|
AccessToken = accessToken,
|
|
IDToken = idToken,
|
|
RefreshToken = refreshToken,
|
|
ExpiresAtRaw = expiresAtRaw
|
|
});
|
|
});
|
|
app.MapGet("DebugProxy", (IHttpContextAccessor httpContextAccessor) =>
|
|
{
|
|
var ctx = httpContextAccessor.HttpContext!;
|
|
var request = ctx.Request;
|
|
|
|
Dictionary<string, string> headers = new Dictionary<string, string>();
|
|
headers.Add("Host", request.Host.Value ?? "");
|
|
headers.Add("Scheme", request.Scheme);
|
|
headers.Add("Method", request.Method);
|
|
headers.Add("Path", request.Path.Value ?? "");
|
|
headers.Add("QueryString", request.QueryString.Value ?? "");
|
|
headers.Add("RemoteIpAddress", ctx.Connection.RemoteIpAddress?.ToString() ?? "");
|
|
headers.Add("RemotePort", ctx.Connection.RemotePort.ToString());
|
|
headers.Add("LocalIpAddress", ctx.Connection.LocalIpAddress?.ToString() ?? "");
|
|
headers.Add("LocalPort", ctx.Connection.LocalPort.ToString());
|
|
headers.Add("IsHttps", request.IsHttps.ToString());
|
|
headers.Add("X-Forwarded-For", request.Headers["X-Forwarded-For"].ToString());
|
|
headers.Add("X-Forwarded-Proto", request.Headers["X-Forwarded-Proto"].ToString());
|
|
headers.Add("X-Forwarded-Host", request.Headers["X-Forwarded-Host"].ToString());
|
|
headers.Add("X-Forwarded-Port", request.Headers["X-Forwarded-Port"].ToString());
|
|
headers.Add("X-Forwarded-Prefix", request.Headers["X-Forwarded-Prefix"].ToString());
|
|
headers.Add("X-Forwarded-Server", request.Headers["X-Forwarded-Server"].ToString());
|
|
headers.Add("X-Forwarded-Path", request.Headers["X-Forwarded-Path"].ToString());
|
|
headers.Add("X-Forwarded-PathBase", request.Headers["X-Forwarded-PathBase"].ToString());
|
|
headers.Add("X-Forwarded-Query", request.Headers["X-Forwarded-Query"].ToString());
|
|
headers.Add("X-Forwarded-Query-String", request.Headers["X-Forwarded-Query-String"].ToString());
|
|
headers.Add("Connection", request.Headers["Connection"].ToString());
|
|
headers.Add("Accept", request.Headers["Accept"].ToString());
|
|
headers.Add("Accept-Encoding", request.Headers["Accept-Encoding"].ToString());
|
|
headers.Add("Accept-Language", request.Headers["Accept-Language"].ToString());
|
|
headers.Add("Cache-Control", request.Headers["Cache-Control"].ToString());
|
|
headers.Add("Content-Length", request.Headers["Content-Length"].ToString());
|
|
headers.Add("Content-Type", request.Headers["Content-Type"].ToString());
|
|
headers.Add("Cookie", request.Headers["Cookie"].ToString());
|
|
headers.Add("Pragma", request.Headers["Pragma"].ToString());
|
|
headers.Add("Referer", request.Headers["Referer"].ToString());
|
|
|
|
String json = JsonSerializer.Serialize(headers, new JsonSerializerOptions() { WriteIndented = true });
|
|
|
|
return json;
|
|
});
|
|
app.MapGet("/Tesla", async ([FromServices] ILogger<Configurator> logger, [FromServices] IHttpContextAccessor httpContextAccessor, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
HttpContext? context = httpContextAccessor.HttpContext;
|
|
|
|
if (context is null)
|
|
return Results.BadRequest();
|
|
|
|
string? access_token = await context.GetTokenAsync("access_token");
|
|
string? refresh_token = await context.GetTokenAsync("refresh_token");
|
|
logger.LogCritical("User has access_token: {access_token} and refresh_token: {refresh_token}", access_token, refresh_token);
|
|
|
|
if (String.IsNullOrEmpty(access_token))
|
|
return Results.LocalRedirect("/Authorize");
|
|
|
|
HttpClient client = httpClientFactory.CreateClient("InsecureClient");
|
|
client.BaseAddress = new Uri("https://tesla_command_proxy");
|
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {access_token}");
|
|
client.DefaultRequestHeaders.Add("Content-Type", "application/json");
|
|
|
|
//Get cars
|
|
VehiclesEnvelope? vehiclesEnvelope = await client.GetFromJsonAsync<VehiclesEnvelope>("/api/1/vehicles");
|
|
string[] vins = vehiclesEnvelope?.Response.Select(x => x.Vin ?? "").Where(v => !String.IsNullOrWhiteSpace(v)).ToArray() ?? Array.Empty<string>();
|
|
logger.LogCritical("User has access to {count} cars: {vins}", vins.Length, String.Join(", ", vins));
|
|
|
|
TelemetryConfigRequest configRequest = new TelemetryConfigRequest()
|
|
{
|
|
Vins = new List<string>(vins),
|
|
Config = new TelemetryConfig()
|
|
{
|
|
Hostname = "tesla-connector.automatic-parking.app",
|
|
Port = 443,
|
|
CertificateAuthority = "-----BEGIN CERTIFICATE-----\\nMIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw\\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\\nWhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\\nRW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK\\na2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO\\nVHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw\\ngfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\\nATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw\\ni9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\\nAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\\nBAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\\nY3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C\\n2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+\\nbcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG\\n6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV\\nXP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO\\nkoAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq\\ncm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI\\nE1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e\\nK1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX\\nGWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL\\nsVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd\\nVQD9F6Na/+zmXCc=\\n-----END CERTIFICATE-----\\n---\\nServer certificate\\nsubject=CN=tesla-connector.automatic-parking.app\\nissuer=C=US, O=Let's Encrypt, CN=E5\\n---\\nAcceptable client certificate CA names\\nCN=Tesla Issuing CA, O=Tesla Motors, L=Palo Alto, ST=California, C=US\\nCN=Tesla Motors GF Austin Product Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors GF Berlin Product Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors GF0 Product Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors GF3 Product Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors GF3 Product RSA Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors Product Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors Product RSA Issuing CA, OU=Motors, OU=PKI, O=Tesla Inc., C=US\\nCN=Tesla Motors Products CA\\nCN=Tesla Motors Root CA\\nCN=Tesla Policy CA, O=Tesla Motors, L=Palo Alto, ST=California, C=US\\nCN=Tesla Product RSA Root CA, OU=PKI, O=Tesla, C=US\\nCN=Tesla Product Root CA, OU=PKI, O=Tesla, C=US\\nCN=Tesla Root CA, O=Tesla Motors, L=Palo Alto, ST=California, C=US\\nRequested Signature Algorithms: RSA-PSS+SHA256:ECDSA+SHA256:ed25519:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA384:ECDSA+SHA512:RSA+SHA1:ECDSA+SHA1\\nShared Requested Signature Algorithms: RSA-PSS+SHA256:ECDSA+SHA256:ed25519:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA384:ECDSA+SHA512\\nPeer signing digest: SHA256\\nPeer signature type: ECDSA\\nServer Temp Key: X25519, 253 bits\\n---\\nSSL handshake has read 3874 bytes and written 439 bytes\\nVerification: OK\\n---\\nNew, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256\\nServer public key is 256 bit\\nThis TLS version forbids renegotiation.\\nNo ALPN negotiated\\nEarly data was not sent\\nVerify return code: 0 (ok)\\n---\\n-----BEGIN CERTIFICATE-----\\nMIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\\nWhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\\nZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\\nMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\\nh77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\\n0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\\nA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\\nT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\\nB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\\nB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\\nKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\\nOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\\njh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\\nqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\\nrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\\nhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\\nubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\\n3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\\nNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\\nORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\\nTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\\njNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\\noyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\\n4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\\nmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\\nemyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\\n-----END CERTIFICATE-----\\n",
|
|
Fields = new Dictionary<string, TelemetryFieldConfig>()
|
|
{
|
|
{ "Gear", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Locked", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "DriverSeatOccupied", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "GpsState", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Location", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
}
|
|
}
|
|
};
|
|
|
|
HttpResponseMessage response = await client.PostAsJsonAsync("/api/1/vehicles/fleet_telemetry_config", configRequest);
|
|
return Results.Ok(response.Content.ReadAsStringAsync());
|
|
});
|
|
}
|
|
|
|
//Map static assets
|
|
app.MapStaticAssets();
|
|
|
|
//TODO: Build a middleware that responds with 503 if the public key is not registered at Tesla
|
|
|
|
app.MapRazorPages();
|
|
|
|
app.Run(); |