All checks were successful
Build, Push and Run Container / build (push) Successful in 25s
Removes the explicit setting of the "Content-Type" header, as HttpClient handles this automatically based on the content being sent. This change simplifies the code and avoids potential conflicts if the content type is already being set elsewhere.
288 lines
20 KiB
C#
288 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}");
|
|
|
|
//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(); |