Compare commits
	
		
			7 Commits
		
	
	
		
			1e4aaf33f9
			...
			a21cd0cea2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a21cd0cea2 | |||
| 47cbdc21c1 | |||
| 724b1d4dae | |||
| 7cce413f79 | |||
| 6fd6c9f20d | |||
| dd5d556963 | |||
| 39b28386ae | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -482,3 +482,7 @@ $RECYCLE.BIN/ | |||||||
|  |  | ||||||
| # Vim temporary swap files | # Vim temporary swap files | ||||||
| *.swp | *.swp | ||||||
|  |  | ||||||
|  | # App data files and data for dev | ||||||
|  | /data | ||||||
|  | /data_dev | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								OptixServe.Api/Configuration/AppSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								OptixServe.Api/Configuration/AppSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | namespace OptixServe.Api.Configuration; | ||||||
|  |  | ||||||
|  | public record OptixServeSettings | ||||||
|  | { | ||||||
|  |     public ApiSettings? Api { get; set; } = new(); | ||||||
|  |     public DatabaseSettings? Database { get; set; } = new(); | ||||||
|  |     public JwtSettings? Jwt { get; set; } = new(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public record ApiSettings | ||||||
|  | { | ||||||
|  |     public string? Listen { get; set; } = "127.0.0.1"; | ||||||
|  |     public int? Port { get; set; } = 10086; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public record JwtSettings | ||||||
|  | { | ||||||
|  |     public string Secret { get; set; } = string.Empty; | ||||||
|  |     public string Issuer { get; set; } = "OptixServe"; | ||||||
|  |     public string Audience { get; set; } = "OptixServeUsers"; | ||||||
|  |     public int TokenExpirationMinutes { get; set; } = 60; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public enum DatabaseType | ||||||
|  | { | ||||||
|  |     Sqlite, | ||||||
|  |     MySQL | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public record DatabaseSettings | ||||||
|  | { | ||||||
|  |     public DatabaseType Type { get; set; } = DatabaseType.Sqlite; | ||||||
|  |     public string? Host { get; set; } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								OptixServe.Api/Configuration/ConfigurationHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								OptixServe.Api/Configuration/ConfigurationHelper.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | using System; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Api.Configuration; | ||||||
|  |  | ||||||
|  | public static class ConfigurationHelper | ||||||
|  | { | ||||||
|  |     public static IConfigurationBuilder CreateDefaultBuilder() | ||||||
|  |     { | ||||||
|  |         var aspEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); | ||||||
|  |         var netEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"); | ||||||
|  |         // Console.WriteLine($"ASPNETCORE_ENVIRONMENT: {aspEnv}, DOTNET_ENVIRONMENT: {netEnv}"); | ||||||
|  |         var env = aspEnv ?? netEnv ?? null; | ||||||
|  |  | ||||||
|  |         var builder = new ConfigurationBuilder() | ||||||
|  |             .SetBasePath(Directory.GetCurrentDirectory()) | ||||||
|  |             .AddJsonFile("appsettings.json", optional: true) | ||||||
|  |             .AddJsonFile("config.json", optional: true); | ||||||
|  |          | ||||||
|  |         if (env != null) | ||||||
|  |         { | ||||||
|  |             builder.AddJsonFile($"appsettings.{env}.json", optional: true) | ||||||
|  |                 .AddJsonFile($"config.{env}.json", optional: true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return builder; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								OptixServe.Api/Dtos/Auth.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								OptixServe.Api/Dtos/Auth.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | namespace OptixServe.Api.Dtos; | ||||||
|  |  | ||||||
|  | public record LoginRequestDto | ||||||
|  | { | ||||||
|  |     public string? UserName { get; set; } | ||||||
|  |     public string? Password { get; set; } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public record LoginResponseDto | ||||||
|  | { | ||||||
|  |     public string? Token { get; set; } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								OptixServe.Api/Dtos/Error.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								OptixServe.Api/Dtos/Error.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | namespace OptixServe.Api.Dtos; | ||||||
|  |  | ||||||
|  | public record CommonErrorDto | ||||||
|  | { | ||||||
|  |     public string? Message { get; set; } | ||||||
|  | } | ||||||
| @ -1,32 +1,61 @@ | |||||||
| using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||||
|  | using OptixServe.Core.Services; | ||||||
| using OptixServe.Api.Dtos; | using OptixServe.Api.Dtos; | ||||||
|  | using OptixServe.Api.Services; | ||||||
|  | using Microsoft.AspNetCore.Authorization; | ||||||
|  |  | ||||||
| namespace OptixServe.Api.Endpoints; | namespace OptixServe.Api.Endpoints; | ||||||
|  |  | ||||||
|  |  | ||||||
| [JsonSerializable(typeof(UserDto))] | [JsonSerializable(typeof(UserDto))] | ||||||
| [JsonSerializable(typeof(IEnumerable<UserDto>))] | [JsonSerializable(typeof(IEnumerable<UserDto>))] | ||||||
|  | [JsonSerializable(typeof(LoginRequestDto))] | ||||||
|  | [JsonSerializable(typeof(LoginResponseDto))] // For returning the token string | ||||||
| public partial class UserJsonContext : JsonSerializerContext { } | public partial class UserJsonContext : JsonSerializerContext { } | ||||||
|  |  | ||||||
| public static class UserEndpoint | public static class UserEndpoint | ||||||
| { | { | ||||||
|     public static IEnumerable<UserDto> GetUsers() |     public static void Register(RouteGroupBuilder parentGroup) | ||||||
|     { |     { | ||||||
|         return [ |         var group = parentGroup.MapGroup("/users"); | ||||||
|             new() {Id="1234", UserName = "xxx"}, |  | ||||||
|             new() {Id="5678", UserName = "yyy"}, |         group.MapPost("/login", LoginUser); | ||||||
|         ]; |         group.MapGet("/", GetAllUsers).RequireAuthorization(); | ||||||
|  |         group.MapGet("/{id}", GetUserById).RequireAuthorization(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void Register(WebApplication app) |     public static IResult LoginUser(LoginRequestDto loginRequest, IUserService userService, ITokenService tokenService) | ||||||
|     { |     { | ||||||
|         var group = app.MapGroup("/users"); |         if (string.IsNullOrEmpty(loginRequest.UserName) || string.IsNullOrEmpty(loginRequest.Password)) | ||||||
|  |         { | ||||||
|  |             return Results.BadRequest("Username and password are required."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         group.MapGet("/", GetAllUsers); |         // Password hashing and salting will be implemented later. | ||||||
|  |         var user = userService.GetUserByUsername(loginRequest.UserName); | ||||||
|  |  | ||||||
|  |         if (user == null || user.Password != loginRequest.Password) | ||||||
|  |         { | ||||||
|  |             return Results.Unauthorized(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var token = tokenService.GenerateToken(user); | ||||||
|  |         return Results.Ok(new LoginResponseDto { Token = token }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static IResult GetAllUsers() |     public static IResult GetAllUsers(IUserService userService) | ||||||
|     { |     { | ||||||
|         return Results.Ok(GetUsers()); |         var users = userService.GetUsers() | ||||||
|  |             .Select(u => new UserDto { Id = u.Id, UserName = u.UserName }); | ||||||
|  |         return Results.Ok(users); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IResult GetUserById(string id, IUserService userService) | ||||||
|  |     { | ||||||
|  |         var user = userService.GetUserById(id); | ||||||
|  |         if (user == null) | ||||||
|  |             return Results.NotFound(); | ||||||
|  |  | ||||||
|  |         return Results.Ok(new UserDto { Id = user.Id, UserName = user.UserName }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										35
									
								
								OptixServe.Api/Endpoints/VersionEndpoint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								OptixServe.Api/Endpoints/VersionEndpoint.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | using System.Text.Json.Serialization; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
|  | using OptixServe.Api.Configuration; | ||||||
|  | using OptixServe.Api.Dtos; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Api.Endpoints; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | [JsonSerializable(typeof(string))] | ||||||
|  | [JsonSerializable(typeof(CommonErrorDto))] | ||||||
|  | public partial class VersionJsonContext : JsonSerializerContext { } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// This is a endpoint ONLY FOR TEST! | ||||||
|  | /// Should not expect ANY stable behavior on it! | ||||||
|  | /// </summary> | ||||||
|  | public static class VersionEndpoint | ||||||
|  | { | ||||||
|  |     public static void Register(RouteGroupBuilder parentGroup) | ||||||
|  |     { | ||||||
|  |         var group = parentGroup.MapGroup("/version"); | ||||||
|  |  | ||||||
|  |         group.MapGet("/", () => "v1"); | ||||||
|  |         group.MapGet("/test/dbconfig", (IOptions<OptixServeSettings> appSettings) => | ||||||
|  |         { | ||||||
|  |             var dbType = appSettings.Value.Database?.Type; | ||||||
|  |             var dbHost = appSettings.Value.Database?.Host; | ||||||
|  |             return Results.Ok(new CommonErrorDto | ||||||
|  |             { | ||||||
|  |                 Message = $"Set up {dbType} database on {dbHost}" | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -5,7 +5,13 @@ | |||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" /> | ||||||
|     <PackageReference Include="System.CommandLine" Version="2.0.0-beta5.25306.1" /> |     <PackageReference Include="System.CommandLine" Version="2.0.0-beta5.25306.1" /> | ||||||
|  |     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6"> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <PropertyGroup> |   <PropertyGroup> | ||||||
| @ -13,7 +19,7 @@ | |||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|     <InvariantGlobalization>true</InvariantGlobalization> |     <InvariantGlobalization>true</InvariantGlobalization> | ||||||
|     <PublishAot>true</PublishAot> |     <PublishAot>false</PublishAot> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|  | |||||||
| @ -1,5 +1,13 @@ | |||||||
| using System.CommandLine; | using System.CommandLine; | ||||||
|  | using System.Text; | ||||||
|  | using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||||
|  | using Microsoft.IdentityModel.Tokens; | ||||||
|  | using OptixServe.Api.Configuration; | ||||||
| using OptixServe.Api.Endpoints; | using OptixServe.Api.Endpoints; | ||||||
|  | using OptixServe.Api.Services; | ||||||
|  | using OptixServe.Core.Data; | ||||||
|  | using OptixServe.Core.Services; | ||||||
|  | using OptixServe.Api.Utilites; | ||||||
|  |  | ||||||
| class Program | class Program | ||||||
| { | { | ||||||
| @ -41,10 +49,22 @@ class Program | |||||||
|             var configFile = parseResult.GetValue(configOption); |             var configFile = parseResult.GetValue(configOption); | ||||||
|             builder.AddConfigurationWithCommand(configFile); |             builder.AddConfigurationWithCommand(configFile); | ||||||
|  |  | ||||||
|  |             builder.RegisterServices(); | ||||||
|             builder.RegiserJsonContext(); |             builder.RegiserJsonContext(); | ||||||
|  |  | ||||||
|             var app = builder.Build(); |             var app = builder.Build(); | ||||||
|             app.RegisterEndpoints(); |  | ||||||
|  |             app.UseAuthentication(); | ||||||
|  |             app.UseAuthorization(); | ||||||
|  |  | ||||||
|  |             using (var scope = app.Services.CreateScope()) | ||||||
|  |             { | ||||||
|  |                 var initializer = scope.ServiceProvider.GetRequiredService<DbInitializer>(); | ||||||
|  |                 initializer.Initialize(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var apiGroup = app.MapGroup("api/v1"); | ||||||
|  |             StartupHelper.RegisterEndpoints(apiGroup); | ||||||
|  |  | ||||||
|             app.Run(); |             app.Run(); | ||||||
|         }); |         }); | ||||||
| @ -59,29 +79,8 @@ class Program | |||||||
| /// <summary> | /// <summary> | ||||||
| /// Contains extension methods for WebApplicationBuilder and WebApplication | /// Contains extension methods for WebApplicationBuilder and WebApplication | ||||||
| /// </summary> | /// </summary> | ||||||
| static class ExtensionMethods | static class StartupHelper | ||||||
| { | { | ||||||
|     /// <summary> |  | ||||||
|     /// Registers all API endpoints |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="app">WebApplication instance</param> |  | ||||||
|     public static void RegisterEndpoints(this WebApplication app) |  | ||||||
|     { |  | ||||||
|         UserEndpoint.Register(app); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// Configures JSON serialization options with custom context |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="builder">WebApplicationBuilder instance</param> |  | ||||||
|     public static void RegiserJsonContext(this WebApplicationBuilder builder) |  | ||||||
|     { |  | ||||||
|         builder.Services.ConfigureHttpJsonOptions(options => |  | ||||||
|         { |  | ||||||
|             options.SerializerOptions.TypeInfoResolverChain.Add(UserJsonContext.Default); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Adds configuration sources to the application builder |     /// Adds configuration sources to the application builder | ||||||
|     /// </summary> |     /// </summary> | ||||||
| @ -105,4 +104,78 @@ static class ExtensionMethods | |||||||
|  |  | ||||||
|         builder.Configuration.AddConfiguration(configurationBuilder.Build()); |         builder.Configuration.AddConfiguration(configurationBuilder.Build()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Configures DbContext services | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="services"></param> | ||||||
|  |     /// <param name="configuration"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public static IServiceCollection AddAppDatabase(this IServiceCollection services, DatabaseSettings dbSettings) | ||||||
|  |     { | ||||||
|  |         services.AddDbContext<AppDbContext>(options => DatabaseHelper.ConfigureDbContext(options, dbSettings)); | ||||||
|  |         return services; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Configures services for DI | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="builder">WebApplicationBuilder instance</param> | ||||||
|  |     public static void RegisterServices(this WebApplicationBuilder builder) | ||||||
|  |     { | ||||||
|  |         // Add configuration class | ||||||
|  |         var optixSettigns = builder.Configuration.GetSection("OptixServe"); | ||||||
|  |         var onConfigSettings = optixSettigns.Get<OptixServeSettings>(); | ||||||
|  |         builder.Services.Configure<OptixServeSettings>(optixSettigns); | ||||||
|  |  | ||||||
|  |         // Add DBContext class | ||||||
|  |         builder.Services.AddAppDatabase(onConfigSettings?.Database!); | ||||||
|  |         builder.Services.AddScoped<DbInitializer>(); | ||||||
|  |  | ||||||
|  |         // Application services | ||||||
|  |         builder.Services.AddScoped<IUserService, UserService>(); | ||||||
|  |         builder.Services.AddScoped<ITokenService, TokenService>(); | ||||||
|  |  | ||||||
|  |         // Add Authentication and Authorization | ||||||
|  |         builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) | ||||||
|  |             .AddJwtBearer(options => | ||||||
|  |             { | ||||||
|  |                 var jwtSettings = onConfigSettings?.Jwt ?? throw new ArgumentNullException(nameof(builder), "JWT settings are not configured."); | ||||||
|  |                 options.TokenValidationParameters = new TokenValidationParameters | ||||||
|  |                 { | ||||||
|  |                     ValidateIssuer = true, | ||||||
|  |                     ValidateAudience = true, | ||||||
|  |                     ValidateLifetime = true, | ||||||
|  |                     ValidateIssuerSigningKey = true, | ||||||
|  |                     ValidIssuer = jwtSettings.Issuer, | ||||||
|  |                     ValidAudience = jwtSettings.Audience, | ||||||
|  |                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret)) | ||||||
|  |                 }; | ||||||
|  |             }); | ||||||
|  |         builder.Services.AddAuthorization(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Configures JSON serialization options with custom context | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="builder">WebApplicationBuilder instance</param> | ||||||
|  |     public static void RegiserJsonContext(this WebApplicationBuilder builder) | ||||||
|  |     { | ||||||
|  |         builder.Services.ConfigureHttpJsonOptions(options => | ||||||
|  |         { | ||||||
|  |             options.SerializerOptions.TypeInfoResolverChain.Add(UserJsonContext.Default); | ||||||
|  |             options.SerializerOptions.TypeInfoResolverChain.Add(VersionJsonContext.Default); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Registers all API endpoints | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="rootGroup">Root RouteGroupBuilder instance</param> | ||||||
|  |     public static void RegisterEndpoints(RouteGroupBuilder rootGroup) | ||||||
|  |     { | ||||||
|  |         UserEndpoint.Register(rootGroup); | ||||||
|  |         VersionEndpoint.Register(rootGroup); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
							
								
								
									
										44
									
								
								OptixServe.Api/Services/TokenService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								OptixServe.Api/Services/TokenService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | using System.IdentityModel.Tokens.Jwt; | ||||||
|  | using System.Security.Claims; | ||||||
|  | using System.Text; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
|  | using Microsoft.IdentityModel.Tokens; | ||||||
|  | using OptixServe.Api.Configuration; | ||||||
|  | using OptixServe.Core.Models; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Api.Services; | ||||||
|  |  | ||||||
|  | public interface ITokenService | ||||||
|  | { | ||||||
|  |     public string GenerateToken(User user); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public class TokenService(IOptions<OptixServeSettings> optixServeSettings) : ITokenService | ||||||
|  | { | ||||||
|  |     private readonly JwtSettings _jwtSettings = optixServeSettings.Value.Jwt ?? throw new ArgumentNullException(nameof(optixServeSettings), "JWT settings are not configured."); | ||||||
|  |  | ||||||
|  |     public string GenerateToken(User user) | ||||||
|  |     { | ||||||
|  |         var tokenHandler = new JwtSecurityTokenHandler(); | ||||||
|  |         var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret); | ||||||
|  |  | ||||||
|  |         var claims = new List<Claim> | ||||||
|  |         { | ||||||
|  |             new (ClaimTypes.NameIdentifier, user.Id.ToString()), | ||||||
|  |             new (ClaimTypes.Name, user.UserName) | ||||||
|  |             // Add roles if applicable: new Claim(ClaimTypes.Role, user.Role) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var tokenDescriptor = new SecurityTokenDescriptor | ||||||
|  |         { | ||||||
|  |             Subject = new ClaimsIdentity(claims), | ||||||
|  |             Expires = DateTime.UtcNow.AddMinutes(_jwtSettings.TokenExpirationMinutes), | ||||||
|  |             Issuer = _jwtSettings.Issuer, | ||||||
|  |             Audience = _jwtSettings.Audience, | ||||||
|  |             SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var token = tokenHandler.CreateToken(tokenDescriptor); | ||||||
|  |         return tokenHandler.WriteToken(token); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								OptixServe.Api/Utilites/DatabaseHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								OptixServe.Api/Utilites/DatabaseHelper.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using OptixServe.Api.Configuration; | ||||||
|  | using OptixServe.Core.Data; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Api.Utilites; | ||||||
|  |  | ||||||
|  | public static class DatabaseHelper | ||||||
|  | { | ||||||
|  |     public static string BuildConnectionString(DatabaseSettings dbSettings) | ||||||
|  |     { | ||||||
|  |         return dbSettings.Type switch | ||||||
|  |         { | ||||||
|  |             DatabaseType.Sqlite => $"Data Source={dbSettings.Host ?? "optixserve.db"}", | ||||||
|  |             DatabaseType.MySQL => throw new NotSupportedException("MySQL connection is not yet implemented"), | ||||||
|  |             _ => throw new NotSupportedException($"Database type {dbSettings.Type} is not supported") | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void ConfigureDbContext(DbContextOptionsBuilder options, DatabaseSettings dbSettings) | ||||||
|  |     { | ||||||
|  |         if (dbSettings?.Type == DatabaseType.Sqlite) | ||||||
|  |         { | ||||||
|  |             var dbPath = dbSettings.Host ?? "optixserve.db"; | ||||||
|  |             var connectionString = $"Data Source={dbPath}"; | ||||||
|  |  | ||||||
|  |             options.UseSqlite(connectionString, b => b.MigrationsAssembly("OptixServe.Api")); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             throw new NotImplementedException("Only SQLite database is currently supported"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								OptixServe.Api/Utilites/DesignTimeDbContextFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								OptixServe.Api/Utilites/DesignTimeDbContextFactory.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.EntityFrameworkCore.Design; | ||||||
|  | using OptixServe.Api.Configuration; | ||||||
|  | using OptixServe.Core.Data; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Api.Utilites; | ||||||
|  |  | ||||||
|  | public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> | ||||||
|  | { | ||||||
|  |     public AppDbContext CreateDbContext(string[] args) | ||||||
|  |     { | ||||||
|  |         var configuration = ConfigurationHelper.CreateDefaultBuilder().Build(); | ||||||
|  |  | ||||||
|  |         var dbSettings = configuration.GetSection("OptixServe:Database").Get<DatabaseSettings>()!; | ||||||
|  |         var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); | ||||||
|  |         DatabaseHelper.ConfigureDbContext(optionsBuilder, dbSettings); | ||||||
|  |  | ||||||
|  |         return new AppDbContext(optionsBuilder.Options); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -5,5 +5,21 @@ | |||||||
|       "Microsoft.AspNetCore": "Warning" |       "Microsoft.AspNetCore": "Warning" | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "AllowedHosts": "*" |   "AllowedHosts": "*", | ||||||
|  |   "OptixServe": { | ||||||
|  |     "Api": { | ||||||
|  |       "Listen": "0.0.0.0", | ||||||
|  |       "Port": "54321" | ||||||
|  |     }, | ||||||
|  |     "Database": { | ||||||
|  |       "Type": "Sqlite", | ||||||
|  |       "Host": "optixserve.db" | ||||||
|  |     }, | ||||||
|  |     "Jwt": { | ||||||
|  |       "Secret": "YOUR_SECRET_KEY_HERE_DO_NOT_SHARE_THIS_AND_MAKE_IT_LONG_ENOUGH", | ||||||
|  |       "Issuer": "OptixServe", | ||||||
|  |       "Audience": "OptixServeUsers", | ||||||
|  |       "TokenExpirationMinutes": 60 | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
							
								
								
									
										21
									
								
								OptixServe.Core/Data/AppDbContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								OptixServe.Core/Data/AppDbContext.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using OptixServe.Core.Models; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Core.Data; | ||||||
|  |  | ||||||
|  | public class AppDbContext(DbContextOptions options) : DbContext(options) | ||||||
|  | { | ||||||
|  |     public DbSet<User> Users { get; set; } = null!; | ||||||
|  |  | ||||||
|  |     protected override void OnModelCreating(ModelBuilder modelBuilder) | ||||||
|  |     { | ||||||
|  |         modelBuilder.Entity<User>(user => | ||||||
|  |         { | ||||||
|  |             user.HasKey(u => u.Id); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         modelBuilder.Entity<User>().HasData([ | ||||||
|  |             new() {Id = "1", UserName = "admin", Password = "admin12345"} | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								OptixServe.Core/Data/DbInitializer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								OptixServe.Core/Data/DbInitializer.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | namespace OptixServe.Core.Data; | ||||||
|  |  | ||||||
|  | public class DbInitializer(AppDbContext dbContext) | ||||||
|  | { | ||||||
|  |     private readonly AppDbContext _context = dbContext; | ||||||
|  |  | ||||||
|  |     public void Initialize() | ||||||
|  |     { | ||||||
|  |         _context.Database.EnsureCreated(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,8 +1,15 @@ | |||||||
| namespace OptixServe.Core.Models; | namespace OptixServe.Core.Models; | ||||||
|  |  | ||||||
|  | public enum PrivilegeGroup | ||||||
|  | { | ||||||
|  |     Admin, | ||||||
|  |     User, | ||||||
|  | } | ||||||
|  |  | ||||||
| public record User | public record User | ||||||
| { | { | ||||||
|     public required string Id { get; set; } |     public required string Id { get; set; } | ||||||
|     public required string UserName { get; set; } |     public required string UserName { get; set; } | ||||||
|     public required string Password { get; set; } |     public string? Password { get; set; } | ||||||
|  |     public PrivilegeGroup PrivilegeGroup { get; set; } = PrivilegeGroup.User; | ||||||
| } | } | ||||||
| @ -6,4 +6,8 @@ | |||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								OptixServe.Core/Services/UserService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								OptixServe.Core/Services/UserService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | using OptixServe.Core.Data; | ||||||
|  | using OptixServe.Core.Models; | ||||||
|  |  | ||||||
|  | namespace OptixServe.Core.Services; | ||||||
|  |  | ||||||
|  | public interface IUserService | ||||||
|  | { | ||||||
|  |     IEnumerable<User> GetUsers(); | ||||||
|  |     User? GetUserById(string id); | ||||||
|  |     User? GetUserByUsername(string username); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public class UserService(AppDbContext dbContext) : IUserService | ||||||
|  | { | ||||||
|  |     private readonly AppDbContext _dbContext = dbContext; | ||||||
|  |  | ||||||
|  |     public User? GetUserById(string id) | ||||||
|  |     { | ||||||
|  |         return _dbContext.Users.FirstOrDefault(u => u.Id == id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public User? GetUserByUsername(string username) | ||||||
|  |     { | ||||||
|  |         return _dbContext.Users.FirstOrDefault(u => u.UserName == username); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public IEnumerable<User> GetUsers() | ||||||
|  |     { | ||||||
|  |         return _dbContext.Users.AsEnumerable(); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user