├── .dockerignore
├── .gitignore
├── Clean-Onion_Architecture.sln
├── assets
└── cleanarch.png
├── docker-compose.dcproj
├── docker-compose.yml
├── readme.md
└── src
├── Core
├── Application
│ ├── Application.csproj
│ ├── ApplicationRegistration.cs
│ ├── Behaviors
│ │ └── CachingBehavior.cs
│ ├── Constants
│ │ └── Messages.cs
│ ├── Dtos
│ │ ├── RoleDTO.cs
│ │ ├── TokenDTO.cs
│ │ └── UserDTO.cs
│ ├── Exceptions
│ │ └── ApiException.cs
│ ├── Features
│ │ ├── Roles
│ │ │ ├── Commands
│ │ │ │ ├── CreateRoleCommand.cs
│ │ │ │ ├── RemoveRoleCommand.cs
│ │ │ │ └── UpdateRoleCommand.cs
│ │ │ ├── Queries
│ │ │ │ ├── GetAllRolesQuery.cs
│ │ │ │ ├── GetRoleByIdQuery.cs
│ │ │ │ └── GetRolesByUserIdQuery.cs
│ │ │ └── Validators
│ │ │ │ └── RoleValidator.cs
│ │ └── Users
│ │ │ ├── Commands
│ │ │ ├── ChangeEmailCommand.cs
│ │ │ ├── ChangePasswordCommand.cs
│ │ │ ├── ConfirmEmailCommand.cs
│ │ │ ├── CreateTokenByRefreshTokenCommand.cs
│ │ │ ├── ForgetPasswordCommand.cs
│ │ │ ├── LoginCommand.cs
│ │ │ ├── RegisterCommand.cs
│ │ │ ├── RemoveUserCommand.cs
│ │ │ ├── ResetPasswordCommand.cs
│ │ │ ├── UpdateUserCommand.cs
│ │ │ └── UpdateUserRoleCommand.cs
│ │ │ ├── Queries
│ │ │ ├── GetAllUsersWithRolesQuery.cs
│ │ │ └── GetAuthenticatedUserWithRolesQuery.cs
│ │ │ └── Validators
│ │ │ ├── ChangeEmailValidator.cs
│ │ │ ├── ChangePasswordValidator.cs
│ │ │ ├── ForgetPasswordValidator.cs
│ │ │ ├── LoginValidator.cs
│ │ │ ├── RefreshTokenValidator.cs
│ │ │ ├── RegisterValidator.cs
│ │ │ ├── UpdateUserRoleValidator.cs
│ │ │ └── UpdateUserValidator.cs
│ ├── Helpers
│ │ └── PasswordHelper.cs
│ ├── Interfaces
│ │ ├── ICacheable.cs
│ │ ├── Repositories
│ │ │ ├── IRefreshTokenRepository.cs
│ │ │ ├── IRepository.cs
│ │ │ ├── IRoleRepository.cs
│ │ │ ├── IUnitOfWork.cs
│ │ │ └── IUserRepository.cs
│ │ └── Services
│ │ │ ├── IEasyCacheService.cs
│ │ │ ├── IEmailService.cs
│ │ │ └── ITokenService.cs
│ ├── Mappings
│ │ └── Automapper.cs
│ └── Wrappers
│ │ ├── Abstract
│ │ ├── IDataResponse.cs
│ │ ├── IErrorResponse.cs
│ │ ├── IPagedDataResponse.cs
│ │ ├── IResponse.cs
│ │ └── ISuccessResponse.cs
│ │ └── Concrete
│ │ ├── DataResponse.cs
│ │ ├── ErrorResponse.cs
│ │ ├── PagedDataResponse.cs
│ │ ├── Response.cs
│ │ └── SuccessResponse.cs
└── Domain
│ ├── Domain.csproj
│ └── Entities
│ ├── Base
│ └── BaseEntity.cs
│ ├── RefreshToken.cs
│ ├── Role.cs
│ ├── User.cs
│ └── UserRole.cs
├── Infrastructure
├── Infrastructure
│ ├── Infrastructure.csproj
│ ├── InfrastructureRegistration.cs
│ ├── Services
│ │ ├── EasyCacheService.cs
│ │ ├── EmailService.cs
│ │ └── TokenService.cs
│ └── Settings
│ │ ├── CacheSettings.cs
│ │ ├── EmailSettings.cs
│ │ └── JWTSettings.cs
└── Persistence
│ ├── Context
│ └── CAContext.cs
│ ├── EntityConfigurations
│ ├── RoleConfiguration.cs
│ ├── UserConfiguration.cs
│ └── UserRoleConfiguration.cs
│ ├── Migrations
│ ├── 20220527113337_CreateDatabase.Designer.cs
│ ├── 20220527113337_CreateDatabase.cs
│ └── CAContextModelSnapshot.cs
│ ├── Persistence.csproj
│ ├── PersistenceRegistration.cs
│ └── Repositories
│ ├── EfEntityRepository.cs
│ ├── RefreshTokenRepository.cs
│ ├── RoleRepository.cs
│ ├── UnitOfWork.cs
│ └── UserRepository.cs
└── Presentation
└── WebAPI
├── Controllers
├── AuthController.cs
├── BaseController.cs
├── RolesController.cs
└── UsersController.cs
├── Dockerfile
├── Infrastructure
├── Extensions
│ ├── AuthRegistration.cs
│ └── ExceptionMiddlewareExtension.cs
├── Filters
│ └── ValidationFilter.cs
└── Middleware
│ └── ExceptionMiddleware.cs
├── Program.cs
├── Properties
└── launchSettings.json
├── WebAPI.csproj
├── appsettings.Development.json
└── appsettings.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
298 | *.vbp
299 |
300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
301 | *.dsw
302 | *.dsp
303 |
304 | # Visual Studio 6 technical files
305 | *.ncb
306 | *.aps
307 |
308 | # Visual Studio LightSwitch build output
309 | **/*.HTMLClient/GeneratedArtifacts
310 | **/*.DesktopClient/GeneratedArtifacts
311 | **/*.DesktopClient/ModelManifest.xml
312 | **/*.Server/GeneratedArtifacts
313 | **/*.Server/ModelManifest.xml
314 | _Pvt_Extensions
315 |
316 | # Paket dependency manager
317 | .paket/paket.exe
318 | paket-files/
319 |
320 | # FAKE - F# Make
321 | .fake/
322 |
323 | # CodeRush personal settings
324 | .cr/personal
325 |
326 | # Python Tools for Visual Studio (PTVS)
327 | __pycache__/
328 | *.pyc
329 |
330 | # Cake - Uncomment if you are using it
331 | # tools/**
332 | # !tools/packages.config
333 |
334 | # Tabs Studio
335 | *.tss
336 |
337 | # Telerik's JustMock configuration file
338 | *.jmconfig
339 |
340 | # BizTalk build output
341 | *.btp.cs
342 | *.btm.cs
343 | *.odx.cs
344 | *.xsd.cs
345 |
346 | # OpenCover UI analysis results
347 | OpenCover/
348 |
349 | # Azure Stream Analytics local run output
350 | ASALocalRun/
351 |
352 | # MSBuild Binary and Structured Log
353 | *.binlog
354 |
355 | # NVidia Nsight GPU debugger configuration file
356 | *.nvuser
357 |
358 | # MFractors (Xamarin productivity tool) working folder
359 | .mfractor/
360 |
361 | # Local History for Visual Studio
362 | .localhistory/
363 |
364 | # Visual Studio History (VSHistory) files
365 | .vshistory/
366 |
367 | # BeatPulse healthcheck temp database
368 | healthchecksdb
369 |
370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
371 | MigrationBackup/
372 |
373 | # Ionide (cross platform F# VS Code tools) working folder
374 | .ionide/
375 |
376 | # Fody - auto-generated XML schema
377 | FodyWeavers.xsd
378 |
379 | # VS Code files for those working on multiple tools
380 | .vscode/*
381 | !.vscode/settings.json
382 | !.vscode/tasks.json
383 | !.vscode/launch.json
384 | !.vscode/extensions.json
385 | *.code-workspace
386 |
387 | # Local History for Visual Studio Code
388 | .history/
389 |
390 | # Windows Installer files from build outputs
391 | *.cab
392 | *.msi
393 | *.msix
394 | *.msm
395 | *.msp
396 |
397 | # JetBrains Rider
398 | *.sln.iml
--------------------------------------------------------------------------------
/Clean-Onion_Architecture.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32505.173
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ECD7A81E-BCD2-4927-BF9C-75381E20A612}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{7372E137-8445-4318-B5BE-F41FC69B1E29}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Core\Application\Application.csproj", "{B3F8A539-6801-4741-B706-908E138FE448}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Core\Domain\Domain.csproj", "{7C4D67AA-0090-4A8F-814F-864F18433252}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{01B14A20-4B4B-4EDF-86CC-54D22C76DE5C}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "src\Infrastructure\Persistence\Persistence.csproj", "{CAA907CD-C661-4E00-A7DD-8BD4CBA8F961}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentation", "{D6B846AD-B7A3-4511-A177-DFF307B3B481}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebAPI", "src\Presentation\WebAPI\WebAPI.csproj", "{5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7}"
21 | EndProject
22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "src\Infrastructure\Infrastructure\Infrastructure.csproj", "{453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6}"
23 | EndProject
24 | Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{D75BA8DF-9B1B-4F83-A2A3-B4256E2C0BCA}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Release|Any CPU = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {B3F8A539-6801-4741-B706-908E138FE448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {B3F8A539-6801-4741-B706-908E138FE448}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {B3F8A539-6801-4741-B706-908E138FE448}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {B3F8A539-6801-4741-B706-908E138FE448}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {7C4D67AA-0090-4A8F-814F-864F18433252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {7C4D67AA-0090-4A8F-814F-864F18433252}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {7C4D67AA-0090-4A8F-814F-864F18433252}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {7C4D67AA-0090-4A8F-814F-864F18433252}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {CAA907CD-C661-4E00-A7DD-8BD4CBA8F961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {CAA907CD-C661-4E00-A7DD-8BD4CBA8F961}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {CAA907CD-C661-4E00-A7DD-8BD4CBA8F961}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {CAA907CD-C661-4E00-A7DD-8BD4CBA8F961}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {D75BA8DF-9B1B-4F83-A2A3-B4256E2C0BCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {D75BA8DF-9B1B-4F83-A2A3-B4256E2C0BCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {D75BA8DF-9B1B-4F83-A2A3-B4256E2C0BCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {D75BA8DF-9B1B-4F83-A2A3-B4256E2C0BCA}.Release|Any CPU.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(NestedProjects) = preSolution
61 | {7372E137-8445-4318-B5BE-F41FC69B1E29} = {ECD7A81E-BCD2-4927-BF9C-75381E20A612}
62 | {B3F8A539-6801-4741-B706-908E138FE448} = {7372E137-8445-4318-B5BE-F41FC69B1E29}
63 | {7C4D67AA-0090-4A8F-814F-864F18433252} = {7372E137-8445-4318-B5BE-F41FC69B1E29}
64 | {01B14A20-4B4B-4EDF-86CC-54D22C76DE5C} = {ECD7A81E-BCD2-4927-BF9C-75381E20A612}
65 | {CAA907CD-C661-4E00-A7DD-8BD4CBA8F961} = {01B14A20-4B4B-4EDF-86CC-54D22C76DE5C}
66 | {D6B846AD-B7A3-4511-A177-DFF307B3B481} = {ECD7A81E-BCD2-4927-BF9C-75381E20A612}
67 | {5D865ECB-BB7E-44B3-BAF5-D0C8D218A1D7} = {D6B846AD-B7A3-4511-A177-DFF307B3B481}
68 | {453DD5D1-4366-4CB2-ADCE-4DBCED50A9E6} = {01B14A20-4B4B-4EDF-86CC-54D22C76DE5C}
69 | EndGlobalSection
70 | GlobalSection(ExtensibilityGlobals) = postSolution
71 | SolutionGuid = {711CC539-1283-4A92-84A9-B87F849FB05E}
72 | EndGlobalSection
73 | EndGlobal
74 |
--------------------------------------------------------------------------------
/assets/cleanarch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YALCINCAN/NET6_Clean-OnionArchitectureAPI/212ffb9a634346146a58c472af61bbe62a9dc8e6/assets/cleanarch.png
--------------------------------------------------------------------------------
/docker-compose.dcproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2.1
5 | Linux
6 | d75ba8df-9b1b-4f83-a2a3-b4256e2c0bca
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | rediscache:
5 | image: redis
6 | container_name: rediscache
7 | restart: always
8 | ports:
9 | - "6379:6379"
10 | volumes:
11 | - redis_volume:/data
12 | cleanarchitecturedb:
13 | image: postgres
14 | container_name: cleanarchitecturedb
15 | restart : always
16 | ports:
17 | - 5432:5432
18 | environment:
19 | - "POSTGRES_PASSWORD=1283"
20 | volumes:
21 | - postgres_volume:/var/lib/postgresql/data
22 | webapi:
23 | image: webapi
24 | restart: always
25 | build:
26 | context: .
27 | dockerfile: src/Presentation/WebAPI/Dockerfile
28 | ports:
29 | - "5010:80"
30 | environment:
31 | - ASPNETCORE_ENVIRONMENT=Development
32 | - "ConnectionStrings:PostgreSqlConnection=User ID=postgres; Password=1283; Server=cleanarchitecturedb; Port=5432;Database=CleanArchitecture;Integrated Security=true;"
33 | - "ConnectionStrings:SeriLogConnection=User ID=postgres; Password=1283; Server=cleanarchitecturedb; Port=5432;Database=CleanArchitecture;Integrated Security=true;"
34 | - "CacheSettings:RedisURL=rediscache"
35 | - "CacheSettings:Port=6379"
36 | - "CacheSettings:PreferRedis=true"
37 |
38 |
39 | volumes:
40 | redis_volume:
41 | postgres_volume:
42 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # NET6 Clean-Onion Architecture Project
2 | # New features added [v2 branch](https://github.com/YALCINCAN/NET6_Clean-OnionArchitectureAPI/tree/v2) / [v3 branch](https://github.com/YALCINCAN/NET6_Clean-OnionArchitectureAPI/tree/v3)
3 | # New features added
4 |
5 |
6 | ## Features
7 | - .NET6
8 | - Entity Framework Core – Code First
9 | - Repository Pattern
10 | - UnitOfWork Pattern
11 | - CQRS Pattern
12 | - Mediatr
13 | - CQRS Pipeline Caching (Redis,InMemory configure from appsettings.json file)
14 | - Response Wrappers
15 | - Fluent Validation
16 | - Validation Filter
17 | - Serilog Logging
18 | - Automapper
19 | - Docker
20 | - JWT Authentication,Refresh Token
21 | - Complete User Management (Register / Forgot Password / Confirmation Mail) Without Identity
22 | - Role Based Authorization
23 | - Database Seeding
24 | - Custom Exception Handling Middleware
25 |
26 |
27 | ## How To Start .Net API
28 |
29 | For api, you must edit the appsettings.json file email settings eg.
30 |
31 | Docker support added you can start project with docker, first you must look docker compose yaml file rediscache settings eg. and write
32 |
33 | ```sh
34 | docker compose -f "docker-compose.yml" up -d --build
35 | ```
36 | When the project is up, the migrations run automatically
37 |
38 | After a database will be created.
39 |
40 | Default Admin Account :
41 |
42 | ```sh
43 | Username : admin
44 | Password : 159357456qW
45 | ```
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/Core/Application/Application.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | disable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Core/Application/ApplicationRegistration.cs:
--------------------------------------------------------------------------------
1 | using Application.Behaviors;
2 | using FluentValidation;
3 | using MediatR;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using System.Reflection;
6 |
7 |
8 | namespace Application
9 | {
10 | public static class ApplicationRegistration
11 | {
12 | public static void AddApplicationServices(this IServiceCollection services)
13 | {
14 | var assm = Assembly.GetExecutingAssembly();
15 | services.AddAutoMapper(assm);
16 | services.AddMediatR(assm);
17 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CachingBehavior<,>));
18 | services.AddValidatorsFromAssembly(assm);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Core/Application/Behaviors/CachingBehavior.cs:
--------------------------------------------------------------------------------
1 | using Application.Interfaces;
2 | using Application.Interfaces.Services;
3 | using MediatR;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Application.Behaviors
7 | {
8 | public class CachingBehavior : IPipelineBehavior where TRequest : IRequest
9 | {
10 | private readonly IEasyCacheService _easyCacheService;
11 | private readonly ILogger> _logger;
12 |
13 | public CachingBehavior(IEasyCacheService easyCacheService, ILogger> logger)
14 | {
15 | _easyCacheService = easyCacheService;
16 | _logger = logger;
17 | }
18 |
19 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next)
20 | {
21 | if (request is ICacheable cacheableQuery)
22 | {
23 | TResponse response;
24 | if (cacheableQuery.BypassCache) return await next();
25 | async Task GetResponseAndAddToCache()
26 | {
27 | response = await next();
28 |
29 | await _easyCacheService.SetAsync((string)cacheableQuery.CacheKey, response);
30 | return response;
31 | }
32 | var cachedResponse = await _easyCacheService.GetAsync(cacheableQuery.CacheKey, typeof(TResponse));
33 | if (cachedResponse != null)
34 | {
35 | response = (TResponse)cachedResponse;
36 | _logger.LogInformation($"Fetched from Cache -> '{cacheableQuery.CacheKey}'.");
37 | }
38 | else
39 | {
40 | response = await GetResponseAndAddToCache();
41 | _logger.LogInformation($"Added to Cache -> '{cacheableQuery.CacheKey}'.");
42 | }
43 | return response;
44 | }
45 | else
46 | {
47 | return await next();
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Core/Application/Constants/Messages.cs:
--------------------------------------------------------------------------------
1 | namespace Application.Constants
2 | {
3 | public static class Messages
4 | {
5 | public static string AddedSuccesfully => "Added Successfully";
6 | public static string UpdatedSuccessfully => "Updated Successfully";
7 | public static string DeletedSuccessfully => "Deleted Successfully";
8 | public static string NotFound => "Entity not found";
9 |
10 | public static string UserNameOrPasswordIsIncorrect => "Username or password is incorrect";
11 | public static string ConfirmYourEmail => "Please confirm your email";
12 | public static string EmailIsAlreadyExist => "Email is already exist";
13 | public static string UsernameIsAlreadyExist => "Username is already exist";
14 | public static string PasswordDontMatchWithConfirmation => "Password doesn't match its confirmation";
15 | public static string RegisterSuccessfully => "Register successfuly please look at your mail box for account confirmation.";
16 | public static string UserNotFound => "User not found";
17 | public static string TokenOrUserNotFound => "Token or User Not Found";
18 | public static string RefreshTokenNotFound => "Refresh Token Not Found";
19 | public static string AlreadyEmailConfirmed => "Already your email confirmed";
20 | public static string SuccessfullyEmailConfirmed => "Email confirmed successfully.You can login now";
21 | public static string RefreshTokenExpired => "Refresh Token Expired";
22 | public static string RoleNameAlreadyExist => "Role Name Already Exist";
23 | public static string UserRolesUpdatedSuccessfully => "User Roles Updated Successfully";
24 | public static string PasswordChangedSuccessfully => "Password changed successfully";
25 | public static string CurrentPasswordIsFalse => "Current Password is false";
26 | public static string IfEmailTrue => "If your email address is entered correctly, you will receive a link to reset your password.";
27 | public static string PasswordSuccessfullyReset => "Your password has been successfully reset.Your new password has been sent to your email address.We recommend that you change your password.";
28 | public static string ResetPasswordCodeInvalid => "Your Reset Password Code is invalid";
29 | public static string EmailSuccessfullyChangedConfirmYourEmail => "Email Successfully Changed.Please confirm your email";
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Core/Application/Dtos/RoleDTO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Dtos
8 | {
9 | public class RoleDTO
10 | {
11 | public Guid Id { get; set; }
12 | public string Name { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Core/Application/Dtos/TokenDTO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Dtos
8 | {
9 | public class TokenDTO
10 | {
11 | public string AccessToken { get; set; }
12 | public DateTime AccessTokenExpiration { get; set; }
13 | public string RefreshToken { get; set; }
14 | public DateTime RefreshTokenExpiration { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Core/Application/Dtos/UserDTO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Dtos
8 | {
9 | public class UserDTO
10 | {
11 | public Guid Id { get; set; }
12 | public string UserName { get; set; }
13 | public string FirstName { get; set; }
14 | public string LastName { get; set; }
15 | public string Email { get; set; }
16 | public List Roles { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Core/Application/Exceptions/ApiException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Serialization;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Exceptions
9 | {
10 | [Serializable]
11 | public class ApiException : Exception
12 | {
13 | public int StatusCode { get; }
14 | public List Errors { get; private set; } = new List();
15 |
16 | public ApiException(int statuscode, List errors)
17 | {
18 | StatusCode = statuscode;
19 | Errors = errors;
20 | }
21 | public ApiException(int statuscode, string error)
22 | {
23 | StatusCode = statuscode;
24 | Errors.Add(error);
25 | }
26 | public ApiException(SerializationInfo info, StreamingContext context) : base(info, context) { }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Commands/CreateRoleCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Dtos;
3 | using Application.Exceptions;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using AutoMapper;
9 | using Domain.Entities;
10 | using MediatR;
11 |
12 | namespace Application.Features.Roles.Commands
13 | {
14 | public class CreateRoleCommand : IRequest
15 | {
16 | public string Name { get; set; }
17 |
18 | public class CreateRoleCommandHandler : IRequestHandler
19 | {
20 | private readonly IRoleRepository _roleRepository;
21 | private readonly IUnitOfWork _unitOfWork;
22 | private readonly IMapper _mapper;
23 | private readonly IEasyCacheService _easyCacheService;
24 |
25 | public CreateRoleCommandHandler(IRoleRepository roleRepository, IUnitOfWork unitOfWork, IEasyCacheService easyCacheService, IMapper mapper)
26 | {
27 | _roleRepository = roleRepository;
28 | _unitOfWork = unitOfWork;
29 | _easyCacheService = easyCacheService;
30 | _mapper = mapper;
31 | }
32 |
33 | public async Task Handle(CreateRoleCommand request, CancellationToken cancellationToken)
34 | {
35 | var existrole = await _roleRepository.GetAsync(x => x.Name == request.Name);
36 | if (existrole != null)
37 | {
38 | throw new ApiException(400, Messages.RoleNameAlreadyExist);
39 | }
40 | var role = await _roleRepository.AddAsync(new Role()
41 | {
42 | Name = request.Name
43 | });
44 | await _unitOfWork.SaveChangesAsync();
45 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
46 | var mappedrole = _mapper.Map(role);
47 | return new DataResponse(mappedrole, 200, Messages.AddedSuccesfully);
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Commands/RemoveRoleCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Interfaces.Services;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using MediatR;
8 |
9 | namespace Application.Features.Roles.Commands
10 | {
11 | public class RemoveRoleCommand : IRequest
12 | {
13 | public Guid Id { get; set; }
14 |
15 | public RemoveRoleCommand(Guid id)
16 | {
17 | Id = id;
18 | }
19 |
20 | public class RemoveRoleCommandHandler : IRequestHandler
21 | {
22 | private readonly IRoleRepository _roleRepository;
23 | private readonly IUnitOfWork _unitOfWork;
24 | private readonly IEasyCacheService _easyCacheService;
25 |
26 | public RemoveRoleCommandHandler(IRoleRepository roleRepository, IUnitOfWork unitOfWork, IEasyCacheService easyCacheService)
27 | {
28 | _roleRepository = roleRepository;
29 | _unitOfWork = unitOfWork;
30 | _easyCacheService = easyCacheService;
31 | }
32 |
33 | public async Task Handle(RemoveRoleCommand request, CancellationToken cancellationToken)
34 | {
35 | var exisrole = await _roleRepository.GetByIdAsync(request.Id);
36 | if (exisrole == null)
37 | {
38 | throw new ApiException(404, Messages.NotFound);
39 | }
40 | _roleRepository.Remove(exisrole);
41 | await _unitOfWork.SaveChangesAsync();
42 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
43 | return new SuccessResponse(200, Messages.DeletedSuccessfully);
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Commands/UpdateRoleCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Interfaces.Services;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using AutoMapper;
8 | using MediatR;
9 |
10 | namespace Application.Features.Roles.Commands
11 | {
12 | public class UpdateRoleCommand : IRequest
13 | {
14 | public Guid Id { get; set; }
15 | public string Name { get; set; }
16 |
17 | public class UpdateRoleCommandHandler : IRequestHandler
18 | {
19 | private readonly IRoleRepository _roleRepository;
20 | private readonly IUnitOfWork _unitOfWork;
21 | private readonly IMapper _mapper;
22 | private readonly IEasyCacheService _easyCacheService;
23 |
24 | public UpdateRoleCommandHandler(IRoleRepository roleRepository, IUnitOfWork unitOfWork, IMapper mapper, IEasyCacheService easyCacheService)
25 | {
26 | _roleRepository = roleRepository;
27 | _unitOfWork = unitOfWork;
28 | _mapper = mapper;
29 | _easyCacheService = easyCacheService;
30 | }
31 |
32 |
33 | public async Task Handle(UpdateRoleCommand request, CancellationToken cancellationToken)
34 | {
35 | var existrole = await _roleRepository.GetByIdAsync(request.Id);
36 | if (existrole == null)
37 | {
38 | throw new ApiException(404, Messages.NotFound);
39 | }
40 | var roleName = await _roleRepository.GetAsync(x => x.Name == request.Name);
41 | if (roleName != null && existrole.Name != request.Name)
42 | {
43 | throw new ApiException(400, Messages.RoleNameAlreadyExist);
44 | }
45 | _mapper.Map(request, existrole);
46 | await _unitOfWork.SaveChangesAsync();
47 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
48 | return new SuccessResponse(200, Messages.UpdatedSuccessfully);
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Queries/GetAllRolesQuery.cs:
--------------------------------------------------------------------------------
1 | using Application.Dtos;
2 | using Application.Interfaces;
3 | using Application.Interfaces.Repositories;
4 | using Application.Wrappers.Abstract;
5 | using Application.Wrappers.Concrete;
6 | using AutoMapper;
7 | using MediatR;
8 |
9 | namespace Application.Features.Roles.Queries
10 | {
11 | public class GetAllRolesQuery : IRequest
12 | {
13 | public class GetAllRolesQueryHandler : IRequestHandler
14 | {
15 | private readonly IRoleRepository _roleRepository;
16 | private readonly IMapper _mapper;
17 |
18 | public GetAllRolesQueryHandler(IRoleRepository roleRepository, IMapper mapper)
19 | {
20 | _roleRepository = roleRepository;
21 | _mapper = mapper;
22 | }
23 |
24 | public async Task Handle(GetAllRolesQuery request, CancellationToken cancellationToken)
25 | {
26 | var roles = await _roleRepository.GetAllAsync();
27 | var mappedroles = _mapper.Map>(roles);
28 | return new DataResponse>(mappedroles, 200);
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Queries/GetRoleByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Dtos;
3 | using Application.Exceptions;
4 | using Application.Interfaces;
5 | using Application.Interfaces.Repositories;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using AutoMapper;
9 | using MediatR;
10 |
11 | namespace Application.Features.Roles.Queries
12 | {
13 | public class GetRoleByIdQuery : IRequest>
14 | {
15 | public Guid Id { get; set; }
16 |
17 | public GetRoleByIdQuery(Guid id)
18 | {
19 | Id = id;
20 | }
21 |
22 | public class GetRoleByIdQueryHandler : IRequestHandler>
23 | {
24 | private readonly IRoleRepository _roleRepository;
25 | private readonly IMapper _mapper;
26 |
27 | public GetRoleByIdQueryHandler(IRoleRepository roleRepository, IMapper mapper)
28 | {
29 | _roleRepository = roleRepository;
30 | _mapper = mapper;
31 | }
32 |
33 | public async Task> Handle(GetRoleByIdQuery request, CancellationToken cancellationToken)
34 | {
35 | var role = await _roleRepository.GetByIdAsync(request.Id);
36 | if (role == null)
37 | {
38 | throw new ApiException(404, Messages.NotFound);
39 | }
40 | var mappedrole = _mapper.Map(role);
41 | return new DataResponse(mappedrole, 200);
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Queries/GetRolesByUserIdQuery.cs:
--------------------------------------------------------------------------------
1 | using Application.Dtos;
2 | using Application.Interfaces;
3 | using Application.Interfaces.Repositories;
4 | using Application.Wrappers.Abstract;
5 | using Application.Wrappers.Concrete;
6 | using MediatR;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace Application.Features.Roles.Queries
14 | {
15 | public class GetRolesByUserIdQuery : IRequest>>
16 | {
17 | public Guid UserId { get; set; }
18 |
19 | public GetRolesByUserIdQuery(Guid userid)
20 | {
21 | UserId = userid;
22 | }
23 |
24 | public class GetRolesByUserIdQueryHandler : IRequestHandler>>
25 | {
26 |
27 | private readonly IRoleRepository _roleRepository;
28 |
29 | public GetRolesByUserIdQueryHandler(IRoleRepository roleRepository)
30 | {
31 | _roleRepository = roleRepository;
32 | }
33 |
34 | public async Task>> Handle(GetRolesByUserIdQuery request, CancellationToken cancellationToken)
35 | {
36 | var roles = await _roleRepository.GetRolesByUserIdAsync(request.UserId);
37 | return new DataResponse>(roles, 200);
38 | }
39 | }
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Roles/Validators/RoleValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Roles.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Roles.Validators
10 | {
11 | public class CreateRoleValidator : AbstractValidator
12 | {
13 | public CreateRoleValidator()
14 | {
15 | RuleFor(x => x.Name).NotEmpty().WithMessage("Role name is required");
16 | }
17 | }
18 |
19 | public class UpdateRoleValidator : AbstractValidator
20 | {
21 | public UpdateRoleValidator()
22 | {
23 | RuleFor(x => x.Id).NotEmpty().WithMessage("Role ID is required");
24 | RuleFor(x => x.Name).NotEmpty().WithMessage("Role name is required");
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/ChangeEmailCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Helpers;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using MediatR;
9 | using System.Text.Json.Serialization;
10 |
11 | namespace Application.Features.Users.Commands
12 | {
13 | public class ChangeEmailCommand : IRequest
14 | {
15 | [JsonIgnore]
16 | public Guid UserId { get; set; }
17 |
18 | public string Email { get; set; }
19 |
20 | public ChangeEmailCommand(Guid userid, string email)
21 | {
22 | UserId = userid;
23 | Email = email;
24 | }
25 |
26 | public class ChangeEmailCommandHandler : IRequestHandler
27 | {
28 | private readonly IUserRepository _userRepository;
29 | private readonly IUnitOfWork _unitOfWork;
30 | private readonly IEmailService _emailService;
31 | private readonly IEasyCacheService _easyCacheService;
32 |
33 | public ChangeEmailCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEmailService emailService, IEasyCacheService easyCacheService)
34 | {
35 | _userRepository = userRepository;
36 | _unitOfWork = unitOfWork;
37 | _emailService = emailService;
38 | _easyCacheService = easyCacheService;
39 | }
40 |
41 | public async Task Handle(ChangeEmailCommand request, CancellationToken cancellationToken)
42 | {
43 | var user = await _userRepository.GetByIdAsync(request.UserId);
44 | if (user == null)
45 | {
46 | throw new ApiException(404, Messages.UserNotFound);
47 | }
48 | var email = await _userRepository.GetAsync(x => x.Email == request.Email);
49 | if (email != null && user.Email != request.Email)
50 | {
51 | throw new ApiException(400, Messages.EmailIsAlreadyExist);
52 | }
53 | user.Email = request.Email;
54 | user.EmailConfirmed = false;
55 | user.EmailConfirmedCode = null;
56 | user.EmailConfirmationCode = PasswordHelper.GenerateRandomString(20);
57 | await _unitOfWork.SaveChangesAsync();
58 | string link = "http://localhost:5010/api/users/confirmemail/" + user.EmailConfirmationCode;
59 | await _emailService.ConfirmationMailAsync(link, request.Email);
60 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
61 | return new SuccessResponse(200, Messages.EmailSuccessfullyChangedConfirmYourEmail);
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/ChangePasswordCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Helpers;
4 | using Application.Interfaces.Repositories;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using MediatR;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Text.Json.Serialization;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Features.Users.Commands
16 | {
17 | public class ChangePasswordCommand : IRequest
18 | {
19 | [JsonIgnore]
20 | public Guid UserId { get; set; }
21 | public string CurrentPassword { get; set; }
22 | public string NewPassword { get; set; }
23 | public string ConfirmPassword { get; set; }
24 |
25 |
26 | public class ChangePasswordCommandHandler : IRequestHandler
27 | {
28 | private readonly IUserRepository _userRepository;
29 | private readonly IUnitOfWork _unitOfWork;
30 |
31 | public ChangePasswordCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork)
32 | {
33 | _userRepository = userRepository;
34 | _unitOfWork = unitOfWork;
35 | }
36 |
37 | public async Task Handle(ChangePasswordCommand request, CancellationToken cancellationToken)
38 | {
39 | var user = await _userRepository.GetByIdAsync(request.UserId);
40 | if (user == null)
41 | {
42 | throw new ApiException(404, Messages.UserNotFound);
43 | }
44 | if (request.NewPassword != request.ConfirmPassword)
45 | {
46 | throw new ApiException(400, Messages.PasswordDontMatchWithConfirmation);
47 | }
48 | if (!PasswordHelper.VerifyHash(request.CurrentPassword, user.PasswordHash, user.PasswordSalt))
49 | {
50 | throw new ApiException(400, Messages.CurrentPasswordIsFalse);
51 | }
52 |
53 | var (passwordHash, passwordSalt) = PasswordHelper.CreateHash(request.NewPassword);
54 | user.PasswordHash = passwordHash;
55 | user.PasswordSalt = passwordSalt;
56 | await _unitOfWork.SaveChangesAsync();
57 | return new SuccessResponse(200, Messages.PasswordChangedSuccessfully);
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/ConfirmEmailCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Wrappers.Abstract;
5 | using Application.Wrappers.Concrete;
6 | using MediatR;
7 |
8 | namespace Application.Features.Users.Commands
9 | {
10 | public class ConfirmEmailCommand : IRequest
11 | {
12 | public string EmailConfirmationCode { get; set; }
13 |
14 | public ConfirmEmailCommand(string code)
15 | {
16 | EmailConfirmationCode = code;
17 | }
18 | public class ConfirmEmailCommandHandler : IRequestHandler
19 | {
20 | private readonly IUserRepository _userRepository;
21 | private readonly IUnitOfWork _unitOfWork;
22 |
23 | public ConfirmEmailCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork)
24 | {
25 | _userRepository = userRepository;
26 | _unitOfWork = unitOfWork;
27 | }
28 |
29 | public async Task Handle(ConfirmEmailCommand request, CancellationToken cancellationToken)
30 | {
31 | var user = await _userRepository.GetAsync(x => x.EmailConfirmationCode == request.EmailConfirmationCode || x.EmailConfirmedCode == request.EmailConfirmationCode);
32 | if (user == null)
33 | {
34 | throw new ApiException(404, Messages.UserNotFound);
35 | }
36 | if (user.EmailConfirmed)
37 | {
38 | throw new ApiException(400, Messages.AlreadyEmailConfirmed);
39 | }
40 | user.EmailConfirmed = true;
41 | user.EmailConfirmationCode = null;
42 | user.EmailConfirmedCode = request.EmailConfirmationCode;
43 | await _unitOfWork.SaveChangesAsync();
44 | return new SuccessResponse(200, Messages.SuccessfullyEmailConfirmed);
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/CreateTokenByRefreshTokenCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Dtos;
3 | using Application.Exceptions;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using MediatR;
9 |
10 | namespace Application.Features.Users.Commands
11 | {
12 | public class CreateTokenByRefreshTokenCommand : IRequest
13 | {
14 | public string RefreshToken { get; set; }
15 |
16 | public class CreateTokenByRefreshTokenHandler : IRequestHandler
17 | {
18 | private readonly IUserRepository _userRepository;
19 | private readonly IRoleRepository _roleRepository;
20 | private readonly IRefreshTokenRepository _refreshTokenRepository;
21 | private readonly ITokenService _tokenService;
22 | private readonly IUnitOfWork _unitOfWork;
23 | private readonly IEasyCacheService _easyCacheService;
24 |
25 | public CreateTokenByRefreshTokenHandler(IUserRepository userRepository, IRefreshTokenRepository refreshTokenRepository, IUnitOfWork unitOfWork, ITokenService tokenService, IRoleRepository roleRepository, IEasyCacheService easyCacheService)
26 | {
27 | _userRepository = userRepository;
28 | _unitOfWork = unitOfWork;
29 | _tokenService = tokenService;
30 | _refreshTokenRepository = refreshTokenRepository;
31 | _roleRepository = roleRepository;
32 | _easyCacheService = easyCacheService;
33 | }
34 |
35 | public async Task Handle(CreateTokenByRefreshTokenCommand request, CancellationToken cancellationToken)
36 | {
37 | var existRefreshToken = await _refreshTokenRepository.GetAsync(x => x.Code == request.RefreshToken);
38 | if (existRefreshToken == null)
39 | {
40 | throw new ApiException(404, Messages.RefreshTokenNotFound);
41 | }
42 | var user = await _userRepository.GetByIdAsync(existRefreshToken.UserId);
43 | if (user == null)
44 | {
45 | throw new ApiException(404, Messages.UserNotFound);
46 | }
47 | if (existRefreshToken.Expiration < DateTime.Now)
48 | {
49 | throw new ApiException(404, Messages.RefreshTokenExpired);
50 | }
51 | var roles = await _roleRepository.GetAllAsync(x => x.UserRoles.Any(y => y.UserId == user.Id));
52 | List roleNames = new List();
53 | foreach (var role in roles)
54 | {
55 | roleNames.Add(role.Name);
56 | }
57 | var tokendto = _tokenService.CreateToken(user, roleNames);
58 | existRefreshToken.Code = tokendto.RefreshToken;
59 | existRefreshToken.Expiration = tokendto.RefreshTokenExpiration;
60 | await _unitOfWork.SaveChangesAsync();
61 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
62 | return new DataResponse(tokendto, 200);
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/ForgetPasswordCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Helpers;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using MediatR;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Features.Users.Commands
16 | {
17 | public class ForgetPasswordCommand : IRequest
18 | {
19 | public string Email { get; set; }
20 |
21 | public class ForgetPasswordCommandHandler : IRequestHandler
22 | {
23 | private readonly IUserRepository _userRepository;
24 | private readonly IUnitOfWork _unitOfWork;
25 | private readonly IEmailService _emailService;
26 | public ForgetPasswordCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEmailService emailService)
27 | {
28 | _userRepository = userRepository;
29 | _unitOfWork = unitOfWork;
30 | _emailService = emailService;
31 | }
32 | public async Task Handle(ForgetPasswordCommand request, CancellationToken cancellationToken)
33 | {
34 | var user = await _userRepository.GetAsync(x => x.Email == request.Email);
35 | if (user == null)
36 | {
37 | throw new ApiException(404, Messages.UserNotFound);
38 | }
39 | user.ResetPasswordCode = PasswordHelper.GenerateRandomString(20);
40 | await _unitOfWork.SaveChangesAsync();
41 | string link = "http://localhost:5010/api/users/resetpassword/" + user.ResetPasswordCode + "/" + user.Email;
42 | await _emailService.ForgetPasswordMailAsync(link, user.Email);
43 | return new SuccessResponse(200, Messages.IfEmailTrue);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/LoginCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Dtos;
3 | using Application.Exceptions;
4 | using Application.Helpers;
5 | using Application.Interfaces.Repositories;
6 | using Application.Interfaces.Services;
7 | using Application.Wrappers.Abstract;
8 | using Application.Wrappers.Concrete;
9 | using Domain.Entities;
10 | using MediatR;
11 | using Serilog;
12 |
13 | namespace Application.Features.Users.Commands
14 | {
15 | public class LoginCommand : IRequest
16 | {
17 | public string UserName { get; set; }
18 | public string Password { get; set; }
19 |
20 | public class LoginCommandHandler : IRequestHandler
21 | {
22 | private readonly IUserRepository _userRepository;
23 | private readonly IRoleRepository _roleRepository;
24 | private readonly ITokenService _tokenService;
25 | private readonly IRefreshTokenRepository _refreshTokenRepository;
26 | private readonly IUnitOfWork _unitOfWork;
27 |
28 | public LoginCommandHandler(IUserRepository userRepository, IRoleRepository roleRepository, IUnitOfWork unitOfWork, ITokenService tokenService, IRefreshTokenRepository refreshTokenRepository)
29 | {
30 | _userRepository = userRepository;
31 | _roleRepository = roleRepository;
32 | _tokenService = tokenService;
33 | _refreshTokenRepository = refreshTokenRepository;
34 | _unitOfWork = unitOfWork;
35 | }
36 |
37 | public async Task Handle(LoginCommand request, CancellationToken cancellationToken)
38 | {
39 | var user = await _userRepository.GetAsync(x => x.UserName == request.UserName, noTracking: true);
40 | if (user == null)
41 | {
42 | throw new ApiException(404, Messages.UserNotFound);
43 | }
44 | if (!user.EmailConfirmed)
45 | {
46 | throw new ApiException(400, Messages.ConfirmYourEmail);
47 | }
48 | if (!PasswordHelper.VerifyHash(request.Password, user.PasswordHash, user.PasswordSalt))
49 | {
50 | throw new ApiException(400, Messages.UserNameOrPasswordIsIncorrect);
51 | }
52 | var roles = await _roleRepository.GetAllAsync(x => x.UserRoles.Any(y => y.UserId == user.Id));
53 | List roleNames = new List();
54 | foreach (var role in roles)
55 | {
56 | roleNames.Add(role.Name);
57 | }
58 | var tokendto = _tokenService.CreateToken(user, roleNames);
59 | var refreshToken = await _refreshTokenRepository.GetAsync(x => x.UserId == user.Id);
60 | if (refreshToken == null)
61 | {
62 | await _refreshTokenRepository.AddAsync(new RefreshToken { UserId = user.Id, Code = tokendto.RefreshToken, Expiration = tokendto.RefreshTokenExpiration });
63 | await _unitOfWork.SaveChangesAsync();
64 | }
65 | else
66 | {
67 | refreshToken.Code = tokendto.RefreshToken;
68 | refreshToken.Expiration = tokendto.RefreshTokenExpiration;
69 | await _unitOfWork.SaveChangesAsync();
70 | }
71 | return new DataResponse(tokendto, 200);
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/RegisterCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Helpers;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using AutoMapper;
9 | using Domain.Entities;
10 | using MediatR;
11 |
12 | namespace Application.Features.Users.Commands
13 | {
14 | public class RegisterCommand : IRequest
15 | {
16 | public string UserName { get; set; }
17 | public string FirstName { get; set; }
18 | public string LastName { get; set; }
19 | public string Email { get; set; }
20 | public string Password { get; set; }
21 | public string ConfirmPassword { get; set; }
22 |
23 | public class RegisterCommandHandler : IRequestHandler
24 | {
25 | private readonly IUserRepository _userRepository;
26 | private readonly IUnitOfWork _unitOfWork;
27 | private readonly IEmailService _emailService;
28 | private readonly IMapper _mapper;
29 |
30 | public RegisterCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEmailService emailService, IMapper mapper)
31 | {
32 | _userRepository = userRepository;
33 | _unitOfWork = unitOfWork;
34 | _emailService = emailService;
35 | _mapper = mapper;
36 | }
37 |
38 | public async Task Handle(RegisterCommand request, CancellationToken cancellationToken)
39 | {
40 | // Example transaction usage
41 | // using var transaction = _unitOfWork.BeginTransaction();
42 | //try
43 | //{
44 | var existuser = await _userRepository.GetAsync(x => x.UserName == request.UserName || x.Email == request.Email, noTracking: true);
45 | if (existuser?.UserName == request.UserName)
46 | throw new ApiException(400, Messages.UsernameIsAlreadyExist);
47 |
48 | if (existuser?.Email == request.Email)
49 | throw new ApiException(400, Messages.EmailIsAlreadyExist);
50 |
51 |
52 | var (passwordHash, passwordSalt) = PasswordHelper.CreateHash(request.Password);
53 | var user = _mapper.Map(request);
54 | user.PasswordHash = passwordHash;
55 | user.PasswordSalt = passwordSalt;
56 | user.EmailConfirmationCode = PasswordHelper.GenerateRandomString(20);
57 | await _userRepository.AddAsync(user);
58 | await _unitOfWork.SaveChangesAsync();
59 | //transaction.Commit();
60 |
61 | //string link = "http://localhost:8080/confirmemail/" + user.EmailConfirmationCode; if u use spa you must use this link example
62 | string link = "http://localhost:5010/api/users/confirmemail/" + user.EmailConfirmationCode;
63 | await _emailService.ConfirmationMailAsync(link, request.Email);
64 | return new SuccessResponse(200, Messages.RegisterSuccessfully);
65 | //}
66 | //catch (Exception ex)
67 | //{
68 | // transaction.Rollback();
69 | // // Logging ...
70 | //}
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/RemoveUserCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Interfaces.Services;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using MediatR;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace Application.Features.Users.Commands
15 | {
16 | public class RemoveUserCommand : IRequest
17 | {
18 | public Guid UserId { get; set; }
19 |
20 | public RemoveUserCommand(Guid userid)
21 | {
22 | UserId = userid;
23 | }
24 |
25 | public class RemoveUserCommandHandler : IRequestHandler
26 | {
27 | private readonly IUserRepository _userRepository;
28 | private readonly IUnitOfWork _unitOfWork;
29 | private readonly IEasyCacheService _easyCacheService;
30 |
31 | public RemoveUserCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEasyCacheService easyCacheService)
32 | {
33 | _userRepository = userRepository;
34 | _unitOfWork = unitOfWork;
35 | _easyCacheService = easyCacheService;
36 | }
37 |
38 | public async Task Handle(RemoveUserCommand request, CancellationToken cancellationToken)
39 | {
40 | var user = await _userRepository.GetByIdAsync(request.UserId);
41 | if (user == null)
42 | {
43 | throw new ApiException(404, Messages.UserNotFound);
44 | }
45 | _userRepository.Remove(user);
46 | await _unitOfWork.SaveChangesAsync();
47 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
48 | return new SuccessResponse(200, Messages.DeletedSuccessfully);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/ResetPasswordCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Helpers;
4 | using Application.Interfaces.Repositories;
5 | using Application.Interfaces.Services;
6 | using Application.Wrappers.Abstract;
7 | using Application.Wrappers.Concrete;
8 | using MediatR;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Features.Users.Commands
16 | {
17 | public class ResetPasswordCommand : IRequest
18 | {
19 | public string ResetPasswordCode { get; set; }
20 | public string Email { get; set; }
21 |
22 | public ResetPasswordCommand(string code, string email)
23 | {
24 | ResetPasswordCode = code;
25 | Email = email;
26 | }
27 |
28 | public class ResetPasswordCommandHandler : IRequestHandler
29 | {
30 | private readonly IUserRepository _userRepository;
31 | private readonly IUnitOfWork _unitOfWork;
32 | private readonly IEmailService _emailService;
33 |
34 | public ResetPasswordCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEmailService emailService)
35 | {
36 | _userRepository = userRepository;
37 | _unitOfWork = unitOfWork;
38 | _emailService = emailService;
39 | }
40 |
41 | public async Task Handle(ResetPasswordCommand request, CancellationToken cancellationToken)
42 | {
43 | var user = await _userRepository.GetAsync(x => x.Email == request.Email);
44 | if (user == null)
45 | {
46 | throw new ApiException(404, Messages.UserNotFound);
47 | }
48 | var controlcode = await _userRepository.GetAsync(x => x.ResetPasswordCode == request.ResetPasswordCode);
49 | if (controlcode == null)
50 | {
51 | throw new ApiException(400, Messages.ResetPasswordCodeInvalid);
52 | }
53 |
54 | var newPassword = PasswordHelper.GenerateRandomString(8);
55 | var (passwordHash, passwordSalt) = PasswordHelper.CreateHash(newPassword);
56 | user.PasswordHash = passwordHash;
57 | user.PasswordSalt = passwordSalt;
58 | user.ResetPasswordCode = null;
59 | await _unitOfWork.SaveChangesAsync();
60 | await _emailService.SendNewPasswordAsync(newPassword, user.Email);
61 | return new SuccessResponse(200, Messages.PasswordSuccessfullyReset);
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/UpdateUserCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Interfaces.Services;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using AutoMapper;
8 | using MediatR;
9 | using System.Text.Json.Serialization;
10 |
11 | namespace Application.Features.Users.Commands
12 | {
13 | public class UpdateUserCommand : IRequest
14 | {
15 | [JsonIgnore]
16 | public Guid UserId { get; set; }
17 | public string UserName { get; set; }
18 | public string FirstName { get; set; }
19 | public string LastName { get; set; }
20 |
21 | public class UpdateUserCommandHandler : IRequestHandler
22 | {
23 | private readonly IUserRepository _userRepository;
24 | private readonly IUnitOfWork _unitOfWork;
25 | private readonly IMapper _mapper;
26 | private readonly IEasyCacheService _easyCacheService;
27 |
28 | public UpdateUserCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IMapper mapper, IEasyCacheService easyCacheService)
29 | {
30 | _userRepository = userRepository;
31 | _unitOfWork = unitOfWork;
32 | _mapper = mapper;
33 | _easyCacheService = easyCacheService;
34 | }
35 |
36 | public async Task Handle(UpdateUserCommand request, CancellationToken cancellationToken)
37 | {
38 | var user = await _userRepository.GetByIdAsync(request.UserId);
39 | if (user == null)
40 | {
41 | throw new ApiException(404, Messages.UserNotFound);
42 | }
43 | var username = await _userRepository.GetAsync(x => x.UserName == request.UserName);
44 | if (username != null && user.UserName != request.UserName)
45 | {
46 | throw new ApiException(400, Messages.UsernameIsAlreadyExist);
47 | }
48 | _mapper.Map(request, user);
49 | await _unitOfWork.SaveChangesAsync();
50 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
51 | return new SuccessResponse(200, Messages.UpdatedSuccessfully);
52 | }
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Commands/UpdateUserRoleCommand.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Exceptions;
3 | using Application.Interfaces.Repositories;
4 | using Application.Interfaces.Services;
5 | using Application.Wrappers.Abstract;
6 | using Application.Wrappers.Concrete;
7 | using Domain.Entities;
8 | using MediatR;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Text;
13 | using System.Threading.Tasks;
14 |
15 | namespace Application.Features.Users.Commands
16 | {
17 | public class UpdateUserRoleCommand : IRequest
18 | {
19 | public Guid UserId { get; set; }
20 | public Guid[] RoleIds { get; set; }
21 |
22 |
23 | public class UpdateUserRoleCommandHandler : IRequestHandler
24 | {
25 | private readonly IUserRepository _userRepository;
26 | private readonly IUnitOfWork _unitOfWork;
27 | private readonly IEasyCacheService _easyCacheService;
28 |
29 | public UpdateUserRoleCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork, IEasyCacheService easyCacheService)
30 | {
31 | _userRepository = userRepository;
32 | _unitOfWork = unitOfWork;
33 | _easyCacheService = easyCacheService;
34 | }
35 |
36 | public async Task Handle(UpdateUserRoleCommand request, CancellationToken cancellationToken)
37 | {
38 | var user = await _userRepository.GetUserRolesByUserIdAsync(request.UserId);
39 | if (user == null)
40 | {
41 | throw new ApiException(404, Messages.UserNotFound);
42 | }
43 | if (request.RoleIds != null)
44 | {
45 | user.UserRoles = request.RoleIds.Select(roleid => new UserRole
46 | {
47 | RoleId = roleid
48 | }).ToList();
49 | }
50 | else
51 | {
52 | user.UserRoles = new List();
53 | }
54 | await _unitOfWork.SaveChangesAsync();
55 | await _easyCacheService.RemoveByPrefixAsync("GetAuthenticatedUserWithRoles");
56 | return new SuccessResponse(200, Messages.UserRolesUpdatedSuccessfully);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Queries/GetAllUsersWithRolesQuery.cs:
--------------------------------------------------------------------------------
1 | using Application.Dtos;
2 | using Application.Interfaces.Repositories;
3 | using Application.Wrappers.Concrete;
4 | using MediatR;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Application.Features.Users.Queries
8 | {
9 | public class GetAllUsersWithRolesQuery : IRequest>>
10 | {
11 | public class GetAllUsersWithRolesQueryHandler : IRequestHandler>>
12 | {
13 | private readonly IUserRepository _userRepository;
14 | private readonly ILogger _logger;
15 |
16 | public GetAllUsersWithRolesQueryHandler(IUserRepository userRepository, ILogger logger)
17 | {
18 | _userRepository = userRepository;
19 | _logger = logger;
20 | }
21 |
22 | public async Task>> Handle(GetAllUsersWithRolesQuery request, CancellationToken cancellationToken)
23 | {
24 | var userswithroles = await _userRepository.GetAllUsersWithRolesAsync();
25 | _logger.LogInformation("GetAllUserWithRoles = {@GetAllUserWithRoles}",userswithroles);
26 | return new DataResponse>(userswithroles, 200);
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Queries/GetAuthenticatedUserWithRolesQuery.cs:
--------------------------------------------------------------------------------
1 | using Application.Constants;
2 | using Application.Dtos;
3 | using Application.Exceptions;
4 | using Application.Interfaces;
5 | using Application.Interfaces.Repositories;
6 | using Application.Wrappers.Concrete;
7 | using MediatR;
8 |
9 | namespace Application.Features.Users.Queries
10 | {
11 | public class GetAuthenticatedUserWithRolesQuery : IRequest>, ICacheable
12 | {
13 | public Guid UserId { get; set; }
14 |
15 | public bool BypassCache => false;
16 |
17 | public string CacheKey => $"GetAuthenticatedUserWithRoles-{UserId}";
18 |
19 | public GetAuthenticatedUserWithRolesQuery(Guid userId)
20 | {
21 | UserId = userId;
22 | }
23 |
24 | public class GetAuthenticatedUserWithRolesQueryHandler : IRequestHandler>
25 | {
26 | private readonly IUserRepository _userRepository;
27 |
28 | public GetAuthenticatedUserWithRolesQueryHandler(IUserRepository userRepository)
29 | {
30 | _userRepository = userRepository;
31 | }
32 |
33 | public async Task> Handle(GetAuthenticatedUserWithRolesQuery request, CancellationToken cancellationToken)
34 | {
35 | var user = await _userRepository.GetByIdAsync(request.UserId);
36 | if (user == null)
37 | {
38 | throw new ApiException(404, Messages.UserNotFound);
39 | }
40 | var userwithroles = await _userRepository.GetUserWithRolesAsync(request.UserId);
41 | return new DataResponse(userwithroles, 200);
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/ChangeEmailValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class ChangeEmailValidator : AbstractValidator
12 | {
13 | public ChangeEmailValidator()
14 | {
15 | RuleFor(x => x.Email).NotEmpty().WithMessage("Email is required");
16 | RuleFor(x => x.Email).EmailAddress().WithMessage("Email is not valid");
17 |
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/ChangePasswordValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class ChangePasswordValidator : AbstractValidator
12 | {
13 | public ChangePasswordValidator()
14 | {
15 | RuleFor(x => x.CurrentPassword).NotEmpty().WithMessage("Current password is required");
16 | RuleFor(x => x.NewPassword).NotEmpty().WithMessage("New Password is required")
17 | .MinimumLength(8).WithMessage("Your password length must be at least 8.")
18 | .MaximumLength(16).WithMessage("Your password length must not exceed 16.")
19 | .Matches(@"[A-Z]+").WithMessage("Your password must contain at least one uppercase letter.")
20 | .Matches(@"[a-z]+").WithMessage("Your password must contain at least one lowercase letter.")
21 | .Matches(@"[0-9]+").WithMessage("Your password must contain at least one number.");
22 | RuleFor(x => x.ConfirmPassword).NotEmpty().WithMessage("Confirm password is required").Equal(x => x.NewPassword).WithMessage("New Password and ConfirmPassword must match");
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/ForgetPasswordValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class ForgetPasswordValidator : AbstractValidator
12 | {
13 | public ForgetPasswordValidator()
14 | {
15 | RuleFor(x => x.Email).NotEmpty().EmailAddress();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/LoginValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class LoginValidator : AbstractValidator
12 | {
13 | public LoginValidator()
14 | {
15 | RuleFor(x => x.UserName).NotEmpty().WithMessage("UserName is required");
16 | RuleFor(x => x.Password).NotEmpty().WithMessage("Password is required");
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/RefreshTokenValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class RefreshTokenValidator : AbstractValidator
12 | {
13 | public RefreshTokenValidator()
14 | {
15 | RuleFor(x => x.RefreshToken).NotEmpty().WithMessage("Refresh token is required");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/RegisterValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 |
4 | namespace Application.Features.Users.Validators
5 | {
6 | public class RegisterValidator : AbstractValidator
7 | {
8 | public RegisterValidator()
9 | {
10 | RuleFor(x => x.UserName).NotEmpty().WithMessage("UserName is required");
11 | RuleFor(x => x.FirstName).NotEmpty().WithMessage("FirstName is required");
12 | RuleFor(x => x.LastName).NotEmpty().WithMessage("LastName is required");
13 | RuleFor(x => x.Email).NotEmpty().WithMessage("Email is required");
14 | RuleFor(x => x.Email).EmailAddress().WithMessage("Email is not valid");
15 | RuleFor(x => x.Password).NotEmpty().WithMessage("Password is required")
16 | .MinimumLength(8).WithMessage("Your password length must be at least 8.")
17 | .MaximumLength(16).WithMessage("Your password length must not exceed 16.")
18 | .Matches(@"[A-Z]+").WithMessage("Your password must contain at least one uppercase letter.")
19 | .Matches(@"[a-z]+").WithMessage("Your password must contain at least one lowercase letter.")
20 | .Matches(@"[0-9]+").WithMessage("Your password must contain at least one number.");
21 | RuleFor(x => x.ConfirmPassword).NotEmpty().WithMessage("Confirm password is required").Equal(x => x.Password).WithMessage("Password and ConfirmPassword must match");
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/UpdateUserRoleValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class UpdateUserRoleValidator : AbstractValidator
12 | {
13 | public UpdateUserRoleValidator()
14 | {
15 | RuleFor(x => x.UserId).NotEmpty().WithMessage("UserId is required");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Core/Application/Features/Users/Validators/UpdateUserValidator.cs:
--------------------------------------------------------------------------------
1 | using Application.Features.Users.Commands;
2 | using FluentValidation;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Features.Users.Validators
10 | {
11 | public class UpdateUserValidator : AbstractValidator
12 | {
13 | public UpdateUserValidator()
14 | {
15 | RuleFor(x => x.FirstName).NotEmpty().WithMessage("First name is required");
16 | RuleFor(x => x.LastName).NotEmpty().WithMessage("Last name is required");
17 | RuleFor(x => x.UserName).NotEmpty().WithMessage("User name is required");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Core/Application/Helpers/PasswordHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Helpers
9 | {
10 | public static class PasswordHelper
11 | {
12 | public static (byte[] passwordHash, byte[] passwordSalt) CreateHash(string password)
13 | {
14 | using var hmac = new HMACSHA512();
15 | return (
16 | passwordHash: hmac.ComputeHash(Encoding.UTF8.GetBytes(password)),
17 | passwordSalt: hmac.Key);
18 | }
19 |
20 | public static bool VerifyHash(string password, byte[] hash, byte[] salt)
21 | {
22 | using var hmac = new HMACSHA512(salt);
23 | var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
24 | for (var i = 0; i < computedHash.Length; i++)
25 | if (computedHash[i] != hash[i])
26 | return false;
27 |
28 | return true;
29 | }
30 |
31 | public static string GenerateRandomString(int length,bool createGuid = true)
32 | {
33 | var randomstring = new string(Enumerable
34 | .Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", length)
35 | .Select(x =>
36 | {
37 | var cryptoResult = new byte[4];
38 | using (var cryptoProvider = RandomNumberGenerator.Create())
39 | cryptoProvider.GetBytes(cryptoResult);
40 | return x[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(x.Length)];
41 | })
42 | .ToArray());
43 | if (createGuid)
44 | return randomstring + Guid.NewGuid().ToString().Replace("-", "");
45 | else
46 | return randomstring;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/ICacheable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Interfaces
8 | {
9 | public interface ICacheable
10 | {
11 | bool BypassCache { get; }
12 | string CacheKey { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Repositories/IRefreshTokenRepository.cs:
--------------------------------------------------------------------------------
1 | using Domain.Entities;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Application.Interfaces.Repositories
9 | {
10 | public interface IRefreshTokenRepository : IRepository
11 | {
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Repositories/IRepository.cs:
--------------------------------------------------------------------------------
1 | using Domain.Entities.Base;
2 | using System.Linq.Expressions;
3 |
4 | namespace Application.Interfaces.Repositories
5 | {
6 | public interface IRepository where TEntity : BaseEntity
7 | {
8 | TEntity Add(TEntity entity);
9 |
10 | Task AddAsync(TEntity entity);
11 |
12 | IEnumerable AddRange(IEnumerable entities);
13 |
14 | Task> AddRangeAsync(IEnumerable entities);
15 |
16 | void Update(TEntity entity);
17 |
18 | void UpdateRange(IEnumerable entities);
19 |
20 | void Remove(TEntity entity);
21 |
22 | void RemoveRange(IEnumerable entities);
23 |
24 | TEntity GetById(TKey id);
25 |
26 | Task GetByIdAsync(TKey id);
27 |
28 | IEnumerable GetAll(Expression> expression = null, bool noTracking = true);
29 |
30 | Task> GetAllAsync(Expression> expression = null, bool noTracking = true);
31 |
32 | TEntity Get(Expression> expression, bool noTracking = false);
33 |
34 | Task GetAsync(Expression> expression, bool noTracking = false);
35 |
36 | IQueryable Queryable();
37 |
38 | Task GetCountAsync(Expression> expression = null);
39 |
40 | int GetCount(Expression> expression = null);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Repositories/IRoleRepository.cs:
--------------------------------------------------------------------------------
1 | using Application.Dtos;
2 | using Domain.Entities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Interfaces.Repositories
10 | {
11 | public interface IRoleRepository : IRepository
12 | {
13 | Task> GetRolesByUserIdAsync(Guid userId);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Repositories/IUnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | namespace Application.Interfaces.Repositories
4 | {
5 | public interface IUnitOfWork
6 | {
7 | Task SaveChangesAsync();
8 | void SaveChanges();
9 |
10 | IDbTransaction BeginTransaction();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Repositories/IUserRepository.cs:
--------------------------------------------------------------------------------
1 | using Application.Dtos;
2 | using Domain.Entities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Application.Interfaces.Repositories
10 | {
11 | public interface IUserRepository : IRepository
12 | {
13 | Task GetUserWithRolesAsync(Guid userid);
14 | Task> GetAllUsersWithRolesAsync();
15 | Task GetUserRolesByUserIdAsync(Guid userid);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Core/Application/Interfaces/Services/IEasyCacheService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Application.Interfaces.Services
8 | {
9 | public interface IEasyCacheService
10 | {
11 | Task