diff --git a/Projects/Config/WebApi.AuthApi.Config.json b/Projects/Config/WebApi.AuthApi.Config.json new file mode 100644 index 0000000..d1ab4d3 --- /dev/null +++ b/Projects/Config/WebApi.AuthApi.Config.json @@ -0,0 +1,35 @@ +{ + "Server": { + "Address": "https://*", + "Port": 11000, + "IIS": false + }, + "Auth": { + "issuer": "SystemX.WebApi.Auth", + "audience": "AuthApi", + "accessTokenSecret": "t6zdogyrT0U1bYw3gJvMm3JHmj2Iyawr7O2WKE2truX+MK0l/XNGmpU2ofagdUWBN4DxAUv7c8xSYVv/8abL6A==", + "accessTokenExpires": 1440, //minutes + "refreshTokenSecret": "1vVuoGqIqkStFI3QUXHMr0/yO1feLPnhqcfFGjZyk478+4WY7dhrUjCfVeWjmmSZYgb+rtP0X6ec+3iL35Yezw==", + "refreshTokenExpires": 1440 //minuts, 60*24 (1day) + }, + "DataBase": [ + { + "IP": "127.0.0.1", + "Port": 1433, + "DBName": "AccountDB", + "DBID": 1, + "DBContext": "AccountDB", + "UserID": "SystemX", + "Password": "X" + }, + { + "IP": "127.0.0.1", + "Port": 1433, + "DBName": "AccountDB_DEV", + "DBID": 2, + "DBContext": "AccountDB", + "UserID": "SystemX", + "Password": "X" + } + ] +} \ No newline at end of file diff --git a/Projects/Config/log4net.config b/Projects/Config/log4net.config new file mode 100644 index 0000000..b970851 --- /dev/null +++ b/Projects/Config/log4net.config @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Projects/DLL/SystemX.Core.dll b/Projects/DLL/SystemX.Core.dll index 44c0858..5ab830f 100644 Binary files a/Projects/DLL/SystemX.Core.dll and b/Projects/DLL/SystemX.Core.dll differ diff --git a/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac b/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac index 86a3ba6..56825d6 100644 Binary files a/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac and b/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac differ diff --git a/Projects/HubX/HubX.Library.DB/DB/HubX/Context/HubXContext.cs b/Projects/HubX/HubX.Library.DB/DB/HubX/Context/HubXContext.cs index a125fdb..488d700 100644 --- a/Projects/HubX/HubX.Library.DB/DB/HubX/Context/HubXContext.cs +++ b/Projects/HubX/HubX.Library.DB/DB/HubX/Context/HubXContext.cs @@ -19,7 +19,7 @@ public partial class HubXContext : DbContext protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. - => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=VPKI; password=Kefico!@34; database=HubX; TrustServerCertificate=true;"); + => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=alis; password=Kefico!@34; database=HubX; TrustServerCertificate=true;"); protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/Projects/HubX/HubX.Server/Controllers/UniqueKeyController.cs b/Projects/HubX/HubX.Server/Controllers/UniqueKeyController.cs index 2168f24..6e8bf6e 100644 --- a/Projects/HubX/HubX.Server/Controllers/UniqueKeyController.cs +++ b/Projects/HubX/HubX.Server/Controllers/UniqueKeyController.cs @@ -1,4 +1,5 @@ -using HubX.Library.Http.Packet; +using Azure.Core; +using HubX.Library.Http.Packet; using HubX.Server.Services; using Microsoft.AspNetCore.Mvc; @@ -47,6 +48,30 @@ namespace HubX.Server.Controllers return Results.Ok(res); } + [HttpGet] + public async Task SelectUniqueKeyGet([FromQuery] string key) + { + var guid = Guid.NewGuid(); + // Log4net.WriteLine($"[Requeust]({guid}) UniqueKey/SelectUniqueKey::{request.ToJson()}", LogType.CONTROLLER); + + Response_SelectUniqueKy res = await _uniqueKeyService.Request_SelectUniqueKey(new Request_SelectUniqueKey { Identity = key } ); + // Log4net.WriteLine($"[Response]({guid}) UniqueKey/SelectUniqueKey::{res.ToJson()}", LogType.CONTROLLER); + + return Results.Ok(res); + } + + [HttpGet] + public async Task SelectUniqueKeyGetAll() + { + var guid = Guid.NewGuid(); + // Log4net.WriteLine($"[Requeust]({guid}) UniqueKey/SelectUniqueKey::{request.ToJson()}", LogType.CONTROLLER); + + var res = await _uniqueKeyService.Request_SelectUniqueKeyAll(); + // Log4net.WriteLine($"[Response]({guid}) UniqueKey/SelectUniqueKey::{res.ToJson()}", LogType.CONTROLLER); + + return Results.Ok(res); + } + [HttpPost] public async Task UpdateUniqueKey(Request_UpdateUniqueKey request) { diff --git a/Projects/HubX/HubX.Server/Services/UniqueKeyService.cs b/Projects/HubX/HubX.Server/Services/UniqueKeyService.cs index 7b05f6c..36b7d2b 100644 --- a/Projects/HubX/HubX.Server/Services/UniqueKeyService.cs +++ b/Projects/HubX/HubX.Server/Services/UniqueKeyService.cs @@ -4,6 +4,7 @@ using HubX.Library.Http.Packet; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Client.Extensions.Msal; using System; +using System.Collections.Generic; using System.Data; using System.Xml; using SystemX.Core.DB; @@ -116,6 +117,33 @@ namespace HubX.Server.Services return response; } + public async Task> Request_SelectUniqueKeyAll(string guid = "") + { + List result = new List(); + using (var scope = _scopeFactory.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + if (context != null) + { + try + { + using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted)) + { + result = await context.TStorages.AsNoTracking().ToListAsync(); + await context.CloseTransactionAsync(transaction); + } + } + catch (Exception e) + { + Log4net.WriteLine($"Select Unique Key Transaction Error::{guid}", LogType.Error); + Log4net.WriteLine(e); + } + } + } + + return result; + } + public async Task Request_UpdateUniqueKey(Request_UpdateUniqueKey request, string guid = "") { Response_UpdateUniqueKy response = new Response_UpdateUniqueKy(); diff --git a/Projects/SystemX.Core/DBPatch/CreateAccountDB.bat b/Projects/SystemX.Core/DBPatch/CreateAccountDB.bat new file mode 100644 index 0000000..d2c77f5 --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/CreateAccountDB.bat @@ -0,0 +1,18 @@ +@echo off +::log +IF NOT EXIST .\logs mkdir logs + +::서버연결정보 +SET ServerIP=127.0.0.1 +SET ServerPort=1433 + +::DB 정보 +SET UserID=SystemX +SET Passwd=X +SET DBName=AccountDB + +::Default DB +@echo off +CD .\sqlScripts\ +CALL _CreateScript.bat %ServerIP% %ServerPort% %UserID% %Passwd% %DBName% +CALL _CreateScript.bat %ServerIP% %ServerPort% %UserID% %Passwd% %DBName%_DEV \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/UpdateAccountDB.bat b/Projects/SystemX.Core/DBPatch/UpdateAccountDB.bat new file mode 100644 index 0000000..0d0b8db --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/UpdateAccountDB.bat @@ -0,0 +1,25 @@ +@echo off +::log +IF NOT EXIST .\logs mkdir logs + +::서버연결정보 +SET ServerIP=127.0.0.1 +SET ServerPort=1433 + +::DB 정보 +SET UserID=SystemX +SET Passwd=X +SET DBName=AccountDB + +::Update script 정보 +SET Dacpac=.\dacpac\SystemX.DB.AccountDB.dacpac +SET OUTPUT=SystemX.DB.AccountDB_Update.sql + +@echo off +::generate update script +CD .\sqlScripts\ +CALL _UpdateScriptGenerate.bat %ServerIP% %ServerPort% %UserID% %Passwd% %DBName% %Dacpac% %OUTPUT% + +::Default DB +CALL _UpdateAccountDB.bat %ServerIP% %ServerPort% %UserID% %Passwd% %DBName% +CALL _UpdateAccountDB.bat %ServerIP% %ServerPort% %UserID% %Passwd% %DBName%_DEV \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/_CreateSqlServerAccount_관리자권한으로실행.bat b/Projects/SystemX.Core/DBPatch/_CreateSqlServerAccount_관리자권한으로실행.bat new file mode 100644 index 0000000..c6c8430 --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/_CreateSqlServerAccount_관리자권한으로실행.bat @@ -0,0 +1,19 @@ +@echo Create Admin Account Start +@echo off + +SET SqlCmdOption=-E -C +sqlcmd %SqlCmdOption% -i %~dp0\sqlScripts\AdminAccount_Create.sql +if errorlevel 1 goto errexit +goto end +:errexit +echo DB Patch Fail +goto end +:end +@echo on + +@echo Create Admin Account End + +net stop /y MSSQLSERVER +net start /y MSSQLSERVER + +pause \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/AdminAccount_Create.sql b/Projects/SystemX.Core/DBPatch/sqlScripts/AdminAccount_Create.sql new file mode 100644 index 0000000..744268c --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/sqlScripts/AdminAccount_Create.sql @@ -0,0 +1,43 @@ +USE [master] +GO + +CREATE LOGIN [SystemX] WITH PASSWORD=N'X', DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[English], CHECK_POLICY=ON +GO + +ALTER LOGIN [SystemX] ENABLE +GO + +ALTER SERVER ROLE [sysadmin] ADD MEMBER [SystemX] +GO + +ALTER SERVER ROLE [securityadmin] ADD MEMBER [SystemX] +GO + +ALTER SERVER ROLE [serveradmin] ADD MEMBER [SystemX] +GO + +ALTER SERVER ROLE [setupadmin] ADD MEMBER [SystemX] +GO + + + +USE [master] +GO + +CREATE LOGIN [Alis] WITH PASSWORD=N'Kefico!@34', DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[English], CHECK_POLICY=ON +GO + +ALTER LOGIN [Alis] ENABLE +GO + +ALTER SERVER ROLE [sysadmin] ADD MEMBER [Alis] +GO + +ALTER SERVER ROLE [securityadmin] ADD MEMBER [Alis] +GO + +ALTER SERVER ROLE [serveradmin] ADD MEMBER [Alis] +GO + +ALTER SERVER ROLE [setupadmin] ADD MEMBER [Alis] +GO \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/SystemX.DB.AccountDB_Create.sql b/Projects/SystemX.Core/DBPatch/sqlScripts/SystemX.DB.AccountDB_Create.sql new file mode 100644 index 0000000..72848a5 --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/sqlScripts/SystemX.DB.AccountDB_Create.sql @@ -0,0 +1,346 @@ +/* +SystemX.DB.AccountDB의 배포 스크립트 + +이 코드는 도구를 사용하여 생성되었습니다. +파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +변경 내용이 손실됩니다. +*/ + +GO +SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; + +SET NUMERIC_ROUNDABORT OFF; + + +GO +/* +:setvar DatabaseName "SystemX.DB.AccountDB" +:setvar DefaultFilePrefix "SystemX.DB.AccountDB" +:setvar DefaultDataPath "" +:setvar DefaultLogPath "" +*/ + +GO +:on error exit +GO +/* +SQLCMD 모드가 지원되지 않으면 SQLCMD 모드를 검색하고 스크립트를 실행하지 않습니다. +SQLCMD 모드를 설정한 후에 이 스크립트를 다시 사용하려면 다음을 실행합니다. +SET NOEXEC OFF; +*/ +:setvar __IsSqlCmdEnabled "True" +GO +IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True' + BEGIN + PRINT N'이 스크립트를 실행하려면 SQLCMD 모드를 사용하도록 설정해야 합니다.'; + SET NOEXEC ON; + END + + +GO +USE [master]; + + +GO + +IF (DB_ID(N'$(DatabaseName)') IS NOT NULL) +BEGIN + ALTER DATABASE [$(DatabaseName)] + SET SINGLE_USER WITH ROLLBACK IMMEDIATE; + DROP DATABASE [$(DatabaseName)]; +END + +GO +PRINT N'$(DatabaseName) 데이터베이스를 만드는 중...' +GO +CREATE DATABASE [$(DatabaseName)] COLLATE Korean_Wansung_CI_AS +GO +USE [$(DatabaseName)]; + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET ANSI_NULLS ON, + ANSI_PADDING ON, + ANSI_WARNINGS ON, + ARITHABORT ON, + CONCAT_NULL_YIELDS_NULL ON, + NUMERIC_ROUNDABORT OFF, + QUOTED_IDENTIFIER ON, + ANSI_NULL_DEFAULT ON, + CURSOR_DEFAULT LOCAL, + RECOVERY FULL, + CURSOR_CLOSE_ON_COMMIT OFF, + AUTO_CREATE_STATISTICS ON, + AUTO_SHRINK OFF, + AUTO_UPDATE_STATISTICS ON, + RECURSIVE_TRIGGERS OFF + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET ALLOW_SNAPSHOT_ISOLATION OFF; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET READ_COMMITTED_SNAPSHOT OFF + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET AUTO_UPDATE_STATISTICS_ASYNC OFF, + PAGE_VERIFY NONE, + DATE_CORRELATION_OPTIMIZATION OFF, + DISABLE_BROKER, + PARAMETERIZATION SIMPLE, + SUPPLEMENTAL_LOGGING OFF + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF IS_SRVROLEMEMBER(N'sysadmin') = 1 + BEGIN + IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + EXECUTE sp_executesql N'ALTER DATABASE [$(DatabaseName)] + SET TRUSTWORTHY OFF, + DB_CHAINING OFF + WITH ROLLBACK IMMEDIATE'; + END + END +ELSE + BEGIN + PRINT N'데이터베이스 설정을 수정할 수 없습니다. 이러한 설정을 적용하려면 SysAdmin이어야 합니다.'; + END + + +GO +IF IS_SRVROLEMEMBER(N'sysadmin') = 1 + BEGIN + IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + EXECUTE sp_executesql N'ALTER DATABASE [$(DatabaseName)] + SET HONOR_BROKER_PRIORITY OFF + WITH ROLLBACK IMMEDIATE'; + END + END +ELSE + BEGIN + PRINT N'데이터베이스 설정을 수정할 수 없습니다. 이러한 설정을 적용하려면 SysAdmin이어야 합니다.'; + END + + +GO +ALTER DATABASE [$(DatabaseName)] + SET TARGET_RECOVERY_TIME = 0 SECONDS + WITH ROLLBACK IMMEDIATE; + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET FILESTREAM(NON_TRANSACTED_ACCESS = OFF), + CONTAINMENT = NONE + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET AUTO_CREATE_STATISTICS ON(INCREMENTAL = OFF), + MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = OFF, + DELAYED_DURABILITY = DISABLED + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET QUERY_STORE (QUERY_CAPTURE_MODE = ALL, DATA_FLUSH_INTERVAL_SECONDS = 900, INTERVAL_LENGTH_MINUTES = 60, MAX_PLANS_PER_QUERY = 200, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 367), MAX_STORAGE_SIZE_MB = 100) + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET QUERY_STORE = OFF + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0; + ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET MAXDOP = PRIMARY; + ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = OFF; + ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET LEGACY_CARDINALITY_ESTIMATION = PRIMARY; + ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = ON; + ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET PARAMETER_SNIFFING = PRIMARY; + ALTER DATABASE SCOPED CONFIGURATION SET QUERY_OPTIMIZER_HOTFIXES = OFF; + ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET QUERY_OPTIMIZER_HOTFIXES = PRIMARY; + END + + +GO +IF EXISTS (SELECT 1 + FROM [master].[dbo].[sysdatabases] + WHERE [name] = N'$(DatabaseName)') + BEGIN + ALTER DATABASE [$(DatabaseName)] + SET TEMPORAL_HISTORY_RETENTION ON + WITH ROLLBACK IMMEDIATE; + END + + +GO +IF fulltextserviceproperty(N'IsFulltextInstalled') = 1 + EXECUTE sp_fulltext_database 'enable'; + + +GO +PRINT N'테이블 [dbo].[tRefreshToken]을(를) 만드는 중...'; + + +GO +CREATE TABLE [dbo].[tRefreshToken] ( + [cAuid] NVARCHAR (250) NOT NULL, + [cRefreshToken] NVARCHAR (1000) NOT NULL, + PRIMARY KEY CLUSTERED ([cAuid] ASC) +); + + +GO +PRINT N'테이블 [dbo].[tRole]을(를) 만드는 중...'; + + +GO +CREATE TABLE [dbo].[tRole] ( + [cAuid] NVARCHAR (250) NOT NULL, + [cRoleID] TINYINT NOT NULL, + [cRoleName] NVARCHAR (20) NOT NULL, + PRIMARY KEY CLUSTERED ([cAuid] ASC) +); + + +GO +PRINT N'테이블 [dbo].[tUser]을(를) 만드는 중...'; + + +GO +CREATE TABLE [dbo].[tUser] ( + [cUserID] NVARCHAR (50) NOT NULL, + [cAuid] NVARCHAR (250) NOT NULL, + [cPasswordHashed] NVARCHAR (250) NOT NULL, + [cState] TINYINT NOT NULL, + [cCreateDateTime] DATETIME2 (7) NOT NULL, + [cLastLoginDateTime] DATETIME2 (7) NULL, + PRIMARY KEY CLUSTERED ([cUserID] ASC) +); + + +GO +/* +배포 후 스크립트 템플릿 +-------------------------------------------------------------------------------------- + 이 파일에는 빌드 스크립트에 추가될 SQL 문이 있습니다. + SQLCMD 구문을 사용하여 파일을 배포 후 스크립트에 포함합니다. + 예: :r .\myfile.sql + SQLCMD 구문을 사용하여 배포 후 스크립트의 변수를 참조합니다. + 예: :setvar TableName MyTable + SELECT * FROM [$(TableName)] +-------------------------------------------------------------------------------------- +*/ + +IF NOT EXISTS (SELECT 1 FROM tUser WHERE cUserID = 'Alis') +BEGIN + INSERT INTO tUser (cUserID, cAuid, cPasswordHashed, cState, cCreateDateTime, cLastLoginDateTime) + VALUES ('Alis', 'SuperUserAlis' ,'oKLQCdunc2kT5aAVfK+POKwd8R3p8OZvs/NATwpg4gM=' ,1 ,GETDATE(), GETDATE()); + + INSERT INTO tRole(cAuid, cRoleID, cRoleName) + VALUES ('SuperUserAlis','20','SuperUser'); +END + +IF NOT EXISTS (SELECT 1 FROM tUser WHERE cUserID = 'SystemX') +BEGIN + INSERT INTO tUser (cUserID, cAuid, cPasswordHashed, cState, cCreateDateTime, cLastLoginDateTime) + VALUES ('SystemX', 'SuperUserSystemX' ,'S2irOEf+2n1sYsH7y+6/o16rc1HtXnj03a3qXfZLgBU=' ,1 ,GETDATE(), GETDATE()); + + INSERT INTO tRole(cAuid, cRoleID, cRoleName) + VALUES ('SuperUserSystemX','20','SuperUser'); +END +GO + +GO +DECLARE @VarDecimalSupported AS BIT; + +SELECT @VarDecimalSupported = 0; + +IF ((ServerProperty(N'EngineEdition') = 3) + AND (((@@microsoftversion / power(2, 24) = 9) + AND (@@microsoftversion & 0xffff >= 3024)) + OR ((@@microsoftversion / power(2, 24) = 10) + AND (@@microsoftversion & 0xffff >= 1600)))) + SELECT @VarDecimalSupported = 1; + +IF (@VarDecimalSupported > 0) + BEGIN + EXECUTE sp_db_vardecimal_storage_format N'$(DatabaseName)', 'ON'; + END + + +GO +ALTER DATABASE [$(DatabaseName)] + SET MULTI_USER + WITH ROLLBACK IMMEDIATE; + + +GO +PRINT N'업데이트가 완료되었습니다.'; + + +GO diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/_CreateScript.bat b/Projects/SystemX.Core/DBPatch/sqlScripts/_CreateScript.bat new file mode 100644 index 0000000..1627dc9 --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/sqlScripts/_CreateScript.bat @@ -0,0 +1,18 @@ +@echo off + +SET ServerIP=%1 +SET ServerPort=%2 +SET UserID=%3 +SET Passwd=%4 +SET DBName=%5 + +SET SqlCmdOption=-C -U %UserID% -P %Passwd% -S %ServerIP%,%ServerPort% -f 65001 -o ..\logs\%DBName%.log +SET DatabaseName=%DBName% +sqlcmd %SqlCmdOption% -i .\SystemX.DB.AccountDB_Create.sql +if errorlevel 1 goto errexit +goto end +:errexit +echo DB Patch Failed +goto end +:end +@echo on \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateAccountDB.bat b/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateAccountDB.bat new file mode 100644 index 0000000..d2df2be --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateAccountDB.bat @@ -0,0 +1,19 @@ +@echo off + +SET ServerIP=%1 +SET ServerPort=%2 +SET UserID=%3 +SET Passwd=%4 +SET DBName=%5 + +SET SqlCmdOption=-U %UserID% -P %Passwd% -S %ServerIP%,%ServerPort% -d %DBName% -o ..\logs\%DBName%.log +SET DatabaseName=%DBName% +sqlcmd %SqlCmdOption% -i .\SystemX.DB.AccountDB_Update.sql + +if errorlevel 1 goto errexit +goto end +:errexit +echo DB Patch Fail +goto end +:end +@echo on \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateScriptGenerate.bat b/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateScriptGenerate.bat new file mode 100644 index 0000000..56db9d9 --- /dev/null +++ b/Projects/SystemX.Core/DBPatch/sqlScripts/_UpdateScriptGenerate.bat @@ -0,0 +1,12 @@ +@echo off + +SET ServerIP=%1 +SET ServerPort=%2 +SET UserID=%3 +SET Passwd=%4 +SET DBName=%5 +SET Dacpac=%6 +SET OUTPUT=%7 + +::create update sql file +sqlpackage /Action:Script /SourceFile:%Dacpac% /TargetConnectionString:"server=%ServerIP%,%ServerPort%; user id=%UserID%; password=%Passwd%; database=%DBName%; TrustServerCertificate=true" /OutputPath:".\%OUTPUT%" /p:CommentOutSetVarDeclarations=True \ No newline at end of file diff --git a/Projects/SystemX.Core/DBPatch/sqlScripts/dacpac/SystemX.DB.AccountDB.dacpac b/Projects/SystemX.Core/DBPatch/sqlScripts/dacpac/SystemX.DB.AccountDB.dacpac new file mode 100644 index 0000000..67cb16e Binary files /dev/null and b/Projects/SystemX.Core/DBPatch/sqlScripts/dacpac/SystemX.DB.AccountDB.dacpac differ diff --git a/Projects/SystemX.Core/SystemX.Core.sln b/Projects/SystemX.Core/SystemX.Core.sln index 61b9793..15eec93 100644 --- a/Projects/SystemX.Core/SystemX.Core.sln +++ b/Projects/SystemX.Core/SystemX.Core.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemX.Core", "SystemX.Core\SystemX.Core.csproj", "{F057A1E8-F5FF-4241-BEEA-1A57E971F379}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemX.Core", "SystemX.Core\SystemX.Core.csproj", "{F057A1E8-F5FF-4241-BEEA-1A57E971F379}" +EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "SystemX.DB.AccountDB", "SystemX.DB.AccountDB\SystemX.DB.AccountDB.sqlproj", "{B44C85FA-BD31-419F-8481-477E166A5753}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,12 @@ Global {F057A1E8-F5FF-4241-BEEA-1A57E971F379}.Debug|Any CPU.Build.0 = Debug|Any CPU {F057A1E8-F5FF-4241-BEEA-1A57E971F379}.Release|Any CPU.ActiveCfg = Release|Any CPU {F057A1E8-F5FF-4241-BEEA-1A57E971F379}.Release|Any CPU.Build.0 = Release|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Release|Any CPU.Build.0 = Release|Any CPU + {B44C85FA-BD31-419F-8481-477E166A5753}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Projects/SystemX.Core/SystemX.Core/Config/Model/Auth.cs b/Projects/SystemX.Core/SystemX.Core/Config/Model/Auth.cs new file mode 100644 index 0000000..bc5f7ab --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Config/Model/Auth.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace SystemX.Core.Config.Model +{ + public class Auth + { + //jwt common + [JsonPropertyName("issuer")] + public string? issuer { get; set; } + + [JsonPropertyName("audience")] + public string? audience { get; set; } + + //access token + [JsonPropertyName("accessTokenSecret")] + public string? accessTokenSecret { get; set; } + + [JsonPropertyName("accessTokenExpires")] + public uint? accessTokenExpires { get; set; } + + //refresh token + [JsonPropertyName("refreshTokenSecret")] + public string? refreshTokenSecret { get; set; } + + [JsonPropertyName("refreshTokenExpires")] + public uint? refreshTokenExpires { get; set; } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs new file mode 100644 index 0000000..f330eda --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Context/AccountDbContext.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using SystemX.Core.DB.DBContext.AccountDB.Tables; + +namespace SystemX.Core.DB.DBContext.AccountDB.Context; + +public partial class AccountDbContext : DbContext +{ + public AccountDbContext() + { + } + + public AccountDbContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet TRefreshTokens { get; set; } + + public virtual DbSet TRoles { get; set; } + + public virtual DbSet TUsers { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. + => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=SystemX; password=X; database=AccountDB; TrustServerCertificate=true;"); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CAuid).HasName("PK__tRefresh__FBF0855465EB95AB"); + + entity.ToTable("tRefreshToken"); + + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CRefreshToken) + .HasMaxLength(1000) + .HasColumnName("cRefreshToken"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CAuid).HasName("PK__tRole__FBF085540BB887D7"); + + entity.ToTable("tRole"); + + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CRoleId).HasColumnName("cRoleID"); + entity.Property(e => e.CRoleName) + .HasMaxLength(20) + .HasColumnName("cRoleName"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CUserId).HasName("PK__tUser__A75DC19A721265FF"); + + entity.ToTable("tUser"); + + entity.Property(e => e.CUserId) + .HasMaxLength(50) + .HasColumnName("cUserID"); + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CCreateDateTime).HasColumnName("cCreateDateTime"); + entity.Property(e => e.CLastLoginDateTime).HasColumnName("cLastLoginDateTime"); + entity.Property(e => e.CPasswordHashed) + .HasMaxLength(250) + .HasColumnName("cPasswordHashed"); + entity.Property(e => e.CState).HasColumnName("cState"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs new file mode 100644 index 0000000..c2578dc --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRefreshToken.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace SystemX.Core.DB.DBContext.AccountDB.Tables; + +public partial class TRefreshToken +{ + public string CAuid { get; set; } = null!; + + public string CRefreshToken { get; set; } = null!; +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs new file mode 100644 index 0000000..5404196 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TRole.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace SystemX.Core.DB.DBContext.AccountDB.Tables; + +public partial class TRole +{ + public string CAuid { get; set; } = null!; + + public byte CRoleId { get; set; } + + public string CRoleName { get; set; } = null!; +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs new file mode 100644 index 0000000..4a10d36 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/DBContext/AccountDB/Tables/TUser.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace SystemX.Core.DB.DBContext.AccountDB.Tables; + +public partial class TUser +{ + public string CUserId { get; set; } = null!; + + public string CAuid { get; set; } = null!; + + public string CPasswordHashed { get; set; } = null!; + + public byte CState { get; set; } + + public DateTime CCreateDateTime { get; set; } + + public DateTime? CLastLoginDateTime { get; set; } +} diff --git a/Projects/SystemX.Core/SystemX.Core/ERROR_CODE.cs b/Projects/SystemX.Core/SystemX.Core/ERROR_CODE.cs new file mode 100644 index 0000000..e58b1e6 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/ERROR_CODE.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core +{ + public enum ERROR_CODE + { + //기본 에러 + EC_NONE = 0, + EC_OK = 1, + + //DB 관련 에러 + EC_DEFAULT_ERROR = 1000, + EC_DATABASE_ERROR = 1001, + + //유저 관련 에러 + EC_USER_REGISTER_FAILED = 2000, + EC_USER_REGISTER_EXIST = 2001, + EC_USER_REGISTER_CONFIRM_PASSWORD_ERROR = 2002, + + EC_USER_LOGIN_FAILED = 2100, + EC_USER_LOGIN_NOT_EXIST = 2101, + EC_USER_LOGIN_INVALID_PASSWORD = 2102, + EC_USER_LOGIN_INAVTIVE = 2103, + EC_USER_LOGIN_BLOCKED = 2104, + + EC_USER_LOGOUT_FAILED = 2200 + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs index 30c0754..adef78f 100644 --- a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs +++ b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs @@ -57,6 +57,8 @@ public static class Log4net public static bool IsConfigLoad { get; set; } = false; + public static string Log4netConfigPath { get; } = @"../../Config/log4net.config"; + //로그 사용여부 public static bool IsDebugEnabled { get; set; } = true; public static bool IsDBEnabled { get; set; } = true; @@ -70,7 +72,7 @@ public static class Log4net static Log4net() { - string log4netConfigPath = @"../Config/log4net.config"; + string log4netConfigPath = Log4netConfigPath; if (File.Exists(log4netConfigPath) == true) { @@ -82,7 +84,7 @@ public static class Log4net IsConfigLoad = OpenConfig(log4netConfigPath); } - + private static bool OpenConfig(string path) { bool result = true; diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs new file mode 100644 index 0000000..997084b --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/LoginModel.cs @@ -0,0 +1,34 @@ +using log4net.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Model.Auth +{ + //로그인 요청 모델 + public class LoginModel + { + [Required] + public string? UserID { get; set; } + + [Required] + public string? Password { get; set; } + } + + //로그인 응답 모델 + public class LoginResponseModel + { + public string? UserID { get; set; } + public UserRole Role { get; set; } + public string? RoleName { get; set; } + + public string? AccessToken { get; set; } + public long AccessTokenExpired { get; set; } + public string? RefreshToken { get; set; } + + public ERROR_CODE? EC { get; set; } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs new file mode 100644 index 0000000..bf8f7a1 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/LogoutModel.cs @@ -0,0 +1,23 @@ +using log4net.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Model.Auth +{ + public class LogoutModel + { + [Required] + public string? UserID { get; set; } + } + + public class LogoutResponseModel + { + public string? UserID { get; set; } + + public ERROR_CODE? EC { get; set; } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs new file mode 100644 index 0000000..67085f8 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/RegisterModel.cs @@ -0,0 +1,35 @@ +using log4net.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Model.Auth +{ + //유저 등록 모델 + public class RegisterModel + { + [Required] + public string UserID { get; set; } = string.Empty; + + [Required] + public string Password { get; set; } = string.Empty; + + [Required] + public string PasswordConfirm { get; set; } = string.Empty; + + [Required] + public UserRole Role { get; set; } = UserRole.User; + } + + public class RegisterResponseModel + { + public string? UserID { get; set; } + public UserRole Role { get; set; } + public string? RoleName { get; set; } + + public ERROR_CODE? EC { get; set; } = ERROR_CODE.EC_USER_REGISTER_FAILED; + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs new file mode 100644 index 0000000..660f847 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Model.Auth +{ + public class UserModel + { + //public TUser? TUser { get; set; } + //public TRole? TRole { get; set; } + } +} diff --git a/Projects/VPKI/VPKI/VPKI.Library/Enums/UserRole.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserRole.cs similarity index 89% rename from Projects/VPKI/VPKI/VPKI.Library/Enums/UserRole.cs rename to Projects/SystemX.Core/SystemX.Core/Model/Auth/UserRole.cs index b5ca981..07840ae 100644 --- a/Projects/VPKI/VPKI/VPKI.Library/Enums/UserRole.cs +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserRole.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace VPKI.Library.Enums +namespace SystemX.Core.Model.Auth { //User 권한 public enum UserRole : byte diff --git a/Projects/VPKI/VPKI/VPKI.Library/Enums/UserState.cs b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserState.cs similarity index 74% rename from Projects/VPKI/VPKI/VPKI.Library/Enums/UserState.cs rename to Projects/SystemX.Core/SystemX.Core/Model/Auth/UserState.cs index 0d48b84..0b15639 100644 --- a/Projects/VPKI/VPKI/VPKI.Library/Enums/UserState.cs +++ b/Projects/SystemX.Core/SystemX.Core/Model/Auth/UserState.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace VPKI.Library.Enums +namespace SystemX.Core.Model.Auth { - public enum UserState: byte + public enum UserState : byte { Inactive = 0, Active = 1, diff --git a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj index 17016a0..3ffe46e 100644 --- a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj +++ b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj @@ -9,18 +9,26 @@ 9999 + False 9999 + False - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Projects/SystemX.Core/SystemX.DB.AccountDB/SystemX.DB.AccountDB.sqlproj b/Projects/SystemX.Core/SystemX.DB.AccountDB/SystemX.DB.AccountDB.sqlproj new file mode 100644 index 0000000..d94a9b7 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.DB.AccountDB/SystemX.DB.AccountDB.sqlproj @@ -0,0 +1,80 @@ + + + + Debug + AnyCPU + SystemX.DB.AccountDB + 2.0 + 4.1 + {b44c85fa-bd31-419f-8481-477e166a5753} + Microsoft.Data.Tools.Schema.Sql.Sql160DatabaseSchemaProvider + Database + + + SystemX.DB.AccountDB + SystemX.DB.AccountDB + 1042,CI + BySchemaAndSchemaType + True + v4.7.2 + CS + Properties + False + True + True + True + Korean_Wansung_CI_AS + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + true + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + + + xcopy /y $(ProjectDir)$(OutputPath)$(TargetName)_Create.sql $(SolutionDir)DBPatch\sqlScripts\ + +xcopy /y $(ProjectDir)$(OutputPath)$(TargetName).dacpac $(SolutionDir)DBPatch\sqlScripts\dacpac\ + + \ No newline at end of file diff --git a/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Scripts/scriptAfterBuild.sql b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Scripts/scriptAfterBuild.sql new file mode 100644 index 0000000..b57fab3 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Scripts/scriptAfterBuild.sql @@ -0,0 +1,29 @@ +/* +배포 후 스크립트 템플릿 +-------------------------------------------------------------------------------------- + 이 파일에는 빌드 스크립트에 추가될 SQL 문이 있습니다. + SQLCMD 구문을 사용하여 파일을 배포 후 스크립트에 포함합니다. + 예: :r .\myfile.sql + SQLCMD 구문을 사용하여 배포 후 스크립트의 변수를 참조합니다. + 예: :setvar TableName MyTable + SELECT * FROM [$(TableName)] +-------------------------------------------------------------------------------------- +*/ + +IF NOT EXISTS (SELECT 1 FROM tUser WHERE cUserID = 'Alis') +BEGIN + INSERT INTO tUser (cUserID, cAuid, cPasswordHashed, cState, cCreateDateTime, cLastLoginDateTime) + VALUES ('Alis', 'SuperUserAlis' ,'oKLQCdunc2kT5aAVfK+POKwd8R3p8OZvs/NATwpg4gM=' ,1 ,GETDATE(), GETDATE()); + + INSERT INTO tRole(cAuid, cRoleID, cRoleName) + VALUES ('SuperUserAlis','20','SuperUser'); +END + +IF NOT EXISTS (SELECT 1 FROM tUser WHERE cUserID = 'SystemX') +BEGIN + INSERT INTO tUser (cUserID, cAuid, cPasswordHashed, cState, cCreateDateTime, cLastLoginDateTime) + VALUES ('SystemX', 'SuperUserSystemX' ,'S2irOEf+2n1sYsH7y+6/o16rc1HtXnj03a3qXfZLgBU=' ,1 ,GETDATE(), GETDATE()); + + INSERT INTO tRole(cAuid, cRoleID, cRoleName) + VALUES ('SuperUserSystemX','20','SuperUser'); +END \ No newline at end of file diff --git a/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRefreshToken.sql b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRefreshToken.sql new file mode 100644 index 0000000..0ec9fd9 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRefreshToken.sql @@ -0,0 +1,5 @@ +CREATE TABLE [dbo].[tRefreshToken] +( + [cAuid] NVARCHAR(250) NOT NULL PRIMARY KEY, + [cRefreshToken] NVARCHAR(1000) NOT NULL +) diff --git a/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRole.sql b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRole.sql new file mode 100644 index 0000000..5a49a82 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tRole.sql @@ -0,0 +1,6 @@ +CREATE TABLE [dbo].[tRole] +( + [cAuid] NVARCHAR(250) NOT NULL PRIMARY KEY, + [cRoleID] TINYINT NOT NULL, + [cRoleName] NVARCHAR(20) NOT NULL +) diff --git a/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tUser.sql b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tUser.sql new file mode 100644 index 0000000..a7e6a64 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.DB.AccountDB/dbo/Tables/tUser.sql @@ -0,0 +1,10 @@ +CREATE TABLE [dbo].[tUser] +( + [cUserID] NVARCHAR(50) NOT NULL, + [cAuid] NVARCHAR(250) NOT NULL , + [cPasswordHashed] NVARCHAR(250) NOT NULL, + [cState] tinyint NOT NULL, + [cCreateDateTime] DATETIME2 NOT NULL, + [cLastLoginDateTime] DATETIME2 NULL, + PRIMARY KEY ([cUserID]) +) diff --git a/Projects/Tools/Tools_Scaffold_AccountDB.bat b/Projects/Tools/Tools_Scaffold_AccountDB.bat new file mode 100644 index 0000000..425d4f8 --- /dev/null +++ b/Projects/Tools/Tools_Scaffold_AccountDB.bat @@ -0,0 +1,5 @@ +::AccountDB +cd ../SystemX.Core/SystemX.Core + +::WebApi +dotnet ef dbcontext scaffold "server=127.0.0.1; user id=SystemX; password=X; database=AccountDB; TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer --namespace SystemX.Core.DBContext --context-dir DBContext\AccountDB\Context --output-dir DBContext\AccountDB\Tables -f \ No newline at end of file diff --git a/Projects/WebApi/AuthApi/AuthApi.csproj b/Projects/WebApi/AuthApi/AuthApi.csproj index 9daa180..e65ec2b 100644 --- a/Projects/WebApi/AuthApi/AuthApi.csproj +++ b/Projects/WebApi/AuthApi/AuthApi.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -6,8 +6,32 @@ enable + + True + + + + True + + + + + + + + + + + ..\..\DLL\SystemX.Core.dll + + + + + + + diff --git a/Projects/WebApi/AuthApi/Controllers/AuthController.cs b/Projects/WebApi/AuthApi/Controllers/AuthController.cs new file mode 100644 index 0000000..83c9889 --- /dev/null +++ b/Projects/WebApi/AuthApi/Controllers/AuthController.cs @@ -0,0 +1,146 @@ +using AuthApi.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using SystemX.Core; +using SystemX.Core.Model.Auth; + +namespace AuthApi.Controllers +{ + [Tags("Auth")] + [Route("api/auth")] + [ApiController] + [ApiExplorerSettings(IgnoreApi = true)] + public class AuthController : CommonController + { + private readonly AuthService _authService; + + public AuthController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor, + AuthService authService) + : base(serviceProvider, httpContextAccessor) + { + _authService = authService; + } + + [HttpGet("/health")] + public async Task Health() + { + await Task.CompletedTask; + return Results.Ok("Healthy"); + } + + [HttpPost("regisger")] + public async Task Register([FromBody] RegisterModel request) + { + // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + + RegisterResponseModel response = new RegisterResponseModel(); + + if (request?.UserID != null && request?.Password != null) + { + response = await _authService.CreateUser(request); + } + + // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + + return Results.Ok(response); + } + + [HttpPost("login")] + public async Task Login([FromBody] LoginModel request) + { + // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + + LoginResponseModel response = new LoginResponseModel(); + response.UserID = request.UserID; + response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED; + + if (request.UserID != null && request.Password != null) + { + response = await _authService.SelectUser(request); + + if (response.EC == ERROR_CODE.EC_OK) + { + double convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.accessTokenExpires); + + response.AccessToken = GenerateJwtToken(response); + response.AccessTokenExpired = DateTime.UtcNow.AddMinutes(convertExpires).ToUnixTime(); + + response.RefreshToken = GenerateJwtToken(response, true); + } + + await _authService.UpdateLoginInfo(request, response.RefreshToken); + } + + // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + + return Results.Ok(response); + } + + [HttpPost("logout")] + public async Task Logout([FromBody] LogoutModel request) + { + // Log4net.WriteLine(GetRequestLog(request).LogModelToString("Request Auth"), LogType.CONTROLLER); + + var response = _authService.LogoutUser(request); + await Task.CompletedTask; + + // Log4net.WriteLine(GetResponseLog(response).LogModelToString("Response Auth"), LogType.CONTROLLER); + + return Results.Ok(response); + } + + [Authorize] + [HttpPost("validate")] + public ActionResult Validate([FromBody] string authToken) + { + return ""; + } + + private TokenValidationParameters GetValidationParameters() + { + return new TokenValidationParameters() + { + ValidateLifetime = true, + ValidateAudience = true, + ValidateIssuer = true, + ValidIssuer = $"{_configService?.GetConfig()?.Auth?.issuer}", + ValidAudience = $"{_configService?.GetConfig()?.Auth?.issuer}", + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{_configService?.GetConfig()?.Auth?.accessTokenSecret}")) + }; + } + + private string GenerateJwtToken(LoginResponseModel loginResponseModel, bool isRefreshToken = false) + { + var claims = new[] + { + new Claim(ClaimTypes.Name, $"{loginResponseModel.UserID}"), + new Claim(ClaimTypes.Role, $"{loginResponseModel.RoleName}"), + }; + + string secret = $"{_configService?.GetConfig()?.Auth?.accessTokenSecret}"; + double convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.accessTokenExpires); + if (isRefreshToken == true) + { + secret = $"{_configService?.GetConfig()?.Auth?.refreshTokenSecret}"; + convertExpires = Convert.ToDouble(_configService?.GetConfig()?.Auth?.refreshTokenExpires); + } + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + issuer: $"{_configService?.GetConfig()?.Auth?.issuer}", + audience: $"{_configService?.GetConfig()?.Auth?.audience}", + claims: claims, + expires: DateTime.UtcNow.AddMinutes(convertExpires), + signingCredentials: creds + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/Projects/WebApi/AuthApi/Controllers/CommonController.cs b/Projects/WebApi/AuthApi/Controllers/CommonController.cs new file mode 100644 index 0000000..ef12193 --- /dev/null +++ b/Projects/WebApi/AuthApi/Controllers/CommonController.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Mvc; +using System.Runtime.CompilerServices; +using SystemX.Core.Services; +using WebApi.Library.Config; + +namespace AuthApi.Controllers +{ + public class CommonController : ControllerBase + { + public readonly IServiceProvider _serviceProvider; + public readonly IHttpContextAccessor _httpContextAccessor; + + public readonly ConfigService? _configService; + + protected static Guid guid { get; private set; } = Guid.NewGuid(); + + public CommonController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor) + { + //provider + _serviceProvider = serviceProvider; + _httpContextAccessor = httpContextAccessor; + + //service + _configService = _serviceProvider.GetService>(); + } + + /// + /// Request 클라이언트 IP + /// + protected virtual string? GetClientIP() + { + return _httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString(); + } + + /// + /// Request 클라이언트 Url + /// + protected virtual string? GetRequestUrl() + { + return _httpContextAccessor?.HttpContext?.Request?.Path; + } + + /// + /// Request 클라이언트 method: [GET] or [POST] + /// + protected virtual string? GetRequestMethod() + { + return _httpContextAccessor?.HttpContext?.Request?.Method; + } + + /// + /// 현재 Action(함수) 이름 가져오기 + /// + protected virtual string GetMethodName([CallerMemberName] string callerMemberName = "") + { + return callerMemberName; + } + } +} diff --git a/Projects/WebApi/AuthApi/Controllers/WeatherForecastController.cs b/Projects/WebApi/AuthApi/Controllers/WeatherForecastController.cs deleted file mode 100644 index a18781e..0000000 --- a/Projects/WebApi/AuthApi/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace AuthApi.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/Projects/WebApi/AuthApi/Program.cs b/Projects/WebApi/AuthApi/Program.cs index 48863a6..5135652 100644 --- a/Projects/WebApi/AuthApi/Program.cs +++ b/Projects/WebApi/AuthApi/Program.cs @@ -1,25 +1,118 @@ +using AuthApi.Services; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.ComponentModel; +using System.Text; +using SystemX.Core.Services; +using WebApi.Library.Config; + +string configDir = @"../../Config"; +string configFileName = "WebApi.AuthApi.Config.json"; + +//raed log4net configs +if (Log4net.IsConfigLoad == true) +{ + Log4net.WriteLine("Log4net Init Success"); + Log4net.AutoRemoveLog(); +} +else +{ + Console.WriteLine("Log4net Init Failed"); + return; +} + var builder = WebApplication.CreateBuilder(args); // Add services to the container. +//singleton +builder.Services.AddSingleton>(); +builder.Services.AddScoped(); + +//scoped builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +//config preload, auth set +ConfigService preloadConfig = new ConfigService(); +if (preloadConfig.OpenConfig($@"{configDir}/{configFileName}") == true) +{ + var config = preloadConfig.GetConfig(); + + //auth + builder.Services + .AddAuthentication(option => + { + option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ClockSkew = TimeSpan.Zero, + ValidIssuer = $"{config?.Auth?.issuer}", + ValidAudience = $"{config?.Auth?.audience}", + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes($"{config?.Auth?.accessTokenSecret}")) + }; + }); +} +else +{ + Log4net.WriteLine("Config Preload Load Error.", LogType.Error); + return; +} + var app = builder.Build(); +//read api config and set +string serverUrl = string.Empty; +var configService = app.Services.GetService>(); +bool isIIS = false; + +if (configService?.OpenConfig($@"{configDir}/{configFileName}") == true) +{ + Log4net.WriteLine("WebApi Config Success."); + var apiConfig = ConfigService.Config; + if (apiConfig != null) + { + serverUrl = $"{apiConfig?.Server?.Address}:{apiConfig?.Server?.Port}"; + isIIS = apiConfig!.Server.IIS; + } +} +else +{ + Log4net.WriteLine("WebApi Config Error."); + return; +} + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { + Log4net.WriteLine($"IsDevelopment:{app.Environment.IsDevelopment()}"); + Log4net.WriteLine($"Swagger Url: {serverUrl}/swagger"); app.UseSwagger(); app.UseSwaggerUI(); } +app.UseAuthentication(); app.UseHttpsRedirection(); - app.UseAuthorization(); app.MapControllers(); -app.Run(); +if (isIIS == true) +{ + app.Run(); +} +else +{ + Log4net.WriteLine($"Operation Url: {serverUrl}"); + app.Run($"{serverUrl}"); +} diff --git a/Projects/WebApi/AuthApi/Services/AuthService.cs b/Projects/WebApi/AuthApi/Services/AuthService.cs new file mode 100644 index 0000000..acced8d --- /dev/null +++ b/Projects/WebApi/AuthApi/Services/AuthService.cs @@ -0,0 +1,263 @@ +using SystemX.Core.Model.Auth; +using SystemX.Core.Services; +using SystemX.Core; +using WebApi.Library.Config; +using SystemX.Core.Config.Model; +using System.Data; +using SystemX.Core.DB; +using Microsoft.EntityFrameworkCore; +using SystemX.Core.DB.DBContext.AccountDB.Context; +using SystemX.Core.DB.DBContext.AccountDB.Tables; + +namespace AuthApi.Services +{ + public class AuthService + { + private readonly IServiceProvider _serviceProvider; + private readonly IServiceScopeFactory _scopeFactory; + private readonly ConfigService? _configService; + + private readonly DataBase? _accountDB; + + private static List Session = new List(); + + public AuthService(IServiceProvider serviceProvider, IServiceScopeFactory scopeFactory, ConfigService configSerice) + { + _serviceProvider = serviceProvider; + _configService = configSerice; + _scopeFactory = scopeFactory; + _accountDB = _configService?.GetConfig()?.DataBase?.Find(x => x.DBContext == "VpkiAccountDbContext"); + } + + /// + /// create new user + /// + public async Task CreateUser(RegisterModel registerModel) + { + //response + RegisterResponseModel response = new RegisterResponseModel(); + response.EC = ERROR_CODE.EC_USER_REGISTER_FAILED; + response.UserID = registerModel.UserID; + response.Role = registerModel.Role; + response.RoleName = registerModel.Role.ToString(); + + //context + using (var scope = _scopeFactory.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + if (context is not null) + { + var user = await context.TUsers.AsNoTracking().Where(x => x.CUserId.ToLower() == registerModel.UserID.ToLower()).ToListAsync(); + if (user?.Count <= 0) + { + string auid = Guid.NewGuid().ToString(); + //user + TUser newUser = new TUser + { + CAuid = auid, + CUserId = registerModel.UserID, + CPasswordHashed = registerModel.Password, + CCreateDateTime = DateTime.Now, + CLastLoginDateTime = new DateTime() + }; + //role + TRole newUserRole = new TRole + { + CAuid = auid, + CRoleId = Convert.ToByte(registerModel.Role), + CRoleName = registerModel.Role.ToString() + }; + + using (var transaction = await context.CreateTransactionAsync()) + { + await context.AddAsync(newUser); + await context.AddAsync(newUserRole); + + var result = await context.CloseTransactionAsync(transaction); + if (result == true) + { + response.EC = ERROR_CODE.EC_OK; + } + } + } + } + } + + return response; + } + + /// + /// select user(login) + /// + public async Task SelectUser(LoginModel loginModel) + { + //response + LoginResponseModel response = new LoginResponseModel(); + response.EC = ERROR_CODE.EC_USER_LOGIN_FAILED; + response.UserID = loginModel.UserID; + + //var session = Session.Find(x => x.UserID?.ToLower() == loginModel.UserID?.ToLower()); + //if (session?.AccessTokenExpired < DateTime.Now.ToUnixTime()) + //{ + // Session.Remove(session); + //} + + //기존 로그인 체크 + // if (Session.Exists(x => x.UserID == $"{loginModel.UserID?.ToLower()}") == false) + { + if (loginModel != null) + { + //context + using (var scope = _scopeFactory.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + if (context is not null) + { + try + { + using (var transaction = await context.CreateTransactionAsync(IsolationLevel.ReadUncommitted)) + { + //select user + var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); + if (selectUser is not null) + { + if (selectUser.CPasswordHashed == loginModel?.Password) + { + //select role + var selectRole = await context.TRoles.FindAsync(selectUser.CAuid); + if (selectRole != null) + { + response.Role = (UserRole)Enum.Parse(typeof(UserRole), selectRole.CRoleId.ToString()); + response.RoleName = selectRole.CRoleName; + } + + // Session.Add(response); + + if (selectUser.CState == (byte)UserState.Active) + { + response.EC = ERROR_CODE.EC_OK; + } + else if (selectUser.CState == (byte)UserState.Inactive) + { + response.EC = ERROR_CODE.EC_USER_LOGIN_INAVTIVE; + } + else if (selectUser.CState == (byte)UserState.Block) + { + response.EC = ERROR_CODE.EC_USER_LOGIN_BLOCKED; + } + } + else + { + response.EC = ERROR_CODE.EC_USER_LOGIN_INVALID_PASSWORD; + } + } + else + { + response.EC = ERROR_CODE.EC_USER_LOGIN_NOT_EXIST; + Log4net.WriteLine($"{response.EC}", LogType.Error); + } + await context.CloseTransactionAsync(transaction); + } + } + catch (Exception e) + { + Log4net.WriteLine($"Select User Transaction Error", LogType.Exception); + Log4net.WriteLine(e); + } + } + } + } + } + + return response; + } + + public async Task UpdateLoginInfo(LoginModel loginModel, string? RefreshToken = "") + { + bool result = false; + bool transactionResult = true; + + using (var scope = _scopeFactory.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + if (context is not null) + { + var selectUser = await context.TUsers.AsNoTracking().FirstOrDefaultAsync(x => x.CUserId.ToLower() == loginModel!.UserID!.ToLower()); + if (selectUser is not null) + { + using (var transaction = await context.CreateTransactionAsync()) + { + try + { + //user info + selectUser.CLastLoginDateTime = DateTime.Now; + context.Update(selectUser); + + //refresh token + var findRefreshToken = await context.TRefreshTokens.AsNoTracking().FirstOrDefaultAsync(x => x.CAuid == selectUser.CAuid); + //null이면(없으면) add + if (findRefreshToken == null) + { + await context.AddAsync(new TRefreshToken + { + CAuid = selectUser.CAuid, + CRefreshToken = $"{RefreshToken}" + }); + } + //있으면 update + else + { + findRefreshToken.CRefreshToken = $"{RefreshToken}"; + context.Update(findRefreshToken); + } + + //commit + Log4net.WriteLine(findRefreshToken?.ToJson(), LogType.Debug); + + result = true; + } + catch (Exception ex) + { + Log4net.WriteLine(ex); + } + + transactionResult = await context.CloseTransactionAsync(transaction); + } + } + else + { + Log4net.WriteLine($"Not Exist User {loginModel.UserID}", LogType.Error); + } + + //db error + if (transactionResult == false) + { + Log4net.WriteLine($"Transaction Error", LogType.Error); + } + else + { + Log4net.WriteLine($"Transaction Success", LogType.DB); + } + } + } + + return result; + } + + public LogoutResponseModel LogoutUser(LogoutModel logoutModel) + { + LogoutResponseModel response = new LogoutResponseModel(); + response.UserID = logoutModel.UserID; + response.EC = ERROR_CODE.EC_USER_LOGOUT_FAILED; + + var session = Session.Find(x => x.UserID?.ToLower() == logoutModel?.UserID?.ToLower()); + if (session != null) + { + Session.Remove(session); + response.EC = ERROR_CODE.EC_OK; + } + + return response; + } + } +} diff --git a/Projects/WebApi/AuthApi/WeatherForecast.cs b/Projects/WebApi/AuthApi/WeatherForecast.cs deleted file mode 100644 index 417734b..0000000 --- a/Projects/WebApi/AuthApi/WeatherForecast.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AuthApi -{ - public class WeatherForecast - { - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } - } -} diff --git a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs new file mode 100644 index 0000000..a053ff5 --- /dev/null +++ b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Context/AccountDbContext.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using WebApi.Library.DBContext.DB.DBContext.AccountDB.Tables; + +namespace WebApi.Library.DBContext.DB.DBContext.AccountDB.Context; + +public partial class AccountDbContext : DbContext +{ + public AccountDbContext() + { + } + + public AccountDbContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet TRefreshTokens { get; set; } + + public virtual DbSet TRoles { get; set; } + + public virtual DbSet TUsers { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. + => optionsBuilder.UseSqlServer("server=127.0.0.1; user id=SystemX; password=X; database=AccountDB; TrustServerCertificate=true;"); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CAuid).HasName("PK__tRefresh__FBF0855465EB95AB"); + + entity.ToTable("tRefreshToken"); + + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CRefreshToken) + .HasMaxLength(1000) + .HasColumnName("cRefreshToken"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CAuid).HasName("PK__tRole__FBF085540BB887D7"); + + entity.ToTable("tRole"); + + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CRoleId).HasColumnName("cRoleID"); + entity.Property(e => e.CRoleName) + .HasMaxLength(20) + .HasColumnName("cRoleName"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.CUserId).HasName("PK__tUser__A75DC19A721265FF"); + + entity.ToTable("tUser"); + + entity.Property(e => e.CUserId) + .HasMaxLength(50) + .HasColumnName("cUserID"); + entity.Property(e => e.CAuid) + .HasMaxLength(250) + .HasColumnName("cAuid"); + entity.Property(e => e.CCreateDateTime).HasColumnName("cCreateDateTime"); + entity.Property(e => e.CLastLoginDateTime).HasColumnName("cLastLoginDateTime"); + entity.Property(e => e.CPasswordHashed) + .HasMaxLength(250) + .HasColumnName("cPasswordHashed"); + entity.Property(e => e.CState).HasColumnName("cState"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRefreshToken.cs b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRefreshToken.cs new file mode 100644 index 0000000..d807df1 --- /dev/null +++ b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRefreshToken.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace WebApi.Library.DBContext.DB.DBContext.AccountDB.Tables; + +public partial class TRefreshToken +{ + public string CAuid { get; set; } = null!; + + public string CRefreshToken { get; set; } = null!; +} diff --git a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRole.cs b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRole.cs new file mode 100644 index 0000000..bc6fa15 --- /dev/null +++ b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TRole.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace WebApi.Library.DBContext.DB.DBContext.AccountDB.Tables; + +public partial class TRole +{ + public string CAuid { get; set; } = null!; + + public byte CRoleId { get; set; } + + public string CRoleName { get; set; } = null!; +} diff --git a/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TUser.cs b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TUser.cs new file mode 100644 index 0000000..4c4ec32 --- /dev/null +++ b/Projects/WebApi/WebApi.Library.DBContext/DB/DBContext/AccountDB/Tables/TUser.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace WebApi.Library.DBContext.DB.DBContext.AccountDB.Tables; + +public partial class TUser +{ + public string CUserId { get; set; } = null!; + + public string CAuid { get; set; } = null!; + + public string CPasswordHashed { get; set; } = null!; + + public byte CState { get; set; } + + public DateTime CCreateDateTime { get; set; } + + public DateTime? CLastLoginDateTime { get; set; } +} diff --git a/Projects/WebApi/WebApi.Library.DBContext/WebApi.Library.DBContext.csproj b/Projects/WebApi/WebApi.Library.DBContext/WebApi.Library.DBContext.csproj new file mode 100644 index 0000000..085266c --- /dev/null +++ b/Projects/WebApi/WebApi.Library.DBContext/WebApi.Library.DBContext.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/Projects/WebApi/WebApi.Library/Config/WebApiConfig.cs b/Projects/WebApi/WebApi.Library/Config/WebApiConfig.cs new file mode 100644 index 0000000..51fb9fe --- /dev/null +++ b/Projects/WebApi/WebApi.Library/Config/WebApiConfig.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using SystemX.Core.Config; +using SystemX.Core.Config.Model; + +namespace WebApi.Library.Config +{ + public class WebApiConfig : WebCommonConfig + { + [JsonPropertyName("Auth")] + public Auth? Auth { get; set; } + + [JsonPropertyName("DataBase")] + public List? DataBase { get; set; } + } +} diff --git a/Projects/WebApi/WebApi.Library/WebApi.Library.csproj b/Projects/WebApi/WebApi.Library/WebApi.Library.csproj new file mode 100644 index 0000000..8f25caa --- /dev/null +++ b/Projects/WebApi/WebApi.Library/WebApi.Library.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + + + True + + + + True + + + + + ..\..\DLL\SystemX.Core.dll + + + + diff --git a/Projects/WebApi/WebApi.sln b/Projects/WebApi/WebApi.sln index e587684..9b91b76 100644 --- a/Projects/WebApi/WebApi.sln +++ b/Projects/WebApi/WebApi.sln @@ -5,6 +5,15 @@ VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthApi", "AuthApi\AuthApi.csproj", "{321DD194-9455-48F7-A0BE-EF6E95881714}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.Library", "WebApi.Library\WebApi.Library.csproj", "{1B109CFE-B860-4125-8F2B-06D95DE85E91}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.Library.DBContext", "WebApi.Library.DBContext\WebApi.Library.DBContext.csproj", "{92599205-8D5B-4630-B669-AA390193BC9E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{C8D5274F-AC00-46C7-1F8D-E88E81087A52}" + ProjectSection(SolutionItems) = preProject + ..\Config\WebApi.AuthApi.Config.json = ..\Config\WebApi.AuthApi.Config.json + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +24,14 @@ Global {321DD194-9455-48F7-A0BE-EF6E95881714}.Debug|Any CPU.Build.0 = Debug|Any CPU {321DD194-9455-48F7-A0BE-EF6E95881714}.Release|Any CPU.ActiveCfg = Release|Any CPU {321DD194-9455-48F7-A0BE-EF6E95881714}.Release|Any CPU.Build.0 = Release|Any CPU + {1B109CFE-B860-4125-8F2B-06D95DE85E91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B109CFE-B860-4125-8F2B-06D95DE85E91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B109CFE-B860-4125-8F2B-06D95DE85E91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B109CFE-B860-4125-8F2B-06D95DE85E91}.Release|Any CPU.Build.0 = Release|Any CPU + {92599205-8D5B-4630-B669-AA390193BC9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92599205-8D5B-4630-B669-AA390193BC9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92599205-8D5B-4630-B669-AA390193BC9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92599205-8D5B-4630-B669-AA390193BC9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE