├── .gitattributes ├── .github └── workflows │ └── dotnet-core.yml ├── .gitignore ├── LICENSE ├── README.md └── src ├── .dockerignore ├── .template.config └── template.json ├── Libraries ├── Caching │ ├── Caching.csproj │ ├── ICacheManager.cs │ ├── ILocker.cs │ ├── IRedisConnectionWrapper.cs │ ├── RedisCacheManager.cs │ ├── RedisConnectionWrapper.cs │ ├── RedisDatabaseNumber.cs │ └── ServiceExtensions.cs ├── Core │ ├── Core.csproj │ ├── Exceptions │ │ └── ApiException.cs │ ├── Helpers │ │ └── IpHelper.cs │ ├── Interfaces │ │ └── IEmailService.cs │ ├── ServiceExtensions.cs │ └── Services │ │ └── EmailService.cs ├── Data.Mongo │ ├── Attributes │ │ └── BsonCollectionAttribute.cs │ ├── Collections │ │ ├── LoginLog.cs │ │ └── MongoBaseDocument.cs │ ├── Data.Mongo.csproj │ ├── MongoDbOptions.cs │ ├── Repo │ │ ├── IMongoRepository.cs │ │ └── MongoRepository.cs │ └── ServiceExtensions.cs ├── Data │ ├── Contexts │ │ ├── ApplicationDbContext.cs │ │ └── IDbContext.cs │ ├── Data.csproj │ ├── Mapping │ │ ├── IMappingConfiguration.cs │ │ ├── MappingEntityTypeConfiguration.cs │ │ └── NoteMap.cs │ ├── Migrations │ │ ├── 20201008073308_first_appDb_migration.Designer.cs │ │ ├── 20201008073308_first_appDb_migration.cs │ │ ├── 20201008074018_new_appDb_migration.Designer.cs │ │ ├── 20201008074018_new_appDb_migration.cs │ │ └── ApplicationDbContextModelSnapshot.cs │ ├── Repos │ │ ├── GenericRepository.cs │ │ └── IGenericRepository.cs │ └── UnitOfWork │ │ ├── IUnitOfWork.cs │ │ └── UnitOfWork.cs ├── Identity │ ├── Contexts │ │ └── IdentityContext.cs │ ├── Identity.csproj │ ├── Migrations │ │ ├── 20201005130141_my_new_identity_migration.Designer.cs │ │ ├── 20201005130141_my_new_identity_migration.cs │ │ ├── 20201027103615_update_identity_migration.Designer.cs │ │ ├── 20201027103615_update_identity_migration.cs │ │ └── IdentityContextModelSnapshot.cs │ ├── Models │ │ ├── ApplicationRole.cs │ │ ├── ApplicationRoleClaim.cs │ │ ├── ApplicationUser.cs │ │ ├── ApplicationUserClaim.cs │ │ ├── ApplicationUserLogin.cs │ │ ├── ApplicationUserRole.cs │ │ └── ApplicationUserToken.cs │ ├── Seeds │ │ ├── DefaultRoles.cs │ │ └── DefaultSuperAdmin.cs │ ├── ServiceExtensions.cs │ └── Services │ │ ├── Concrete │ │ └── AccountService.cs │ │ └── Interfaces │ │ └── IAccountService.cs ├── Models │ ├── DTOs │ │ ├── Account │ │ │ ├── AuthenticationRequest.cs │ │ │ ├── ForgotPasswordRequest.cs │ │ │ ├── RefreshToken.cs │ │ │ ├── RefreshTokenRequest.cs │ │ │ ├── RegisterRequest.cs │ │ │ ├── ResetPasswordRequest.cs │ │ │ └── UserDto.cs │ │ ├── Email │ │ │ └── EmailRequest.cs │ │ └── Log │ │ │ └── LogDto.cs │ ├── DbEntities │ │ ├── BaseEntity.cs │ │ └── Note.cs │ ├── Enums │ │ └── Roles.cs │ ├── Models.csproj │ ├── ResponseModels │ │ ├── AuthenticationResponse.cs │ │ └── BaseResponse.cs │ └── Settings │ │ ├── JWTSettings.cs │ │ ├── MailSettings.cs │ │ └── RedisSettings.cs └── Services │ ├── Concrete │ ├── LoginLogService.cs │ └── NoteService.cs │ ├── Interfaces │ ├── IAuthenticatedUserService.cs │ ├── ILoginLogService.cs │ └── INoteService.cs │ └── Services.csproj ├── Presentations └── WebApi │ ├── Attributes │ └── CachedAttribute.cs │ ├── Controllers │ ├── AccountController.cs │ ├── AdminController.cs │ ├── GraphQLController.cs │ ├── LogController.cs │ └── NoteController.cs │ ├── Dockerfile │ ├── Extensions │ └── AppExtensions.cs │ ├── GraphQL │ ├── GraphQLQuery.cs │ ├── Mutations │ │ └── MyNoteMutation.cs │ ├── MyNoteSchema.cs │ ├── Queries │ │ └── MyNoteQuery.cs │ ├── ServiceExtensions.cs │ └── Types │ │ └── Note │ │ ├── NoteInputType.cs │ │ └── NoteType.cs │ ├── Helpers │ ├── IApiAssemblyMarker.cs │ └── MappingProfiles.cs │ ├── Middlewares │ └── ErrorHandlerMiddleware.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Services │ └── AuthenticatedUserService.cs │ ├── Startup.cs │ ├── WebApi.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── Template.WebApi.sln ├── Tests └── UnitTests │ ├── NoteTest.cs │ ├── UnitTests.csproj │ └── core │ ├── ApiFactory.cs │ └── Enums │ └── TypeControllerTesting.cs └── docker-compose.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain -------------------------------------------------------------------------------- /.github/workflows/dotnet-core.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | name: Build 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v3 22 | 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v3 25 | with: 26 | dotnet-version: '8.0.x' 27 | 28 | - name: Restore dependencies 29 | run: dotnet restore ./src/Presentations/WebApi/WebApi.csproj --verbosity minimal 30 | 31 | - name: Build solution 32 | run: dotnet build ./src/Presentations/WebApi/WebApi.csproj -c Release --verbosity minimal 33 | 34 | - name: Publish 35 | run: dotnet publish ./src/Presentations/WebApi/WebApi.csproj -c Release --no-build --no-restore 36 | 37 | - name: Upload Core artifacts 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: Core 41 | path: ./Publish/Core 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/visualstudio 2 | 3 | ### VisualStudio ### 4 | ## Ignore Visual Studio temporary files, build results, and 5 | ## files generated by popular Visual Studio add-ons. 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | *.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.pfx 193 | *.publishsettings 194 | node_modules/ 195 | orleans.codegen.cs 196 | 197 | # Since there are multiple workflows, uncomment next line to ignore bower_components 198 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 199 | #bower_components/ 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # Paket dependency manager 245 | .paket/paket.exe 246 | paket-files/ 247 | 248 | # FAKE - F# Make 249 | .fake/ 250 | 251 | # JetBrains Rider 252 | .idea/ 253 | *.sln.iml 254 | 255 | db-data/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sinan Tok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ASP.NET Core Web Api Template 4 | 5 | This project is an Web API Open-Source Boilerplate Template that includes Net 8, Web API standards, clean n-tier architecture, GraphQL service, Redis, Mssql, Mongo databases and User Auditing (Identity) with a lot of best practices. 6 | 7 | ## Releases 8 | v1.0, v2.0, v3.0, v4.0 9 | 10 | ## v4.0 11 | 12 | Follow these steps to get started with this Boilerplate Template. 13 | 14 | ### Download the Extension 15 | Make sure Visual Studio 2019 is installed on your machine with the latest SDK. 16 | [Download from Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SinanTok.ASPNETCoreWebAPI). Install it on your machine. 17 | 18 | Follow these Steps to get started. 19 | 20 | ![enter image description here](https://i.hizliresim.com/HCtEh0.png) 21 | 22 | You Solution Template is Ready! 23 | 24 | ![enter image description here](https://i.hizliresim.com/o9t66ud.PNG) 25 | 26 | ### Alternatively you can also clone the [Repository](https://github.com/sinantok/aspnetcore-webapi-template). 27 | 28 | 1. Clone this Repository and Extract it to a Folder. 29 | 3. Change the Connection Strings for the "DefaultConnection" and "IdentityConnection" in the appsettings.json 30 | 2. Run the following commands on Powershell in the Projecct's Directory. 31 | - dotnet restore 32 | - dotnet ef database update -Context IdentityContext 33 | - dotnet ef database update -Context ApplicationDbContext 34 | - dotnet run (OR) Run the Solution using Visual Studio 35 | 36 | ### Swagger 37 | You can view endpoints with swagger 38 | ![enter image description here](https://i.hizliresim.com/3jyeib7.PNG) 39 | 40 | ### HealthCheck 41 | You can check the status of the services with HealthCheck 42 | 43 | ### Default Roles & Credentials 44 | As soon you build and run your application, default users and roles get added to the database. 45 | 46 | Default Roles: 47 | - SuperAdmin 48 | - Admin 49 | - Moderator 50 | - Basic 51 | 52 | Here are the credentials for the default user. 53 | - Email - superadmin@gmail.com / Password - 123Pa$$word! 54 | 55 | You can use these default credentials to generate valid JWTokens at the ../api/account/authenticate endpoint. 56 | 57 | ## Technologies 58 | - .Net 8 WebApi 59 | - .NET 8 60 | - REST Standards 61 | - GraphQL 62 | - MSSQL 63 | - MongoDB 64 | - Microsoft Identity 65 | - Redis 66 | - SeriLog(seq) 67 | - AutoMapper 68 | - Smtp / Mailkit 69 | - Swagger Open Api 70 | - Health Checks 71 | 72 | ## Features 73 | - [x] Net 8 74 | - [x] N-Tier Architecture 75 | - [x] Restful 76 | - [x] GraphQl 77 | - [x] Entity Framework Core - Code First 78 | - [x] Repository Pattern - Generic 79 | - [x] UnitOfWork 80 | - [x] Redis Caching 81 | - [x] Response Wrappers 82 | - [x] Microsoft Identity with JWT Authentication 83 | - [x] Role based Authorization 84 | - [x] Identity Seeding 85 | - [x] Database Seeding 86 | - [x] Custom Exception Handling Middlewares 87 | - [x] Serilog 88 | - [x] Automapper 89 | - [x] Swagger UI 90 | - [x] Healthchecks 91 | - [x] SMTP / Mailkit / Sendgrid Email Service 92 | - [x] Complete User Management Module (Register / Generate Token / Forgot Password / Confirmation Mail) 93 | - [x] User Auditing 94 | - [ ] Pagination 95 | - [ ] Refit 96 | - [ ] Fluent Validation 97 | - [ ] Unit Test 98 | - [x] .Net 8 migration 99 | - [x] MongoDb Operations 100 | - [x] Docker Support `docker-compose.yml` and `Dockerfile` 101 | 102 | ## Purpose of this Project 103 | 104 | This template project has been developed to ensure that the necessary structures are not installed over and over again when creating each new WebAPI project. In addition, the structures that should be in a WepAPI are developed with a clean architecture and up-to-date technologies. 105 | 106 | ## Prerequisites 107 | - Visual Studio 2019 Community and above 108 | - .NET 8 SDK and above 109 | - Basic Understanding of Architectures and Clean Code Principles 110 | 111 | ## Give a Star ⭐️ 112 | If you found this Implementation helpful or used it in your Projects, do give it a star. Thanks! 113 | 114 | ## Licensing 115 | 116 | sinantok/aspnetcore-webapi-template Project is licensed with the [MIT License](https://github.com/sinantok/aspnetcore-webapi-template/blob/master/LICENSE). 117 | 118 | ## About the Author 119 | ### Sinan Tok 120 | - Github [github.com/sinantok](https://github.com/sinantok) 121 | - Linkedin - [Sinan Tok](https://www.linkedin.com/in/sinantok/) 122 | -------------------------------------------------------------------------------- /src/.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 26 | !**/.gitignore 27 | !.git/HEAD 28 | !.git/config 29 | !.git/packed-refs 30 | !.git/refs/heads/** -------------------------------------------------------------------------------- /src/.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/template", 3 | "author": "Delta", 4 | "classifications": ["Web", "Api"], 5 | "name": "Web API", 6 | "sourceName": "WebApi", 7 | "shortName": "webapi", 8 | "identity": "WebApi", 9 | "tags": { 10 | "language": "C#", 11 | "type": "project" 12 | }, 13 | "description": "Creates a Web API project with NET8", 14 | "preferNameDirectory": true 15 | } 16 | -------------------------------------------------------------------------------- /src/Libraries/Caching/Caching.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Libraries/Caching/ICacheManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Caching 5 | { 6 | public interface ICacheManager : IDisposable 7 | { 8 | Task GetAsync(string cacheKey); 9 | Task GetAsync(string key, Func> acquire, int? cacheTime = null); 10 | T Get(string key, Func acquire, int? cacheTime = null); 11 | Task SetAsync(string key, object data, int cacheTime); 12 | void Set(string key, object data, int cacheTime); 13 | bool IsSet(string key); 14 | void Remove(string key); 15 | void RemoveByPrefix(string prefix); 16 | void Clear(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Libraries/Caching/ILocker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Caching 4 | { 5 | public interface ILocker 6 | { 7 | bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Libraries/Caching/IRedisConnectionWrapper.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | using System; 3 | using System.Net; 4 | 5 | namespace Caching 6 | { 7 | public interface IRedisConnectionWrapper : IDisposable 8 | { 9 | IDatabase GetDatabase(int db); 10 | 11 | IServer GetServer(EndPoint endPoint); 12 | 13 | EndPoint[] GetEndPoints(); 14 | 15 | void FlushDatabase(RedisDatabaseNumber db); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Libraries/Caching/RedisCacheManager.cs: -------------------------------------------------------------------------------- 1 | using Models.Settings; 2 | using Newtonsoft.Json; 3 | using StackExchange.Redis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | 10 | namespace Caching 11 | { 12 | public partial class RedisCacheManager : ICacheManager 13 | { 14 | #region Fields 15 | 16 | private readonly IRedisConnectionWrapper _connectionWrapper; 17 | private readonly IDatabase _db; 18 | private readonly RedisSettings _config; 19 | 20 | #endregion 21 | 22 | #region Ctor 23 | 24 | public RedisCacheManager(IRedisConnectionWrapper connectionWrapper, RedisSettings config) 25 | { 26 | 27 | _config = config; 28 | if (string.IsNullOrEmpty(_config.RedisConnectionString)) 29 | throw new Exception("Redis connection string is empty"); 30 | 31 | _connectionWrapper = connectionWrapper; 32 | 33 | _db = _connectionWrapper.GetDatabase(_config.RedisDatabaseId ?? (int)RedisDatabaseNumber.Cache); 34 | 35 | } 36 | 37 | #endregion 38 | 39 | #region Utilities 40 | 41 | protected virtual IEnumerable GetKeys(EndPoint endPoint, string prefix = null) 42 | { 43 | var server = _connectionWrapper.GetServer(endPoint); 44 | 45 | var keys = server.Keys(_db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*"); 46 | 47 | keys = keys.Where(key => !key.ToString().Equals(_config.RedisDataProtectionKey, StringComparison.OrdinalIgnoreCase)); 48 | 49 | return keys; 50 | } 51 | 52 | protected virtual async Task GetAsync(string key) 53 | { 54 | //get serialized item from cache 55 | var serializedItem = await _db.StringGetAsync(key); 56 | if (!serializedItem.HasValue) 57 | return default(T); 58 | 59 | //deserialize item 60 | var item = JsonConvert.DeserializeObject(serializedItem); 61 | if (item == null) 62 | return default(T); 63 | 64 | return item; 65 | } 66 | 67 | protected virtual async Task IsSetAsync(string key) 68 | { 69 | return await _db.KeyExistsAsync(key); 70 | } 71 | 72 | #endregion 73 | 74 | #region Methods 75 | 76 | public async Task GetAsync(string cacheKey) 77 | { 78 | var cachedResponse = await _db.StringGetAsync(cacheKey); 79 | 80 | if (cachedResponse.IsNullOrEmpty) 81 | { 82 | return null; 83 | } 84 | 85 | return cachedResponse; 86 | } 87 | 88 | public async Task GetAsync(string key, Func> acquire, int? cacheTime = null) 89 | { 90 | //item already is in cache, so return it 91 | if (await IsSetAsync(key)) 92 | return await GetAsync(key); 93 | 94 | //or create it using passed function 95 | var result = await acquire(); 96 | 97 | //and set in cache (if cache time is defined) 98 | if ((cacheTime ?? _config.CacheTime) > 0) 99 | await SetAsync(key, result, cacheTime ?? _config.CacheTime); 100 | 101 | return result; 102 | } 103 | 104 | public virtual T Get(string key) 105 | { 106 | 107 | //get serialized item from cache 108 | var serializedItem = _db.StringGet(key); 109 | if (!serializedItem.HasValue) 110 | return default(T); 111 | 112 | //deserialize item 113 | var item = JsonConvert.DeserializeObject(serializedItem); 114 | if (item == null) 115 | return default(T); 116 | 117 | 118 | return item; 119 | } 120 | 121 | public virtual T Get(string key, Func acquire, int? cacheTime = null) 122 | { 123 | //item already is in cache, so return it 124 | if (IsSet(key)) 125 | return Get(key); 126 | 127 | //or create it using passed function 128 | var result = acquire(); 129 | 130 | //and set in cache (if cache time is defined) 131 | if ((cacheTime ?? _config.CacheTime) > 0) 132 | Set(key, result, cacheTime ?? _config.CacheTime); 133 | 134 | return result; 135 | } 136 | 137 | public async Task SetAsync(string key, object data, int cacheTime) 138 | { 139 | if (data == null) 140 | return; 141 | 142 | //set cache time 143 | var expiresIn = TimeSpan.FromSeconds(cacheTime); 144 | 145 | //serialize item 146 | var serializedItem = JsonConvert.SerializeObject(data); 147 | 148 | //and set it to cache 149 | await _db.StringSetAsync(key, serializedItem, expiresIn); 150 | } 151 | 152 | public virtual void Set(string key, object data, int cacheTime) 153 | { 154 | if (data == null) 155 | return; 156 | 157 | //set cache time 158 | var expiresIn = TimeSpan.FromMinutes(cacheTime); 159 | 160 | //serialize item 161 | var serializedItem = JsonConvert.SerializeObject(data); 162 | 163 | //and set it to cache 164 | _db.StringSet(key, serializedItem, expiresIn); 165 | } 166 | 167 | public virtual bool IsSet(string key) 168 | { 169 | 170 | return _db.KeyExists(key); 171 | } 172 | 173 | public virtual void Remove(string key) 174 | { 175 | //we should always persist the data protection key list 176 | if (key.Equals(_config.RedisDataProtectionKey, StringComparison.OrdinalIgnoreCase)) 177 | return; 178 | 179 | //remove item from caches 180 | _db.KeyDelete(key); 181 | } 182 | 183 | public virtual void RemoveByPrefix(string prefix) 184 | { 185 | 186 | foreach (var endPoint in _connectionWrapper.GetEndPoints()) 187 | { 188 | var keys = GetKeys(endPoint, prefix); 189 | 190 | _db.KeyDelete(keys.ToArray()); 191 | } 192 | } 193 | 194 | public virtual void Clear() 195 | { 196 | foreach (var endPoint in _connectionWrapper.GetEndPoints()) 197 | { 198 | var keys = GetKeys(endPoint).ToArray(); 199 | 200 | _db.KeyDelete(keys); 201 | } 202 | } 203 | 204 | public virtual void Dispose() 205 | { 206 | if (_connectionWrapper != null) 207 | { 208 | _connectionWrapper.Dispose(); 209 | } 210 | // Dispose(true); 211 | } 212 | 213 | private void Dispose(bool clear) 214 | { 215 | GC.SuppressFinalize(this); 216 | } 217 | 218 | 219 | #endregion 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/Libraries/Caching/RedisConnectionWrapper.cs: -------------------------------------------------------------------------------- 1 | using Models.Settings; 2 | using RedLockNet.SERedis; 3 | using RedLockNet.SERedis.Configuration; 4 | using StackExchange.Redis; 5 | using System; 6 | using System.Linq; 7 | using System.Net; 8 | 9 | namespace Caching 10 | { 11 | public class RedisConnectionWrapper : IRedisConnectionWrapper, ILocker 12 | { 13 | #region Fields 14 | 15 | private readonly RedisSettings _config; 16 | 17 | private readonly object _lock = new object(); 18 | private volatile ConnectionMultiplexer _connection; 19 | private readonly Lazy _connectionString; 20 | private volatile RedLockFactory _redisLockFactory; 21 | 22 | #endregion 23 | 24 | #region Ctor 25 | 26 | public RedisConnectionWrapper(RedisSettings config) 27 | { 28 | _config = config; 29 | _connectionString = new Lazy(GetConnectionString); 30 | _redisLockFactory = CreateRedisLockFactory(); 31 | } 32 | 33 | #endregion 34 | 35 | #region Utilities 36 | 37 | 38 | protected string GetConnectionString() 39 | { 40 | return _config.RedisConnectionString; 41 | } 42 | 43 | 44 | protected ConnectionMultiplexer GetConnection() 45 | { 46 | if (_connection != null && _connection.IsConnected) return _connection; 47 | 48 | lock (_lock) 49 | { 50 | if (_connection != null && _connection.IsConnected) return _connection; 51 | 52 | //Connection disconnected. Disposing connection... 53 | _connection?.Dispose(); 54 | 55 | //Creating new instance of Redis Connection 56 | _connection = ConnectionMultiplexer.Connect(_connectionString.Value); 57 | } 58 | 59 | return _connection; 60 | } 61 | 62 | 63 | protected RedLockFactory CreateRedisLockFactory() 64 | { 65 | //get RedLock endpoints 66 | var configurationOptions = ConfigurationOptions.Parse(_connectionString.Value); 67 | var redLockEndPoints = GetEndPoints().Select(endPoint => new RedLockEndPoint 68 | { 69 | EndPoint = endPoint, 70 | Password = configurationOptions.Password, 71 | Ssl = configurationOptions.Ssl, 72 | RedisDatabase = configurationOptions.DefaultDatabase, 73 | ConfigCheckSeconds = configurationOptions.ConfigCheckSeconds, 74 | ConnectionTimeout = configurationOptions.ConnectTimeout, 75 | SyncTimeout = configurationOptions.SyncTimeout 76 | }).ToList(); 77 | 78 | //create RedLock factory to use RedLock distributed lock algorithm 79 | return RedLockFactory.Create(redLockEndPoints); 80 | } 81 | 82 | #endregion 83 | 84 | #region Methods 85 | 86 | public IDatabase GetDatabase(int db) 87 | { 88 | return GetConnection().GetDatabase(db); 89 | } 90 | 91 | public IServer GetServer(EndPoint endPoint) 92 | { 93 | return GetConnection().GetServer(endPoint); 94 | } 95 | public EndPoint[] GetEndPoints() 96 | { 97 | return GetConnection().GetEndPoints(); 98 | } 99 | 100 | public void FlushDatabase(RedisDatabaseNumber db) 101 | { 102 | var endPoints = GetEndPoints(); 103 | 104 | foreach (var endPoint in endPoints) 105 | { 106 | GetServer(endPoint).FlushDatabase((int)db); 107 | } 108 | } 109 | 110 | public bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action) 111 | { 112 | //use RedLock library 113 | using (var redisLock = _redisLockFactory.CreateLock(resource, expirationTime)) 114 | { 115 | //ensure that lock is acquired 116 | if (!redisLock.IsAcquired) 117 | return false; 118 | 119 | //perform action 120 | action(); 121 | 122 | return true; 123 | } 124 | } 125 | 126 | public void Dispose() 127 | { 128 | //dispose ConnectionMultiplexer 129 | _connection?.Dispose(); 130 | 131 | //dispose RedLock factory 132 | _redisLockFactory?.Dispose(); 133 | } 134 | 135 | 136 | 137 | #endregion 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Libraries/Caching/RedisDatabaseNumber.cs: -------------------------------------------------------------------------------- 1 | namespace Caching 2 | { 3 | public enum RedisDatabaseNumber 4 | { 5 | /// 6 | /// Database for caching 7 | /// 8 | Cache = 1, 9 | /// 10 | /// Database for plugins 11 | /// 12 | Plugin = 2, 13 | /// 14 | /// Database for data protection keys 15 | /// 16 | DataProtectionKeys = 3 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Libraries/Caching/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Options; 4 | using Models.Settings; 5 | 6 | namespace Caching 7 | { 8 | public static class ServiceExtensions 9 | { 10 | public static IServiceCollection AddRedis(this IServiceCollection services, IConfiguration config) 11 | { 12 | #region Configure 13 | services.Configure(config.GetSection("RedisSettings")); 14 | #endregion 15 | 16 | #region Services 17 | services.AddScoped(sp => sp.GetService>().Value); 18 | services.AddTransient(); 19 | services.AddTransient(); 20 | #endregion 21 | 22 | return services; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Libraries/Core/Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Libraries/Core/Exceptions/ApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Text; 5 | 6 | namespace Core.Exceptions 7 | { 8 | public class ApiException : Exception 9 | { 10 | public ApiException() : base() { } 11 | 12 | public ApiException(string message) : base(message) { } 13 | 14 | public ApiException(string message, params object[] args) 15 | : base(String.Format(CultureInfo.CurrentCulture, message, args)) 16 | { 17 | } 18 | public int StatusCode { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Libraries/Core/Helpers/IpHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | 4 | namespace Core.Helpers 5 | { 6 | public class IpHelper 7 | { 8 | public static string GetIpAddress() 9 | { 10 | var host = Dns.GetHostEntry(Dns.GetHostName()); 11 | foreach (var ip in host.AddressList) 12 | { 13 | if (ip.AddressFamily == AddressFamily.InterNetwork) 14 | { 15 | return ip.ToString(); 16 | } 17 | } 18 | return string.Empty; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Libraries/Core/Interfaces/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using Models.DTOs.Email; 2 | using System.Threading.Tasks; 3 | 4 | namespace Core.Interfaces 5 | { 6 | public interface IEmailService 7 | { 8 | Task SendAsync(EmailRequest request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Libraries/Core/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Core.Interfaces; 2 | using Core.Services; 3 | using Data.Contexts; 4 | using Data.Repos; 5 | using Data.UnitOfWork; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.OpenApi.Models; 10 | using Models.Settings; 11 | using Services.Concrete; 12 | using Services.Interfaces; 13 | using System.Collections.Generic; 14 | 15 | namespace Core 16 | { 17 | public static class ServiceExtensions 18 | { 19 | public static void AddSharedServices(this IServiceCollection services, IConfiguration config) 20 | { 21 | #region Configure 22 | services.Configure(config.GetSection("MailSettings")); 23 | #endregion 24 | 25 | #region Services 26 | services.AddTransient(); 27 | #endregion 28 | } 29 | public static void AddApplicationSqlServer(this IServiceCollection services, IConfiguration config) 30 | { 31 | services.AddDbContext(options => 32 | { 33 | options.UseSqlServer(config.GetConnectionString("DefaultConnection"), 34 | b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)); 35 | }); 36 | services.AddTransient(); 37 | 38 | // Ensure the database is created. 39 | using var context = services.BuildServiceProvider().GetService(); 40 | context.Database.EnsureCreated(); 41 | } 42 | public static void AddRepoServices(this IServiceCollection services, IConfiguration config) 43 | { 44 | services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>)); 45 | 46 | services.AddScoped(); 47 | } 48 | public static void AddAppServices(this IServiceCollection services, IConfiguration config) 49 | { 50 | services.AddTransient(); 51 | 52 | services.AddTransient(); 53 | } 54 | public static void AddCustomSwagger(this IServiceCollection services, IConfiguration config) 55 | { 56 | services.AddSwaggerGen(c => 57 | { 58 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApi", Version = "v1" }); 59 | c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme 60 | { 61 | Description = @"JWT Authorization header using the Bearer scheme.

