diff --git a/Faturas/Fatura.cs b/Faturas/Fatura.cs
index 36354f2..6bead12 100644
--- a/Faturas/Fatura.cs
+++ b/Faturas/Fatura.cs
@@ -34,28 +34,24 @@ namespace Faturas
/// Identificador da fatura no serviço 4docs.
/// Caminho completo para o arquivo PDF da fatura local.
/// Instância de HttpClient usada para realizar chamadas HTTP à API.
- public Fatura(string id, string fatura_path, HttpClient httpClient)
+ public Fatura(string id, JsonElement fatura_Parsed, string fatura_path)
{
// Utilizado para gerar novo token
// this.token = Req_token(HttpClient).ToString();
- HttpResponseMessage fatura_response = GetStatus(httpClient, Token, id);
- if (fatura_response.IsSuccessStatusCode)
+ faturaParsed = fatura_Parsed;
+ Agrupada = faturaParsed.TryGetProperty("multiple", out var _);
+ Status = faturaParsed.GetProperty("newStatus").GetString();
+ Arquivo = new FileInfo(fatura_path);
+ this.id = id;
+
+ if (faturaParsed.TryGetProperty("external", out var _))
{
- faturaParsed = JsonDocument.Parse(fatura_response.Content.ReadAsStringAsync().Result).RootElement;
- Agrupada = faturaParsed.TryGetProperty("multiple", out var _);
- Status = faturaParsed.GetProperty("status").GetString();
- Arquivo = new FileInfo(fatura_path);
- this.id = id;
+ pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString() !).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32();
+ }
- if (faturaParsed.GetProperty("external").GetString() is not null)
- {
- pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString() !).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32();
- }
-
- if (Agrupada)
- {
- Agrupada_children = faturaParsed.GetProperty("children").EnumerateArray();
- }
+ if (Agrupada)
+ {
+ Agrupada_children = faturaParsed.GetProperty("children").EnumerateArray();
}
}
diff --git a/Webhook 4docs/Program.cs b/Webhook 4docs/Program.cs
index 52d21d1..0c1a48c 100644
--- a/Webhook 4docs/Program.cs
+++ b/Webhook 4docs/Program.cs
@@ -13,33 +13,14 @@ namespace Webhook_4docs
{
PermitLimit = 1
});
+
public static void Main(string[] args)
{
- static string IndexedFilename(string stub, string extension)
- {
- int ix = 0;
- string? filename = null;
-
- if (File.Exists(String.Format("{0}.{1}", stub, extension)))
- {
- do
- {
- ix++;
- filename = String.Format("{0} ({1}).{2}", stub, ix, extension);
- } while (File.Exists(filename));
- }
- else
- {
- filename = String.Format("{0}.{1}", stub, extension);
- }
- return filename;
- }
-
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json");
var connectionString = builder.Configuration.GetConnectionString("WebhookDbContext");
- string? appFolder = builder.Configuration["PathBase"];
+ var appFolder = builder.Configuration["PathBase"];
builder.Services.AddDbContext(options =>
options.UseNpgsql(connectionString)
@@ -51,213 +32,382 @@ namespace Webhook_4docs
loggingBuilder.AddConsole();
});
- // Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
-
var logger = app.Services.GetRequiredService>();
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService();
- dbContext.Database.EnsureCreated();
dbContext.Database.Migrate();
}
- string fatura_arquivo;
- // Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
-
- app.UsePathBase(appFolder);
+
+ if (!string.IsNullOrWhiteSpace(appFolder))
+ {
+ app.UsePathBase(appFolder);
+ }
app.UseRouting();
-
app.UseStaticFiles();
-
app.UseAuthorization();
app.MapRazorPages();
- app.Use((context, next) =>
+ // PATCH /api -> health ou invoice (json)
+ app.MapPatch("/api", async context =>
{
- context.Request.PathBase = new PathString(appFolder);
- return next(context);
+ var logger = context.RequestServices.GetRequiredService>();
+ var bodyText = await SafeReadBodyAsync(context.Request, logger);
+ if (bodyText is null)
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid JSON");
+ return;
+ }
+
+ if (!TryParseJson(bodyText, out var root, logger))
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid JSON");
+ return;
+ }
+
+ // Health: healthy + blame + locationID
+ bool isHealth =
+ root.TryGetProperty("healthy", out _) &&
+ root.TryGetProperty("blame", out _) &&
+ root.TryGetProperty("locationID", out _);
+
+ if (isHealth)
+ {
+ await ProcessHealthAsync(root, GetCaminhoDb(), logger);
+ context.Response.StatusCode = StatusCodes.Status200OK;
+ return;
+ }
+
+ // Invoice: requestID/requestId + json
+ bool hasRequestId = root.TryGetProperty("requestID", out var reqIdEl);
+ bool hasJson = root.TryGetProperty("json", out var dataEl);
+
+ if (hasRequestId && hasJson)
+ {
+ await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
+
+ context.Response.StatusCode = StatusCodes.Status200OK;
+ return;
+ }
+
+ logger.LogDebug("PATCH /api payload não reconhecido.");
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
});
- // Endpoint para \api
- app.UseEndpoints(endpoints =>
+ // PUT /api -> somente invoice (result)
+ app.MapPut("/api", async context =>
{
- endpoints.MapControllers();
- endpoints.MapPatch("/api", async context =>
+ var logger = context.RequestServices.GetRequiredService>();
+ var bodyText = await SafeReadBodyAsync(context.Request, logger);
+ if (bodyText is null)
{
-
- string requestBody;
- using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true))
- {
- requestBody = await reader.ReadToEndAsync();
- }
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid JSON");
+ return;
+ }
- var JsonBody = JsonDocument.Parse(requestBody).RootElement;
- string CaminhoDB = "X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
-
- if (JsonBody.TryGetProperty("requestID", out JsonElement fatura_ID_json))
- {
- string fatura_ID = fatura_ID_json.ToString();
-
- if (!JsonBody.TryGetProperty("json", out JsonElement root)) { return; }
-
- JsonElement DadosJson = JsonDocument.Parse(root.ToString()).RootElement;
-
- string tipoDocumento = root.Get("documentType").ToString() ?? string.Empty.ToLower();
-
- if (tipoDocumento != string.Empty && tipoDocumento != "nota_fiscal")
- {
- return;
- }
-
- Fatura fatura = new(fatura_ID, JsonBody);
- bool completed = false;
-
- while (!completed)
- {
- var connLease = await connRateLimiter.AcquireAsync();
-
- if (connLease.IsAcquired)
- {
- using (OleDbConnection conn = new(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
- {
- if (conn != null)
- {
- if (conn.State == System.Data.ConnectionState.Closed)
- {
- await conn.OpenAsync();
- }
-
- try
- {
- fatura.Processar(conn);
- }
- catch (Exception ex)
- {
- completed = true;
- connLease.Dispose();
- throw new Exception(ex.Message);
- }
- }
- }
- completed = true;
- connLease.Dispose();
- }
- }
-
- string? status = fatura.Status;
-
- int status_id = 0;
-
- switch (status)
- {
- case ("FATURA DUPLICADA NO BD"):
- status_id = 4;
- break;
- case ("UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD"):
- status_id = 5;
- break;
- case ("FATURA INCLUIDA NO BD"):
- status_id = 6;
- break;
- case (_):
- status = "ERRO";
- status_id = 7;
- break;
- }
-
- try
- {
- string path = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
- if (status_id == 6 && fatura.PastaTUSD!.Exists)
- {
- path = fatura.PastaTUSD!.FullName;
- }
-
- if (status_id != 4)
- {
- fatura_arquivo = IndexedFilename($@"{path}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
- CriarArquivo(fatura_arquivo, JsonBody.GetProperty("pdfFile").ToString());
-
- var DatabaseModel = new ProcessedInvoices
- {
- InvoiceId = Int32.Parse(fatura_ID),
- DateTimeProcessed = DateTime.UtcNow,
- Status = status,
- InvoicePath = fatura_arquivo
- };
-
- logger.LogInformation("Fatura incluída no BD");
-
- logger.LogInformation("Fatura salva na pasta " + fatura_arquivo);
-
- using (var scope = app.Services.CreateScope())
- {
- var dbContext = scope.ServiceProvider.GetRequiredService();
- dbContext.ProcessedInvoices.Add(DatabaseModel);
- await dbContext.SaveChangesAsync();
- }
- }
- else
- {
- logger.LogInformation("Fatura duplicada no BD");
-
- logger.LogInformation("Fatura descartada");
- }
- }
- catch
- {
- logger.LogError("Erro no processamento da fatura");
- }
- }
- else if (JsonBody.TryGetProperty("healthy", out _) && JsonBody.TryGetProperty("blame", out _) && JsonBody.TryGetProperty("locationID", out _))
- {
- double errorID = 0;
-
- switch (JsonBody.GetProperty("healthy").GetBoolean(), JsonBody.GetProperty("blame").ToString().ToLower())
- {
- case (false, "user"):
- logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Sem acesso");
- errorID = 1;
- break;
- case (false, "system"):
- logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro de sistema");
- errorID = 2;
- break;
- case (false, _):
- logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro " + JsonBody.GetProperty("blame").ToString());
- errorID = 3;
- break;
- case (true, _):
- //logger.LogInformation("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " está saudável");
- break;
- }
-
- var errorText = GetErrorText(JsonBody);
- await UpdateErrorIdStatusAsync(CaminhoDB, JsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
-
- }
- });
- //implementação futura para receber as faturas enviadas via api de forma assíncrona - Upload4Docs (substituir parte do agendador de tarefas)
- endpoints.MapPut("/api", async context =>
+ if (!TryParseJson(bodyText, out var root, logger))
{
- var a = 10;
- a++;
- await Task.CompletedTask;
- });
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid JSON");
+ return;
+ }
+
+ // Invoice: requestID/requestId + result
+ bool hasRequestId = root.TryGetProperty("requestId", out var reqIdEl);
+ bool hasResult = root.TryGetProperty("result", out var dataEl);
+
+ if (!(hasRequestId && hasResult))
+ {
+ logger.LogDebug("PUT /api payload inválido para este endpoint.");
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ return;
+ }
+
+ await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
+
+ context.Response.StatusCode = StatusCodes.Status200OK;
});
+
app.Run();
+
+ string GetCaminhoDb() =>
+ @"X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
+ }
+
+ // ===== Utilitários de request/JSON =====
+ static async Task SafeReadBodyAsync(HttpRequest request, ILogger logger, int maxBytes = 10 * 1024 * 1024)
+ {
+ request.EnableBuffering();
+ using var ms = new MemoryStream(capacity: Math.Min(maxBytes, 1024 * 64));
+ await request.Body.CopyToAsync(ms);
+ if (ms.Length > maxBytes)
+ {
+ logger.LogWarning("Payload excede o limite de {MaxBytes} bytes.", maxBytes);
+ return null;
+ }
+ ms.Position = 0;
+ using var reader = new StreamReader(ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
+ return await reader.ReadToEndAsync();
+ }
+
+ static bool TryParseJson(string text, out JsonElement root, ILogger logger)
+ {
+ try
+ {
+ root = JsonDocument.Parse(text).RootElement.Clone();
+ return true;
+ }
+ catch (JsonException ex)
+ {
+ logger.LogWarning(ex, "Falha ao parsear JSON.");
+ root = default;
+ return false;
+ }
+ }
+
+ // ===== Processamento de Invoice =====
+ static async Task ProcessInvoiceAsync(JsonElement root, JsonElement dataEl, string caminhoDB, ILogger logger, WebApplication app, string fatura_ID)
+ {
+ var tipoDocumento = dataEl.Get("documentType")?.GetString()?.ToLowerInvariant()
+ ?? string.Empty;
+
+ var deveProcessar = string.IsNullOrEmpty(tipoDocumento) || tipoDocumento == "nota_fiscal";
+ if (!deveProcessar)
+ {
+ logger.LogInformation("Invoice ignorada por tipoDocumento: {TipoDocumento}", tipoDocumento);
+ return;
+ }
+
+ var hasPath = !string.IsNullOrEmpty(root.GetProperty("clientData").ToString());
+
+
+ if (hasPath)
+ {
+ root.GetProperty("clientData").TryGetProperty("fatura_PATH", out var pathEl);
+ await ProcessInvoiceFromPath(root, fatura_ID, pathEl!.GetString()!, caminhoDB, logger, app);
+ }
+ else
+ {
+ await ProcessInvoiceFromJson(root, fatura_ID, caminhoDB, logger, app);
+ }
+ }
+
+ static async Task ProcessInvoiceFromPath(JsonElement root, string fatura_ID, string pdfPath, string caminhoDB, ILogger logger, WebApplication app)
+ {
+ var fatura = new Fatura(fatura_ID, root, pdfPath);
+ var completed = false;
+
+ while (!completed)
+ {
+ var connLease = await connRateLimiter.AcquireAsync();
+ if (!connLease.IsAcquired) continue;
+
+ try
+ {
+ using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
+ if (conn.State == System.Data.ConnectionState.Closed)
+ {
+ await conn.OpenAsync();
+ }
+
+ fatura.Processar(conn);
+ completed = true;
+ }
+ catch (Exception ex)
+ {
+ completed = true;
+ throw new Exception(ex.Message);
+ }
+ finally
+ {
+ connLease.Dispose();
+ }
+ }
+
+ await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
+ {
+ try
+ {
+ if (!File.Exists(targetPath))
+ {
+ fatura.Mover(false);
+ }
+ }
+ catch (IOException ioEx)
+ {
+ logger.LogWarning(ioEx, "Falha ao copiar arquivo para {Target}", targetPath);
+ }
+ await Task.CompletedTask;
+ }, logger, app);
+ }
+
+ static async Task ProcessInvoiceFromJson(JsonElement root, string fatura_ID, string caminhoDB, ILogger logger, WebApplication app)
+ {
+ var fatura = new Fatura(fatura_ID, root);
+ var completed = false;
+
+ while (!completed)
+ {
+ var connLease = await connRateLimiter.AcquireAsync();
+ if (!connLease.IsAcquired) continue;
+
+ try
+ {
+ using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
+ if (conn.State == System.Data.ConnectionState.Closed)
+ {
+ await conn.OpenAsync();
+ }
+
+ fatura.Processar(conn);
+ completed = true;
+ }
+ catch (Exception ex)
+ {
+ completed = true;
+ throw new Exception(ex.Message);
+ }
+ finally
+ {
+ connLease.Dispose();
+ }
+ }
+
+ await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
+ {
+ var base64 = root.Get("pdfFile")?.GetString();
+
+ if (string.IsNullOrWhiteSpace(base64))
+ {
+ logger.LogWarning("Campo pdfFile/pdfFileBase64 ausente no payload.");
+ return;
+ }
+
+ CriarArquivo(targetPath, base64!);
+ await Task.CompletedTask;
+ }, logger, app);
+ }
+
+ static async Task FinalizarPersistenciaAsync(Fatura fatura, string fatura_ID, Func persistFile, ILogger logger, WebApplication app)
+ {
+ var status = fatura.Status;
+ var status_id = status switch
+ {
+ "FATURA DUPLICADA NO BD" => 4,
+ "UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD" => 5,
+ "FATURA INCLUIDA NO BD" => 6,
+ _ => 7
+ };
+ status = status_id == 7 ? "ERRO" : status;
+
+ try
+ {
+ var pathBase = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
+ if (status_id == 6 && fatura.PastaTUSD!.Exists)
+ {
+ pathBase = fatura.PastaTUSD!.FullName;
+ }
+
+ if (status_id != 4)
+ {
+ Directory.CreateDirectory(pathBase);
+ var targetFile = IndexedFilename($@"{pathBase}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
+
+ await persistFile(targetFile);
+
+ var dbModel = new ProcessedInvoices
+ {
+ InvoiceId = int.Parse(fatura_ID),
+ DateTimeProcessed = DateTime.UtcNow,
+ Status = status,
+ InvoicePath = targetFile
+ };
+
+ logger.LogInformation("Fatura incluída no BD");
+ logger.LogInformation("Fatura salva na pasta {Arquivo}", targetFile);
+
+ using var scope = app.Services.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ dbContext.ProcessedInvoices.Add(dbModel);
+ await dbContext.SaveChangesAsync();
+ }
+ else
+ {
+ logger.LogInformation("Fatura duplicada no BD");
+ logger.LogInformation("Fatura descartada");
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Erro no processamento da fatura");
+ }
+ }
+
+ public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
+ {
+ if (!File.Exists(fatura_arquivo))
+ {
+ byte[] bytes = Convert.FromBase64String(pdfFile64);
+ using var stream = new FileStream(fatura_arquivo, FileMode.CreateNew, FileAccess.Write, FileShare.None);
+ using var writer = new BinaryWriter(stream);
+ writer.Write(bytes, 0, bytes.Length);
+ }
+ }
+
+ static string IndexedFilename(string stub, string extension)
+ {
+ int ix = 0;
+ string? filename;
+
+ if (File.Exists($"{stub}.{extension}"))
+ {
+ do
+ {
+ ix++;
+ filename = $"{stub} ({ix}).{extension}";
+ } while (File.Exists(filename));
+ }
+ else
+ {
+ filename = $"{stub}.{extension}";
+ }
+ return filename;
+ }
+
+ // ===== Processamento de Health =====
+ static async Task ProcessHealthAsync(JsonElement jsonBody, string caminhoDB, ILogger logger)
+ {
+ var healthy = jsonBody.GetProperty("healthy").GetBoolean();
+ var blame = jsonBody.GetProperty("blame").GetString()?.ToLowerInvariant() ?? string.Empty;
+ var locationId = jsonBody.GetProperty("locationID").ToString();
+
+ var (errorID, logAction) = (healthy, blame) switch
+ {
+ (false, "user") => (1d, (Action)(() => logger.LogError("Loc ID: {LocationId} Sem acesso", locationId))),
+ (false, "system") => (2d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro de sistema", locationId))),
+ (false, var other) => (3d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro {Blame}", locationId, other))),
+ (true, _) => (0d, (Action)(() => { }))
+ };
+
+ logAction();
+
+ var errorText = GetErrorText(jsonBody);
+ await UpdateErrorIdStatusAsync(caminhoDB, jsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
}
//tenta pegar o deactivationReport, se não conseguir ou for null, tenta pegar o websiteText, se não conseguir ou for null, tenta pegar o systemText. As propriedades podem não exisir
public static string GetErrorText(JsonElement root)
@@ -283,6 +433,7 @@ namespace Webhook_4docs
return errorText;
}
+
public static async Task UpdateErrorIdStatusAsync(string CaminhoDB, double location_id, double errorID, string systemText, ILogger logger)
{
int test = 0;
@@ -297,40 +448,35 @@ namespace Webhook_4docs
{
try
{
- using (OleDbConnection conn = new (@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
+ using (OleDbConnection conn = new(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
{
await conn.OpenAsync();
- using (OleDbCommand cmd = new (
+ using (OleDbCommand cmd = new(
@"UPDATE AgVirtual4Docs
SET errorID = @errorID, status = @status
WHERE location_id = @location_id", conn))
{
- // Adiciona parâmetros de forma segura
cmd.Parameters.AddWithValue("@errorID", errorID);
cmd.Parameters.AddWithValue("@status", systemText);
cmd.Parameters.AddWithValue("@location_id", location_id);
- // Executa o comando e captura o resultado
test = await cmd.ExecuteNonQueryAsync();
}
}
- break; // Sai do loop em caso de sucesso
+ break;
}
catch (OleDbException ex)
{
- // Registra o erro
logger.LogInformation($"Erro no OleDb update: {ex.Message} (Tentativa {attempt + 1} de {maxRetries})");
if (attempt < maxRetries - 1)
{
- // Aguarda antes de tentar novamente
- await Task.Delay(1000 * (int)Math.Pow(2, attempt)); // Retry com Backoff Exponencial
+ await Task.Delay(1000 * (int)Math.Pow(2, attempt));
}
else
{
- // Propaga a exceção na última tentativa
throw;
}
}
@@ -339,32 +485,17 @@ namespace Webhook_4docs
connLease.Dispose();
}
- attempt++; // Incrementa a tentativa após adquirir o lease, mesmo que falhe
+ attempt++;
}
else
{
- // Aguarda um curto período antes de tentar novamente se não conseguiu adquirir o lease
await Task.Delay(200);
}
}
-
return test;
}
- public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
- {
- //string fatura_arquivo = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\test.pdf";
- if (!File.Exists(fatura_arquivo))
- {
- byte[] bytes = Convert.FromBase64String(pdfFile64);
-
- System.IO.FileStream stream = new(fatura_arquivo, FileMode.CreateNew);
- System.IO.BinaryWriter writer =
- new(stream);
- writer.Write(bytes, 0, bytes.Length);
- writer.Close();
- }
- }
}
+
public static partial class JsonExtensions
{
public static JsonElement? Get(this JsonElement element, string name) =>
@@ -375,7 +506,7 @@ namespace Webhook_4docs
{
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
return null;
- // Throw if index < 0
+
return index < element.GetArrayLength() ? element[index] : null;
}
}
diff --git a/Webhook 4docs/appsettings.json b/Webhook 4docs/appsettings.json
index a710ae7..8c2e770 100644
--- a/Webhook 4docs/appsettings.json
+++ b/Webhook 4docs/appsettings.json
@@ -1,4 +1,4 @@
-{
+{
"Logging": {
"LogLevel": {
"Default": "Information",
@@ -17,7 +17,7 @@
"Kestrel": {
"Endpoints": {
"HttpLocalhost": {
- "Url": "http://localhost:8664/"
+ "Url": "http://*:8664/"
}
}
},