Compare commits
3 Commits
37de164c4d
...
393f40fa41
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
393f40fa41 | ||
|
|
653de55fb4 | ||
|
|
93ce4db4d7 |
42
Chat.Domain/Tools/Base64ToFormFile.cs
Normal file
42
Chat.Domain/Tools/Base64ToFormFile.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Chat.Domain.Tools
|
||||||
|
{
|
||||||
|
public class Base64ToFormFile
|
||||||
|
{
|
||||||
|
public static IFormFile ConvertBase64ToFormFile(string base64String, string fileName)
|
||||||
|
{
|
||||||
|
// Remove o prefixo "data:image/png;base64," se existir
|
||||||
|
if (base64String.Contains(","))
|
||||||
|
{
|
||||||
|
base64String = base64String.Split(',')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converte base64 para bytes
|
||||||
|
byte[] bytes = Convert.FromBase64String(base64String);
|
||||||
|
|
||||||
|
// Cria um stream de memória com os bytes
|
||||||
|
var stream = new MemoryStream(bytes);
|
||||||
|
|
||||||
|
// Cria um IFormFile usando o stream
|
||||||
|
var formFile = new FormFile(
|
||||||
|
baseStream: stream,
|
||||||
|
baseStreamOffset: 0,
|
||||||
|
length: bytes.Length,
|
||||||
|
name: "file",
|
||||||
|
fileName: fileName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Headers = new HeaderDictionary(),
|
||||||
|
ContentType = "image/png"
|
||||||
|
};
|
||||||
|
|
||||||
|
return formFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Chat.sln
Normal file
37
Chat.sln
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.7.34031.279
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatMvc", "Chat\ChatMvc.csproj", "{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chat.Infra", "Chat.Infra\Chat.Infra.csproj", "{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chat.Domain", "Chat.Domain\Chat.Domain.csproj", "{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {EC4AB36F-49CB-4819-ACA4-3A14CB1D9B50}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@ -26,6 +26,7 @@
|
|||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.3.0" />
|
<PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.3.0" />
|
||||||
<PackageReference Include="Stripe.net" Version="45.13.0" />
|
<PackageReference Include="Stripe.net" Version="45.13.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -36,6 +37,10 @@
|
|||||||
<None Include="wwwroot\js\notif.js" />
|
<None Include="wwwroot\js\notif.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Chat.Domain\Chat.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Resource.Designer.cs">
|
<Compile Update="Resource.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
|
|||||||
@ -7,6 +7,10 @@ using System.Text.Json;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Microsoft.AspNetCore.Antiforgery;
|
using Microsoft.AspNetCore.Antiforgery;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Chat.Domain.Tools;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace ChatMvc.Controllers
|
namespace ChatMvc.Controllers
|
||||||
{
|
{
|
||||||
@ -27,12 +31,14 @@ namespace ChatMvc.Controllers
|
|||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
|
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
|
||||||
|
var token = HttpContext.GetTokenAsync("access_token").Result;
|
||||||
|
ViewBag.Token = token;
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("chat/proxy-response")]
|
[HttpPost("chat/proxy-response")]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> ProxyResponse(string sessionId, string message)
|
public async Task<IActionResult> ProxyResponse([FromForm] string sessionId, [FromForm] string message, [FromForm] IFormFile file1 = null, [FromForm] IFormFile file2 = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -41,20 +47,60 @@ namespace ChatMvc.Controllers
|
|||||||
return BadRequest("Requisição inválida");
|
return BadRequest("Requisição inválida");
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
var userId = User.Claims.FirstOrDefault(f => f.Type == "UserId")?.Value;
|
||||||
var baseUrl = _configuration["ExternalApiBaseUrl"];
|
var name = User.Claims.FirstOrDefault(f => f.Type == "FirstName")?.Value;
|
||||||
var token = Request.Headers["Authorization"].ToString();
|
var company = User.Claims.FirstOrDefault(f => f.Type == "CompanyName")?.Value;
|
||||||
|
var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal")?.Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
return BadRequest("Token não encontrado");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se há arquivos e palavras-chave
|
||||||
|
var keyWords = new[] { "diferença", "diferenças", "comparação", "comparar", "compare" };
|
||||||
|
bool hasKeyword = keyWords.Any(k => message.ToLower().Contains(k.ToLower()));
|
||||||
|
bool hasBothFiles = file1 != null && file2 != null;
|
||||||
|
bool hasOneFile = file1 != null || file2 != null;
|
||||||
|
bool isPDF = file1 != null && file1.ContentType == "application/pdf" && file2 != null && file2.ContentType == "application/pdf";
|
||||||
|
bool isImage = file1 != null && file1.ContentType.Contains("image") && file2 != null && file2.ContentType.Contains("image");
|
||||||
|
|
||||||
|
// Se tem apenas um arquivo ou se tem arquivos mas não tem palavra-chave
|
||||||
|
if ((hasOneFile && !hasBothFiles) || (hasBothFiles && !hasKeyword))
|
||||||
|
{
|
||||||
|
return Ok("Por favor, especifique o que deseja fazer com os arquivos. " +
|
||||||
|
"Para comparar dois PDFs, inclua palavras como 'diferença' ou 'comparação' em sua mensagem.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
client.DefaultRequestHeaders.Authorization =
|
client.DefaultRequestHeaders.Authorization =
|
||||||
new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
|
new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
|
||||||
|
|
||||||
var response = await client.GetAsync(
|
var baseUrl = _configuration["ExternalApiBaseUrl"];
|
||||||
$"{baseUrl}/chat/response?sessionId={sessionId}&message={message}");
|
|
||||||
|
if (hasBothFiles && hasKeyword && isPDF)
|
||||||
|
{
|
||||||
|
var content = await CompareFiles(baseUrl, sessionId, client, file1, file2);
|
||||||
|
return Ok(content);
|
||||||
|
}
|
||||||
|
else if (hasBothFiles && hasKeyword && isImage)
|
||||||
|
{
|
||||||
|
var content = await CompareFilesImage(baseUrl, sessionId, client, file1, file2);
|
||||||
|
return Ok(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatContent = new FormUrlEncodedContent(new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("sessionId", sessionId),
|
||||||
|
new KeyValuePair<string, string>("message", message)
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await client.PostAsync($"{baseUrl}/chat/response", chatContent);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
return Ok(content);
|
return Ok(responseContent);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -62,6 +108,112 @@ namespace ChatMvc.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> CompareFiles(string? baseUrl, string sessionId, HttpClient? client, [FromForm] IFormFile? file1 = null, [FromForm] IFormFile? file2 = null)
|
||||||
|
{
|
||||||
|
// Primeiro, comparar os PDFs
|
||||||
|
var formData = new MultipartFormDataContent();
|
||||||
|
var content1 = new StreamContent(file1.OpenReadStream());
|
||||||
|
var content2 = new StreamContent(file2.OpenReadStream());
|
||||||
|
content1.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||||
|
content2.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||||
|
formData.Add(content1, "File1", file1.FileName);
|
||||||
|
formData.Add(content2, "File2", file2.FileName);
|
||||||
|
|
||||||
|
var comparisonResponse = await client.PostAsync(
|
||||||
|
"https://localhost:7200/api/PdfComparison/compare",
|
||||||
|
formData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!comparisonResponse.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new Exception("Erro ao comparar os PDFs");
|
||||||
|
}
|
||||||
|
|
||||||
|
var differences = await comparisonResponse.Content.ReadAsStringAsync();
|
||||||
|
var dynamicContent = JsonConvert.DeserializeObject<dynamic>(differences);
|
||||||
|
var formattedDifferences = FormatDifferencesForChat(dynamicContent);
|
||||||
|
|
||||||
|
var promptMessage = $"Ao comparar dois textos obtive as diferenças do json. Você poderia explicar e resumir: {formattedDifferences}";
|
||||||
|
|
||||||
|
var chatContentSend = new FormUrlEncodedContent(new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("sessionId", sessionId),
|
||||||
|
new KeyValuePair<string, string>("message", promptMessage)
|
||||||
|
});
|
||||||
|
|
||||||
|
var chatResponse = await client.PostAsync($"{baseUrl}/chat/response-fromia", chatContentSend);
|
||||||
|
|
||||||
|
chatResponse.EnsureSuccessStatusCode();
|
||||||
|
var content = await chatResponse.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CompareFilesImage(string? baseUrl, string sessionId, HttpClient? client, [FromForm] IFormFile? file1 = null, [FromForm] IFormFile? file2 = null)
|
||||||
|
{
|
||||||
|
var formData = new MultipartFormDataContent();
|
||||||
|
var content1 = new StreamContent(file1.OpenReadStream());
|
||||||
|
var content2 = new StreamContent(file2.OpenReadStream());
|
||||||
|
content1.Headers.ContentType = new MediaTypeHeaderValue(file1.ContentType);
|
||||||
|
content2.Headers.ContentType = new MediaTypeHeaderValue(file2.ContentType);
|
||||||
|
formData.Add(content1, "Drawing1", file1.FileName);
|
||||||
|
formData.Add(content2, "Drawing2", file2.FileName);
|
||||||
|
|
||||||
|
var comparisonResponse = await client.PostAsync(
|
||||||
|
"https://localhost:7200/api/TechnicalDrawing/compare-images",
|
||||||
|
formData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!comparisonResponse.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new Exception("Erro ao comparar os PDFs");
|
||||||
|
}
|
||||||
|
|
||||||
|
var differences = await comparisonResponse.Content.ReadAsStringAsync();
|
||||||
|
JObject obj = JObject.Parse(differences);
|
||||||
|
string image = obj["image"].ToString();
|
||||||
|
|
||||||
|
var file3 = Base64ToFormFile.ConvertBase64ToFormFile(image, "compared.png");
|
||||||
|
|
||||||
|
var promptMessage = $"Ao comparar dois desenhos técnicos, obtive as diferenças entre eles. Você precisa explicar e resumir estas diferenças ressaltando mudanças em medidas da peça. O arquivo ";
|
||||||
|
|
||||||
|
var response = "";
|
||||||
|
using (formData = AddFormDataToResponse(sessionId, promptMessage, file1, file2, file3))
|
||||||
|
{
|
||||||
|
var chatResponse = await client.PostAsync($"{baseUrl}/chat/response-fromia", formData);
|
||||||
|
chatResponse.EnsureSuccessStatusCode();
|
||||||
|
response = await chatResponse.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MultipartFormDataContent AddFormDataToResponse(string sessionId, string message, IFormFile? file1 = null, IFormFile? file2 = null, IFormFile? file3 = null)
|
||||||
|
{
|
||||||
|
var formData = new MultipartFormDataContent();
|
||||||
|
formData.Add(new StringContent(sessionId), "SessionId");
|
||||||
|
formData.Add(new StringContent(message), "Message");
|
||||||
|
|
||||||
|
if (file1 != null)
|
||||||
|
{
|
||||||
|
var streamContent1 = new StreamContent(file1.OpenReadStream());
|
||||||
|
formData.Add(streamContent1, "File1", file1.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file2 != null)
|
||||||
|
{
|
||||||
|
var streamContent2 = new StreamContent(file2.OpenReadStream());
|
||||||
|
formData.Add(streamContent2, "File2", file2.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file3 != null)
|
||||||
|
{
|
||||||
|
var streamContent3 = new StreamContent(file3.OpenReadStream());
|
||||||
|
formData.Add(streamContent3, "File3", file3.FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("chat/authenticate")]
|
[HttpPost("chat/authenticate")]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequest request)
|
public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequest request)
|
||||||
@ -117,6 +269,53 @@ namespace ChatMvc.Controllers
|
|||||||
return BadRequest($"Erro na autenticação: {ex.Message}");
|
return BadRequest($"Erro na autenticação: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string FormatDifferencesForChat(dynamic jsonDifferences)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("Análise das diferenças entre os documentos:");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Focar apenas nas diferenças reais (onlyDifferences)
|
||||||
|
var onlyDiffs = jsonDifferences.onlyDifferences;
|
||||||
|
|
||||||
|
if (onlyDiffs != null && onlyDiffs.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var diff in onlyDiffs)
|
||||||
|
{
|
||||||
|
string content = diff.content?.ToString() ?? "";
|
||||||
|
string type = diff.typeName?.ToString() ?? "";
|
||||||
|
int lineNumber = diff.lineNumber;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "Deleted":
|
||||||
|
sb.AppendLine($"Removido (linha {lineNumber}):");
|
||||||
|
sb.AppendLine($"- {content}");
|
||||||
|
break;
|
||||||
|
case "Inserted":
|
||||||
|
sb.AppendLine($"Adicionado (linha {lineNumber}):");
|
||||||
|
sb.AppendLine($"+ {content}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("Nenhuma diferença significativa encontrada entre os documentos.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
sb.AppendLine("Erro ao processar diferenças. Resumo simplificado:");
|
||||||
|
sb.AppendLine("As diferenças entre os documentos não puderam ser formatadas adequadamente.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AuthenticateRequest
|
public class AuthenticateRequest
|
||||||
|
|||||||
102
Chat/Controllers/DocumentsController.cs
Normal file
102
Chat/Controllers/DocumentsController.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using ChatMvc.Managers;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
|
||||||
|
namespace ChatMvc.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class DocumentsController : Controller
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly TokenManager _tokenManager;
|
||||||
|
|
||||||
|
public DocumentsController(TokenManager tokenManager)
|
||||||
|
{
|
||||||
|
_httpClient = new HttpClient();
|
||||||
|
_httpClient.BaseAddress = new Uri("http://localhost:5020/");
|
||||||
|
this._tokenManager = tokenManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Index()
|
||||||
|
{
|
||||||
|
var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value;
|
||||||
|
|
||||||
|
if (_tokenManager.IsTokenExpired(token)) {
|
||||||
|
var userId = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value;
|
||||||
|
token = await _tokenManager.GetToken(User);
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync("Chat/texts");
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var texts = await response.Content.ReadFromJsonAsync<IEnumerable<TextResponse>>();
|
||||||
|
return View(texts);
|
||||||
|
}
|
||||||
|
return View(new List<TextResponse>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult New()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Edit(string id)
|
||||||
|
{
|
||||||
|
var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value;
|
||||||
|
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(string.Format("chat/texts/id/{0}", id));
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var text = await response.Content.ReadFromJsonAsync<TextResponse>();
|
||||||
|
var request = new TextRequest()
|
||||||
|
{
|
||||||
|
Id = text.Id,
|
||||||
|
Title = text.Title,
|
||||||
|
Content = text.Content
|
||||||
|
};
|
||||||
|
return View("New", request);
|
||||||
|
}
|
||||||
|
return View("New");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Save([FromForm] TextRequest request)
|
||||||
|
{
|
||||||
|
var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value;
|
||||||
|
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));
|
||||||
|
|
||||||
|
var response = await _httpClient.PostAsJsonAsync("chat/savetext", request);
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
return View("Novo", request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextResponse
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextRequest
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
using ChatMvc.Managers;
|
||||||
|
|
||||||
namespace ChatMvc.Controllers
|
namespace ChatMvc.Controllers
|
||||||
{
|
{
|
||||||
@ -12,12 +13,14 @@ namespace ChatMvc.Controllers
|
|||||||
{
|
{
|
||||||
private readonly ILogger<LoginController> logger;
|
private readonly ILogger<LoginController> logger;
|
||||||
private readonly IHttpClientFactory httpClientFactory;
|
private readonly IHttpClientFactory httpClientFactory;
|
||||||
|
private readonly TokenManager tokenManager;
|
||||||
private readonly StripeClient _stripeClient;
|
private readonly StripeClient _stripeClient;
|
||||||
|
|
||||||
public LoginController(ILogger<LoginController> logger, IHttpClientFactory httpClientFactory)
|
public LoginController(ILogger<LoginController> logger, IHttpClientFactory httpClientFactory, TokenManager tokenManager)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.httpClientFactory = httpClientFactory;
|
this.httpClientFactory = httpClientFactory;
|
||||||
|
this.tokenManager = tokenManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
@ -55,11 +58,16 @@ namespace ChatMvc.Controllers
|
|||||||
|
|
||||||
if (HttpContext.User.FindFirst(ClaimTypes.GivenName) != null)
|
if (HttpContext.User.FindFirst(ClaimTypes.GivenName) != null)
|
||||||
{
|
{
|
||||||
|
var token = await tokenManager.GetToken(emailExist, "Domvs iT", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value);
|
||||||
|
|
||||||
claims = new List<Claim>
|
claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim(ClaimTypes.Email, emailExist),
|
new Claim(ClaimTypes.Email, emailExist),
|
||||||
new Claim("FirstName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value),
|
new Claim("FirstName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value),
|
||||||
new Claim("FullName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value + " " + HttpContext.User.FindFirst(ClaimTypes.Surname).Value),
|
new Claim("FullName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value + " " + HttpContext.User.FindFirst(ClaimTypes.Surname).Value),
|
||||||
|
new Claim("CompanyName", "Domvs iT"),
|
||||||
|
new Claim("UserId", emailExist),
|
||||||
|
new Claim("TokenExternal", token),
|
||||||
new Claim(ClaimTypes.Role, "User"),
|
new Claim(ClaimTypes.Role, "User"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -68,11 +76,16 @@ namespace ChatMvc.Controllers
|
|||||||
var name = HttpContext.User.FindFirst(ClaimTypes.Name).Value;
|
var name = HttpContext.User.FindFirst(ClaimTypes.Name).Value;
|
||||||
var firstName = name.Split(' ')[0];
|
var firstName = name.Split(' ')[0];
|
||||||
|
|
||||||
|
var token = await tokenManager.GetToken(emailExist, "Domvs iT", firstName);
|
||||||
|
|
||||||
claims = new List<Claim>
|
claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim(ClaimTypes.Email, emailExist),
|
new Claim(ClaimTypes.Email, emailExist),
|
||||||
new Claim("FirstName", firstName),
|
new Claim("FirstName", firstName),
|
||||||
new Claim("FullName", name),
|
new Claim("FullName", name),
|
||||||
|
new Claim("UserId", emailExist),
|
||||||
|
new Claim("CompanyName", "Domvs iT"),
|
||||||
|
new Claim("TokenExternal", token),
|
||||||
new Claim(ClaimTypes.Role, "User"),
|
new Claim(ClaimTypes.Role, "User"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
121
Chat/Managers/TokenManager.cs
Normal file
121
Chat/Managers/TokenManager.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using ChatMvc.Controllers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Stripe.Forwarding;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ChatMvc.Managers
|
||||||
|
{
|
||||||
|
public class TokenManager
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
public TokenManager(IHttpClientFactory httpClientFactory, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
this._httpClientFactory = httpClientFactory;
|
||||||
|
this._configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetToken(string userId, string company, string name)
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var baseUrl = _configuration["ExternalApiBaseUrl"];
|
||||||
|
|
||||||
|
// Primeira requisição - newclient
|
||||||
|
var newClientRequest = new
|
||||||
|
{
|
||||||
|
localId = userId,
|
||||||
|
companyTenant = company,
|
||||||
|
name = name
|
||||||
|
};
|
||||||
|
|
||||||
|
var newClientResponse = await client.PostAsync(
|
||||||
|
$"{baseUrl}/login/newclient",
|
||||||
|
new StringContent(JsonConvert.SerializeObject(newClientRequest),
|
||||||
|
Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
|
newClientResponse.EnsureSuccessStatusCode();
|
||||||
|
var clientContent = await newClientResponse.Content.ReadAsStringAsync();
|
||||||
|
var clientResult = JsonConvert.DeserializeObject<NewClientResponse>(clientContent);
|
||||||
|
|
||||||
|
// Segunda requisição - token
|
||||||
|
var tokenRequest = new
|
||||||
|
{
|
||||||
|
clientId = userId,
|
||||||
|
clientName = name,
|
||||||
|
clientSecret = clientResult.Secret
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenResponse = await client.PostAsync(
|
||||||
|
$"{baseUrl}/login/token",
|
||||||
|
new StringContent(JsonConvert.SerializeObject(tokenRequest),
|
||||||
|
Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
|
tokenResponse.EnsureSuccessStatusCode();
|
||||||
|
var tokenContent = await tokenResponse.Content.ReadAsStringAsync();
|
||||||
|
var tokenResult = JsonConvert.DeserializeObject<TokenResponse>(tokenContent);
|
||||||
|
|
||||||
|
return tokenResult.Token;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetToken(ClaimsPrincipal user)
|
||||||
|
{
|
||||||
|
var userId = user.Claims.FirstOrDefault(f => f.Type == "UserId")?.Value;
|
||||||
|
var name = user.Claims.FirstOrDefault(f => f.Type == "FirstName")?.Value;
|
||||||
|
var company = user.Claims.FirstOrDefault(f => f.Type == "CompanyName")?.Value;
|
||||||
|
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var baseUrl = _configuration["ExternalApiBaseUrl"];
|
||||||
|
|
||||||
|
// Primeira requisição - newclient
|
||||||
|
var newClientRequest = new
|
||||||
|
{
|
||||||
|
localId = userId,
|
||||||
|
companyTenant = company,
|
||||||
|
name = name
|
||||||
|
};
|
||||||
|
|
||||||
|
var newClientResponse = await client.PostAsync(
|
||||||
|
$"{baseUrl}/login/newclient",
|
||||||
|
new StringContent(JsonConvert.SerializeObject(newClientRequest),
|
||||||
|
Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
|
newClientResponse.EnsureSuccessStatusCode();
|
||||||
|
var clientContent = await newClientResponse.Content.ReadAsStringAsync();
|
||||||
|
var clientResult = JsonConvert.DeserializeObject<NewClientResponse>(clientContent);
|
||||||
|
|
||||||
|
// Segunda requisição - token
|
||||||
|
var tokenRequest = new
|
||||||
|
{
|
||||||
|
clientId = userId,
|
||||||
|
clientName = name,
|
||||||
|
clientSecret = clientResult.Secret
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenResponse = await client.PostAsync(
|
||||||
|
$"{baseUrl}/login/token",
|
||||||
|
new StringContent(JsonConvert.SerializeObject(tokenRequest),
|
||||||
|
Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
|
tokenResponse.EnsureSuccessStatusCode();
|
||||||
|
var tokenContent = await tokenResponse.Content.ReadAsStringAsync();
|
||||||
|
var tokenResult = JsonConvert.DeserializeObject<TokenResponse>(tokenContent);
|
||||||
|
|
||||||
|
return tokenResult.Token;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTokenExpired(string token)
|
||||||
|
{
|
||||||
|
var handler = new JwtSecurityTokenHandler();
|
||||||
|
var jwtToken = handler.ReadJwtToken(token);
|
||||||
|
|
||||||
|
var expiracao = jwtToken.ValidTo;
|
||||||
|
return expiracao < DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using ChatMvc.LogConfig;
|
using ChatMvc.LogConfig;
|
||||||
|
using ChatMvc.Managers;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authentication.Google;
|
using Microsoft.AspNetCore.Authentication.Google;
|
||||||
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
|
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
|
||||||
@ -18,7 +19,7 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.MinimumLevel.Information()
|
.MinimumLevel.Information()
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.Enrich.WithProperty("app", "blinks")
|
.Enrich.WithProperty("app", "chat")
|
||||||
.WriteTo.Console()
|
.WriteTo.Console()
|
||||||
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
|
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
@ -37,9 +38,12 @@ options =>
|
|||||||
})
|
})
|
||||||
.AddCookie(options =>
|
.AddCookie(options =>
|
||||||
{
|
{
|
||||||
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
|
//options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
|
||||||
options.SlidingExpiration = true;
|
//options.SlidingExpiration = true;
|
||||||
options.AccessDeniedPath = "/Forbidden/";
|
options.AccessDeniedPath = "/Forbidden/";
|
||||||
|
options.Cookie.Name = ".AspNet.SharedCookie";
|
||||||
|
options.ExpireTimeSpan = TimeSpan.FromDays(30); // Define o tempo de expiração
|
||||||
|
options.SlidingExpiration = true; // Renova o cookie a cada acesso
|
||||||
})
|
})
|
||||||
.AddGoogle(googleOptions =>
|
.AddGoogle(googleOptions =>
|
||||||
{
|
{
|
||||||
@ -51,8 +55,7 @@ options =>
|
|||||||
microsoftOptions.ClientId = config.GetSection("Microsoft_ClientId").Value;
|
microsoftOptions.ClientId = config.GetSection("Microsoft_ClientId").Value;
|
||||||
//microsoftOptions.ClientSecret = "2a7cb1bd-037a-49fa-9e5e-2b2655431af9";
|
//microsoftOptions.ClientSecret = "2a7cb1bd-037a-49fa-9e5e-2b2655431af9";
|
||||||
microsoftOptions.ClientSecret = config.GetSection("Microsoft_ClientSecret").Value;
|
microsoftOptions.ClientSecret = config.GetSection("Microsoft_ClientSecret").Value;
|
||||||
})
|
});
|
||||||
;
|
|
||||||
|
|
||||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
@ -91,6 +94,8 @@ builder.Services.AddAntiforgery(options =>
|
|||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddScoped<TokenManager>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
var locOptions = app.Services.GetService<IOptions<RequestLocalizationOptions>>();
|
var locOptions = app.Services.GetService<IOptions<RequestLocalizationOptions>>();
|
||||||
|
|||||||
@ -11,12 +11,15 @@
|
|||||||
},
|
},
|
||||||
"https": {
|
"https": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"applicationUrl": "https://localhost:7078;http://localhost:5094"
|
"applicationUrl": "https://localhost:7078"
|
||||||
|
//"applicationUrl": "https://192.168.0.13:7078;http://192.168.0.13:5094;https://localhost:7078;http://localhost:5094"
|
||||||
},
|
},
|
||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
|
|||||||
@ -2,29 +2,67 @@
|
|||||||
ViewData["Title"] = "Chat";
|
ViewData["Title"] = "Chat";
|
||||||
}
|
}
|
||||||
|
|
||||||
@section Styles{
|
@section Styles {
|
||||||
<link href="~/css/chat_index.css" rel="stylesheet" />
|
<link href="~/css/chat_index.css" rel="stylesheet" />
|
||||||
|
<style>
|
||||||
|
.file-upload-button {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload-button input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
text-align: right;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
opacity: 0;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-files {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input {
|
||||||
|
resize: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-height: 40px;
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input:focus {
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="container-fluid bg-light">
|
<div class="container-fluid bg-light main-content">
|
||||||
<div class="row h-100">
|
<div class="row h-100">
|
||||||
<div class="col-md-10 mx-auto">
|
<div class="col-md-12 mx-auto">
|
||||||
<div class="card shadow-sm my-4" style="background-color: #f8fff8;">
|
<div class="card shadow-sm my-4" style="background-color: #f8fff8;">
|
||||||
@*
|
|
||||||
<div class="card-header bg-success text-white">
|
|
||||||
<h5 class="mb-0">Chat</h5>
|
|
||||||
</div>
|
|
||||||
*@
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="chat-messages" class="mb-4" style="height: 350px; overflow-y: auto;">
|
<div id="chat-messages" class="mb-4" style="height: 350px; overflow-y: auto;">
|
||||||
<!-- Mensagens serão inseridas aqui -->
|
<!-- Mensagens serão inseridas aqui -->
|
||||||
</div>
|
</div>
|
||||||
<form id="chat-form" class="mt-3">
|
<form id="chat-form" class="mt-3">
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
|
<div class="selected-files" id="selected-files-info"></div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<textarea id="message-input" class="form-control"
|
<textarea id="message-input" class="form-control"
|
||||||
placeholder="Digite sua mensagem... Use **texto** para negrito"
|
placeholder="Digite sua mensagem... Use **texto** para negrito"
|
||||||
rows="3" required></textarea>
|
rows="1" required></textarea>
|
||||||
|
<button type="button" class="btn btn-outline-secondary file-upload-button">
|
||||||
|
<i class="bi bi-paperclip"></i>
|
||||||
|
<input type="file" id="file-input" accept="application/pdf,image/png" multiple>
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-success" id="send-message">
|
<button type="button" class="btn btn-success" id="send-message">
|
||||||
Enviar
|
Enviar
|
||||||
</button>
|
</button>
|
||||||
@ -43,8 +81,7 @@
|
|||||||
const id = name + '_Web';
|
const id = name + '_Web';
|
||||||
const company = 'Domvs iT';
|
const company = 'Domvs iT';
|
||||||
const baseUrl = window.location.origin;
|
const baseUrl = window.location.origin;
|
||||||
|
var token = '@ViewBag.Token';
|
||||||
var token = '';
|
|
||||||
|
|
||||||
// Aguarda todos os recursos serem carregados
|
// Aguarda todos os recursos serem carregados
|
||||||
window.addEventListener('load', async function () {
|
window.addEventListener('load', async function () {
|
||||||
@ -52,24 +89,28 @@
|
|||||||
const chatMessages = $('#chat-messages');
|
const chatMessages = $('#chat-messages');
|
||||||
const messageInput = $('#message-input');
|
const messageInput = $('#message-input');
|
||||||
const chatForm = $('#chat-form');
|
const chatForm = $('#chat-form');
|
||||||
|
const fileInput = $('#file-input');
|
||||||
|
const selectedFilesInfo = $('#selected-files-info');
|
||||||
|
let selectedFiles = [];
|
||||||
|
|
||||||
token = await autenticar(id, company, name);
|
//token = await autenticar(id, company, name);
|
||||||
|
//token = '@ViewBag.Token';
|
||||||
|
|
||||||
// Configuração do Marked
|
// // Configuração do Marked
|
||||||
marked.setOptions({
|
// marked.setOptions({
|
||||||
breaks: true,
|
// breaks: true,
|
||||||
gfm: true
|
// gfm: true
|
||||||
});
|
// });
|
||||||
|
|
||||||
const msgInit = `Olá! Eu sou uma IA desenvolvida para atuar internamente na Domvs iT. Você pode:
|
const msgInit = `Olá! Eu sou uma IA desenvolvida para atuar internamente na Domvs iT. Você pode:
|
||||||
- Fazer perguntas sobre os serviços da Domvs iT
|
- Fazer perguntas sobre os serviços da Domvs iT
|
||||||
- Abrir uma solicitação ao RH no Pipefy
|
- Abrir uma solicitação ao RH no Pipefy
|
||||||
- Ou simplesmente perguntar qualquer coisa.
|
- Ou simplesmente perguntar qualquer coisa.
|
||||||
|
|
||||||
Use **negrito** para destacar texto importante.
|
Use **negrito** para destacar texto importante.
|
||||||
Para código, use \\\`\\\`\\\`linguagem
|
Para código, use \\\`\\\`\\\`linguagem
|
||||||
seu código
|
seu código
|
||||||
\\\`\\\`\\\``;
|
\\\`\\\`\\\``;
|
||||||
|
|
||||||
function appendMessage(message, isUser = true) {
|
function appendMessage(message, isUser = true) {
|
||||||
const messageDiv = $('<div>').addClass('chat-message ' + (isUser ? 'user-message' : 'bot-message'));
|
const messageDiv = $('<div>').addClass('chat-message ' + (isUser ? 'user-message' : 'bot-message'));
|
||||||
@ -134,57 +175,79 @@ seu código
|
|||||||
// Exibe mensagem inicial
|
// Exibe mensagem inicial
|
||||||
appendMessage(msgInit, false);
|
appendMessage(msgInit, false);
|
||||||
|
|
||||||
$('#send-message').on('click', function (e) {
|
// Arquivo handling
|
||||||
|
fileInput.on('change', function (e) {
|
||||||
|
const files = Array.from(e.target.files);
|
||||||
|
|
||||||
|
if (files.length > 2) {
|
||||||
|
alert('Por favor, selecione no máximo 2 arquivos PDF/PNG.');
|
||||||
|
fileInput.val('');
|
||||||
|
selectedFiles = [];
|
||||||
|
selectedFilesInfo.html('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.some(file => file.type !== 'application/pdf' && file.type !== 'image/png')) {
|
||||||
|
alert('Por favor, selecione apenas arquivos PDF/PNG.');
|
||||||
|
fileInput.val('');
|
||||||
|
selectedFiles = [];
|
||||||
|
selectedFilesInfo.html('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedFiles = files;
|
||||||
|
selectedFilesInfo.html(
|
||||||
|
files.map(file => `<div>${file.name}</div>`).join('')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#message-input").keypress(function (e) {
|
||||||
|
if (e.which === 13) {
|
||||||
|
$("#send-message").click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#send-message').on('click', async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const message = messageInput.val().trim();
|
const message = messageInput.val().trim();
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
appendMessage(message);
|
|
||||||
messageInput.val('');
|
const formData = new FormData();
|
||||||
$.ajax({
|
formData.append('message', message);
|
||||||
url: baseUrl + '/chat/proxy-response', // Novo endpoint no controller
|
formData.append('sessionId', id);
|
||||||
type: 'GET',
|
|
||||||
headers: {
|
selectedFiles.forEach((file, index) => {
|
||||||
"X-CSRF-TOKEN": $('input[name="__RequestVerificationToken"]').val(),
|
formData.append(`file${index + 1}`, file);
|
||||||
'Authorization': 'Bearer ' + token
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
sessionId: id,
|
|
||||||
message: message
|
|
||||||
},
|
|
||||||
success: function (response) {
|
|
||||||
appendMessage(response, false);
|
|
||||||
},
|
|
||||||
error: function (xhr, status, error) {
|
|
||||||
console.error('Erro na requisição do cliente:', error);
|
|
||||||
console.error('Status:', xhr.status);
|
|
||||||
console.error('Response:', xhr.responseText);
|
|
||||||
appendMessage('Erro ao processar cliente: ' + error, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function autenticar(userId, company, name) {
|
appendMessage(message);
|
||||||
|
messageInput.val('');
|
||||||
|
fileInput.val('');
|
||||||
|
selectedFiles = [];
|
||||||
|
selectedFilesInfo.html('');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Agora fazemos apenas uma requisição para o novo endpoint
|
const response = await fetch(baseUrl + '/chat/proxy-response', {
|
||||||
const response = await $.ajax({
|
method: 'POST',
|
||||||
url: baseUrl + '/chat/authenticate',
|
|
||||||
type: 'POST',
|
|
||||||
headers: {
|
headers: {
|
||||||
"X-CSRF-TOKEN": $('input[name="__RequestVerificationToken"]').val()
|
"X-CSRF-TOKEN": $('input[name="__RequestVerificationToken"]').val(),
|
||||||
|
'Authorization': 'Bearer ' + token,
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
},
|
},
|
||||||
contentType: 'application/json',
|
body: formData
|
||||||
data: JSON.stringify({
|
|
||||||
userId: userId,
|
|
||||||
company: company,
|
|
||||||
name: name
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
return response.token;
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Erro na requisição');
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseText = await response.text();
|
||||||
|
appendMessage(responseText, false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Erro na requisição:', error);
|
||||||
appendMessage('Erro ao processar mensagem: ' + error, false);
|
appendMessage('Erro ao processar mensagem: ' + error, false);
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
50
Chat/Views/Documents/Index.cshtml
Normal file
50
Chat/Views/Documents/Index.cshtml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@using ChatMvc.Controllers
|
||||||
|
@model IEnumerable<TextResponse>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="card shadow-lg mt-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h2 class="card-title">Documentos</h2>
|
||||||
|
<a href="@Url.Action("New", "Documents")" class="btn btn-primary">
|
||||||
|
<i class="fas fa-plus mr-2"></i>Novo
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Título</th>
|
||||||
|
<th>Conteúdo</th>
|
||||||
|
<th>Ações</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var item in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@item.Title</td>
|
||||||
|
<td>@(item.Content?.Length > 80 ? item.Content.Substring(0, 80) + "..." : item.Content)</td>
|
||||||
|
<td>
|
||||||
|
<a href="@Url.Action("Edit", "Documents", new { id = item.Id })" class="btn btn-primary btn-sm">
|
||||||
|
<i class="fas fa-edit"></i> Editar
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
31
Chat/Views/Documents/New.cshtml
Normal file
31
Chat/Views/Documents/New.cshtml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@using ChatMvc.Controllers
|
||||||
|
@model TextRequest
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="card shadow-lg mt-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title mb-4 text-center">Novo Texto</h2>
|
||||||
|
@using (Html.BeginForm("Save", "Documents", FormMethod.Post))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
|
||||||
|
@if (Model!=null && Model.Id != null)
|
||||||
|
{
|
||||||
|
@Html.HiddenFor(m => m.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Title, "Título")
|
||||||
|
@Html.TextBoxFor(m => m.Title, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Content, "Conteúdo")
|
||||||
|
@Html.TextAreaFor(m => m.Content, new { @class = "form-control", rows = "5" })
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Salvar</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -16,7 +16,9 @@
|
|||||||
#wrapper {
|
#wrapper {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.3s ease-in-out;
|
transition: opacity 0.3s ease-in-out;
|
||||||
display: block;
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper.fade-out {
|
#wrapper.fade-out {
|
||||||
@ -50,8 +52,42 @@
|
|||||||
100% { transform: rotate(360deg); }
|
100% { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
display: block;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.container, .container-fluid {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -61,8 +97,11 @@
|
|||||||
<partial name="_Busy" />
|
<partial name="_Busy" />
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<nav id="nav-bar" class="navbar navbar-expand-lg navbar-dark">
|
<nav id="nav-bar" class="navbar navbar-expand-lg navbar-dark">
|
||||||
<!-- Seu código de navegação existente -->
|
<!-- Substituir esta linha no _layout.cshtml -->
|
||||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Chat IA</a>
|
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">
|
||||||
|
<img src="~/img/logo.png" alt="DOMVS iT" height="30" class="d-inline-block align-top mr-2">
|
||||||
|
Chat IA
|
||||||
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -79,6 +118,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-white" asp-area="" asp-controller="Chat" asp-action="Index">Chat</a>
|
<a class="nav-link text-white" asp-area="" asp-controller="Chat" asp-action="Index">Chat</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-white" asp-area="" asp-controller="Documents" asp-action="Index">Documentos</a>
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@if (User!=null && User.Identity!=null && !User.Identity.IsAuthenticated)
|
@if (User!=null && User.Identity!=null && !User.Identity.IsAuthenticated)
|
||||||
@ -111,8 +153,8 @@
|
|||||||
@RenderBody()
|
@RenderBody()
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="border-top footer text-muted">
|
<footer class="border-top footer text-muted ">
|
||||||
<div class="container">
|
<div>
|
||||||
© 2024 - Chat IA - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
© 2024 - Chat IA - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -1,28 +1,27 @@
|
|||||||
|
/* Color palette variables */
|
||||||
/* Color palette variables */
|
|
||||||
:root {
|
:root {
|
||||||
--primary-green: #2E7D32; /* Darker green for headers */
|
--primary-blue: #000722; /* Cor base - azul escuro */
|
||||||
--secondary-green: #43A047; /* Slightly lighter green for interactive elements */
|
--secondary-blue: #0A1D4C; /* Azul um pouco mais claro para elementos interativos */
|
||||||
--light-green: #C8E6C9; /* Very light green for backgrounds */
|
--light-blue: #C7D2E9; /* Azul bem claro para backgrounds */
|
||||||
--hover-green: #1B5E20; /* Darker green for hover states */
|
--hover-blue: #010B3B; /* Azul mais escuro para hover states */
|
||||||
--accent-green: #81C784; /* Medium green for accents */
|
--accent-blue: #2D4275; /* Azul médio para acentos */
|
||||||
--bg-light: #F1F8E9; /* Super light green for main background */
|
--bg-light: #EFF2F9; /* Azul super claro para background principal */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navbar styling */
|
/* Navbar styling */
|
||||||
#nav-bar {
|
#nav-bar {
|
||||||
background-color: var(--primary-green) !important;
|
background-color: var(--primary-blue) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button primary override */
|
/* Button primary override */
|
||||||
.btn-primary, .btn-success {
|
.btn-primary, .btn-success {
|
||||||
background-color: var(--secondary-green) !important;
|
background-color: var(--secondary-blue) !important;
|
||||||
border-color: var(--secondary-green) !important;
|
border-color: var(--secondary-blue) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover, .btn-success:hover {
|
.btn-primary:hover, .btn-success:hover {
|
||||||
background-color: var(--hover-green) !important;
|
background-color: var(--hover-blue) !important;
|
||||||
border-color: var(--hover-green) !important;
|
border-color: var(--hover-blue) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chat card styling */
|
/* Chat card styling */
|
||||||
@ -32,36 +31,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
background-color: var(--primary-green) !important;
|
background-color: var(--primary-blue) !important;
|
||||||
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message bubbles */
|
/* Message bubbles */
|
||||||
.chat-message-user {
|
.chat-message-user {
|
||||||
background-color: var(--secondary-green) !important;
|
background-color: var(--secondary-blue) !important;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 15px 15px 0 15px !important;
|
border-radius: 15px 15px 0 15px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message-bot {
|
.chat-message-bot {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
border: 1px solid var(--light-green) !important;
|
border: 1px solid var(--light-blue) !important;
|
||||||
color: #333;
|
color: #333;
|
||||||
border-radius: 15px 15px 15px 0 !important;
|
border-radius: 15px 15px 15px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input group styling */
|
/* Input group styling */
|
||||||
.input-group .form-control {
|
.input-group .form-control {
|
||||||
border: 1px solid var(--light-green);
|
border: 1px solid var(--light-blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group .form-control:focus {
|
.input-group .form-control:focus {
|
||||||
border-color: var(--accent-green);
|
border-color: var(--accent-blue);
|
||||||
box-shadow: 0 0 0 0.2rem rgba(67, 160, 71, 0.25);
|
box-shadow: 0 0 0 0.2rem rgba(10, 29, 76, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navbar active state */
|
/* Navbar active state */
|
||||||
.navbar .navbar-collapse ul.navbar-nav .nav-item.active {
|
.navbar .navbar-collapse ul.navbar-nav .nav-item.active {
|
||||||
background-color: var(--accent-green) !important;
|
background-color: var(--accent-blue) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar for chat messages */
|
/* Custom scrollbar for chat messages */
|
||||||
@ -74,10 +74,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#chat-messages::-webkit-scrollbar-thumb {
|
#chat-messages::-webkit-scrollbar-thumb {
|
||||||
background: var(--accent-green);
|
background: var(--accent-blue);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat-messages::-webkit-scrollbar-thumb:hover {
|
#chat-messages::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--secondary-green);
|
background: var(--secondary-blue);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 16 KiB |
Loading…
Reference in New Issue
Block a user