diff --git a/.gitignore b/.gitignore
index e10897e..f349058 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,4 +54,8 @@ TestResult.xml
nunit-*.xml
-appsettings.Development.json
\ No newline at end of file
+appsettings.Development.json
+appsettings.json
+*appsettings.json
+
+.env
\ No newline at end of file
diff --git a/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/CodeChunks.db b/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/CodeChunks.db
new file mode 100644
index 0000000..82e55a1
Binary files /dev/null and b/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/CodeChunks.db differ
diff --git a/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/SemanticSymbols.db b/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/SemanticSymbols.db
new file mode 100644
index 0000000..a2a78ed
Binary files /dev/null and b/.vs/Fin-Backend/CopilotIndices/17.14.734.62261/SemanticSymbols.db differ
diff --git a/.vs/Fin-Backend/DesignTimeBuild/.dtbcache.v2 b/.vs/Fin-Backend/DesignTimeBuild/.dtbcache.v2
new file mode 100644
index 0000000..275c4e5
Binary files /dev/null and b/.vs/Fin-Backend/DesignTimeBuild/.dtbcache.v2 differ
diff --git a/.vs/Fin-Backend/FileContentIndex/6f825f2f-6b18-4bcc-9a27-756adc3ec514.vsidx b/.vs/Fin-Backend/FileContentIndex/6f825f2f-6b18-4bcc-9a27-756adc3ec514.vsidx
new file mode 100644
index 0000000..ebc3c49
Binary files /dev/null and b/.vs/Fin-Backend/FileContentIndex/6f825f2f-6b18-4bcc-9a27-756adc3ec514.vsidx differ
diff --git a/.vs/Fin-Backend/FileContentIndex/8be34b6f-428b-401a-b31e-b52358fdea5c.vsidx b/.vs/Fin-Backend/FileContentIndex/8be34b6f-428b-401a-b31e-b52358fdea5c.vsidx
new file mode 100644
index 0000000..89767a3
Binary files /dev/null and b/.vs/Fin-Backend/FileContentIndex/8be34b6f-428b-401a-b31e-b52358fdea5c.vsidx differ
diff --git a/.vs/Fin-Backend/v17/.futdcache.v2 b/.vs/Fin-Backend/v17/.futdcache.v2
new file mode 100644
index 0000000..f46f1a6
Binary files /dev/null and b/.vs/Fin-Backend/v17/.futdcache.v2 differ
diff --git a/.vs/Fin-Backend/v17/.suo b/.vs/Fin-Backend/v17/.suo
new file mode 100644
index 0000000..679eb1d
Binary files /dev/null and b/.vs/Fin-Backend/v17/.suo differ
diff --git a/.vs/Fin-Backend/v17/DocumentLayout.json b/.vs/Fin-Backend/v17/DocumentLayout.json
new file mode 100644
index 0000000..6d93739
--- /dev/null
+++ b/.vs/Fin-Backend/v17/DocumentLayout.json
@@ -0,0 +1,394 @@
+{
+ "Version": 1,
+ "WorkspaceRootPath": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\",
+ "Documents": [
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\authentications\\activatedusermiddleware.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\authentications\\activatedusermiddleware.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\tenants\\entities\\tenantuser.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\tenants\\entities\\tenantuser.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\tenants\\entities\\tenant.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\tenants\\entities\\tenant.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{1F2A1D01-85F1-46DC-A5BD-03B7FCF47B82}|Fin.Application\\Fin.Application.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.application\\globals\\services\\randomgenerator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{1F2A1D01-85F1-46DC-A5BD-03B7FCF47B82}|Fin.Application\\Fin.Application.csproj|solutionrelative:fin.application\\globals\\services\\randomgenerator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{1F2A1D01-85F1-46DC-A5BD-03B7FCF47B82}|Fin.Application\\Fin.Application.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.application\\authentications\\services\\authenticationservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{1F2A1D01-85F1-46DC-A5BD-03B7FCF47B82}|Fin.Application\\Fin.Application.csproj|solutionrelative:fin.application\\authentications\\services\\authenticationservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\users\\dtos\\userdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\users\\dtos\\userdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\users\\dtos\\userdeleterequestdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\users\\dtos\\userdeleterequestdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\notifications\\entities\\notificationuserdelivery.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\notifications\\entities\\notificationuserdelivery.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.domain\\notifications\\enums\\notificationway.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{E698F897-BB2D-42EF-879A-03B0DA36F212}|Fin.Domain\\Fin.Domain.csproj|solutionrelative:fin.domain\\notifications\\enums\\notificationway.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\unitofworks\\unitofwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\unitofworks\\unitofwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\authentications\\enums\\loginerrorcode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\authentications\\enums\\loginerrorcode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\authentications\\dtos\\logininput.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\authentications\\dtos\\logininput.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\authentications\\dtos\\loginoutput.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\authentications\\dtos\\loginoutput.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\firebases\\firebaseclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\firebases\\firebaseclient.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\firebases\\firebasenotificationservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\firebases\\firebasenotificationservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\firebases\\addfirebaseextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\firebases\\addfirebaseextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\redis\\addredisextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\redis\\addredisextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\notifications\\hubs\\notificationhub.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\notifications\\hubs\\notificationhub.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\notifications\\hubs\\customuseridprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\notifications\\hubs\\customuseridprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\notifications\\hubs\\addnotificationbackgroundjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\notifications\\hubs\\addnotificationbackgroundjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\consts\\appconsts.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\consts\\appconsts.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|c:\\users\\gustavo passos\\desktop\\develop\\fin\\fin-backend\\fin.infrastructure\\firebases\\firebasesendresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{8A2A53B4-B6F1-47DC-A681-E1819AE39A0B}|Fin.Infrastructure\\Fin.Infrastructure.csproj|solutionrelative:fin.infrastructure\\firebases\\firebasesendresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ }
+ ],
+ "DocumentGroupContainers": [
+ {
+ "Orientation": 0,
+ "VerticalTabListWidth": 256,
+ "DocumentGroups": [
+ {
+ "DockedWidth": 200,
+ "SelectedChildIndex": 8,
+ "Children": [
+ {
+ "$type": "Document",
+ "DocumentIndex": 3,
+ "Title": "RandomGenerator.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Application\\Globals\\Services\\RandomGenerator.cs",
+ "RelativeDocumentMoniker": "Fin.Application\\Globals\\Services\\RandomGenerator.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Application\\Globals\\Services\\RandomGenerator.cs",
+ "RelativeToolTip": "Fin.Application\\Globals\\Services\\RandomGenerator.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAbAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T14:49:22.412Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 4,
+ "Title": "AuthenticationService.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Application\\Authentications\\Services\\AuthenticationService.cs",
+ "RelativeDocumentMoniker": "Fin.Application\\Authentications\\Services\\AuthenticationService.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Application\\Authentications\\Services\\AuthenticationService.cs",
+ "RelativeToolTip": "Fin.Application\\Authentications\\Services\\AuthenticationService.cs",
+ "ViewState": "AgIAABMAAAAAAAAAAIAwwCAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T14:18:54.877Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 5,
+ "Title": "UserDto.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Users\\Dtos\\UserDto.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Users\\Dtos\\UserDto.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Users\\Dtos\\UserDto.cs*",
+ "RelativeToolTip": "Fin.Domain\\Users\\Dtos\\UserDto.cs*",
+ "ViewState": "AgIAAAMAAAAAAAAAAAAAABEAAAAWAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T14:05:36.16Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 6,
+ "Title": "UserDeleteRequestDto.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Users\\Dtos\\UserDeleteRequestDto.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Users\\Dtos\\UserDeleteRequestDto.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Users\\Dtos\\UserDeleteRequestDto.cs",
+ "RelativeToolTip": "Fin.Domain\\Users\\Dtos\\UserDeleteRequestDto.cs",
+ "ViewState": "AgIAAAcAAAAAAAAAAAAQwB4AAAA1AAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T14:05:29.028Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 7,
+ "Title": "NotificationUserDelivery.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Notifications\\Entities\\NotificationUserDelivery.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Notifications\\Entities\\NotificationUserDelivery.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Notifications\\Entities\\NotificationUserDelivery.cs",
+ "RelativeToolTip": "Fin.Domain\\Notifications\\Entities\\NotificationUserDelivery.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAAFAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T13:11:44.769Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 8,
+ "Title": "NotificationWay.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Notifications\\Enums\\NotificationWay.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Notifications\\Enums\\NotificationWay.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Notifications\\Enums\\NotificationWay.cs",
+ "RelativeToolTip": "Fin.Domain\\Notifications\\Enums\\NotificationWay.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:39:58.617Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 1,
+ "Title": "TenantUser.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Tenants\\Entities\\TenantUser.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Tenants\\Entities\\TenantUser.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Tenants\\Entities\\TenantUser.cs",
+ "RelativeToolTip": "Fin.Domain\\Tenants\\Entities\\TenantUser.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:39:48.55Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 2,
+ "Title": "Tenant.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Tenants\\Entities\\Tenant.cs",
+ "RelativeDocumentMoniker": "Fin.Domain\\Tenants\\Entities\\Tenant.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Domain\\Tenants\\Entities\\Tenant.cs",
+ "RelativeToolTip": "Fin.Domain\\Tenants\\Entities\\Tenant.cs",
+ "ViewState": "AgIAAAUAAAAAAAAAAAAxwBwAAAABAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:39:38.11Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 0,
+ "Title": "ActivatedUserMiddleware.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\ActivatedUserMiddleware.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Authentications\\ActivatedUserMiddleware.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\ActivatedUserMiddleware.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Authentications\\ActivatedUserMiddleware.cs",
+ "ViewState": "AgIAAAYAAAAAAAAAAAAAABcAAAAJAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:38:34.582Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 9,
+ "Title": "UnitOfWork.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\UnitOfWorks\\UnitOfWork.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\UnitOfWorks\\UnitOfWork.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\UnitOfWorks\\UnitOfWork.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\UnitOfWorks\\UnitOfWork.cs",
+ "ViewState": "AgIAADIAAAAAAAAAAAAUwAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:39:08.167Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 10,
+ "Title": "LoginErrorCode.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Enums\\LoginErrorCode.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Authentications\\Enums\\LoginErrorCode.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Enums\\LoginErrorCode.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Authentications\\Enums\\LoginErrorCode.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:38:20.554Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 12,
+ "Title": "LoginOutput.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Dtos\\LoginOutput.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Authentications\\Dtos\\LoginOutput.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Dtos\\LoginOutput.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Authentications\\Dtos\\LoginOutput.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:38:03.449Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 11,
+ "Title": "LoginInput.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Dtos\\LoginInput.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Authentications\\Dtos\\LoginInput.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Authentications\\Dtos\\LoginInput.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Authentications\\Dtos\\LoginInput.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:38:02.088Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 13,
+ "Title": "FirebaseClient.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseClient.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Firebases\\FirebaseClient.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseClient.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Firebases\\FirebaseClient.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:37:26.15Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 14,
+ "Title": "FirebaseNotificationService.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseNotificationService.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Firebases\\FirebaseNotificationService.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseNotificationService.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Firebases\\FirebaseNotificationService.cs",
+ "ViewState": "AgIAAAEAAAAAAAAAAAAgwAwAAAAkAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:52.361Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 15,
+ "Title": "AddFirebaseExtension.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\AddFirebaseExtension.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Firebases\\AddFirebaseExtension.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\AddFirebaseExtension.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Firebases\\AddFirebaseExtension.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:48.056Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 16,
+ "Title": "AddRedisExtension.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Redis\\AddRedisExtension.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Redis\\AddRedisExtension.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Redis\\AddRedisExtension.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Redis\\AddRedisExtension.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAABcAAAABAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:15.37Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 17,
+ "Title": "NotificationHub.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\NotificationHub.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Notifications\\Hubs\\NotificationHub.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\NotificationHub.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Notifications\\Hubs\\NotificationHub.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:12.834Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 18,
+ "Title": "CustomUserIdProvider.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\CustomUserIdProvider.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Notifications\\Hubs\\CustomUserIdProvider.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\CustomUserIdProvider.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Notifications\\Hubs\\CustomUserIdProvider.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:09.812Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 19,
+ "Title": "AddNotificationBackgroundJob.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\AddNotificationBackgroundJob.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Notifications\\Hubs\\AddNotificationBackgroundJob.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Notifications\\Hubs\\AddNotificationBackgroundJob.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Notifications\\Hubs\\AddNotificationBackgroundJob.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:36:07.883Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 20,
+ "Title": "AppConsts.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Consts\\AppConsts.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Consts\\AppConsts.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Consts\\AppConsts.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Consts\\AppConsts.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAUAAAABAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:35:46.004Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 21,
+ "Title": "FirebaseSendResult.cs",
+ "DocumentMoniker": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseSendResult.cs",
+ "RelativeDocumentMoniker": "Fin.Infrastructure\\Firebases\\FirebaseSendResult.cs",
+ "ToolTip": "C:\\Users\\Gustavo Passos\\Desktop\\Develop\\Fin\\fin-backend\\Fin.Infrastructure\\Firebases\\FirebaseSendResult.cs",
+ "RelativeToolTip": "Fin.Infrastructure\\Firebases\\FirebaseSendResult.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-08-15T12:32:54.541Z",
+ "EditorCaption": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vs/ProjectEvaluation/fin-backend.metadata.v9.bin b/.vs/ProjectEvaluation/fin-backend.metadata.v9.bin
new file mode 100644
index 0000000..f3a089d
Binary files /dev/null and b/.vs/ProjectEvaluation/fin-backend.metadata.v9.bin differ
diff --git a/.vs/ProjectEvaluation/fin-backend.projects.v9.bin b/.vs/ProjectEvaluation/fin-backend.projects.v9.bin
new file mode 100644
index 0000000..8d5050b
Binary files /dev/null and b/.vs/ProjectEvaluation/fin-backend.projects.v9.bin differ
diff --git a/.vs/ProjectEvaluation/fin-backend.strings.v9.bin b/.vs/ProjectEvaluation/fin-backend.strings.v9.bin
new file mode 100644
index 0000000..c462925
Binary files /dev/null and b/.vs/ProjectEvaluation/fin-backend.strings.v9.bin differ
diff --git a/Fin.Api/Fin.Api.csproj.user b/Fin.Api/Fin.Api.csproj.user
new file mode 100644
index 0000000..9ff5820
--- /dev/null
+++ b/Fin.Api/Fin.Api.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ https
+
+
\ No newline at end of file
diff --git a/Fin.Api/Program.cs b/Fin.Api/Program.cs
index 4e6f02a..99e99bf 100644
--- a/Fin.Api/Program.cs
+++ b/Fin.Api/Program.cs
@@ -1,7 +1,7 @@
using Fin.Application.Notifications.Extensions;
using Fin.Infrastructure.Constants;
using Fin.Infrastructure.Extensions;
-using Fin.Infrastructure.Notifications.Hubs;
+using Fin.Infrastructure.Seeders.Extensions;
using Hangfire;
var builder = WebApplication.CreateBuilder(args);
@@ -10,7 +10,6 @@
builder.Services
.AddInfrastructure(builder.Configuration)
- .AddNotifications()
.AddOpenApiDocument()
.AddCors(options =>
{
@@ -19,7 +18,8 @@
{
policy.WithOrigins(frontEndUrl)
.AllowAnyHeader()
- .AllowAnyMethod();
+ .AllowAnyMethod()
+ .AllowCredentials();
});
})
.AddControllers();
@@ -36,9 +36,9 @@
app.UseCors("AllowAngularLocalhost");
}
-
app.UseNotifications();
app.UseFinMiddlewares();
+await app.UseSeeders();
app.UseAuthentication();
app.UseAuthorization();
diff --git a/Fin.Application/Notifications/Extensions/UseNotificationExtension.cs b/Fin.Application/Notifications/Extensions/UseNotificationExtension.cs
index 0f51081..6fcc2bb 100644
--- a/Fin.Application/Notifications/Extensions/UseNotificationExtension.cs
+++ b/Fin.Application/Notifications/Extensions/UseNotificationExtension.cs
@@ -7,6 +7,7 @@ public static class UseNotificationExtension
{
public static void UseNotifications(this WebApplication app)
{
- app.MapHub("/notifications");
+ app.MapHub("/notifications-hub");
}
+
}
\ No newline at end of file
diff --git a/Fin.Application/Notifications/Services/DeliveryServices/NotificationDeliveryService.cs b/Fin.Application/Notifications/Services/DeliveryServices/NotificationDeliveryService.cs
index 54b1f9e..b16006b 100644
--- a/Fin.Application/Notifications/Services/DeliveryServices/NotificationDeliveryService.cs
+++ b/Fin.Application/Notifications/Services/DeliveryServices/NotificationDeliveryService.cs
@@ -15,6 +15,7 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
using Notification = FirebaseAdmin.Messaging.Notification;
namespace Fin.Application.Notifications.Services.DeliveryServices;
@@ -32,19 +33,19 @@ public class NotificationDeliveryService(
IRepository userSettingsRepository,
IConfiguration configuration,
IAmbientData ambientData,
- IDateTimeProvider _dateTimeProvider,
-
+ IDateTimeProvider dateTimeProvider,
IHubContext hubContext,
IEmailSenderService emailSenderService,
- IFirebaseNotificationService firebaseNotification
- )
+ IFirebaseNotificationService firebaseNotification,
+ ILogger logger)
: INotificationDeliveryService, IAutoTransient
{
private readonly string SEND_NOTIFICATION_ACTION = "ReceiveNotification";
+
private readonly CryptoHelper _cryptoHelper = new(
configuration.GetSection(AuthenticationConstants.EncryptKeyConfigKey).Value ?? "",
configuration.GetSection(AuthenticationConstants.EncryptIvConfigKey).Value ?? ""
- );
+ );
public async Task SendNotification(NotifyUserDto notifyUser, bool autoSave = true)
@@ -52,31 +53,46 @@ public async Task SendNotification(NotifyUserDto notifyUser, bool autoSave = tru
var notificationDelivery = await deliveryRepository.Query()
.FirstOrDefaultAsync(n => n.NotificationId == notifyUser.NotificationId && n.UserId == notifyUser.UserId);
if (notificationDelivery == null)
- throw new Exception($"Notification not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
+ throw new Exception(
+ $"Notification not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
var userSettings = await userSettingsRepository.Query()
.FirstOrDefaultAsync(u => u.UserId == notifyUser.UserId);
if (userSettings == null)
- throw new Exception($"User Notification Settings not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
+ throw new Exception(
+ $"User Notification Settings not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
- foreach (var way in notifyUser.Ways)
- {
- if (way is NotificationWay.Snack or NotificationWay.Message)
- {
- await hubContext.Clients.User(notifyUser.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notifyUser);
- continue;
- }
+ var allowedWaysToSend = userSettings.AllowedWays.Intersect(notifyUser.Ways).ToList();
- if (!userSettings.Enabled || !userSettings.AllowedWays.Contains(way)) continue;
+ if (!userSettings.Enabled || !allowedWaysToSend.Any())
+ {
+ logger.LogWarning(
+ "User {userId}, don't get notification {notificationId} on any way because notifications is disabled or any way is allowed.",
+ notifyUser.UserId, notifyUser.NotificationId);
+ return;
+ }
- if (way is NotificationWay.Push)
- await SendPush(notifyUser, userSettings, false);
- if (way is NotificationWay.Email)
- await SendEmail(notifyUser);
+ try
+ {
+ if (
+ allowedWaysToSend.Contains(NotificationWay.Snack)
+ | allowedWaysToSend.Contains(NotificationWay.Message)
+ | allowedWaysToSend.Contains(NotificationWay.Push)
+ ) await SendWebSocket(notifyUser);
+ if (allowedWaysToSend.Contains(NotificationWay.Push))
+ await SendFirebase(notifyUser, userSettings, false);
+ if (allowedWaysToSend.Contains(NotificationWay.Email))
+ await SendEmail(notifyUser);
+ notificationDelivery.MarkAsDelivered();
+ }
+ catch (Exception e)
+ {
+ logger.LogError(
+ "Error on send notification with id {notificationId} to user id: {userId}.\nError: {err}",
+ notifyUser.NotificationId, notifyUser.UserId, e.ToString());
}
- notificationDelivery.MarkAsDelivered();
- await deliveryRepository.UpdateAsync(notificationDelivery, false);
+ await deliveryRepository.UpdateAsync(notificationDelivery);
if (autoSave)
await deliveryRepository.SaveChangesAsync();
@@ -87,7 +103,7 @@ public async Task MarkAsVisualized(Guid notificationId, bool autoSave = tr
if (!ambientData.IsLogged)
throw new UnauthorizedAccessException("User not logged");
- var userId = ambientData.UserId;
+ var userId = ambientData.UserId.Value;
var notification = await deliveryRepository.Query()
.FirstOrDefaultAsync(n => n.NotificationId == notificationId && n.UserId == userId);
if (notification == null)
@@ -100,37 +116,48 @@ public async Task MarkAsVisualized(Guid notificationId, bool autoSave = tr
public async Task> GetUnvisualizedNotifications(bool autoSave = true)
{
- var userId = ambientData.UserId;
- var now = _dateTimeProvider.UtcNow();
+ if (!ambientData.IsLogged)
+ throw new UnauthorizedAccessException("User not logged");
+
+ var userId = ambientData.UserId.Value;
+ var now = dateTimeProvider.UtcNow();
- var notifications = await deliveryRepository.Query(tracking: false)
+ var userNotification = await deliveryRepository.Query(tracking: false)
.Include(u => u.Notification)
.Where(n => !n.Visualized && n.UserId == userId)
- .Where(n => n.Notification.StartToDelivery <= now)
+ .Where(n => n.Notification.StartToDelivery <= now.AddMinutes(1))
.Where(n => !n.Notification.StopToDelivery.HasValue ||
n.Notification.StopToDelivery.Value >= now)
- .ToListAsync();
- var userNotification = notifications
- .Where(n => n.Notification.Ways.Contains(NotificationWay.Push) ||
- n.Notification.Ways.Contains(NotificationWay.Message) ||
- n.Notification.Ways.Contains(NotificationWay.Snack))
.Select(n => new NotifyUserDto(n.Notification, n))
+ .ToListAsync();
+
+ userNotification = userNotification
+ .Where(n => n.Ways.Any(n => n != NotificationWay.Email))
+ .ToList();
+
+ var notificationToMarkAsDelivery = userNotification
+ .Select(u => u.NotificationId)
.ToList();
await deliveryRepository.Query()
- .Where(n => userNotification.Select(u => u.NotificationId).Contains(n.NotificationId))
+ .Where(n => notificationToMarkAsDelivery.Contains(n.NotificationId))
.ExecuteUpdateAsync(x => x
- .SetProperty(a => a.Visualized, true));
+ .SetProperty(a => a.Delivery, true));
if (autoSave) await deliveryRepository.SaveChangesAsync();
return userNotification;
}
- private async Task SendPush(NotifyUserDto notify, UserNotificationSettings userSettings, bool autoSave)
+ private async Task SendWebSocket(NotifyUserDto notifyUser)
{
- await hubContext.Clients.User(userSettings.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notify);
+ await hubContext.Clients.User(notifyUser.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notifyUser);
+ }
+ private async Task SendFirebase(NotifyUserDto notify, UserNotificationSettings userSettings, bool autoSave)
+ {
+ if (userSettings.FirebaseTokens is not { Count: > 0 }) return;
+
var messages = userSettings.FirebaseTokens
.Select(t => new Message
{
@@ -140,6 +167,8 @@ private async Task SendPush(NotifyUserDto notify, UserNotificationSettings userS
{ "htmlBody", notify.HtmlBody },
{ "textBody", notify.TextBody },
{ "notificationId", notify.NotificationId.ToString() },
+ { "severity", notify.Severity.ToString() },
+ { "link", notify.Link },
},
Notification = new Notification
{
@@ -165,7 +194,7 @@ private async Task SendEmail(NotifyUserDto notification)
var userCredencial = await credencialRepository.Query(false)
.FirstOrDefaultAsync(n => n.UserId == notification.UserId);
if (userCredencial == null)
- throw new Exception($"User not found to send email notification. UserId {notification.UserId}, NotificationId {notification.NotificationId}");
+ throw new Exception("User not found to send email notification.");
var email = _cryptoHelper.Decrypt(userCredencial.EncryptedEmail);
await emailSenderService.SendEmailAsync(email, notification.Title, notification.HtmlBody);
diff --git a/Fin.Application/Users/Services/UserCreateService.cs b/Fin.Application/Users/Services/UserCreateService.cs
index 4a5731f..77c5c15 100644
--- a/Fin.Application/Users/Services/UserCreateService.cs
+++ b/Fin.Application/Users/Services/UserCreateService.cs
@@ -8,6 +8,7 @@
using Fin.Domain.Tenants.Entities;
using Fin.Domain.Users.Dtos;
using Fin.Domain.Users.Entities;
+using Fin.Domain.Users.Factories;
using Fin.Infrastructure.Authentications.Constants;
using Fin.Infrastructure.AutoServices.Interfaces;
using Fin.Infrastructure.Constants;
@@ -197,7 +198,7 @@ public async Task> CreateUser(string creationToken,
var now = _dateTimeProvider.UtcNow();
var user = new User(input, now);
- var credential = new UserCredential(user.Id, process.EncryptedEmail, process.EncryptedPassword);
+ var credential = UserCredentialFactory.Create(user.Id, process.EncryptedEmail, process.EncryptedPassword, UserCredentialFactoryType.Password);
var tenant = new Tenant(now);
user.Tenants.Add(tenant);
@@ -205,15 +206,15 @@ public async Task> CreateUser(string creationToken,
var notificationSetting = new UserNotificationSettings(user.Id, tenant.Id);
var rememberUseSetting = new UserRememberUseSetting(user.Id, tenant.Id);
- await _unitOfWork.BeginTransactionAsync();
-
- await _tenantRepository.AddAsync(tenant);
- await _userRepository.AddAsync(user);
- await _credentialRepository.AddAsync(credential);
- await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
- await _notificationSettingsRepository.AddAsync(notificationSetting);
- await _unitOfWork.CommitAsync();
-
+ await using (await _unitOfWork.BeginTransactionAsync())
+ {
+ await _tenantRepository.AddAsync(tenant);
+ await _userRepository.AddAsync(user);
+ await _credentialRepository.AddAsync(credential);
+ await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
+ await _notificationSettingsRepository.AddAsync(notificationSetting);
+ await _unitOfWork.CommitAsync();
+ }
await _cache.RemoveAsync(GenerateProcessCacheKey(creationToken));
@@ -241,7 +242,7 @@ public async Task> CreateUser(string googleId, stri
var now = _dateTimeProvider.UtcNow();
var user = new User(input, now);
- var credential = UserCredential.CreateWithGoogle(user.Id, encryptedEmail, googleId);
+ var credential = UserCredentialFactory.Create(user.Id, encryptedEmail, googleId, UserCredentialFactoryType.Google);
var tenant = new Tenant(now);
user.Tenants.Add(tenant);
@@ -249,14 +250,15 @@ public async Task> CreateUser(string googleId, stri
var notificationSetting = new UserNotificationSettings(user.Id, tenant.Id);
var rememberUseSetting = new UserRememberUseSetting(user.Id, tenant.Id);
- await _unitOfWork.BeginTransactionAsync();
- await _tenantRepository.AddAsync(tenant);
- await _userRepository.AddAsync(user);
- await _credentialRepository.AddAsync(credential);
- await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
- await _notificationSettingsRepository.AddAsync(notificationSetting);
- await _unitOfWork.CommitAsync();
-
+ await using (await _unitOfWork.BeginTransactionAsync())
+ {
+ await _tenantRepository.AddAsync(tenant);
+ await _userRepository.AddAsync(user);
+ await _credentialRepository.AddAsync(credential);
+ await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
+ await _notificationSettingsRepository.AddAsync(notificationSetting);
+ await _unitOfWork.CommitAsync();
+ }
user.Tenants.First().Users = null;
user.Credential.User = null;
diff --git a/Fin.Application/Users/Services/UserDeleteService.cs b/Fin.Application/Users/Services/UserDeleteService.cs
index abca735..033f3bd 100644
--- a/Fin.Application/Users/Services/UserDeleteService.cs
+++ b/Fin.Application/Users/Services/UserDeleteService.cs
@@ -153,31 +153,34 @@ private async Task DeleteUser(Guid userId, CancellationToken cancellationToken =
var notifications = notificationDeliveries.Select(n => n.Notification)
.Where(n => n.UserDeliveries.Count == 1);
- await unitOfWork.BeginTransactionAsync(cancellationToken);
+ await using (await unitOfWork.BeginTransactionAsync(cancellationToken))
+ {
- foreach (var notification in notifications)
- await notificationRepo.DeleteAsync(notification, cancellationToken);
- foreach (var delivery in notificationDeliveries)
- await notificationDeliveryRepo.DeleteAsync(delivery, cancellationToken);
+ foreach (var notification in notifications)
+ await notificationRepo.DeleteAsync(notification, cancellationToken);
+ foreach (var delivery in notificationDeliveries)
+ await notificationDeliveryRepo.DeleteAsync(delivery, cancellationToken);
- await rememberRepo.DeleteAsync(rememberSetting, cancellationToken);
- await notificationSettingsRepo.DeleteAsync(notificationSetting, cancellationToken);
+ await rememberRepo.DeleteAsync(rememberSetting, cancellationToken);
+ await notificationSettingsRepo.DeleteAsync(notificationSetting, cancellationToken);
- foreach (var otherDeleteRequest in otherDeleteRequests)
- await userDeleteRequestRepo.DeleteAsync(otherDeleteRequest, cancellationToken);
+ foreach (var otherDeleteRequest in otherDeleteRequests)
+ await userDeleteRequestRepo.DeleteAsync(otherDeleteRequest, cancellationToken);
- foreach (var tenantUser in tenantsUser)
- await tenantUserRepo.DeleteAsync(tenantUser, cancellationToken);
+ foreach (var tenantUser in tenantsUser)
+ await tenantUserRepo.DeleteAsync(tenantUser, cancellationToken);
- foreach (var tenant in tenants)
- await tenantRepo.DeleteAsync(tenant, cancellationToken);
+ foreach (var tenant in tenants)
+ await tenantRepo.DeleteAsync(tenant, cancellationToken);
- await credentialRepo.DeleteAsync(credential, cancellationToken);
- await userDeleteRequestRepo.DeleteAsync(deleteRequest, cancellationToken);
- await userRepo.DeleteAsync(user, cancellationToken);
+ await credentialRepo.DeleteAsync(credential, cancellationToken);
+ await userDeleteRequestRepo.DeleteAsync(deleteRequest, cancellationToken);
+ await userRepo.DeleteAsync(user, cancellationToken);
- await emailSender.SendEmailAsync(userEmail, "Conta deletada", "Sua conta no FinApp foi deletada. Agora você não poderá mais acessar seus dados e eles foram removidos da plataforma.");
+ await emailSender.SendEmailAsync(userEmail, "Conta deletada",
+ "Sua conta no FinApp foi deletada. Agora você não poderá mais acessar seus dados e eles foram removidos da plataforma.");
- await unitOfWork.CommitAsync(cancellationToken);
+ await unitOfWork.CommitAsync(cancellationToken);
+ }
}
}
\ No newline at end of file
diff --git a/Fin.Domain/Global/Interfaces/ITenantEntity.cs b/Fin.Domain/Global/Interfaces/ITenantEntity.cs
index df5590f..a26541f 100644
--- a/Fin.Domain/Global/Interfaces/ITenantEntity.cs
+++ b/Fin.Domain/Global/Interfaces/ITenantEntity.cs
@@ -1,6 +1,4 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Fin.Domain.Global.Interfaces;
+namespace Fin.Domain.Global.Interfaces;
public interface ITenantEntity: IEntity
{
diff --git a/Fin.Domain/Notifications/Dtos/NotificationInput.cs b/Fin.Domain/Notifications/Dtos/NotificationInput.cs
index e5c4389..4dbe864 100644
--- a/Fin.Domain/Notifications/Dtos/NotificationInput.cs
+++ b/Fin.Domain/Notifications/Dtos/NotificationInput.cs
@@ -12,4 +12,6 @@ public class NotificationInput
public DateTime StartToDelivery { get; set; }
public DateTime? StopToDelivery { get; set; }
public List UserIds { get; set; } = new();
+ public string Link { get; set; }
+ public NotificationSeverity Severity { get; set; }
}
\ No newline at end of file
diff --git a/Fin.Domain/Notifications/Dtos/NotificationOutput.cs b/Fin.Domain/Notifications/Dtos/NotificationOutput.cs
index 72e54dc..0cdeab2 100644
--- a/Fin.Domain/Notifications/Dtos/NotificationOutput.cs
+++ b/Fin.Domain/Notifications/Dtos/NotificationOutput.cs
@@ -13,6 +13,8 @@ public class NotificationOutput(Notification input)
public bool Continuous { get; set; } = input.Continuous;
public DateTime StartToDelivery { get; set; } = input.StartToDelivery;
public DateTime? StopToDelivery { get; set; } = input.StopToDelivery;
+ public string Link { get; set; } = input.Link;
+ public NotificationSeverity Severity { get; set; } = input.Severity;
public NotificationOutput() : this(new Notification())
{
diff --git a/Fin.Domain/Notifications/Dtos/NotificationUserDeliveryOutput.cs b/Fin.Domain/Notifications/Dtos/NotificationUserDeliveryOutput.cs
deleted file mode 100644
index 981c015..0000000
--- a/Fin.Domain/Notifications/Dtos/NotificationUserDeliveryOutput.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Fin.Domain.Notifications.Entities;
-
-namespace Fin.Domain.Notifications.Dtos;
-
-public class NotificationUserDeliveryOutput(NotificationUserDelivery input)
-{
- public Guid NotificationId { get; set; } = input.NotificationId;
- public Guid UserId { get; set; } = input.UserId;
- public bool Delivery { get; set; } = input.Delivery;
- public bool Visualized { get; set; } = input.Visualized;
-}
\ No newline at end of file
diff --git a/Fin.Domain/Notifications/Dtos/NotifyUserDto.cs b/Fin.Domain/Notifications/Dtos/NotifyUserDto.cs
index 2efc723..49183b0 100644
--- a/Fin.Domain/Notifications/Dtos/NotifyUserDto.cs
+++ b/Fin.Domain/Notifications/Dtos/NotifyUserDto.cs
@@ -9,6 +9,10 @@ public class NotifyUserDto(Notification notification, NotificationUserDelivery n
public string TextBody { get; set; } = notification.TextBody;
public string HtmlBody { get; set; } = notification.HtmlBody;
public string Title { get; set; } = notification.Title;
+ public string Link { get; set; } = notification.Link;
+ public bool Continuous { get; set; } = notification.Continuous;
+ public NotificationSeverity Severity { get; set; } = notification.Severity;
+
public Guid NotificationId { get; set; } = notification.Id;
public Guid UserId { get; set; } = notificationUserDelivery.UserId;
diff --git a/Fin.Domain/Notifications/Entities/Notification.cs b/Fin.Domain/Notifications/Entities/Notification.cs
index b99da6d..1d5b396 100644
--- a/Fin.Domain/Notifications/Entities/Notification.cs
+++ b/Fin.Domain/Notifications/Entities/Notification.cs
@@ -17,6 +17,8 @@ public class Notification: IAuditedEntity
public string NormalizedTitle { get; set; }
public DateTime StartToDelivery { get; set; }
public DateTime? StopToDelivery { get; set; }
+ public string Link { get; set; }
+ public NotificationSeverity Severity { get; set; }
public Guid Id { get; set; }
public Guid CreatedBy { get; set; }
@@ -41,6 +43,8 @@ public Notification(NotificationInput input)
Continuous = input.Continuous;
Title = input.Title;
+ Link = input.Link;
+ Severity = input.Severity;
NormalizedTitle = Title.NormalizeForComparison();
StartToDelivery = input.StartToDelivery;
StopToDelivery = input.StopToDelivery;
@@ -59,6 +63,8 @@ public List UpdateAndReturnToRemoveDeliveries(Notifica
StartToDelivery = input.StartToDelivery;
StopToDelivery = input.StopToDelivery;
Continuous = input.Continuous;
+ Link = input.Link;
+ Severity = input.Severity;
var updatedDeliveries = input.UserIds.Select(userId => new NotificationUserDelivery(userId, Id)).ToList();
diff --git a/Fin.Domain/Notifications/Enums/NotificationSeverity.cs b/Fin.Domain/Notifications/Enums/NotificationSeverity.cs
new file mode 100644
index 0000000..6964b79
--- /dev/null
+++ b/Fin.Domain/Notifications/Enums/NotificationSeverity.cs
@@ -0,0 +1,10 @@
+namespace Fin.Domain.Notifications.Enums;
+
+public enum NotificationSeverity
+{
+ Default = 0,
+ Success = 1,
+ Error = 2,
+ Warning = 3,
+ Info = 4,
+}
\ No newline at end of file
diff --git a/Fin.Domain/Users/Entities/UserCredential.cs b/Fin.Domain/Users/Entities/UserCredential.cs
index d9f7d75..10a1d50 100644
--- a/Fin.Domain/Users/Entities/UserCredential.cs
+++ b/Fin.Domain/Users/Entities/UserCredential.cs
@@ -7,44 +7,21 @@ public class UserCredential : IEntity
{
public Guid Id { get; set; }
- public string EncryptedEmail { get; private set; }
- public string EncryptedPassword { get; private set; }
+ public string EncryptedEmail { get; set; }
+ public string EncryptedPassword { get; set; }
public string GoogleId { get; set; }
public string ResetToken { get; set; } = "";
- public int FailLoginAttempts { get; private set; }
+ public int FailLoginAttempts { get; set; }
- public Guid UserId { get; private set; }
+ public Guid UserId { get; set; }
public virtual User User { get; set; }
public bool HasGoogle => !string.IsNullOrEmpty(GoogleId);
public bool HasPassword => !string.IsNullOrEmpty(EncryptedPassword);
public bool ExceededAttempts => FailLoginAttempts >= 5;
- public UserCredential()
- {
- }
-
- public UserCredential(Guid userId, string encryptedEmail, string encryptedPassword)
- {
- Id = Guid.NewGuid();
- UserId = userId;
- EncryptedEmail = encryptedEmail;
- EncryptedPassword = encryptedPassword;
- }
-
- public static UserCredential CreateWithGoogle(Guid userId, string encryptedEmail, string googleId)
- {
- return new UserCredential
- {
- Id = Guid.NewGuid(),
- UserId = userId,
- EncryptedEmail = encryptedEmail,
- GoogleId = googleId,
- };
- }
-
public bool ResetPassword(string newPasswordEncrypted, string resetToken)
{
if (resetToken != ResetToken) return false;
diff --git a/Fin.Domain/Users/Factories/UserCredentialFactory.cs b/Fin.Domain/Users/Factories/UserCredentialFactory.cs
new file mode 100644
index 0000000..225b0ff
--- /dev/null
+++ b/Fin.Domain/Users/Factories/UserCredentialFactory.cs
@@ -0,0 +1,36 @@
+using Fin.Domain.Users.Entities;
+
+namespace Fin.Domain.Users.Factories;
+
+public enum UserCredentialFactoryType
+{
+ Password = 0,
+ Google = 1
+}
+
+public static class UserCredentialFactory
+{
+ public static UserCredential Create(Guid userId, string encryptedEmail, string value, UserCredentialFactoryType type)
+ {
+ var credential = new UserCredential
+ {
+ Id = Guid.NewGuid(),
+ UserId = userId,
+ EncryptedEmail = encryptedEmail
+ };
+
+ switch (type)
+ {
+ case UserCredentialFactoryType.Password:
+ credential.EncryptedPassword = value;
+ break;
+ case UserCredentialFactoryType.Google:
+ credential.GoogleId = value;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type), type, null);
+ }
+
+ return credential;
+ }
+}
\ No newline at end of file
diff --git a/Fin.Infrastructure/Authentications/AddAuthenticationExtension.cs b/Fin.Infrastructure/Authentications/AddAuthenticationExtension.cs
index 49532f4..0f20173 100644
--- a/Fin.Infrastructure/Authentications/AddAuthenticationExtension.cs
+++ b/Fin.Infrastructure/Authentications/AddAuthenticationExtension.cs
@@ -42,6 +42,22 @@ public static IServiceCollection AddFinAuthentication(this IServiceCollection se
ValidAudience = configuration.GetSection(AuthenticationConstants.TokenJwtAudienceConfigKey).Value ?? "",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration.GetSection(AuthenticationConstants.TokenJwtKeyConfigKey).Value ?? ""))
};
+
+ options.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ var accessToken = context.Request.Query["access_token"];
+ var path = context.HttpContext.Request.Path;
+
+ if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/notifications-hub"))
+ {
+ context.Token = accessToken;
+ }
+
+ return Task.CompletedTask;
+ }
+ };
});
services.AddAuthorization();
diff --git a/Fin.Infrastructure/AutoServices/Extensions/AddAutoServicesExtension.cs b/Fin.Infrastructure/AutoServices/Extensions/AddAutoServicesExtension.cs
index e7b0d54..31a38f7 100644
--- a/Fin.Infrastructure/AutoServices/Extensions/AddAutoServicesExtension.cs
+++ b/Fin.Infrastructure/AutoServices/Extensions/AddAutoServicesExtension.cs
@@ -35,7 +35,7 @@ public static IServiceCollection AddAutoSingletonServices(this IServiceCollectio
return services;
}
- private static void RegisterDependencyByType(IServiceCollection serviceCollection, Type dependencyType, ServiceLifetime lifeStyle)
+ public static void RegisterDependencyByType(IServiceCollection serviceCollection, Type dependencyType, ServiceLifetime lifeStyle)
{
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
diff --git a/Fin.Infrastructure/Extensions/AddInfrastructureExtension.cs b/Fin.Infrastructure/Extensions/AddInfrastructureExtension.cs
index 36d36ab..c3f4670 100644
--- a/Fin.Infrastructure/Extensions/AddInfrastructureExtension.cs
+++ b/Fin.Infrastructure/Extensions/AddInfrastructureExtension.cs
@@ -4,7 +4,9 @@
using Fin.Infrastructure.BackgroundJobs;
using Fin.Infrastructure.Database.Extensions;
using Fin.Infrastructure.Firebases;
+using Fin.Infrastructure.Notifications.Hubs;
using Fin.Infrastructure.Redis;
+using Fin.Infrastructure.Seeders.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -20,10 +22,12 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
.AddAutoServices()
.AddBackgroundJobs(configuration)
.AddScoped()
- .AddScoped()
.AddScoped()
+ .AddScoped()
.AddDatabase(configuration)
- .AddFirebase(configuration);
+ .AddFirebase(configuration)
+ .AddSeeders()
+ .AddNotifications();
return services;
}
diff --git a/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.Designer.cs b/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.Designer.cs
new file mode 100644
index 0000000..5dc579b
--- /dev/null
+++ b/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.Designer.cs
@@ -0,0 +1,513 @@
+//
+using System;
+using Fin.Infrastructure.Database;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Fin.Infrastructure.Migrations
+{
+ [DbContext(typeof(FinDbContext))]
+ [Migration("20250922223535_adding_severity_link_on_notification")]
+ partial class adding_severity_link_on_notification
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("public")
+ .HasAnnotation("ProductVersion", "9.0.4")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Fin.Domain.Menus.Entities.Menu", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Color")
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("FrontRoute")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Icon")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("KeyWords")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("OnlyForAdmin")
+ .HasColumnType("boolean");
+
+ b.Property("Position")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.ToTable("Menus", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.Notification", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Continuous")
+ .HasColumnType("boolean");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("HtmlBody")
+ .HasColumnType("text");
+
+ b.Property("Link")
+ .HasColumnType("text");
+
+ b.Property("NormalizedTextBody")
+ .HasColumnType("text");
+
+ b.Property("NormalizedTitle")
+ .HasColumnType("text");
+
+ b.Property("Severity")
+ .HasColumnType("integer");
+
+ b.Property("StartToDelivery")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("StopToDelivery")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TextBody")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .HasMaxLength(250)
+ .HasColumnType("character varying(250)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("Ways")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Notifications", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.NotificationUserDelivery", b =>
+ {
+ b.Property("NotificationId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("BackgroundJobId")
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("Delivery")
+ .HasColumnType("boolean");
+
+ b.Property("Visualized")
+ .HasColumnType("boolean");
+
+ b.HasKey("NotificationId", "UserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("NotificationUserDeliveries", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.UserNotificationSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AllowedWays")
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("FirebaseTokens")
+ .HasColumnType("text");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("UserNotificationSettings", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.UserRememberUseSetting", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("NotifyOn")
+ .HasColumnType("interval");
+
+ b.Property("TenantId")
+ .HasColumnType("uuid");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Ways")
+ .HasColumnType("text");
+
+ b.Property("WeekDays")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("UserRememberUseSettings", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Tenants.Entities.Tenant", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Locale")
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("Timezone")
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Tenants", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Tenants.Entities.TenantUser", b =>
+ {
+ b.Property("TenantId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("TenantId", "UserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("TenantUsers", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("BirthDate")
+ .HasColumnType("date");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DisplayName")
+ .HasMaxLength(150)
+ .HasColumnType("character varying(150)");
+
+ b.Property("FirstName")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Gender")
+ .HasColumnType("integer");
+
+ b.Property("ImagePublicUrl")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("IsActivity")
+ .HasColumnType("boolean");
+
+ b.Property("IsAdmin")
+ .HasColumnType("boolean");
+
+ b.Property("LastName")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.UserCredential", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("EncryptedEmail")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("EncryptedPassword")
+ .HasMaxLength(300)
+ .HasColumnType("character varying(300)");
+
+ b.Property("FailLoginAttempts")
+ .HasColumnType("integer");
+
+ b.Property("GoogleId")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("ResetToken")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("EncryptedEmail")
+ .IsUnique();
+
+ b.HasIndex("GoogleId")
+ .IsUnique();
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("Credentials", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.UserDeleteRequest", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Aborted")
+ .HasColumnType("boolean");
+
+ b.Property("AbortedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeleteEffectivatedAt")
+ .HasColumnType("date");
+
+ b.Property("DeleteRequestedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("UserAbortedId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserAbortedId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("UserDeleteRequests", "public");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.NotificationUserDelivery", b =>
+ {
+ b.HasOne("Fin.Domain.Notifications.Entities.Notification", "Notification")
+ .WithMany("UserDeliveries")
+ .HasForeignKey("NotificationId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Fin.Domain.Users.Entities.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Notification");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.UserNotificationSettings", b =>
+ {
+ b.HasOne("Fin.Domain.Users.Entities.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.UserRememberUseSetting", b =>
+ {
+ b.HasOne("Fin.Domain.Users.Entities.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Tenants.Entities.TenantUser", b =>
+ {
+ b.HasOne("Fin.Domain.Tenants.Entities.Tenant", null)
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Fin.Domain.Users.Entities.User", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.UserCredential", b =>
+ {
+ b.HasOne("Fin.Domain.Users.Entities.User", "User")
+ .WithOne("Credential")
+ .HasForeignKey("Fin.Domain.Users.Entities.UserCredential", "UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.UserDeleteRequest", b =>
+ {
+ b.HasOne("Fin.Domain.Users.Entities.User", "UserAborted")
+ .WithMany()
+ .HasForeignKey("UserAbortedId");
+
+ b.HasOne("Fin.Domain.Users.Entities.User", "User")
+ .WithMany("DeleteRequests")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+
+ b.Navigation("UserAborted");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Notifications.Entities.Notification", b =>
+ {
+ b.Navigation("UserDeliveries");
+ });
+
+ modelBuilder.Entity("Fin.Domain.Users.Entities.User", b =>
+ {
+ b.Navigation("Credential");
+
+ b.Navigation("DeleteRequests");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.cs b/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.cs
new file mode 100644
index 0000000..9917395
--- /dev/null
+++ b/Fin.Infrastructure/Migrations/20250922223535_adding_severity_link_on_notification.cs
@@ -0,0 +1,43 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Fin.Infrastructure.Migrations
+{
+ ///
+ public partial class adding_severity_link_on_notification : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Link",
+ schema: "public",
+ table: "Notifications",
+ type: "text",
+ nullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "Severity",
+ schema: "public",
+ table: "Notifications",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Link",
+ schema: "public",
+ table: "Notifications");
+
+ migrationBuilder.DropColumn(
+ name: "Severity",
+ schema: "public",
+ table: "Notifications");
+ }
+ }
+}
diff --git a/Fin.Infrastructure/Migrations/FinDbContextModelSnapshot.cs b/Fin.Infrastructure/Migrations/FinDbContextModelSnapshot.cs
index 07fecbe..1e384e8 100644
--- a/Fin.Infrastructure/Migrations/FinDbContextModelSnapshot.cs
+++ b/Fin.Infrastructure/Migrations/FinDbContextModelSnapshot.cs
@@ -93,12 +93,18 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("HtmlBody")
.HasColumnType("text");
+ b.Property("Link")
+ .HasColumnType("text");
+
b.Property("NormalizedTextBody")
.HasColumnType("text");
b.Property("NormalizedTitle")
.HasColumnType("text");
+ b.Property("Severity")
+ .HasColumnType("integer");
+
b.Property("StartToDelivery")
.HasColumnType("timestamp with time zone");
diff --git a/Fin.Infrastructure/Notifications/Hubs/CustomUserIdProvider.cs b/Fin.Infrastructure/Notifications/Hubs/CustomUserIdProvider.cs
index de78544..5307784 100644
--- a/Fin.Infrastructure/Notifications/Hubs/CustomUserIdProvider.cs
+++ b/Fin.Infrastructure/Notifications/Hubs/CustomUserIdProvider.cs
@@ -6,6 +6,6 @@ public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
- return connection.User?.FindFirst("user_id")?.Value;
+ return connection.User.FindFirst("userId")?.Value;
}
}
\ No newline at end of file
diff --git a/Fin.Infrastructure/Notifications/Hubs/NotificationHub.cs b/Fin.Infrastructure/Notifications/Hubs/NotificationHub.cs
index 397c1a3..1f930a0 100644
--- a/Fin.Infrastructure/Notifications/Hubs/NotificationHub.cs
+++ b/Fin.Infrastructure/Notifications/Hubs/NotificationHub.cs
@@ -1,7 +1,9 @@
-using Microsoft.AspNetCore.SignalR;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
namespace Fin.Infrastructure.Notifications.Hubs;
+[Authorize]
public class NotificationHub: Hub
{
}
\ No newline at end of file
diff --git a/Fin.Infrastructure/Seeders/Extensions/SeedersExtensions.cs b/Fin.Infrastructure/Seeders/Extensions/SeedersExtensions.cs
new file mode 100644
index 0000000..df665c7
--- /dev/null
+++ b/Fin.Infrastructure/Seeders/Extensions/SeedersExtensions.cs
@@ -0,0 +1,26 @@
+using Fin.Infrastructure.AutoServices.Extensions;
+using Fin.Infrastructure.Seeders.interfaces;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Fin.Infrastructure.Seeders.Extensions;
+
+public static class SeedersExtensions
+{
+ public static IServiceCollection AddSeeders(this IServiceCollection services)
+ {
+ AddAutoServicesExtension.RegisterDependencyByType(services, typeof(ISeeder), ServiceLifetime.Transient);
+ return services;
+ }
+
+ public static async Task UseSeeders(this WebApplication app)
+ {
+ using var scope = app.Services.CreateScope();
+ var seeders = scope.ServiceProvider.GetServices();
+ foreach (var seeder in seeders)
+ {
+ await seeder.SeedAsync();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Fin.Infrastructure/Seeders/Seeders/DefaultMenusSeeder.cs b/Fin.Infrastructure/Seeders/Seeders/DefaultMenusSeeder.cs
new file mode 100644
index 0000000..ed7bf31
--- /dev/null
+++ b/Fin.Infrastructure/Seeders/Seeders/DefaultMenusSeeder.cs
@@ -0,0 +1,46 @@
+using Fin.Domain.Menus.Entities;
+using Fin.Domain.Menus.Enums;
+using Fin.Infrastructure.Database.Repositories;
+using Fin.Infrastructure.Seeders.interfaces;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace Fin.Infrastructure.Seeders.Seeders;
+
+public class DefaultMenusSeeder(
+ IRepository