62 | Enter 'Bearer' [space] and then your token in the text input below. 63 |

Example: 'Bearer 12345abcdef'", 64 | Name = "Authorization", 65 | In = ParameterLocation.Header, 66 | Type = SecuritySchemeType.ApiKey, 67 | Scheme = "Bearer" 68 | }); 69 | c.AddSecurityRequirement(new OpenApiSecurityRequirement() 70 | { 71 | { 72 | new OpenApiSecurityScheme 73 | { 74 | Reference = new OpenApiReference 75 | { 76 | Type = ReferenceType.SecurityScheme, 77 | Id = "Bearer" 78 | }, 79 | Scheme = "oauth2", 80 | Name = "Bearer", 81 | In = ParameterLocation.Header, 82 | 83 | }, 84 | new List() 85 | } 86 | }); 87 | 88 | }); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Libraries/Core/Services/EmailService.cs: -------------------------------------------------------------------------------- 1 | using Core.Interfaces; 2 | using MailKit.Security; 3 | using Microsoft.Extensions.Logging; 4 | using MimeKit; 5 | using Models.DTOs.Email; 6 | using Models.Settings; 7 | using System; 8 | using System.Threading.Tasks; 9 | using MailKit.Net.Smtp; 10 | using Core.Exceptions; 11 | using Microsoft.Extensions.Options; 12 | 13 | namespace Core.Services 14 | { 15 | public class EmailService : IEmailService 16 | { 17 | private readonly MailSettings _mailSettings; 18 | private readonly ILogger _logger; 19 | 20 | public EmailService(IOptions mailSettings, ILogger logger) 21 | { 22 | _mailSettings = mailSettings.Value; 23 | _logger = logger; 24 | } 25 | 26 | public async Task SendAsync(EmailRequest request) 27 | { 28 | try 29 | { 30 | var email = new MimeMessage(); 31 | email.Sender = MailboxAddress.Parse(request.From ?? _mailSettings.EmailFrom); 32 | email.To.Add(MailboxAddress.Parse(request.To)); 33 | email.Subject = request.Subject; 34 | var builder = new BodyBuilder(); 35 | builder.HtmlBody = request.Body; 36 | email.Body = builder.ToMessageBody(); 37 | using var smtp = new SmtpClient(); 38 | smtp.Connect(_mailSettings.SmtpHost, _mailSettings.SmtpPort, SecureSocketOptions.StartTls); 39 | smtp.Authenticate(_mailSettings.SmtpUser, _mailSettings.SmtpPass); 40 | await smtp.SendAsync(email); 41 | smtp.Disconnect(true); 42 | } 43 | catch (Exception ex) 44 | { 45 | _logger.LogError(ex.Message, ex); 46 | throw new ApiException(ex.Message); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Attributes/BsonCollectionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Data.Mongo.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 6 | public class BsonCollectionAttribute : Attribute 7 | { 8 | public string CollectionName { get; } 9 | 10 | public BsonCollectionAttribute(string collectionName) 11 | { 12 | CollectionName = collectionName; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Collections/LoginLog.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Attributes; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | 5 | namespace Data.Mongo.Collections 6 | { 7 | [BsonCollection("loginlogs")] 8 | [BsonIgnoreExtraElements] 9 | public class LoginLog : MongoBaseDocument 10 | { 11 | public string UserEmail { get; set; } 12 | public DateTime LoginTime { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Collections/MongoBaseDocument.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using Newtonsoft.Json; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Data.Mongo.Collections 7 | { 8 | public abstract class MongoBaseDocument 9 | { 10 | /// 11 | /// Id > String 12 | /// 13 | [JsonProperty(Order = 1)] 14 | [BsonElement(Order = 0)] 15 | [BsonRepresentation(BsonType.ObjectId)] 16 | public string Id => _Id; 17 | 18 | 19 | [BsonId] 20 | [DataMember] 21 | [BsonIgnoreIfDefault] 22 | [BsonRepresentation(BsonType.ObjectId)] 23 | public virtual string _Id { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Data.Mongo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/MongoDbOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Data.Mongo 2 | { 3 | public class MongoDbOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | public string Database { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Repo/IMongoRepository.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Collections; 2 | using MongoDB.Driver; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace Data.Mongo.Repo 9 | { 10 | public interface IMongoRepository where T : MongoBaseDocument 11 | { 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | Task GetByIdAsync(string id); 18 | 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | Task GetAsync(Expression> predicate); 25 | 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | Task> FindAsync(Expression> predicate); 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | IEnumerable Find(Expression> predicate); 38 | 39 | /// 40 | /// 41 | /// 42 | /// 43 | /// 44 | Task AddAsync(T entity); 45 | 46 | /// 47 | /// 48 | /// 49 | /// 50 | /// 51 | Task UpdateAsync(T entity); 52 | 53 | /// 54 | /// 55 | /// 56 | /// 57 | /// 58 | Task DeleteAsync(string id); 59 | 60 | /// 61 | /// 62 | /// 63 | /// 64 | /// 65 | Task ExistsAsync(Expression> predicate); 66 | 67 | /// 68 | /// 69 | /// 70 | /// 71 | /// 72 | /// 73 | Task> FindDistinctAsync(string field, FilterDefinition filter); 74 | 75 | /// 76 | /// 77 | /// 78 | /// 79 | List GetAll(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/Repo/MongoRepository.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Attributes; 2 | using Data.Mongo.Collections; 3 | using MongoDB.Driver; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Data.Mongo.Repo 12 | { 13 | public class MongoRepository : IMongoRepository where T : MongoBaseDocument 14 | { 15 | 16 | protected IMongoCollection Collection { get; } 17 | 18 | public MongoRepository(IMongoDatabase database) 19 | { 20 | string collectionName; 21 | 22 | var att = Attribute.GetCustomAttribute(typeof(T), typeof(BsonCollectionAttribute)); 23 | 24 | if (att != null) 25 | { 26 | collectionName = ((BsonCollectionAttribute)att).CollectionName; 27 | } 28 | else 29 | { 30 | collectionName = typeof(T).Name; 31 | } 32 | 33 | //var collectionName = typeof(TEntity).GetCustomAttribute(false).CollectionName; 34 | Collection = database.GetCollection(collectionName); 35 | 36 | } 37 | 38 | public async Task AddAsync(T entity) 39 | => await Collection.InsertOneAsync(entity); 40 | 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | public async Task DeleteAsync(string id) 47 | => await Collection.DeleteOneAsync(e => e.Id == id); 48 | 49 | /// 50 | /// 51 | /// 52 | /// 53 | /// 54 | public async Task ExistsAsync(Expression> predicate) 55 | => await Collection.Find(predicate).AnyAsync(); 56 | 57 | /// 58 | /// Not Async 59 | /// 60 | /// 61 | /// 62 | public IEnumerable Find(Expression> predicate) 63 | => Collection.Find(predicate).ToList(); 64 | 65 | /// 66 | /// 67 | /// 68 | /// 69 | /// 70 | public async Task> FindAsync(Expression> predicate) 71 | => await Collection.Find(predicate).ToListAsync(); 72 | 73 | public async Task> FindDistinctAsync(string field, FilterDefinition filter) 74 | => (ICollection)await Collection.DistinctAsync(field, filter); 75 | 76 | /// 77 | /// 78 | /// 79 | /// 80 | /// 81 | public async Task GetByIdAsync(string id) 82 | => await GetAsync(e => e.Id == id); 83 | 84 | /// 85 | /// 86 | /// 87 | /// 88 | /// 89 | public async Task GetAsync(Expression> predicate) 90 | => await Collection.Find(predicate).SingleOrDefaultAsync(); 91 | 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | public async Task UpdateAsync(T entity) 98 | => await Collection.ReplaceOneAsync(e => e.Id == entity.Id, entity); 99 | 100 | /// 101 | /// 102 | /// 103 | /// 104 | public List GetAll() 105 | => Collection.Find(Builders.Filter.Empty).ToList(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Libraries/Data.Mongo/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Repo; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using MongoDB.Driver; 5 | 6 | namespace Data.Mongo 7 | { 8 | public static class ServiceExtensions 9 | { 10 | public static void AddMongo(this IServiceCollection services, IConfiguration configuration) 11 | { 12 | var options = new MongoDbOptions(); 13 | configuration.GetSection("MongoDb").Bind(options); 14 | 15 | services.AddSingleton(sp => 16 | { 17 | return new MongoClient(options.ConnectionString); 18 | }); 19 | 20 | services.AddScoped(typeof(IMongoRepository<>), typeof(MongoRepository<>)); 21 | 22 | services.AddScoped(sp => 23 | { 24 | var client = sp.GetRequiredService(); 25 | return client.GetDatabase(options.Database); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Libraries/Data/Contexts/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Data.Mapping; 2 | using Microsoft.EntityFrameworkCore; 3 | using System; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace Data.Contexts 8 | { 9 | public class ApplicationDbContext : DbContext, IDbContext 10 | { 11 | public ApplicationDbContext(DbContextOptions options) : base(options) 12 | { 13 | } 14 | 15 | protected override void OnModelCreating(ModelBuilder modelBuilder) 16 | { 17 | RegisterEntityMapping(modelBuilder); 18 | base.OnModelCreating(modelBuilder); 19 | } 20 | 21 | public void RegisterEntityMapping(ModelBuilder modelBuilder) 22 | { 23 | var typeConfigurations = Assembly.GetExecutingAssembly().GetTypes().Where(type => 24 | (type.BaseType?.IsGenericType ?? false) && 25 | (type.BaseType.GetGenericTypeDefinition() == typeof(MappingEntityTypeConfiguration<>)) 26 | ); 27 | foreach (var item in typeConfigurations) 28 | { 29 | var configuration = (IMappingConfiguration)Activator.CreateInstance(item); 30 | configuration!.ApplyConfiguration(modelBuilder); 31 | } 32 | } 33 | 34 | public new virtual DbSet Set() where TEntity : class 35 | { 36 | return base.Set(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Libraries/Data/Contexts/IDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Data.Contexts 4 | { 5 | public interface IDbContext 6 | { 7 | DbSet Set() where TEntity : class; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Libraries/Data/Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Libraries/Data/Mapping/IMappingConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Data.Mapping 4 | { 5 | public interface IMappingConfiguration 6 | { 7 | void ApplyConfiguration(ModelBuilder modelBuilder); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Libraries/Data/Mapping/MappingEntityTypeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using Models.DbEntities; 4 | 5 | namespace Data.Mapping 6 | { 7 | public class MappingEntityTypeConfiguration : IMappingConfiguration, IEntityTypeConfiguration where TEntity : BaseEntity 8 | { 9 | public virtual void ApplyConfiguration(ModelBuilder modelBuilder) 10 | { 11 | modelBuilder.ApplyConfiguration(this); 12 | } 13 | 14 | public virtual void Configure(EntityTypeBuilder builder) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Libraries/Data/Mapping/NoteMap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using Models.DbEntities; 4 | 5 | namespace Data.Mapping 6 | { 7 | public class NoteMap : MappingEntityTypeConfiguration 8 | { 9 | public override void Configure(EntityTypeBuilder builder) 10 | { 11 | builder.ToTable("Notes"); 12 | builder.HasKey(p => p.Id); 13 | builder.Property(p => p.Title).HasMaxLength(255); 14 | builder.Property(p => p.Category).HasMaxLength(255); 15 | builder.Property(p => p.Description).HasMaxLength(255); 16 | builder.Property(p => p.OwnerEmail).HasMaxLength(255); 17 | builder.Property(p => p.CreateUTC).HasColumnType("DateTime").HasDefaultValueSql("GetUtcDate()"); 18 | base.Configure(builder); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Libraries/Data/Migrations/20201008073308_first_appDb_migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Data.Contexts; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace Data.Migrations 10 | { 11 | [DbContext(typeof(ApplicationDbContext))] 12 | [Migration("20201008073308_first_appDb_migration")] 13 | partial class first_appDb_migration 14 | { 15 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "3.1.8") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 21 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 22 | #pragma warning restore 612, 618 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Libraries/Data/Migrations/20201008073308_first_appDb_migration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Data.Migrations 4 | { 5 | public partial class first_appDb_migration : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | 10 | } 11 | 12 | protected override void Down(MigrationBuilder migrationBuilder) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Libraries/Data/Migrations/20201008074018_new_appDb_migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Data.Contexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 9 | 10 | namespace Data.Migrations 11 | { 12 | [DbContext(typeof(ApplicationDbContext))] 13 | [Migration("20201008074018_new_appDb_migration")] 14 | partial class new_appDb_migration 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "3.1.8") 21 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 23 | 24 | modelBuilder.Entity("Models.DbEntities.Note", b => 25 | { 26 | b.Property("Id") 27 | .ValueGeneratedOnAdd() 28 | .HasColumnType("int") 29 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 30 | 31 | b.Property("Category") 32 | .HasColumnType("nvarchar(255)") 33 | .HasMaxLength(255); 34 | 35 | b.Property("CreateUTC") 36 | .ValueGeneratedOnAdd() 37 | .HasColumnType("DateTime") 38 | .HasDefaultValueSql("GetUtcDate()"); 39 | 40 | b.Property("Description") 41 | .HasColumnType("nvarchar(255)") 42 | .HasMaxLength(255); 43 | 44 | b.Property("OwnerEmail") 45 | .HasColumnType("nvarchar(255)") 46 | .HasMaxLength(255); 47 | 48 | b.Property("Title") 49 | .HasColumnType("nvarchar(255)") 50 | .HasMaxLength(255); 51 | 52 | b.HasKey("Id"); 53 | 54 | b.ToTable("Notes"); 55 | }); 56 | #pragma warning restore 612, 618 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Libraries/Data/Migrations/20201008074018_new_appDb_migration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace Data.Migrations 5 | { 6 | public partial class new_appDb_migration : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "Notes", 12 | columns: table => new 13 | { 14 | Id = table.Column(nullable: false) 15 | .Annotation("SqlServer:Identity", "1, 1"), 16 | CreateUTC = table.Column(type: "DateTime", nullable: false, defaultValueSql: "GetUtcDate()"), 17 | Title = table.Column(maxLength: 255, nullable: true), 18 | Category = table.Column(maxLength: 255, nullable: true), 19 | Description = table.Column(maxLength: 255, nullable: true), 20 | OwnerEmail = table.Column(maxLength: 255, nullable: true) 21 | }, 22 | constraints: table => 23 | { 24 | table.PrimaryKey("PK_Notes", x => x.Id); 25 | }); 26 | } 27 | 28 | protected override void Down(MigrationBuilder migrationBuilder) 29 | { 30 | migrationBuilder.DropTable( 31 | name: "Notes"); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Libraries/Data/Migrations/ApplicationDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Data.Contexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace Data.Migrations 10 | { 11 | [DbContext(typeof(ApplicationDbContext))] 12 | partial class ApplicationDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("ProductVersion", "3.1.8") 19 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("Models.DbEntities.Note", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("int") 27 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 28 | 29 | b.Property("Category") 30 | .HasColumnType("nvarchar(255)") 31 | .HasMaxLength(255); 32 | 33 | b.Property("CreateUTC") 34 | .ValueGeneratedOnAdd() 35 | .HasColumnType("DateTime") 36 | .HasDefaultValueSql("GetUtcDate()"); 37 | 38 | b.Property("Description") 39 | .HasColumnType("nvarchar(255)") 40 | .HasMaxLength(255); 41 | 42 | b.Property("OwnerEmail") 43 | .HasColumnType("nvarchar(255)") 44 | .HasMaxLength(255); 45 | 46 | b.Property("Title") 47 | .HasColumnType("nvarchar(255)") 48 | .HasMaxLength(255); 49 | 50 | b.HasKey("Id"); 51 | 52 | b.ToTable("Notes"); 53 | }); 54 | #pragma warning restore 612, 618 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Libraries/Data/Repos/GenericRepository.cs: -------------------------------------------------------------------------------- 1 | using Data.Contexts; 2 | using Microsoft.EntityFrameworkCore; 3 | using Models.DbEntities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using System.Threading.Tasks; 9 | 10 | namespace Data.Repos 11 | { 12 | public class GenericRepository : IGenericRepository where T : BaseEntity 13 | { 14 | private readonly ApplicationDbContext _context; 15 | public GenericRepository(ApplicationDbContext context = null) 16 | { 17 | _context = context; 18 | } 19 | 20 | public bool BulkInsert(List entities) 21 | { 22 | _context.ChangeTracker.AutoDetectChangesEnabled = false; 23 | try 24 | { 25 | _context.Set().AddRange(entities); 26 | _context.SaveChanges(); 27 | return true; 28 | } 29 | catch 30 | { 31 | return false; 32 | } 33 | } 34 | 35 | public int Delete(T entity) 36 | { 37 | _context.Set().Remove(entity); 38 | return _context.SaveChanges(); 39 | } 40 | 41 | public T Find(Expression> match) 42 | { 43 | return _context.Set().FirstOrDefault(match); 44 | } 45 | 46 | public List FindAll(Expression> match) 47 | { 48 | return _context.Set().Where(match).ToList(); 49 | } 50 | 51 | public async Task> FindAllAsync(Expression> match) 52 | { 53 | return await _context.Set().Where(match).ToListAsync(); 54 | } 55 | 56 | public List GetAll() 57 | { 58 | return _context.Set().ToList(); 59 | } 60 | 61 | public T GetById(int id) 62 | { 63 | return _context.Set().Find(id); 64 | } 65 | 66 | public T Insert(T entity) 67 | { 68 | _context.Set().Add(entity); 69 | _context.SaveChanges(); 70 | return entity; 71 | } 72 | 73 | public T Update(T entity) 74 | { 75 | if (entity == null) 76 | return null; 77 | _context.Set().Attach(entity); 78 | _context.Entry(entity).State = EntityState.Modified; 79 | _context.SaveChanges(); 80 | return entity; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Libraries/Data/Repos/IGenericRepository.cs: -------------------------------------------------------------------------------- 1 | using Models.DbEntities; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | using System.Threading.Tasks; 6 | 7 | namespace Data.Repos 8 | { 9 | public interface IGenericRepository where T : BaseEntity 10 | { 11 | List GetAll(); 12 | T GetById(int id); 13 | T Find(Expression> match); 14 | List FindAll(Expression> match); 15 | Task> FindAllAsync(Expression> match); 16 | T Insert(T entity); 17 | bool BulkInsert(List entities); 18 | T Update(T entity); 19 | int Delete(T entity); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Libraries/Data/UnitOfWork/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using Data.Repos; 2 | using Models.DbEntities; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Data.UnitOfWork 7 | { 8 | public interface IUnitOfWork : IDisposable 9 | { 10 | IGenericRepository Repository() where TEntity : BaseEntity; 11 | Task Complete(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Libraries/Data/UnitOfWork/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using Data.Contexts; 2 | using Data.Repos; 3 | using Models.DbEntities; 4 | using System; 5 | using System.Collections; 6 | using System.Threading.Tasks; 7 | 8 | namespace Data.UnitOfWork 9 | { 10 | public class UnitOfWork : IUnitOfWork 11 | { 12 | private readonly ApplicationDbContext _context; 13 | private Hashtable _repositories; 14 | public UnitOfWork(ApplicationDbContext context) 15 | { 16 | _context = context; 17 | } 18 | 19 | public async Task Complete() 20 | { 21 | return await _context.SaveChangesAsync(); 22 | } 23 | 24 | public void Dispose() 25 | { 26 | _context.Dispose(); 27 | } 28 | 29 | public IGenericRepository Repository() where TEntity : BaseEntity 30 | { 31 | if (_repositories == null) _repositories = new Hashtable(); 32 | 33 | var type = typeof(TEntity).Name; 34 | 35 | if (!_repositories.ContainsKey(type)) 36 | { 37 | var repositoryType = typeof(GenericRepository<>); 38 | var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _context); 39 | 40 | _repositories.Add(type, repositoryInstance); 41 | } 42 | 43 | return (IGenericRepository)_repositories[type]; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Contexts/IdentityContext.cs: -------------------------------------------------------------------------------- 1 | using Identity.Models; 2 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace Identity.Contexts 6 | { 7 | public class IdentityContext 8 | : IdentityDbContext< 9 | ApplicationUser, ApplicationRole, int, 10 | ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, 11 | ApplicationRoleClaim, ApplicationUserToken> 12 | { 13 | public IdentityContext(DbContextOptions options) : base(options) 14 | { 15 | } 16 | protected override void OnModelCreating(ModelBuilder builder) 17 | { 18 | base.OnModelCreating(builder); 19 | 20 | builder.HasDefaultSchema("Identity"); 21 | builder.Entity(entity => 22 | { 23 | entity.ToTable(name: "User"); 24 | }); 25 | 26 | builder.Entity(entity => 27 | { 28 | entity.ToTable(name: "Role"); 29 | }); 30 | 31 | builder.Entity(entity => 32 | { 33 | entity.HasKey(ur => new { ur.UserId, ur.RoleId }); 34 | 35 | entity.HasOne(ur => ur.Role) 36 | .WithMany(r => r.UserRoles) 37 | .HasForeignKey(ur => ur.RoleId) 38 | .IsRequired(); 39 | 40 | entity.HasOne(ur => ur.User) 41 | .WithMany(r => r.UserRoles) 42 | .HasForeignKey(ur => ur.UserId) 43 | .IsRequired(); 44 | entity.ToTable("UserRoles"); 45 | }); 46 | 47 | builder.Entity(entity => 48 | { 49 | entity.ToTable("UserClaims"); 50 | }); 51 | 52 | builder.Entity(entity => 53 | { 54 | entity.ToTable("UserLogins"); 55 | }); 56 | 57 | builder.Entity(entity => 58 | { 59 | entity.ToTable("RoleClaims"); 60 | 61 | }); 62 | 63 | builder.Entity(entity => 64 | { 65 | entity.ToTable("UserTokens"); 66 | }); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Identity.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Migrations/20201005130141_my_new_identity_migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Identity.Contexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 9 | 10 | namespace Identity.Migrations 11 | { 12 | [DbContext(typeof(IdentityContext))] 13 | [Migration("20201005130141_my_new_identity_migration")] 14 | partial class my_new_identity_migration 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasDefaultSchema("Identity") 21 | .HasAnnotation("ProductVersion", "3.1.8") 22 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 23 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 24 | 25 | modelBuilder.Entity("Identity.Models.ApplicationRole", b => 26 | { 27 | b.Property("Id") 28 | .ValueGeneratedOnAdd() 29 | .HasColumnType("int") 30 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 31 | 32 | b.Property("ConcurrencyStamp") 33 | .IsConcurrencyToken() 34 | .HasColumnType("nvarchar(max)"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetime2"); 38 | 39 | b.Property("Name") 40 | .HasColumnType("nvarchar(256)") 41 | .HasMaxLength(256); 42 | 43 | b.Property("NormalizedName") 44 | .HasColumnType("nvarchar(256)") 45 | .HasMaxLength(256); 46 | 47 | b.HasKey("Id"); 48 | 49 | b.HasIndex("NormalizedName") 50 | .IsUnique() 51 | .HasName("RoleNameIndex") 52 | .HasFilter("[NormalizedName] IS NOT NULL"); 53 | 54 | b.ToTable("Role"); 55 | }); 56 | 57 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 58 | { 59 | b.Property("Id") 60 | .ValueGeneratedOnAdd() 61 | .HasColumnType("int") 62 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 63 | 64 | b.Property("ClaimType") 65 | .HasColumnType("nvarchar(max)"); 66 | 67 | b.Property("ClaimValue") 68 | .HasColumnType("nvarchar(max)"); 69 | 70 | b.Property("RoleId") 71 | .HasColumnType("int"); 72 | 73 | b.HasKey("Id"); 74 | 75 | b.HasIndex("RoleId"); 76 | 77 | b.ToTable("RoleClaims"); 78 | }); 79 | 80 | modelBuilder.Entity("Identity.Models.ApplicationUser", b => 81 | { 82 | b.Property("Id") 83 | .ValueGeneratedOnAdd() 84 | .HasColumnType("int") 85 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 86 | 87 | b.Property("AccessFailedCount") 88 | .HasColumnType("int"); 89 | 90 | b.Property("ConcurrencyStamp") 91 | .IsConcurrencyToken() 92 | .HasColumnType("nvarchar(max)"); 93 | 94 | b.Property("Email") 95 | .HasColumnType("nvarchar(256)") 96 | .HasMaxLength(256); 97 | 98 | b.Property("EmailConfirmed") 99 | .HasColumnType("bit"); 100 | 101 | b.Property("FirstName") 102 | .HasColumnType("nvarchar(max)"); 103 | 104 | b.Property("LastName") 105 | .HasColumnType("nvarchar(max)"); 106 | 107 | b.Property("LockoutEnabled") 108 | .HasColumnType("bit"); 109 | 110 | b.Property("LockoutEnd") 111 | .HasColumnType("datetimeoffset"); 112 | 113 | b.Property("NormalizedEmail") 114 | .HasColumnType("nvarchar(256)") 115 | .HasMaxLength(256); 116 | 117 | b.Property("NormalizedUserName") 118 | .HasColumnType("nvarchar(256)") 119 | .HasMaxLength(256); 120 | 121 | b.Property("PasswordHash") 122 | .HasColumnType("nvarchar(max)"); 123 | 124 | b.Property("PhoneNumber") 125 | .HasColumnType("nvarchar(max)"); 126 | 127 | b.Property("PhoneNumberConfirmed") 128 | .HasColumnType("bit"); 129 | 130 | b.Property("SecurityStamp") 131 | .HasColumnType("nvarchar(max)"); 132 | 133 | b.Property("TwoFactorEnabled") 134 | .HasColumnType("bit"); 135 | 136 | b.Property("UserName") 137 | .HasColumnType("nvarchar(256)") 138 | .HasMaxLength(256); 139 | 140 | b.HasKey("Id"); 141 | 142 | b.HasIndex("NormalizedEmail") 143 | .HasName("EmailIndex"); 144 | 145 | b.HasIndex("NormalizedUserName") 146 | .IsUnique() 147 | .HasName("UserNameIndex") 148 | .HasFilter("[NormalizedUserName] IS NOT NULL"); 149 | 150 | b.ToTable("User"); 151 | }); 152 | 153 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 154 | { 155 | b.Property("Id") 156 | .ValueGeneratedOnAdd() 157 | .HasColumnType("int") 158 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 159 | 160 | b.Property("ClaimType") 161 | .HasColumnType("nvarchar(max)"); 162 | 163 | b.Property("ClaimValue") 164 | .HasColumnType("nvarchar(max)"); 165 | 166 | b.Property("UserId") 167 | .HasColumnType("int"); 168 | 169 | b.HasKey("Id"); 170 | 171 | b.HasIndex("UserId"); 172 | 173 | b.ToTable("UserClaims"); 174 | }); 175 | 176 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 177 | { 178 | b.Property("LoginProvider") 179 | .HasColumnType("nvarchar(450)"); 180 | 181 | b.Property("ProviderKey") 182 | .HasColumnType("nvarchar(450)"); 183 | 184 | b.Property("ProviderDisplayName") 185 | .HasColumnType("nvarchar(max)"); 186 | 187 | b.Property("UserId") 188 | .HasColumnType("int"); 189 | 190 | b.HasKey("LoginProvider", "ProviderKey"); 191 | 192 | b.HasIndex("UserId"); 193 | 194 | b.ToTable("UserLogins"); 195 | }); 196 | 197 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 198 | { 199 | b.Property("UserId") 200 | .HasColumnType("int"); 201 | 202 | b.Property("RoleId") 203 | .HasColumnType("int"); 204 | 205 | b.HasKey("UserId", "RoleId"); 206 | 207 | b.HasIndex("RoleId"); 208 | 209 | b.ToTable("UserRoles"); 210 | }); 211 | 212 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 213 | { 214 | b.Property("UserId") 215 | .HasColumnType("int"); 216 | 217 | b.Property("LoginProvider") 218 | .HasColumnType("nvarchar(450)"); 219 | 220 | b.Property("Name") 221 | .HasColumnType("nvarchar(450)"); 222 | 223 | b.Property("Value") 224 | .HasColumnType("nvarchar(max)"); 225 | 226 | b.HasKey("UserId", "LoginProvider", "Name"); 227 | 228 | b.ToTable("UserTokens"); 229 | }); 230 | 231 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 232 | { 233 | b.HasOne("Identity.Models.ApplicationRole", null) 234 | .WithMany() 235 | .HasForeignKey("RoleId") 236 | .OnDelete(DeleteBehavior.Cascade) 237 | .IsRequired(); 238 | }); 239 | 240 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 241 | { 242 | b.HasOne("Identity.Models.ApplicationUser", null) 243 | .WithMany() 244 | .HasForeignKey("UserId") 245 | .OnDelete(DeleteBehavior.Cascade) 246 | .IsRequired(); 247 | }); 248 | 249 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 250 | { 251 | b.HasOne("Identity.Models.ApplicationUser", null) 252 | .WithMany() 253 | .HasForeignKey("UserId") 254 | .OnDelete(DeleteBehavior.Cascade) 255 | .IsRequired(); 256 | }); 257 | 258 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 259 | { 260 | b.HasOne("Identity.Models.ApplicationRole", null) 261 | .WithMany() 262 | .HasForeignKey("RoleId") 263 | .OnDelete(DeleteBehavior.Cascade) 264 | .IsRequired(); 265 | 266 | b.HasOne("Identity.Models.ApplicationUser", null) 267 | .WithMany() 268 | .HasForeignKey("UserId") 269 | .OnDelete(DeleteBehavior.Cascade) 270 | .IsRequired(); 271 | }); 272 | 273 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 274 | { 275 | b.HasOne("Identity.Models.ApplicationUser", null) 276 | .WithMany() 277 | .HasForeignKey("UserId") 278 | .OnDelete(DeleteBehavior.Cascade) 279 | .IsRequired(); 280 | }); 281 | #pragma warning restore 612, 618 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Migrations/20201005130141_my_new_identity_migration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace Identity.Migrations 5 | { 6 | public partial class my_new_identity_migration : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.EnsureSchema( 11 | name: "Identity"); 12 | 13 | migrationBuilder.CreateTable( 14 | name: "Role", 15 | schema: "Identity", 16 | columns: table => new 17 | { 18 | Id = table.Column(nullable: false) 19 | .Annotation("SqlServer:Identity", "1, 1"), 20 | Name = table.Column(maxLength: 256, nullable: true), 21 | NormalizedName = table.Column(maxLength: 256, nullable: true), 22 | ConcurrencyStamp = table.Column(nullable: true), 23 | CreatedDate = table.Column(nullable: true) 24 | }, 25 | constraints: table => 26 | { 27 | table.PrimaryKey("PK_Role", x => x.Id); 28 | }); 29 | 30 | migrationBuilder.CreateTable( 31 | name: "User", 32 | schema: "Identity", 33 | columns: table => new 34 | { 35 | Id = table.Column(nullable: false) 36 | .Annotation("SqlServer:Identity", "1, 1"), 37 | UserName = table.Column(maxLength: 256, nullable: true), 38 | NormalizedUserName = table.Column(maxLength: 256, nullable: true), 39 | Email = table.Column(maxLength: 256, nullable: true), 40 | NormalizedEmail = table.Column(maxLength: 256, nullable: true), 41 | EmailConfirmed = table.Column(nullable: false), 42 | PasswordHash = table.Column(nullable: true), 43 | SecurityStamp = table.Column(nullable: true), 44 | ConcurrencyStamp = table.Column(nullable: true), 45 | PhoneNumber = table.Column(nullable: true), 46 | PhoneNumberConfirmed = table.Column(nullable: false), 47 | TwoFactorEnabled = table.Column(nullable: false), 48 | LockoutEnd = table.Column(nullable: true), 49 | LockoutEnabled = table.Column(nullable: false), 50 | AccessFailedCount = table.Column(nullable: false), 51 | FirstName = table.Column(nullable: true), 52 | LastName = table.Column(nullable: true) 53 | }, 54 | constraints: table => 55 | { 56 | table.PrimaryKey("PK_User", x => x.Id); 57 | }); 58 | 59 | migrationBuilder.CreateTable( 60 | name: "RoleClaims", 61 | schema: "Identity", 62 | columns: table => new 63 | { 64 | Id = table.Column(nullable: false) 65 | .Annotation("SqlServer:Identity", "1, 1"), 66 | RoleId = table.Column(nullable: false), 67 | ClaimType = table.Column(nullable: true), 68 | ClaimValue = table.Column(nullable: true) 69 | }, 70 | constraints: table => 71 | { 72 | table.PrimaryKey("PK_RoleClaims", x => x.Id); 73 | table.ForeignKey( 74 | name: "FK_RoleClaims_Role_RoleId", 75 | column: x => x.RoleId, 76 | principalSchema: "Identity", 77 | principalTable: "Role", 78 | principalColumn: "Id", 79 | onDelete: ReferentialAction.Cascade); 80 | }); 81 | 82 | migrationBuilder.CreateTable( 83 | name: "UserClaims", 84 | schema: "Identity", 85 | columns: table => new 86 | { 87 | Id = table.Column(nullable: false) 88 | .Annotation("SqlServer:Identity", "1, 1"), 89 | UserId = table.Column(nullable: false), 90 | ClaimType = table.Column(nullable: true), 91 | ClaimValue = table.Column(nullable: true) 92 | }, 93 | constraints: table => 94 | { 95 | table.PrimaryKey("PK_UserClaims", x => x.Id); 96 | table.ForeignKey( 97 | name: "FK_UserClaims_User_UserId", 98 | column: x => x.UserId, 99 | principalSchema: "Identity", 100 | principalTable: "User", 101 | principalColumn: "Id", 102 | onDelete: ReferentialAction.Cascade); 103 | }); 104 | 105 | migrationBuilder.CreateTable( 106 | name: "UserLogins", 107 | schema: "Identity", 108 | columns: table => new 109 | { 110 | LoginProvider = table.Column(nullable: false), 111 | ProviderKey = table.Column(nullable: false), 112 | ProviderDisplayName = table.Column(nullable: true), 113 | UserId = table.Column(nullable: false) 114 | }, 115 | constraints: table => 116 | { 117 | table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey }); 118 | table.ForeignKey( 119 | name: "FK_UserLogins_User_UserId", 120 | column: x => x.UserId, 121 | principalSchema: "Identity", 122 | principalTable: "User", 123 | principalColumn: "Id", 124 | onDelete: ReferentialAction.Cascade); 125 | }); 126 | 127 | migrationBuilder.CreateTable( 128 | name: "UserRoles", 129 | schema: "Identity", 130 | columns: table => new 131 | { 132 | UserId = table.Column(nullable: false), 133 | RoleId = table.Column(nullable: false) 134 | }, 135 | constraints: table => 136 | { 137 | table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId }); 138 | table.ForeignKey( 139 | name: "FK_UserRoles_Role_RoleId", 140 | column: x => x.RoleId, 141 | principalSchema: "Identity", 142 | principalTable: "Role", 143 | principalColumn: "Id", 144 | onDelete: ReferentialAction.Cascade); 145 | table.ForeignKey( 146 | name: "FK_UserRoles_User_UserId", 147 | column: x => x.UserId, 148 | principalSchema: "Identity", 149 | principalTable: "User", 150 | principalColumn: "Id", 151 | onDelete: ReferentialAction.Cascade); 152 | }); 153 | 154 | migrationBuilder.CreateTable( 155 | name: "UserTokens", 156 | schema: "Identity", 157 | columns: table => new 158 | { 159 | UserId = table.Column(nullable: false), 160 | LoginProvider = table.Column(nullable: false), 161 | Name = table.Column(nullable: false), 162 | Value = table.Column(nullable: true) 163 | }, 164 | constraints: table => 165 | { 166 | table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); 167 | table.ForeignKey( 168 | name: "FK_UserTokens_User_UserId", 169 | column: x => x.UserId, 170 | principalSchema: "Identity", 171 | principalTable: "User", 172 | principalColumn: "Id", 173 | onDelete: ReferentialAction.Cascade); 174 | }); 175 | 176 | migrationBuilder.CreateIndex( 177 | name: "RoleNameIndex", 178 | schema: "Identity", 179 | table: "Role", 180 | column: "NormalizedName", 181 | unique: true, 182 | filter: "[NormalizedName] IS NOT NULL"); 183 | 184 | migrationBuilder.CreateIndex( 185 | name: "IX_RoleClaims_RoleId", 186 | schema: "Identity", 187 | table: "RoleClaims", 188 | column: "RoleId"); 189 | 190 | migrationBuilder.CreateIndex( 191 | name: "EmailIndex", 192 | schema: "Identity", 193 | table: "User", 194 | column: "NormalizedEmail"); 195 | 196 | migrationBuilder.CreateIndex( 197 | name: "UserNameIndex", 198 | schema: "Identity", 199 | table: "User", 200 | column: "NormalizedUserName", 201 | unique: true, 202 | filter: "[NormalizedUserName] IS NOT NULL"); 203 | 204 | migrationBuilder.CreateIndex( 205 | name: "IX_UserClaims_UserId", 206 | schema: "Identity", 207 | table: "UserClaims", 208 | column: "UserId"); 209 | 210 | migrationBuilder.CreateIndex( 211 | name: "IX_UserLogins_UserId", 212 | schema: "Identity", 213 | table: "UserLogins", 214 | column: "UserId"); 215 | 216 | migrationBuilder.CreateIndex( 217 | name: "IX_UserRoles_RoleId", 218 | schema: "Identity", 219 | table: "UserRoles", 220 | column: "RoleId"); 221 | } 222 | 223 | protected override void Down(MigrationBuilder migrationBuilder) 224 | { 225 | migrationBuilder.DropTable( 226 | name: "RoleClaims", 227 | schema: "Identity"); 228 | 229 | migrationBuilder.DropTable( 230 | name: "UserClaims", 231 | schema: "Identity"); 232 | 233 | migrationBuilder.DropTable( 234 | name: "UserLogins", 235 | schema: "Identity"); 236 | 237 | migrationBuilder.DropTable( 238 | name: "UserRoles", 239 | schema: "Identity"); 240 | 241 | migrationBuilder.DropTable( 242 | name: "UserTokens", 243 | schema: "Identity"); 244 | 245 | migrationBuilder.DropTable( 246 | name: "Role", 247 | schema: "Identity"); 248 | 249 | migrationBuilder.DropTable( 250 | name: "User", 251 | schema: "Identity"); 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Migrations/20201027103615_update_identity_migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Identity.Contexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 9 | 10 | namespace Identity.Migrations 11 | { 12 | [DbContext(typeof(IdentityContext))] 13 | [Migration("20201027103615_update_identity_migration")] 14 | partial class update_identity_migration 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasDefaultSchema("Identity") 21 | .HasAnnotation("ProductVersion", "3.1.8") 22 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 23 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 24 | 25 | modelBuilder.Entity("Identity.Models.ApplicationRole", b => 26 | { 27 | b.Property("Id") 28 | .ValueGeneratedOnAdd() 29 | .HasColumnType("int") 30 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 31 | 32 | b.Property("ConcurrencyStamp") 33 | .IsConcurrencyToken() 34 | .HasColumnType("nvarchar(max)"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetime2"); 38 | 39 | b.Property("Name") 40 | .HasColumnType("nvarchar(256)") 41 | .HasMaxLength(256); 42 | 43 | b.Property("NormalizedName") 44 | .HasColumnType("nvarchar(256)") 45 | .HasMaxLength(256); 46 | 47 | b.HasKey("Id"); 48 | 49 | b.HasIndex("NormalizedName") 50 | .IsUnique() 51 | .HasName("RoleNameIndex") 52 | .HasFilter("[NormalizedName] IS NOT NULL"); 53 | 54 | b.ToTable("Role"); 55 | }); 56 | 57 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 58 | { 59 | b.Property("Id") 60 | .ValueGeneratedOnAdd() 61 | .HasColumnType("int") 62 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 63 | 64 | b.Property("ClaimType") 65 | .HasColumnType("nvarchar(max)"); 66 | 67 | b.Property("ClaimValue") 68 | .HasColumnType("nvarchar(max)"); 69 | 70 | b.Property("RoleId") 71 | .HasColumnType("int"); 72 | 73 | b.HasKey("Id"); 74 | 75 | b.HasIndex("RoleId"); 76 | 77 | b.ToTable("RoleClaims"); 78 | }); 79 | 80 | modelBuilder.Entity("Identity.Models.ApplicationUser", b => 81 | { 82 | b.Property("Id") 83 | .ValueGeneratedOnAdd() 84 | .HasColumnType("int") 85 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 86 | 87 | b.Property("AccessFailedCount") 88 | .HasColumnType("int"); 89 | 90 | b.Property("ConcurrencyStamp") 91 | .IsConcurrencyToken() 92 | .HasColumnType("nvarchar(max)"); 93 | 94 | b.Property("Email") 95 | .HasColumnType("nvarchar(256)") 96 | .HasMaxLength(256); 97 | 98 | b.Property("EmailConfirmed") 99 | .HasColumnType("bit"); 100 | 101 | b.Property("FirstName") 102 | .HasColumnType("nvarchar(max)"); 103 | 104 | b.Property("LastName") 105 | .HasColumnType("nvarchar(max)"); 106 | 107 | b.Property("LockoutEnabled") 108 | .HasColumnType("bit"); 109 | 110 | b.Property("LockoutEnd") 111 | .HasColumnType("datetimeoffset"); 112 | 113 | b.Property("NormalizedEmail") 114 | .HasColumnType("nvarchar(256)") 115 | .HasMaxLength(256); 116 | 117 | b.Property("NormalizedUserName") 118 | .HasColumnType("nvarchar(256)") 119 | .HasMaxLength(256); 120 | 121 | b.Property("PasswordHash") 122 | .HasColumnType("nvarchar(max)"); 123 | 124 | b.Property("PhoneNumber") 125 | .HasColumnType("nvarchar(max)"); 126 | 127 | b.Property("PhoneNumberConfirmed") 128 | .HasColumnType("bit"); 129 | 130 | b.Property("SecurityStamp") 131 | .HasColumnType("nvarchar(max)"); 132 | 133 | b.Property("TwoFactorEnabled") 134 | .HasColumnType("bit"); 135 | 136 | b.Property("UserName") 137 | .HasColumnType("nvarchar(256)") 138 | .HasMaxLength(256); 139 | 140 | b.HasKey("Id"); 141 | 142 | b.HasIndex("NormalizedEmail") 143 | .HasName("EmailIndex"); 144 | 145 | b.HasIndex("NormalizedUserName") 146 | .IsUnique() 147 | .HasName("UserNameIndex") 148 | .HasFilter("[NormalizedUserName] IS NOT NULL"); 149 | 150 | b.ToTable("User"); 151 | }); 152 | 153 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 154 | { 155 | b.Property("Id") 156 | .ValueGeneratedOnAdd() 157 | .HasColumnType("int") 158 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 159 | 160 | b.Property("ClaimType") 161 | .HasColumnType("nvarchar(max)"); 162 | 163 | b.Property("ClaimValue") 164 | .HasColumnType("nvarchar(max)"); 165 | 166 | b.Property("UserId") 167 | .HasColumnType("int"); 168 | 169 | b.HasKey("Id"); 170 | 171 | b.HasIndex("UserId"); 172 | 173 | b.ToTable("UserClaims"); 174 | }); 175 | 176 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 177 | { 178 | b.Property("LoginProvider") 179 | .HasColumnType("nvarchar(450)"); 180 | 181 | b.Property("ProviderKey") 182 | .HasColumnType("nvarchar(450)"); 183 | 184 | b.Property("ProviderDisplayName") 185 | .HasColumnType("nvarchar(max)"); 186 | 187 | b.Property("UserId") 188 | .HasColumnType("int"); 189 | 190 | b.HasKey("LoginProvider", "ProviderKey"); 191 | 192 | b.HasIndex("UserId"); 193 | 194 | b.ToTable("UserLogins"); 195 | }); 196 | 197 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 198 | { 199 | b.Property("UserId") 200 | .HasColumnType("int"); 201 | 202 | b.Property("RoleId") 203 | .HasColumnType("int"); 204 | 205 | b.HasKey("UserId", "RoleId"); 206 | 207 | b.HasIndex("RoleId"); 208 | 209 | b.ToTable("UserRoles"); 210 | }); 211 | 212 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 213 | { 214 | b.Property("UserId") 215 | .HasColumnType("int"); 216 | 217 | b.Property("LoginProvider") 218 | .HasColumnType("nvarchar(450)"); 219 | 220 | b.Property("Name") 221 | .HasColumnType("nvarchar(450)"); 222 | 223 | b.Property("Value") 224 | .HasColumnType("nvarchar(max)"); 225 | 226 | b.HasKey("UserId", "LoginProvider", "Name"); 227 | 228 | b.ToTable("UserTokens"); 229 | }); 230 | 231 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 232 | { 233 | b.HasOne("Identity.Models.ApplicationRole", null) 234 | .WithMany() 235 | .HasForeignKey("RoleId") 236 | .OnDelete(DeleteBehavior.Cascade) 237 | .IsRequired(); 238 | }); 239 | 240 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 241 | { 242 | b.HasOne("Identity.Models.ApplicationUser", null) 243 | .WithMany() 244 | .HasForeignKey("UserId") 245 | .OnDelete(DeleteBehavior.Cascade) 246 | .IsRequired(); 247 | }); 248 | 249 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 250 | { 251 | b.HasOne("Identity.Models.ApplicationUser", null) 252 | .WithMany() 253 | .HasForeignKey("UserId") 254 | .OnDelete(DeleteBehavior.Cascade) 255 | .IsRequired(); 256 | }); 257 | 258 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 259 | { 260 | b.HasOne("Identity.Models.ApplicationRole", "Role") 261 | .WithMany("UserRoles") 262 | .HasForeignKey("RoleId") 263 | .OnDelete(DeleteBehavior.Cascade) 264 | .IsRequired(); 265 | 266 | b.HasOne("Identity.Models.ApplicationUser", "User") 267 | .WithMany("UserRoles") 268 | .HasForeignKey("UserId") 269 | .OnDelete(DeleteBehavior.Cascade) 270 | .IsRequired(); 271 | }); 272 | 273 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 274 | { 275 | b.HasOne("Identity.Models.ApplicationUser", null) 276 | .WithMany() 277 | .HasForeignKey("UserId") 278 | .OnDelete(DeleteBehavior.Cascade) 279 | .IsRequired(); 280 | }); 281 | #pragma warning restore 612, 618 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Migrations/20201027103615_update_identity_migration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Identity.Migrations 4 | { 5 | public partial class update_identity_migration : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | 10 | } 11 | 12 | protected override void Down(MigrationBuilder migrationBuilder) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Migrations/IdentityContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Identity.Contexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace Identity.Migrations 10 | { 11 | [DbContext(typeof(IdentityContext))] 12 | partial class IdentityContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasDefaultSchema("Identity") 19 | .HasAnnotation("ProductVersion", "3.1.8") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 21 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 22 | 23 | modelBuilder.Entity("Identity.Models.ApplicationRole", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd() 27 | .HasColumnType("int") 28 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 29 | 30 | b.Property("ConcurrencyStamp") 31 | .IsConcurrencyToken() 32 | .HasColumnType("nvarchar(max)"); 33 | 34 | b.Property("CreatedDate") 35 | .HasColumnType("datetime2"); 36 | 37 | b.Property("Name") 38 | .HasColumnType("nvarchar(256)") 39 | .HasMaxLength(256); 40 | 41 | b.Property("NormalizedName") 42 | .HasColumnType("nvarchar(256)") 43 | .HasMaxLength(256); 44 | 45 | b.HasKey("Id"); 46 | 47 | b.HasIndex("NormalizedName") 48 | .IsUnique() 49 | .HasName("RoleNameIndex") 50 | .HasFilter("[NormalizedName] IS NOT NULL"); 51 | 52 | b.ToTable("Role"); 53 | }); 54 | 55 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 56 | { 57 | b.Property("Id") 58 | .ValueGeneratedOnAdd() 59 | .HasColumnType("int") 60 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 61 | 62 | b.Property("ClaimType") 63 | .HasColumnType("nvarchar(max)"); 64 | 65 | b.Property("ClaimValue") 66 | .HasColumnType("nvarchar(max)"); 67 | 68 | b.Property("RoleId") 69 | .HasColumnType("int"); 70 | 71 | b.HasKey("Id"); 72 | 73 | b.HasIndex("RoleId"); 74 | 75 | b.ToTable("RoleClaims"); 76 | }); 77 | 78 | modelBuilder.Entity("Identity.Models.ApplicationUser", b => 79 | { 80 | b.Property("Id") 81 | .ValueGeneratedOnAdd() 82 | .HasColumnType("int") 83 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 84 | 85 | b.Property("AccessFailedCount") 86 | .HasColumnType("int"); 87 | 88 | b.Property("ConcurrencyStamp") 89 | .IsConcurrencyToken() 90 | .HasColumnType("nvarchar(max)"); 91 | 92 | b.Property("Email") 93 | .HasColumnType("nvarchar(256)") 94 | .HasMaxLength(256); 95 | 96 | b.Property("EmailConfirmed") 97 | .HasColumnType("bit"); 98 | 99 | b.Property("FirstName") 100 | .HasColumnType("nvarchar(max)"); 101 | 102 | b.Property("LastName") 103 | .HasColumnType("nvarchar(max)"); 104 | 105 | b.Property("LockoutEnabled") 106 | .HasColumnType("bit"); 107 | 108 | b.Property("LockoutEnd") 109 | .HasColumnType("datetimeoffset"); 110 | 111 | b.Property("NormalizedEmail") 112 | .HasColumnType("nvarchar(256)") 113 | .HasMaxLength(256); 114 | 115 | b.Property("NormalizedUserName") 116 | .HasColumnType("nvarchar(256)") 117 | .HasMaxLength(256); 118 | 119 | b.Property("PasswordHash") 120 | .HasColumnType("nvarchar(max)"); 121 | 122 | b.Property("PhoneNumber") 123 | .HasColumnType("nvarchar(max)"); 124 | 125 | b.Property("PhoneNumberConfirmed") 126 | .HasColumnType("bit"); 127 | 128 | b.Property("SecurityStamp") 129 | .HasColumnType("nvarchar(max)"); 130 | 131 | b.Property("TwoFactorEnabled") 132 | .HasColumnType("bit"); 133 | 134 | b.Property("UserName") 135 | .HasColumnType("nvarchar(256)") 136 | .HasMaxLength(256); 137 | 138 | b.HasKey("Id"); 139 | 140 | b.HasIndex("NormalizedEmail") 141 | .HasName("EmailIndex"); 142 | 143 | b.HasIndex("NormalizedUserName") 144 | .IsUnique() 145 | .HasName("UserNameIndex") 146 | .HasFilter("[NormalizedUserName] IS NOT NULL"); 147 | 148 | b.ToTable("User"); 149 | }); 150 | 151 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 152 | { 153 | b.Property("Id") 154 | .ValueGeneratedOnAdd() 155 | .HasColumnType("int") 156 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 157 | 158 | b.Property("ClaimType") 159 | .HasColumnType("nvarchar(max)"); 160 | 161 | b.Property("ClaimValue") 162 | .HasColumnType("nvarchar(max)"); 163 | 164 | b.Property("UserId") 165 | .HasColumnType("int"); 166 | 167 | b.HasKey("Id"); 168 | 169 | b.HasIndex("UserId"); 170 | 171 | b.ToTable("UserClaims"); 172 | }); 173 | 174 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 175 | { 176 | b.Property("LoginProvider") 177 | .HasColumnType("nvarchar(450)"); 178 | 179 | b.Property("ProviderKey") 180 | .HasColumnType("nvarchar(450)"); 181 | 182 | b.Property("ProviderDisplayName") 183 | .HasColumnType("nvarchar(max)"); 184 | 185 | b.Property("UserId") 186 | .HasColumnType("int"); 187 | 188 | b.HasKey("LoginProvider", "ProviderKey"); 189 | 190 | b.HasIndex("UserId"); 191 | 192 | b.ToTable("UserLogins"); 193 | }); 194 | 195 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 196 | { 197 | b.Property("UserId") 198 | .HasColumnType("int"); 199 | 200 | b.Property("RoleId") 201 | .HasColumnType("int"); 202 | 203 | b.HasKey("UserId", "RoleId"); 204 | 205 | b.HasIndex("RoleId"); 206 | 207 | b.ToTable("UserRoles"); 208 | }); 209 | 210 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 211 | { 212 | b.Property("UserId") 213 | .HasColumnType("int"); 214 | 215 | b.Property("LoginProvider") 216 | .HasColumnType("nvarchar(450)"); 217 | 218 | b.Property("Name") 219 | .HasColumnType("nvarchar(450)"); 220 | 221 | b.Property("Value") 222 | .HasColumnType("nvarchar(max)"); 223 | 224 | b.HasKey("UserId", "LoginProvider", "Name"); 225 | 226 | b.ToTable("UserTokens"); 227 | }); 228 | 229 | modelBuilder.Entity("Identity.Models.ApplicationRoleClaim", b => 230 | { 231 | b.HasOne("Identity.Models.ApplicationRole", null) 232 | .WithMany() 233 | .HasForeignKey("RoleId") 234 | .OnDelete(DeleteBehavior.Cascade) 235 | .IsRequired(); 236 | }); 237 | 238 | modelBuilder.Entity("Identity.Models.ApplicationUserClaim", b => 239 | { 240 | b.HasOne("Identity.Models.ApplicationUser", null) 241 | .WithMany() 242 | .HasForeignKey("UserId") 243 | .OnDelete(DeleteBehavior.Cascade) 244 | .IsRequired(); 245 | }); 246 | 247 | modelBuilder.Entity("Identity.Models.ApplicationUserLogin", b => 248 | { 249 | b.HasOne("Identity.Models.ApplicationUser", null) 250 | .WithMany() 251 | .HasForeignKey("UserId") 252 | .OnDelete(DeleteBehavior.Cascade) 253 | .IsRequired(); 254 | }); 255 | 256 | modelBuilder.Entity("Identity.Models.ApplicationUserRole", b => 257 | { 258 | b.HasOne("Identity.Models.ApplicationRole", "Role") 259 | .WithMany("UserRoles") 260 | .HasForeignKey("RoleId") 261 | .OnDelete(DeleteBehavior.Cascade) 262 | .IsRequired(); 263 | 264 | b.HasOne("Identity.Models.ApplicationUser", "User") 265 | .WithMany("UserRoles") 266 | .HasForeignKey("UserId") 267 | .OnDelete(DeleteBehavior.Cascade) 268 | .IsRequired(); 269 | }); 270 | 271 | modelBuilder.Entity("Identity.Models.ApplicationUserToken", b => 272 | { 273 | b.HasOne("Identity.Models.ApplicationUser", null) 274 | .WithMany() 275 | .HasForeignKey("UserId") 276 | .OnDelete(DeleteBehavior.Cascade) 277 | .IsRequired(); 278 | }); 279 | #pragma warning restore 612, 618 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationRole.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationRole : IdentityRole 9 | { 10 | public ICollection UserRoles { get; set; } 11 | public DateTime? CreatedDate { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationRoleClaim.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationRoleClaim : IdentityRoleClaim 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Models.DTOs.Account; 6 | 7 | namespace Identity.Models 8 | { 9 | public class ApplicationUser : IdentityUser 10 | { 11 | public string FirstName { get; set; } 12 | public string LastName { get; set; } 13 | public ICollection UserRoles { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationUserClaim.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationUserClaim : IdentityUserClaim 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationUserLogin.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationUserLogin : IdentityUserLogin 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationUserRole.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationUserRole : IdentityUserRole 9 | { 10 | public virtual ApplicationUser User { get; set; } 11 | public virtual ApplicationRole Role { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Models/ApplicationUserToken.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Identity.Models 7 | { 8 | public class ApplicationUserToken : IdentityUserToken 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Seeds/DefaultRoles.cs: -------------------------------------------------------------------------------- 1 | using Identity.Models; 2 | using Microsoft.AspNetCore.Identity; 3 | using Models.Enums; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Identity.Seeds 8 | { 9 | public static class DefaultRoles 10 | { 11 | public static async Task SeedAsync(RoleManager roleManager) 12 | { 13 | //Seed Roles 14 | await roleManager.CreateAsync(new ApplicationRole() 15 | { 16 | Name = Roles.SuperAdmin.ToString(), 17 | CreatedDate = DateTime.Now 18 | }); 19 | await roleManager.CreateAsync(new ApplicationRole() 20 | { 21 | Name = Roles.Admin.ToString(), 22 | CreatedDate = DateTime.Now 23 | }); 24 | await roleManager.CreateAsync(new ApplicationRole() 25 | { 26 | Name = Roles.Moderator.ToString(), 27 | CreatedDate = DateTime.Now 28 | }); 29 | await roleManager.CreateAsync(new ApplicationRole() 30 | { 31 | Name = Roles.Basic.ToString(), 32 | CreatedDate = DateTime.Now 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Seeds/DefaultSuperAdmin.cs: -------------------------------------------------------------------------------- 1 | using Identity.Models; 2 | using Microsoft.AspNetCore.Identity; 3 | using Models.Enums; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Identity.Seeds 8 | { 9 | public static class DefaultSuperAdmin 10 | { 11 | public static async Task SeedAsync(UserManager userManager) 12 | { 13 | //Seed Default User 14 | var defaultUser = new ApplicationUser 15 | { 16 | UserName = "sinantok", 17 | Email = "superadmin@gmail.com", 18 | FirstName = "Sinan", 19 | LastName = "Tok", 20 | EmailConfirmed = true, 21 | PhoneNumberConfirmed = true 22 | }; 23 | if (userManager.Users.All(u => u.Id != defaultUser.Id)) 24 | { 25 | var user = await userManager.FindByEmailAsync(defaultUser.Email); 26 | if (user == null) 27 | { 28 | await userManager.CreateAsync(defaultUser, "123Pa$$word!"); 29 | await userManager.AddToRoleAsync(defaultUser, Roles.Basic.ToString()); 30 | await userManager.AddToRoleAsync(defaultUser, Roles.Moderator.ToString()); 31 | await userManager.AddToRoleAsync(defaultUser, Roles.Admin.ToString()); 32 | await userManager.AddToRoleAsync(defaultUser, Roles.SuperAdmin.ToString()); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Libraries/Identity/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Identity.Contexts; 2 | using Identity.Models; 3 | using Identity.Services.Concrete; 4 | using Identity.Services.Interfaces; 5 | using Microsoft.AspNetCore.Authentication.JwtBearer; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.EntityFrameworkCore; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.IdentityModel.Tokens; 12 | using Models.ResponseModels; 13 | using Models.Settings; 14 | using Newtonsoft.Json; 15 | using System; 16 | using System.Text; 17 | 18 | namespace Identity 19 | { 20 | public static class ServiceExtensions 21 | { 22 | public static void AddIdentity(this IServiceCollection services, IConfiguration configuration) 23 | { 24 | #region Services 25 | services.AddTransient(); 26 | #endregion 27 | 28 | #region Configure 29 | services.Configure(configuration.GetSection("JWTSettings")); 30 | #endregion 31 | 32 | services.AddDbContext(options => 33 | options.UseSqlServer( 34 | configuration.GetConnectionString("IdentityConnection"), 35 | b => b.MigrationsAssembly(typeof(IdentityContext).Assembly.FullName))); 36 | 37 | // Ensure the database is created. 38 | using var context = services.BuildServiceProvider().GetService(); 39 | context.Database.EnsureCreated(); 40 | 41 | services.AddIdentity().AddEntityFrameworkStores().AddDefaultTokenProviders() 42 | .AddTokenProvider("MyApp", typeof(DataProtectorTokenProvider)); 43 | 44 | services.AddAuthentication(options => 45 | { 46 | options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 47 | options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 48 | }) 49 | .AddJwtBearer(o => 50 | { 51 | o.RequireHttpsMetadata = false; 52 | o.SaveToken = false; 53 | o.TokenValidationParameters = new TokenValidationParameters 54 | { 55 | ValidateIssuerSigningKey = true, 56 | ValidateIssuer = true, 57 | ValidateAudience = true, 58 | ValidateLifetime = true, 59 | ClockSkew = TimeSpan.Zero, 60 | ValidIssuer = configuration["JWTSettings:Issuer"], 61 | ValidAudience = configuration["JWTSettings:Audience"], 62 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWTSettings:Key"])) 63 | }; 64 | o.Events = new JwtBearerEvents() 65 | { 66 | OnAuthenticationFailed = c => 67 | { 68 | c.NoResult(); 69 | c.Response.StatusCode = 500; 70 | c.Response.ContentType = "text/plain"; 71 | return c.Response.WriteAsync(c.Exception.ToString()); 72 | }, 73 | OnChallenge = context => 74 | { 75 | context.HandleResponse(); 76 | context.Response.StatusCode = 401; 77 | context.Response.ContentType = "application/json"; 78 | var result = JsonConvert.SerializeObject(new BaseResponse("You are not Authorized")); 79 | return context.Response.WriteAsync(result); 80 | }, 81 | OnForbidden = context => 82 | { 83 | context.Response.StatusCode = 403; 84 | context.Response.ContentType = "application/json"; 85 | var result = JsonConvert.SerializeObject(new BaseResponse("You are not authorized to access this resource")); 86 | return context.Response.WriteAsync(result); 87 | }, 88 | }; 89 | }); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Services/Concrete/AccountService.cs: -------------------------------------------------------------------------------- 1 | using Core.Exceptions; 2 | using Core.Helpers; 3 | using Core.Interfaces; 4 | using Identity.Models; 5 | using Identity.Services.Interfaces; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.WebUtilities; 8 | using Microsoft.EntityFrameworkCore; 9 | using Microsoft.Extensions.Options; 10 | using Microsoft.IdentityModel.Tokens; 11 | using Models.DTOs.Account; 12 | using Models.DTOs.Email; 13 | using Models.Enums; 14 | using Models.ResponseModels; 15 | using Models.Settings; 16 | using System; 17 | using System.Collections.Generic; 18 | using System.IdentityModel.Tokens.Jwt; 19 | using System.Linq; 20 | using System.Net; 21 | using System.Security.Claims; 22 | using System.Security.Cryptography; 23 | using System.Text; 24 | using System.Threading.Tasks; 25 | 26 | namespace Identity.Services.Concrete 27 | { 28 | public class AccountService : IAccountService 29 | { 30 | private readonly UserManager _userManager; 31 | private readonly RoleManager _roleManager; 32 | private readonly SignInManager _signInManager; 33 | private readonly JWTSettings _jwtSettings; 34 | private readonly IEmailService _emailService; 35 | public AccountService(UserManager userManager, 36 | RoleManager roleManager, 37 | IOptions jwtSettings, 38 | SignInManager signInManager, 39 | IEmailService emailService) 40 | { 41 | _userManager = userManager; 42 | _roleManager = roleManager; 43 | _signInManager = signInManager; 44 | _jwtSettings = jwtSettings.Value; 45 | _emailService = emailService; 46 | } 47 | public async Task> AuthenticateAsync(AuthenticationRequest request) 48 | { 49 | ApplicationUser user = await _userManager.FindByEmailAsync(request.Email.Trim()); 50 | if (user == null) 51 | { 52 | throw new ApiException($"You are not registered with '{request.Email}'.") { StatusCode = (int)HttpStatusCode.BadRequest }; 53 | } 54 | if (!user.EmailConfirmed) 55 | { 56 | throw new ApiException($"Account Not Confirmed for '{request.Email}'.") { StatusCode = (int)HttpStatusCode.BadRequest }; 57 | } 58 | 59 | SignInResult signInResult = await _signInManager.PasswordSignInAsync(user, request.Password, false, lockoutOnFailure: false); 60 | if (!signInResult.Succeeded) 61 | { 62 | throw new ApiException($"Invalid Credentials for '{request.Email}'.") { StatusCode = (int)HttpStatusCode.BadRequest }; 63 | } 64 | 65 | string ipAddress = IpHelper.GetIpAddress(); 66 | JwtSecurityToken jwtSecurityToken = await GenerateJWToken(user, ipAddress); 67 | AuthenticationResponse response = new AuthenticationResponse(); 68 | response.Id = user.Id.ToString(); 69 | response.JWToken = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); 70 | response.Email = user.Email; 71 | response.UserName = user.UserName; 72 | IList rolesList = await _userManager.GetRolesAsync(user).ConfigureAwait(false); 73 | response.Roles = rolesList.ToList(); 74 | response.IsVerified = user.EmailConfirmed; 75 | response.RefreshToken = await GenerateRefreshToken(user); 76 | return new BaseResponse(response, $"Authenticated {user.UserName}"); 77 | } 78 | 79 | public async Task> ConfirmEmailAsync(string userId, string code) 80 | { 81 | var user = await _userManager.FindByIdAsync(userId); 82 | code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); 83 | var result = await _userManager.ConfirmEmailAsync(user, code); 84 | if (result.Succeeded) 85 | { 86 | return new BaseResponse(user.Id.ToString(), message: $"Account Confirmed for {user.Email}. You can now use the /api/Account/authenticate endpoint."); 87 | } 88 | else 89 | { 90 | throw new ApiException($"An error occured while confirming {user.Email}.") { StatusCode = (int)HttpStatusCode.InternalServerError }; 91 | } 92 | } 93 | 94 | public async Task ForgotPasswordAsync(ForgotPasswordRequest request, string uri) 95 | { 96 | var user = await _userManager.FindByEmailAsync(request.Email); 97 | if (user == null) return; 98 | 99 | var code = await _userManager.GeneratePasswordResetTokenAsync(user); 100 | var route = "api/account/reset-password/"; 101 | var enpointUri = new Uri(string.Concat($"{uri}/", route)); 102 | var emailRequest = new EmailRequest() 103 | { 104 | Body = $"You have to send a request to the '{enpointUri}' service with reset token - {code}", 105 | To = request.Email, 106 | Subject = "Reset Password", 107 | }; 108 | await _emailService.SendAsync(emailRequest); 109 | } 110 | 111 | public async Task> LogoutAsync(string userEmail) 112 | { 113 | ApplicationUser user = await _userManager.FindByEmailAsync(userEmail); 114 | if (user != null) 115 | { 116 | await _userManager.RemoveAuthenticationTokenAsync(user, "MyApp", "RefreshToken"); 117 | } 118 | await _signInManager.SignOutAsync(); 119 | 120 | return new BaseResponse(userEmail, message: $"Logout."); 121 | } 122 | 123 | public async Task> RefreshTokenAsync(RefreshTokenRequest request) 124 | { 125 | ApplicationUser user = await _userManager.FindByEmailAsync(request.Email); 126 | if (user == null) 127 | { 128 | throw new ApiException($"You are not registered with '{request.Email}'.") { StatusCode = (int)HttpStatusCode.BadRequest }; 129 | } 130 | if (!user.EmailConfirmed) 131 | { 132 | throw new ApiException($"Account Not Confirmed for '{request.Email}'.") { StatusCode = (int)HttpStatusCode.BadRequest }; 133 | } 134 | 135 | string refreshToken = await _userManager.GetAuthenticationTokenAsync(user, "MyApp", "RefreshToken"); 136 | bool isValid = await _userManager.VerifyUserTokenAsync(user, "MyApp", "RefreshToken", request.Token); 137 | if (!refreshToken.Equals(request.Token) || !isValid) 138 | { 139 | throw new ApiException($"Your token is not valid.") { StatusCode = (int)HttpStatusCode.BadRequest }; 140 | } 141 | 142 | string ipAddress = IpHelper.GetIpAddress(); 143 | JwtSecurityToken jwtSecurityToken = await GenerateJWToken(user, ipAddress); 144 | AuthenticationResponse response = new AuthenticationResponse(); 145 | response.Id = user.Id.ToString(); 146 | response.JWToken = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); 147 | response.Email = user.Email; 148 | response.UserName = user.UserName; 149 | IList rolesList = await _userManager.GetRolesAsync(user).ConfigureAwait(false); 150 | response.Roles = rolesList.ToList(); 151 | response.IsVerified = user.EmailConfirmed; 152 | response.RefreshToken = await GenerateRefreshToken(user); 153 | 154 | await _signInManager.SignInAsync(user, false); 155 | return new BaseResponse(response, $"Authenticated {user.UserName}"); 156 | } 157 | 158 | public async Task> RegisterAsync(RegisterRequest request, string uri) 159 | { 160 | ApplicationUser findUser = await _userManager.FindByNameAsync(request.UserName); 161 | if (findUser != null) 162 | { 163 | throw new ApiException($"Username '{request.UserName}' is already taken.") { StatusCode = (int)HttpStatusCode.BadRequest }; 164 | } 165 | findUser = await _userManager.FindByEmailAsync(request.Email); 166 | if (findUser != null) 167 | { 168 | throw new ApiException($"Email {request.Email } is already registered.") { StatusCode = (int)HttpStatusCode.BadRequest }; 169 | } 170 | ApplicationUser newUser = new ApplicationUser 171 | { 172 | Email = request.Email, 173 | FirstName = request.FirstName, 174 | LastName = request.LastName, 175 | UserName = request.UserName 176 | }; 177 | var result = await _userManager.CreateAsync(newUser, request.Password); 178 | if (result.Succeeded) 179 | { 180 | await _userManager.AddToRoleAsync(newUser, Roles.Basic.ToString()); 181 | var verificationUri = await SendVerificationEmail(newUser, uri); 182 | 183 | return new BaseResponse(newUser.Id.ToString(), message: $"User Registered. Please confirm your account by visiting this URL {verificationUri}"); 184 | } 185 | else 186 | { 187 | throw new ApiException($"{result.Errors}") { StatusCode = (int)HttpStatusCode.InternalServerError }; 188 | } 189 | } 190 | 191 | public async Task> ResetPasswordAsync(ResetPasswordRequest request) 192 | { 193 | var user = await _userManager.FindByEmailAsync(request.Email); 194 | if (user == null) throw new ApiException($"You are not registered with '{request.Email}'."); 195 | 196 | var result = await _userManager.ResetPasswordAsync(user, request.Token, request.Password); 197 | if (result.Succeeded) 198 | { 199 | return new BaseResponse(request.Email, message: $"Password Resetted."); 200 | } 201 | else 202 | { 203 | throw new ApiException($"Error occured while reseting the password. Please try again."); 204 | } 205 | } 206 | 207 | public async Task> GetUsers() 208 | { 209 | //return await _userManager.Users.ToListAsync(); 210 | 211 | return await _userManager.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).ToListAsync(); //lazzyloading 212 | } 213 | 214 | private async Task GenerateJWToken(ApplicationUser user, string ipAddress) 215 | { 216 | var userClaims = await _userManager.GetClaimsAsync(user); 217 | var roles = await _userManager.GetRolesAsync(user); 218 | 219 | var roleClaims = new List(); 220 | 221 | for (int i = 0; i < roles.Count; i++) 222 | { 223 | roleClaims.Add(new Claim("roles", roles[i])); 224 | } 225 | 226 | var claims = new[] 227 | { 228 | new Claim(JwtRegisteredClaimNames.Sub, user.UserName), 229 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), 230 | new Claim(JwtRegisteredClaimNames.Email, user.Email), 231 | new Claim("uid", user.Id.ToString()), 232 | new Claim("ip", ipAddress) 233 | } 234 | .Union(userClaims) 235 | .Union(roleClaims); 236 | 237 | var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key)); 238 | var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256); 239 | 240 | var jwtSecurityToken = new JwtSecurityToken( 241 | issuer: _jwtSettings.Issuer, 242 | audience: _jwtSettings.Audience, 243 | claims: claims, 244 | expires: DateTime.UtcNow.AddMinutes(_jwtSettings.DurationInMinutes), 245 | signingCredentials: signingCredentials); 246 | return jwtSecurityToken; 247 | } 248 | 249 | private async Task GenerateRefreshToken(ApplicationUser user) 250 | { 251 | await _userManager.RemoveAuthenticationTokenAsync(user, "MyApp", "RefreshToken"); 252 | var newRefreshToken = await _userManager.GenerateUserTokenAsync(user, "MyApp", "RefreshToken"); 253 | IdentityResult result = await _userManager.SetAuthenticationTokenAsync(user, "MyApp", "RefreshToken", newRefreshToken); 254 | if (!result.Succeeded) 255 | { 256 | throw new ApiException($"An error occured while set refreshtoken.") { StatusCode = (int)HttpStatusCode.InternalServerError }; 257 | } 258 | return newRefreshToken; 259 | } 260 | 261 | private string RandomTokenString() 262 | { 263 | using var rngCryptoServiceProvider = new RNGCryptoServiceProvider(); 264 | var randomBytes = new byte[40]; 265 | rngCryptoServiceProvider.GetBytes(randomBytes); 266 | // convert random bytes to hex string 267 | return BitConverter.ToString(randomBytes).Replace("-", ""); 268 | } 269 | 270 | private async Task SendVerificationEmail(ApplicationUser newUser, string uri) 271 | { 272 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser); 273 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); 274 | var route = "api/account/confirm-email/"; 275 | var _enpointUri = new Uri(string.Concat($"{uri}/", route)); 276 | var verificationUri = QueryHelpers.AddQueryString(_enpointUri.ToString(), "userId", newUser.Id.ToString()); 277 | verificationUri = QueryHelpers.AddQueryString(verificationUri, "code", code); 278 | 279 | await _emailService.SendAsync(new EmailRequest() 280 | { 281 | From = "sinantok@outlook.com", 282 | To = newUser.Email, 283 | Body = $"Please confirm your account by visiting this URL {verificationUri}", 284 | Subject = "Confirm Registration" 285 | }); 286 | 287 | return verificationUri; 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/Libraries/Identity/Services/Interfaces/IAccountService.cs: -------------------------------------------------------------------------------- 1 | using Identity.Models; 2 | using Models.DTOs.Account; 3 | using Models.ResponseModels; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace Identity.Services.Interfaces 8 | { 9 | public interface IAccountService 10 | { 11 | Task> AuthenticateAsync(AuthenticationRequest request); 12 | Task> RegisterAsync(RegisterRequest request, string uri); 13 | Task> ConfirmEmailAsync(string userId, string code); 14 | Task ForgotPasswordAsync(ForgotPasswordRequest request, string uri); 15 | Task> ResetPasswordAsync(ResetPasswordRequest request); 16 | Task> RefreshTokenAsync(RefreshTokenRequest request); 17 | Task> LogoutAsync(string userEmail); 18 | Task> GetUsers(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/AuthenticationRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Models.DTOs.Account 2 | { 3 | public class AuthenticationRequest 4 | { 5 | public string Email { get; set; } 6 | public string Password { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/ForgotPasswordRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Models.DTOs.Account 4 | { 5 | public class ForgotPasswordRequest 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/RefreshToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Models.DTOs.Account 4 | { 5 | public class RefreshToken 6 | { 7 | public int Id { get; set; } 8 | public string Token { get; set; } 9 | public DateTime Expires { get; set; } 10 | public bool IsExpired => DateTime.UtcNow >= Expires; 11 | public DateTime Created { get; set; } 12 | public string CreatedByIp { get; set; } 13 | public DateTime? Revoked { get; set; } 14 | public string RevokedByIp { get; set; } 15 | public string ReplacedByToken { get; set; } 16 | public bool IsActive => Revoked == null && !IsExpired; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/RefreshTokenRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Models.DTOs.Account 2 | { 3 | public class RefreshTokenRequest 4 | { 5 | public string Email { get; set; } 6 | public string Token { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/RegisterRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Models.DTOs.Account 4 | { 5 | public class RegisterRequest 6 | { 7 | [Required] 8 | [MinLength(6)] 9 | public string UserName { get; set; } 10 | 11 | [Required] 12 | public string FirstName { get; set; } 13 | 14 | [Required] 15 | public string LastName { get; set; } 16 | 17 | [Required] 18 | [EmailAddress] 19 | public string Email { get; set; } 20 | 21 | [Required] 22 | [MinLength(6)] 23 | public string Password { get; set; } 24 | 25 | [Required] 26 | [Compare("Password")] 27 | public string ConfirmPassword { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/ResetPasswordRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Models.DTOs.Account 4 | { 5 | public class ResetPasswordRequest 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | [Required] 11 | public string Token { get; set; } 12 | [Required] 13 | [MinLength(6)] 14 | public string Password { get; set; } 15 | 16 | [Required] 17 | [Compare("Password")] 18 | public string ConfirmPassword { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Account/UserDto.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Models.DTOs.Account 4 | { 5 | public class UserDto 6 | { 7 | public string UserName { get; set; } 8 | public string FirstName { get; set; } 9 | public string LastName { get; set; } 10 | public string Email { get; set; } 11 | public List Roles { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Email/EmailRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Models.DTOs.Email 6 | { 7 | public class EmailRequest 8 | { 9 | public string To { get; set; } 10 | public string Subject { get; set; } 11 | public string Body { get; set; } 12 | public string From { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Libraries/Models/DTOs/Log/LogDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Models.DTOs.Log 6 | { 7 | public class LogDto 8 | { 9 | public string To { get; set; } 10 | public string Subject { get; set; } 11 | public string Body { get; set; } 12 | public string From { get; set; } 13 | public object UserEmail { get; set; } 14 | public object LoginTime { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Libraries/Models/DbEntities/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Models.DbEntities 4 | { 5 | public abstract class BaseEntity 6 | { 7 | public int Id { get; set; } 8 | public DateTime CreateUTC { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Libraries/Models/DbEntities/Note.cs: -------------------------------------------------------------------------------- 1 | namespace Models.DbEntities 2 | { 3 | public class Note : BaseEntity 4 | { 5 | public string Title { get; set; } 6 | public string Category { get; set; } 7 | public string Description { get; set; } 8 | public string OwnerEmail { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Libraries/Models/Enums/Roles.cs: -------------------------------------------------------------------------------- 1 | namespace Models.Enums 2 | { 3 | public enum Roles 4 | { 5 | SuperAdmin, 6 | Admin, 7 | Moderator, 8 | Basic 9 | } 10 | } -------------------------------------------------------------------------------- /src/Libraries/Models/Models.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | -------------------------------------------------------------------------------- /src/Libraries/Models/ResponseModels/AuthenticationResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Models.ResponseModels 4 | { 5 | public class AuthenticationResponse 6 | { 7 | public string Id { get; set; } 8 | public string UserName { get; set; } 9 | public string Email { get; set; } 10 | public List Roles { get; set; } 11 | public bool IsVerified { get; set; } 12 | public string JWToken { get; set; } 13 | 14 | public string RefreshToken { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Libraries/Models/ResponseModels/BaseResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Models.ResponseModels 4 | { 5 | public class BaseResponse 6 | { 7 | public BaseResponse() 8 | { 9 | } 10 | public BaseResponse(T data, string message = null) 11 | { 12 | Message = message; 13 | Data = data; 14 | Succeeded = true; 15 | } 16 | public BaseResponse(string message) 17 | { 18 | Message = message; 19 | } 20 | public bool Succeeded; 21 | public string Message { get; set; } 22 | public List Errors; 23 | public T Data { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Libraries/Models/Settings/JWTSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Models.Settings 2 | { 3 | public class JWTSettings 4 | { 5 | public string Key { get; set; } 6 | public string Issuer { get; set; } 7 | public string Audience { get; set; } 8 | public double DurationInMinutes { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Libraries/Models/Settings/MailSettings.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Models.Settings 3 | { 4 | public class MailSettings 5 | { 6 | public string EmailFrom { get; set; } 7 | public string SmtpHost { get; set; } 8 | public int SmtpPort { get; set; } 9 | public string SmtpUser { get; set; } 10 | public string SmtpPass { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Libraries/Models/Settings/RedisSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Models.Settings 2 | { 3 | public class RedisSettings 4 | { 5 | public string RedisDataProtectionKey { get; set; } 6 | public int CacheTime { get; set; } 7 | public string RedisConnectionString { get; set; } 8 | public int? RedisDatabaseId { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Libraries/Services/Concrete/LoginLogService.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Collections; 2 | using Data.Mongo.Repo; 3 | using Services.Interfaces; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | namespace Services.Concrete 8 | { 9 | public class LoginLogService : ILoginLogService 10 | { 11 | private readonly IMongoRepository _repository; 12 | public LoginLogService(IMongoRepository repository) 13 | { 14 | _repository = repository; 15 | } 16 | public async Task Add(LoginLog model) 17 | { 18 | await _repository.AddAsync(model); 19 | } 20 | 21 | public async Task> Get(string email) 22 | { 23 | var content = await _repository.FindAsync(x => x.UserEmail.Equals(email)); 24 | return content.ToList(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Libraries/Services/Concrete/NoteService.cs: -------------------------------------------------------------------------------- 1 | using Data.Repos; 2 | using Data.UnitOfWork; 3 | using Models.DbEntities; 4 | using Services.Interfaces; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Services.Concrete 9 | { 10 | public class NoteService : INoteService 11 | { 12 | private readonly IGenericRepository _repository; 13 | private readonly IAuthenticatedUserService _authenticatedUserService; 14 | private readonly IUnitOfWork _unitOfWork; 15 | private readonly string _userEmail; 16 | public NoteService(IGenericRepository repository, IAuthenticatedUserService authenticatedUserService, IUnitOfWork unitOfWork) 17 | { 18 | _repository = repository; 19 | _authenticatedUserService = authenticatedUserService; 20 | _userEmail = _authenticatedUserService.UserEmail; 21 | _unitOfWork = unitOfWork; 22 | } 23 | 24 | public bool DeleteNote(int id) 25 | { 26 | Note note = _repository.Find(x => x.Id == id && x.OwnerEmail.Equals(_userEmail)); 27 | if (note != null) 28 | { 29 | if (_repository.Delete(note) > 0) 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | public List GetAllMyNotes() 36 | { 37 | //UnitofWork Usage Sample 38 | //List notes = _unitOfWork.Repository().FindAll(x => x.OwnerEmail.Equals(_userEmail)); 39 | return _repository.FindAll(x => x.OwnerEmail.Equals(_userEmail)); 40 | } 41 | 42 | public async Task> GetAllMyNotesAsync() 43 | { 44 | return await _repository.FindAllAsync(x => x.OwnerEmail.Equals(_userEmail)); 45 | } 46 | 47 | public Note GetNoteById(int id) 48 | { 49 | return _repository.Find(x => x.Id == id && x.OwnerEmail.Equals(_userEmail)); 50 | } 51 | 52 | public List GetNotesByCategory(string category) 53 | { 54 | return _repository.FindAll(x => x.Category.Equals(category) && x.OwnerEmail.Equals(_userEmail)); 55 | } 56 | 57 | public Note InsertNote(Note note) 58 | { 59 | return _repository.Insert(note); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Libraries/Services/Interfaces/IAuthenticatedUserService.cs: -------------------------------------------------------------------------------- 1 | namespace Services.Interfaces 2 | { 3 | public interface IAuthenticatedUserService 4 | { 5 | string UserEmail { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Libraries/Services/Interfaces/ILoginLogService.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Collections; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Services.Interfaces 6 | { 7 | public interface ILoginLogService 8 | { 9 | Task Add(LoginLog model); 10 | Task> Get(string email); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Libraries/Services/Interfaces/INoteService.cs: -------------------------------------------------------------------------------- 1 | using Models.DbEntities; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Services.Interfaces 6 | { 7 | public interface INoteService 8 | { 9 | Note InsertNote(Note note); 10 | Note GetNoteById(int id); 11 | List GetNotesByCategory(string category); 12 | List GetAllMyNotes(); 13 | Task> GetAllMyNotesAsync(); 14 | bool DeleteNote(int id); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Libraries/Services/Services.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Attributes/CachedAttribute.cs: -------------------------------------------------------------------------------- 1 | using Caching; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using System; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace WebApi.Attributes 12 | { 13 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 14 | public class CachedAttribute : Attribute, IAsyncActionFilter 15 | { 16 | private readonly int _timeToLiveSeconds; 17 | public CachedAttribute(int timeToLiveSeconds) 18 | { 19 | _timeToLiveSeconds = timeToLiveSeconds; 20 | } 21 | 22 | public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 23 | { 24 | var cacheService = context.HttpContext.RequestServices.GetRequiredService(); 25 | 26 | var cacheKey = GenerateCacheKeyFromRequest(context.HttpContext.Request); 27 | var cachedResponse = await cacheService.GetAsync(cacheKey); 28 | 29 | if (!string.IsNullOrEmpty(cachedResponse)) 30 | { 31 | var contentResult = new ContentResult 32 | { 33 | Content = cachedResponse, 34 | ContentType = "application/json", 35 | StatusCode = 200 36 | }; 37 | context.Result = contentResult; 38 | 39 | return; 40 | } 41 | 42 | var executedContext = await next(); // move to controller 43 | 44 | if (executedContext.Result is OkObjectResult okObjectResult) 45 | { 46 | await cacheService.SetAsync(cacheKey, okObjectResult.Value, _timeToLiveSeconds); 47 | } 48 | } 49 | 50 | private static string GenerateCacheKeyFromRequest(HttpRequest request) 51 | { 52 | var keyBuilder = new StringBuilder(); 53 | 54 | keyBuilder.Append($"{request.Path}"); 55 | 56 | foreach (var (key, value) in request.Query.OrderBy(x => x.Key)) 57 | { 58 | keyBuilder.Append($"|{key}-{value}"); 59 | } 60 | 61 | return keyBuilder.ToString(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using Data.Mongo.Collections; 2 | using Identity.Services.Interfaces; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Models.DTOs.Account; 6 | using Services.Interfaces; 7 | using System; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace WebApi.Controllers 12 | { 13 | [Route("api/[controller]")] 14 | [ApiController] 15 | public class AccountController : ControllerBase 16 | { 17 | private readonly IAccountService _accountService; 18 | private readonly ILoginLogService _loginLogService; 19 | public AccountController(IAccountService accountService, ILoginLogService loginLogService) 20 | { 21 | _accountService = accountService; 22 | _loginLogService = loginLogService; 23 | } 24 | 25 | [HttpPost("authenticate")] 26 | public async Task AuthenticateAsync(AuthenticationRequest request) 27 | { 28 | //auth 29 | var result = await _accountService.AuthenticateAsync(request); 30 | if (result.Errors == null || !result.Errors.Any()) 31 | { 32 | //mongo usage example 33 | LoginLog log = new LoginLog() 34 | { 35 | LoginTime = DateTime.Now, 36 | UserEmail = request.Email 37 | }; 38 | await _loginLogService.Add(log); 39 | } 40 | return Ok(result); 41 | } 42 | 43 | [HttpPost("register")] 44 | public async Task RegisterAsync(RegisterRequest request) 45 | { 46 | var uri = $"{Request.Scheme}://{Request.Host.Value}"; 47 | return Ok(await _accountService.RegisterAsync(request, uri)); 48 | } 49 | 50 | [HttpGet("confirm-email")] 51 | public async Task ConfirmEmailAsync([FromQuery] string userId, [FromQuery] string code) 52 | { 53 | return Ok(await _accountService.ConfirmEmailAsync(userId, code)); 54 | } 55 | 56 | [HttpPost("forgot-password")] 57 | public async Task ForgotPasswordAsync(ForgotPasswordRequest request) 58 | { 59 | var uri = $"{Request.Scheme}://{Request.Host.Value}"; 60 | await _accountService.ForgotPasswordAsync(request, uri); 61 | return Ok(); 62 | } 63 | 64 | [HttpPost("reset-password")] 65 | public async Task ResetPasswordAsync(ResetPasswordRequest request) 66 | { 67 | return Ok(await _accountService.ResetPasswordAsync(request)); 68 | } 69 | 70 | [HttpPost("refreshtoken")] 71 | public async Task RefreshTokenAsync(RefreshTokenRequest request) 72 | { 73 | return Ok(await _accountService.RefreshTokenAsync(request)); 74 | } 75 | 76 | [HttpGet("logout")] 77 | public async Task LogoutAsync(string userEmail) 78 | { 79 | return Ok(await _accountService.LogoutAsync(userEmail)); 80 | } 81 | 82 | private string GenerateIPAddress() 83 | { 84 | if (Request.Headers.ContainsKey("X-Forwarded-For")) 85 | return Request.Headers["X-Forwarded-For"]; 86 | else 87 | return HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Controllers/AdminController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Identity.Models; 3 | using Identity.Services.Interfaces; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Models.DTOs.Account; 7 | using Models.ResponseModels; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | using WebApi.Attributes; 12 | 13 | namespace WebApi.Controllers 14 | { 15 | [Authorize] 16 | [Route("api/[controller]")] 17 | [ApiController] 18 | public class AdminController : ControllerBase 19 | { 20 | private readonly IAccountService _accountService; 21 | private readonly IMapper _mapper; 22 | public AdminController(IAccountService accountService, IMapper mapper) 23 | { 24 | _accountService = accountService; 25 | _mapper = mapper; 26 | } 27 | 28 | [Cached(2)] 29 | [Authorize(Policy = "OnlyAdmins")] 30 | [HttpGet("alluser")] 31 | public async Task GetAllUser() 32 | { 33 | var userList = await _accountService.GetUsers(); 34 | var data = _mapper 35 | .Map, IReadOnlyList>(userList); 36 | 37 | return Ok(new BaseResponse>(data, $"User List")); 38 | } 39 | 40 | [Cached(1)] 41 | [Authorize(Roles = "SuperAdmin")] 42 | [HttpGet("alluserwithroles")] 43 | public async Task GetAllUserWithRoles() 44 | { 45 | var userList = await _accountService.GetUsers(); 46 | 47 | var result = userList.Select(x => new UserDto 48 | { 49 | Email = x.Email, 50 | UserName = x.UserName, 51 | FirstName = x.FirstName, 52 | LastName = x.LastName, 53 | Roles = x.UserRoles.ToList().Select(y => y.Role.Name.ToString()).ToList() 54 | }); 55 | 56 | return Ok(new BaseResponse>(result, $"User List")); 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Controllers/GraphQLController.cs: -------------------------------------------------------------------------------- 1 | using Core.Exceptions; 2 | using GraphQL; 3 | using GraphQL.Types; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Models.ResponseModels; 7 | using System; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | using WebApi.GraphQL; 11 | 12 | namespace WebApi.Controllers 13 | { 14 | [Route("api/[controller]")] 15 | [Authorize] 16 | public class GraphQLController : ControllerBase 17 | { 18 | private readonly IDocumentExecuter _documentExecuter; 19 | private readonly ISchema _schema; 20 | 21 | public GraphQLController(IDocumentExecuter documentExecuter, ISchema schema) 22 | { 23 | _documentExecuter = documentExecuter; 24 | _schema = schema; 25 | } 26 | 27 | [HttpPost] 28 | public async Task Post([FromBody] GraphQLQuery query) 29 | { 30 | if (query == null) 31 | { 32 | throw new Exception(nameof(query)); 33 | } 34 | try 35 | { 36 | var inputs = query.Variables.ToInputs(); 37 | var executionOptions = new ExecutionOptions 38 | { 39 | Schema = _schema, 40 | Query = query.Query, 41 | Inputs = inputs 42 | }; 43 | var result = await _documentExecuter 44 | .ExecuteAsync(executionOptions); 45 | 46 | if (result.Errors?.Count > 0) 47 | { 48 | return BadRequest(result); 49 | } 50 | return Ok(new BaseResponse(result.Data, "Graph result")); 51 | } 52 | catch (Exception ex) 53 | { 54 | throw new ApiException($"{ex.Message}") { StatusCode = (int)HttpStatusCode.InternalServerError }; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Controllers/LogController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Data.Mongo.Collections; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Models.DTOs.Log; 5 | using Models.ResponseModels; 6 | using Services.Interfaces; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace WebApi.Controllers 11 | { 12 | [Route("api/[controller]")] 13 | [ApiController] 14 | public class LogController : ControllerBase 15 | { 16 | private readonly ILoginLogService _loginLogService; 17 | private readonly IMapper _mapper; 18 | public LogController( ILoginLogService loginLogService, IMapper mapper) 19 | { 20 | _loginLogService = loginLogService; 21 | _mapper = mapper; 22 | } 23 | 24 | [HttpGet("get")] 25 | public async Task GetUserAuthLogs(string email) 26 | { 27 | var userList = await _loginLogService.Get(email); 28 | var data = _mapper 29 | .Map, IReadOnlyList>(userList); 30 | 31 | return Ok(new BaseResponse>(data, $"User Log List")); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Controllers/NoteController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AutoMapper; 3 | using Caching; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Models.DbEntities; 7 | using Models.ResponseModels; 8 | using Services.Interfaces; 9 | using WebApi.Attributes; 10 | 11 | namespace WebApi.Controllers; 12 | 13 | [Route("api/[controller]")] 14 | [ApiController] 15 | [Authorize] 16 | public class NoteController : ControllerBase 17 | { 18 | private readonly INoteService _noteService; 19 | private readonly IMapper _mapper; 20 | 21 | public NoteController(INoteService noteService, ICacheManager cacheManager, IMapper mapper) 22 | { 23 | _noteService = noteService; 24 | _mapper = mapper; 25 | } 26 | 27 | [Cached(2)] 28 | [HttpGet("allnotes")] 29 | public IActionResult GetAllNotes() 30 | { 31 | var notesList = _noteService.GetAllMyNotes(); 32 | return Ok(new BaseResponse>(notesList, "Notes retrieved successfully")); 33 | } 34 | 35 | [HttpPost("addnote")] 36 | public IActionResult AddNewNote([FromBody] Note note) 37 | { 38 | _noteService.InsertNote(note); 39 | return Ok(new BaseResponse("Note added successfully")); 40 | } 41 | 42 | 43 | } -------------------------------------------------------------------------------- /src/Presentations/WebApi/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 4 | USER app 5 | WORKDIR /app 6 | EXPOSE 8080 7 | EXPOSE 8081 8 | 9 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 10 | ARG BUILD_CONFIGURATION=Release 11 | WORKDIR /src 12 | COPY ["Presentations/WebApi/WebApi.csproj", "Presentations/WebApi/"] 13 | COPY ["Libraries/Caching/Caching.csproj", "Libraries/Caching/"] 14 | COPY ["Libraries/Models/Models.csproj", "Libraries/Models/"] 15 | COPY ["Libraries/Core/Core.csproj", "Libraries/Core/"] 16 | COPY ["Libraries/Data/Data.csproj", "Libraries/Data/"] 17 | COPY ["Libraries/Services/Services.csproj", "Libraries/Services/"] 18 | COPY ["Libraries/Data.Mongo/Data.Mongo.csproj", "Libraries/Data.Mongo/"] 19 | COPY ["Libraries/Identity/Identity.csproj", "Libraries/Identity/"] 20 | RUN dotnet restore "./Presentations/WebApi/WebApi.csproj" 21 | COPY . . 22 | WORKDIR "/src/Presentations/WebApi" 23 | RUN dotnet build "./WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/build 24 | 25 | FROM build AS publish 26 | ARG BUILD_CONFIGURATION=Release 27 | RUN dotnet publish "./WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 28 | 29 | FROM base AS final 30 | WORKDIR /app 31 | COPY --from=publish /app/publish . 32 | ENTRYPOINT ["dotnet", "WebApi.dll"] -------------------------------------------------------------------------------- /src/Presentations/WebApi/Extensions/AppExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using WebApi.Middlewares; 3 | 4 | namespace WebApi.Extensions 5 | { 6 | public static class AppExtensions 7 | { 8 | public static void UseErrorHandlingMiddleware(this IApplicationBuilder app) 9 | { 10 | app.UseMiddleware(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/GraphQLQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace WebApi.GraphQL 4 | { 5 | public class GraphQLQuery 6 | { 7 | public string OperationName { get; set; } 8 | public string NamedQuery { get; set; } 9 | public string Query { get; set; } 10 | public JObject Variables { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/Mutations/MyNoteMutation.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Models.DbEntities; 3 | using Services.Interfaces; 4 | using WebApi.GraphQL.Types.Note; 5 | 6 | namespace WebApi.GraphQL.Mutations 7 | { 8 | public class MyNoteMutation : ObjectGraphType 9 | { 10 | public MyNoteMutation(INoteService noteService, IAuthenticatedUserService authenticatedUserService) 11 | { 12 | Field( 13 | name: "createNote", 14 | arguments: new QueryArguments( 15 | new QueryArgument> { Name = "note" }), 16 | resolve: context => 17 | { 18 | Note note = context.GetArgument("note"); 19 | note.OwnerEmail = authenticatedUserService.UserEmail; 20 | return noteService.InsertNote(note); 21 | }); 22 | 23 | Field( 24 | name: "deleteNote", 25 | arguments: new QueryArguments( 26 | new QueryArgument> { Name = "id" }), 27 | resolve: context => 28 | { 29 | int id = context.GetArgument("id"); 30 | bool res = noteService.DeleteNote(id); 31 | if (res) 32 | return "Note deleted"; 33 | return "Try to delete again"; 34 | }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/MyNoteSchema.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using WebApi.GraphQL.Mutations; 4 | using WebApi.GraphQL.Queries; 5 | 6 | namespace WebApi.GraphQL 7 | { 8 | public class MyNoteSchema : Schema 9 | { 10 | public MyNoteSchema(IDependencyResolver resolver) : base(resolver) 11 | { 12 | Query = resolver.Resolve(); 13 | Mutation = resolver.Resolve(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/Queries/MyNoteQuery.cs: -------------------------------------------------------------------------------- 1 | using Caching; 2 | using GraphQL.Types; 3 | using Services.Interfaces; 4 | using WebApi.GraphQL.Types.Note; 5 | 6 | namespace WebApi.GraphQL.Queries 7 | { 8 | public class MyNoteQuery : ObjectGraphType 9 | { 10 | public MyNoteQuery(INoteService noteService, ICacheManager cacheManager, IAuthenticatedUserService authenticatedUserService) 11 | { 12 | Field>( 13 | "my_all_notes", 14 | resolve: context => 15 | { 16 | string cacheKey = $"my_all_notes_{authenticatedUserService.UserEmail}"; 17 | var data = cacheManager.Get(cacheKey, () => { return noteService.GetAllMyNotes(); }); 18 | return data; 19 | }); 20 | 21 | Field( 22 | "note_by_id", 23 | arguments: new QueryArguments(new QueryArgument { Name = "id" }), 24 | resolve: context => 25 | { 26 | int noteId = context.GetArgument("id"); 27 | return noteService.GetNoteById(noteId); 28 | }); 29 | 30 | Field>( 31 | "my_all_notes_by_category", 32 | arguments: new QueryArguments(new QueryArgument { Name = "category" }), 33 | resolve: context => 34 | { 35 | string category = context.GetArgument("category"); 36 | return noteService.GetNotesByCategory(category); 37 | }); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using WebApi.GraphQL.Mutations; 6 | using WebApi.GraphQL.Queries; 7 | using WebApi.GraphQL.Types.Note; 8 | 9 | namespace WebApi.GraphQL 10 | { 11 | public static class ServiceExtensions 12 | { 13 | public static void AddGraphQLServices(this IServiceCollection services, IConfiguration config) 14 | { 15 | services.AddScoped(); 16 | services.AddScoped(); 17 | services.AddScoped(); 18 | services.AddScoped(); 19 | services.AddScoped(); 20 | services.AddScoped(_ => new FuncDependencyResolver(_.GetRequiredService)); 21 | services.AddScoped(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/Types/Note/NoteInputType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace WebApi.GraphQL.Types.Note 8 | { 9 | public class NoteInputType : InputObjectGraphType 10 | { 11 | public NoteInputType() 12 | { 13 | Name = "noteInput"; 14 | Field("title"); 15 | Field>("category"); 16 | Field>("description"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/GraphQL/Types/Note/NoteType.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Services.Interfaces; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace WebApi.GraphQL.Types.Note 9 | { 10 | public class NoteType : ObjectGraphType 11 | { 12 | public NoteType() 13 | { 14 | Field(x => x.Id); 15 | Field(x => x.Title); 16 | Field(x => x.Category); 17 | Field(x => x.Description); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Helpers/IApiAssemblyMarker.cs: -------------------------------------------------------------------------------- 1 | namespace WebApi.Helpers; 2 | 3 | public interface IApiAssemblyMarker 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /src/Presentations/WebApi/Helpers/MappingProfiles.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Data.Mongo.Collections; 3 | using Identity.Models; 4 | using Models.DTOs.Account; 5 | using Models.DTOs.Log; 6 | 7 | namespace WebApi.Helpers 8 | { 9 | public class MappingProfiles : Profile 10 | { 11 | public MappingProfiles() 12 | { 13 | CreateMap() 14 | .ForMember(d => d.UserName, o => o.MapFrom(s => s.UserName)) 15 | .ForMember(d => d.FirstName, o => o.MapFrom(s => s.FirstName)) 16 | .ForMember(d => d.LastName, o => o.MapFrom(s => s.LastName)) 17 | .ForMember(d => d.Email, o => o.MapFrom(s => s.Email)); 18 | 19 | CreateMap() 20 | .ForMember(d => d.UserEmail, o => o.MapFrom(s => s.UserEmail)) 21 | .ForMember(d => d.LoginTime, o => o.MapFrom(s => s.LoginTime)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Middlewares/ErrorHandlerMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Core.Exceptions; 2 | using Microsoft.AspNetCore.Http; 3 | using Models.ResponseModels; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | 10 | namespace WebApi.Middlewares 11 | { 12 | public class ErrorHandlerMiddleware 13 | { 14 | private readonly RequestDelegate _next; 15 | 16 | public ErrorHandlerMiddleware(RequestDelegate next) 17 | { 18 | _next = next; 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | try 24 | { 25 | await _next(context); 26 | } 27 | catch (Exception error) 28 | { 29 | var response = context.Response; 30 | response.ContentType = "application/json"; 31 | var responseModel = new BaseResponse() { Succeeded = false, Message = error?.Message }; 32 | 33 | switch (error) 34 | { 35 | case ApiException e: 36 | // custom application error 37 | response.StatusCode = e.StatusCode; 38 | break; 39 | case KeyNotFoundException e: 40 | // not found error 41 | response.StatusCode = (int)HttpStatusCode.NotFound; 42 | break; 43 | default: 44 | // unhandled error 45 | response.StatusCode = (int)HttpStatusCode.InternalServerError; 46 | break; 47 | } 48 | var result = JsonSerializer.Serialize(responseModel); 49 | 50 | await response.WriteAsync(result); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Identity.Models; 2 | using Identity.Seeds; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | using Serilog; 10 | using Serilog.Events; 11 | using System; 12 | using System.Threading.Tasks; 13 | 14 | namespace WebApi 15 | { 16 | public class Program 17 | { 18 | public async static Task Main(string[] args) 19 | { 20 | var host = CreateHostBuilder(args).Build(); 21 | using (var scope = host.Services.CreateScope()) 22 | { 23 | var services = scope.ServiceProvider; 24 | var loggerFactory = services.GetRequiredService(); 25 | var configuration = services.GetRequiredService(); 26 | //-------------------------------------------------------------------------------- 27 | Log.Logger = new LoggerConfiguration() 28 | .Enrich.FromLogContext() 29 | .Enrich.WithProperty("Application", "WebApi") 30 | .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) 31 | .MinimumLevel.Override("System", LogEventLevel.Warning) 32 | // .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day) 33 | .WriteTo.Console() 34 | .WriteTo.Seq(configuration.GetSection("Logging:Seq:Url").Value) 35 | //.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("")) 36 | //{ 37 | // AutoRegisterTemplate = true, 38 | // OverwriteTemplate = true, 39 | // DetectElasticsearchVersion = true, 40 | // AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7, 41 | // NumberOfReplicas = 1, 42 | // IndexFormat = "serilog-application-{0:yyyy.MM.dd}", 43 | // NumberOfShards = 2, 44 | // RegisterTemplateFailure = RegisterTemplateRecovery.FailSink, 45 | // FailureCallback = e => Console.WriteLine("Unable to submit event " + e.MessageTemplate), 46 | // EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog | 47 | // EmitEventFailureHandling.WriteToFailureSink | 48 | // EmitEventFailureHandling.RaiseCallback 49 | 50 | //}) 51 | .MinimumLevel.Verbose() 52 | .CreateLogger(); 53 | 54 | //-------------------------------------------------------------------------------- 55 | try 56 | { 57 | var userManager = services.GetRequiredService>(); 58 | var roleManager = services.GetRequiredService>(); 59 | await DefaultRoles.SeedAsync(roleManager); 60 | await DefaultSuperAdmin.SeedAsync(userManager); 61 | } 62 | catch { } 63 | finally { } 64 | } 65 | try 66 | { 67 | Log.Information("Application Start"); 68 | host.Run(); 69 | } 70 | catch (Exception ex) 71 | { 72 | 73 | Log.Fatal(ex, "Application Start-up Failed"); 74 | } 75 | 76 | 77 | 78 | } 79 | 80 | public static IHostBuilder CreateHostBuilder(string[] args) => 81 | Host.CreateDefaultBuilder(args) 82 | .ConfigureWebHostDefaults(webBuilder => 83 | { 84 | webBuilder.UseStartup(); 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "IIS Express": { 4 | "commandName": "IISExpress", 5 | "launchBrowser": true, 6 | "launchUrl": "swagger", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | }, 11 | "WebApi": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "launchUrl": "swagger", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | }, 18 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 19 | }, 20 | "Container (Dockerfile)": { 21 | "commandName": "Docker", 22 | "launchBrowser": true, 23 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_HTTPS_PORTS": "8081", 26 | "ASPNETCORE_HTTP_PORTS": "8080" 27 | }, 28 | "publishAllPorts": true, 29 | "useSSL": true 30 | } 31 | }, 32 | "$schema": "http://json.schemastore.org/launchsettings.json", 33 | "iisSettings": { 34 | "windowsAuthentication": false, 35 | "anonymousAuthentication": true, 36 | "iisExpress": { 37 | "applicationUrl": "http://localhost:61803", 38 | "sslPort": 44372 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Presentations/WebApi/Services/AuthenticatedUserService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Services.Interfaces; 3 | using System.Security.Claims; 4 | 5 | namespace WebApi.Services 6 | { 7 | public class AuthenticatedUserService : IAuthenticatedUserService 8 | { 9 | public AuthenticatedUserService(IHttpContextAccessor httpContextAccessor) 10 | { 11 | UserEmail = httpContextAccessor.HttpContext?.User?.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"); 12 | } 13 | 14 | public string UserEmail { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Caching; 2 | using Core; 3 | using Data.Mongo; 4 | using GraphiQl; 5 | using HealthChecks.UI.Client; 6 | using Identity; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Diagnostics.HealthChecks; 13 | using Microsoft.Extensions.Hosting; 14 | using Microsoft.Extensions.Logging; 15 | using Serilog; 16 | using Services.Interfaces; 17 | using WebApi.Extensions; 18 | using WebApi.GraphQL; 19 | using WebApi.Helpers; 20 | using WebApi.Services; 21 | 22 | namespace WebApi 23 | { 24 | public class Startup 25 | { 26 | public Startup(IConfiguration configuration) 27 | { 28 | Configuration = configuration; 29 | } 30 | 31 | public IConfiguration Configuration { get; } 32 | 33 | // This method gets called by the runtime. Use this method to add services to the container. 34 | public void ConfigureServices(IServiceCollection services) 35 | { 36 | services.AddMongo(Configuration); 37 | services.AddLogging(o => o.AddSerilog()); 38 | services.AddIdentity(Configuration); 39 | services.AddSharedServices(Configuration); 40 | services.AddApplicationSqlServer(Configuration); 41 | services.AddRepoServices(Configuration); 42 | services.AddAppServices(Configuration); 43 | services.AddGraphQLServices(Configuration); 44 | services.AddRedis(Configuration); 45 | services.AddScoped(); 46 | services.AddAutoMapper(typeof(MappingProfiles)); 47 | services.AddCustomSwagger(Configuration); 48 | 49 | services.AddControllers(); 50 | //.AddNewtonsoftJson(options => 51 | //options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore 52 | //); 53 | 54 | services.AddHealthChecks() 55 | 56 | .AddRedis(Configuration.GetSection("RedisSettings:RedisConnectionString").Value, 57 | name: "RedisHealt-check", 58 | failureStatus: HealthStatus.Unhealthy, 59 | tags: new string[] { "api", "Redis" }) 60 | 61 | .AddSqlServer(Configuration.GetConnectionString("IdentityConnection"), 62 | name: "identityDb-check", 63 | failureStatus: HealthStatus.Unhealthy, 64 | tags: new string[] { "api", "SqlDb" }) 65 | 66 | .AddSqlServer(Configuration.GetConnectionString("DefaultConnection"), 67 | name: "applicationDb-check", 68 | failureStatus: HealthStatus.Unhealthy, 69 | tags: new string[] { "api", "SqlDb" }); 70 | 71 | 72 | services.AddAuthorization(options => 73 | { 74 | options.AddPolicy("OnlyAdmins", policy => policy.RequireRole("SuperAdmin", "Admin")); 75 | }); 76 | 77 | } 78 | 79 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 80 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 81 | { 82 | if (env.IsDevelopment()) 83 | { 84 | app.UseDeveloperExceptionPage(); 85 | } 86 | 87 | //app.UseSerilogRequestLogging(); 88 | loggerFactory.AddSerilog(); 89 | 90 | app.UseHttpsRedirection(); 91 | 92 | app.UseRouting(); 93 | app.UseGraphiQl(); 94 | app.UseAuthentication(); 95 | app.UseAuthorization(); 96 | 97 | //error middleware 98 | app.UseErrorHandlingMiddleware(); 99 | 100 | app.UseSwagger(); 101 | app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Service Api V1"); }); 102 | 103 | app.UseEndpoints(endpoints => 104 | { 105 | endpoints.MapControllers(); 106 | endpoints.MapHealthChecks("/health", new HealthCheckOptions() 107 | { 108 | Predicate = _ => true, 109 | ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse 110 | }); 111 | }); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/WebApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 603ea6aa-0a0a-4254-a847-85a3b447d0fb 5 | Linux 6 | ..\.. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Presentations/WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | }, 8 | "Seq": { 9 | "Url": "http://localhost:5341" 10 | } 11 | }, 12 | "ConnectionStrings": { 13 | "DefaultConnection": "Data Source=db;Initial Catalog=ApplicationDb;User Id=sa;Password=Password123*;TrustServerCertificate=True", 14 | "IdentityConnection": "Data Source=db;Initial Catalog=IdentityDb;User Id=sa;Password=Password123*;TrustServerCertificate=True" 15 | }, 16 | "JWTSettings": { 17 | "Key": "sinan_tok_graphql_webapi_identiy13", 18 | "Issuer": "CoreIdentity", 19 | "Audience": "CoreIdentityUser", 20 | "DurationInMinutes": 130 21 | }, 22 | "MailSettings": { 23 | "EmailFrom": "sinantok@outlook.com.com", 24 | "SmtpHost": "smtp.ethereal.email", 25 | "SmtpPort": 587, 26 | "SmtpUser": "julie.hansen@ethereal.email", 27 | "SmtpPass": "rcveN5f77wZjgxNx5v", 28 | "DisplayName": "Sinan Tok" 29 | }, 30 | "RedisSettings": { 31 | "RedisDataProtectionKey": "", 32 | "CacheTime": "5", 33 | "RedisConnectionString": "redis:6379", 34 | "RedisDatabaseId": "" 35 | }, 36 | "MongoDb": { 37 | "ConnectionString": "mongodb://mongodb:27017", 38 | "DataBase": "DevLog" 39 | }, 40 | "AllowedHosts": "*" 41 | } -------------------------------------------------------------------------------- /src/Template.WebApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30225.117 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{31F7550D-120E-4361-B905-ADDA0539EDF7}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{6ED22C80-F37F-409C-B307-FE457E95DE58}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentations", "Presentations", "{97B0F4E2-C596-4450-8EB0-959AC0930C28}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Libraries\Data\Data.csproj", "{B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Services", "Libraries\Services\Services.csproj", "{C68F7E2D-653D-4D74-8DB9-580CA2CE878E}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Libraries\Models\Models.csproj", "{61F060ED-F5FD-4A36-A0BE-9E29F21A46D5}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "Presentations\WebApi\WebApi.csproj", "{38112081-5469-48CC-8B1C-195F59FFC24C}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity", "Libraries\Identity\Identity.csproj", "{35A449C0-D3C1-4A9C-AA0D-7FA767B245F3}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Libraries\Core\Core.csproj", "{83DCD3AE-695F-4362-92DC-E7A43A3ECB5E}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Caching", "Libraries\Caching\Caching.csproj", "{C0A5E261-4D9F-410E-A2C6-112CD9D874C9}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data.Mongo", "Libraries\Data.Mongo\Data.Mongo.csproj", "{730AC8DF-924A-49FA-ADCA-E00679DD0930}" 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AF09108-D53F-4C9B-B590-B2797800FAAA}" 29 | ProjectSection(SolutionItems) = preProject 30 | docker-compose.yml = docker-compose.yml 31 | EndProjectSection 32 | EndProject 33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{704EBC72-5E6C-45D5-93A8-857290FF5593}" 34 | EndProject 35 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "Tests\UnitTests\UnitTests.csproj", "{A6A765E5-CD0F-4E0B-B802-236427B13D26}" 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Release|Any CPU = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 43 | {B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {C68F7E2D-653D-4D74-8DB9-580CA2CE878E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {C68F7E2D-653D-4D74-8DB9-580CA2CE878E}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {C68F7E2D-653D-4D74-8DB9-580CA2CE878E}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {C68F7E2D-653D-4D74-8DB9-580CA2CE878E}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {61F060ED-F5FD-4A36-A0BE-9E29F21A46D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {61F060ED-F5FD-4A36-A0BE-9E29F21A46D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {61F060ED-F5FD-4A36-A0BE-9E29F21A46D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {61F060ED-F5FD-4A36-A0BE-9E29F21A46D5}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {38112081-5469-48CC-8B1C-195F59FFC24C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {38112081-5469-48CC-8B1C-195F59FFC24C}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {38112081-5469-48CC-8B1C-195F59FFC24C}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {38112081-5469-48CC-8B1C-195F59FFC24C}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {35A449C0-D3C1-4A9C-AA0D-7FA767B245F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {35A449C0-D3C1-4A9C-AA0D-7FA767B245F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {35A449C0-D3C1-4A9C-AA0D-7FA767B245F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {35A449C0-D3C1-4A9C-AA0D-7FA767B245F3}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {83DCD3AE-695F-4362-92DC-E7A43A3ECB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {83DCD3AE-695F-4362-92DC-E7A43A3ECB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {83DCD3AE-695F-4362-92DC-E7A43A3ECB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {83DCD3AE-695F-4362-92DC-E7A43A3ECB5E}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {C0A5E261-4D9F-410E-A2C6-112CD9D874C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {C0A5E261-4D9F-410E-A2C6-112CD9D874C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {C0A5E261-4D9F-410E-A2C6-112CD9D874C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {C0A5E261-4D9F-410E-A2C6-112CD9D874C9}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {730AC8DF-924A-49FA-ADCA-E00679DD0930}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {730AC8DF-924A-49FA-ADCA-E00679DD0930}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {730AC8DF-924A-49FA-ADCA-E00679DD0930}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {730AC8DF-924A-49FA-ADCA-E00679DD0930}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {A6A765E5-CD0F-4E0B-B802-236427B13D26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 76 | {A6A765E5-CD0F-4E0B-B802-236427B13D26}.Debug|Any CPU.Build.0 = Debug|Any CPU 77 | {A6A765E5-CD0F-4E0B-B802-236427B13D26}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {A6A765E5-CD0F-4E0B-B802-236427B13D26}.Release|Any CPU.Build.0 = Release|Any CPU 79 | EndGlobalSection 80 | GlobalSection(SolutionProperties) = preSolution 81 | HideSolutionNode = FALSE 82 | EndGlobalSection 83 | GlobalSection(NestedProjects) = preSolution 84 | {6ED22C80-F37F-409C-B307-FE457E95DE58} = {31F7550D-120E-4361-B905-ADDA0539EDF7} 85 | {97B0F4E2-C596-4450-8EB0-959AC0930C28} = {31F7550D-120E-4361-B905-ADDA0539EDF7} 86 | {B3DC48BE-3D33-4DF1-B8F0-9D3791FBC4F5} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 87 | {C68F7E2D-653D-4D74-8DB9-580CA2CE878E} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 88 | {61F060ED-F5FD-4A36-A0BE-9E29F21A46D5} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 89 | {38112081-5469-48CC-8B1C-195F59FFC24C} = {97B0F4E2-C596-4450-8EB0-959AC0930C28} 90 | {35A449C0-D3C1-4A9C-AA0D-7FA767B245F3} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 91 | {83DCD3AE-695F-4362-92DC-E7A43A3ECB5E} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 92 | {C0A5E261-4D9F-410E-A2C6-112CD9D874C9} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 93 | {730AC8DF-924A-49FA-ADCA-E00679DD0930} = {6ED22C80-F37F-409C-B307-FE457E95DE58} 94 | {A6A765E5-CD0F-4E0B-B802-236427B13D26} = {704EBC72-5E6C-45D5-93A8-857290FF5593} 95 | EndGlobalSection 96 | GlobalSection(ExtensibilityGlobals) = postSolution 97 | SolutionGuid = {9357748C-EABB-403D-A2D8-E745B257A0F0} 98 | EndGlobalSection 99 | EndGlobal 100 | -------------------------------------------------------------------------------- /src/Tests/UnitTests/NoteTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http.Json; 3 | using Models.DbEntities; 4 | using Models.ResponseModels; 5 | using Newtonsoft.Json; 6 | using UnitTests.core; 7 | using UnitTests.core.Enums; 8 | 9 | namespace UnitTests; 10 | 11 | public class NoteTest 12 | { 13 | private readonly HttpClient _httpClient; 14 | 15 | public NoteTest() 16 | { 17 | var api = new ApiFactory(TypeControllerTesting.Note); 18 | _httpClient = api.GetClientWithAuthenticated(); 19 | } 20 | 21 | [Fact] 22 | public async void Should_return_200_ok_when_fetch_all_notes() 23 | { 24 | var response = await _httpClient.GetAsync("allnotes"); 25 | 26 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 27 | } 28 | 29 | [Fact] 30 | public async void Should_return_zero_notes_when_fetch_all_notes() 31 | { 32 | var response = await _httpClient.GetAsync("allnotes"); 33 | var content = await response.Content.ReadAsStringAsync(); 34 | var notes = JsonConvert.DeserializeObject>>(content); 35 | 36 | Assert.Empty(notes.Data); 37 | } 38 | 39 | [Fact] 40 | public async void Should_return_ok_when_add_new_note() 41 | { 42 | var response = await _httpClient.PostAsJsonAsync("addnote", new Note 43 | { 44 | Title = "Test Note", 45 | Description = "Test Note Content", 46 | Category = "Test Category" 47 | }); 48 | 49 | var content = await response.Content.ReadAsStringAsync(); 50 | 51 | Assert.Contains("Note added successfully", content); 52 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 53 | } 54 | } -------------------------------------------------------------------------------- /src/Tests/UnitTests/UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Tests/UnitTests/core/ApiFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.Net.Http.Json; 3 | using Data.Contexts; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc.Testing; 6 | using Microsoft.AspNetCore.TestHost; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Models.ResponseModels; 10 | using Newtonsoft.Json; 11 | using UnitTests.core.Enums; 12 | using WebApi.Helpers; 13 | 14 | namespace UnitTests.core; 15 | 16 | public class ApiFactory(TypeControllerTesting controller) : WebApplicationFactory 17 | { 18 | private readonly string _controller = controller.ToString(); 19 | 20 | protected override void ConfigureWebHost(IWebHostBuilder builder) 21 | { 22 | builder.ConfigureTestServices( 23 | services => 24 | { 25 | var dbContextDescriptor = services.SingleOrDefault( 26 | d => d.ServiceType == typeof(DbContextOptions) 27 | ); 28 | 29 | services.Remove(dbContextDescriptor); 30 | 31 | var dbConnectionDescriptor = services.SingleOrDefault( 32 | d => d.ServiceType == typeof(DbConnection)); 33 | 34 | services.Remove(dbConnectionDescriptor); 35 | 36 | services.AddDbContext(options => 37 | { 38 | options.UseInMemoryDatabase("ApplicationDb"); 39 | }); 40 | }); 41 | 42 | builder.UseEnvironment("Development"); 43 | } 44 | 45 | public HttpClient GetClient() 46 | { 47 | var httpClient = CreateClient(); 48 | httpClient.BaseAddress = new Uri($"http://localhost/api/{_controller}/"); 49 | 50 | return httpClient; 51 | } 52 | 53 | public HttpClient GetClientWithAuthenticated() 54 | { 55 | var httpClient = CreateClient(); 56 | 57 | var response = CreateClient().PostAsJsonAsync("/api/Account/authenticate", new 58 | { 59 | email = "superadmin@gmail.com", 60 | password = "123Pa$$word!" 61 | }); 62 | 63 | var result = response.Result.Content.ReadAsStringAsync().Result; 64 | var token = JsonConvert.DeserializeObject>(result)?.Data.JWToken; 65 | 66 | httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); 67 | httpClient.BaseAddress = new Uri($"http://localhost/api/{_controller}/"); 68 | 69 | return httpClient; 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /src/Tests/UnitTests/core/Enums/TypeControllerTesting.cs: -------------------------------------------------------------------------------- 1 | namespace UnitTests.core.Enums; 2 | 3 | public enum TypeControllerTesting 4 | { 5 | Account, 6 | Note 7 | } -------------------------------------------------------------------------------- /src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "1.0" 2 | services: 3 | webapi: 4 | image: webapi 5 | build: 6 | context: . 7 | dockerfile: Presentations/WebApi/Dockerfile 8 | environment: 9 | - ASPNETCORE_ENVIRONMENT=Development 10 | - ASPNETCORE_HTTP_PORT=8080 11 | - ASPNETCORE_HTTP_PORT=8081 12 | ports: 13 | - 8080:8080 14 | - 8081:8081 15 | depends_on: 16 | - db 17 | - mongodb 18 | - redis 19 | 20 | db: 21 | image: "mcr.microsoft.com/mssql/server:2022-latest" 22 | environment: 23 | SA_PASSWORD: "Password123*" 24 | ACCEPT_EULA: "Y" 25 | ports: 26 | - "1433:1433" 27 | 28 | mongodb: 29 | image: "mongo:latest" 30 | ports: 31 | - "27017:27017" 32 | 33 | redis: 34 | image: "redis" 35 | ports: 36 | - "6379:6379" 37 | volumes: 38 | - ./db-data/redis:/data --------------------------------------------------------------------------------