diff --git a/OptixServe.Api/Configuration/AppSettings.cs b/OptixServe.Api/Configuration/AppSettings.cs
new file mode 100644
index 0000000..751b9eb
--- /dev/null
+++ b/OptixServe.Api/Configuration/AppSettings.cs
@@ -0,0 +1,25 @@
+namespace OptixServe.Api.Configuration;
+
+public record OptixServeSettings
+{
+ public ApiSettings? Api { get; set; } = new();
+ public DatabaseSettings? Database { get; set; } = new();
+}
+
+public record ApiSettings
+{
+ public string? Listen { get; set; } = "127.0.0.1";
+ public int? Port { get; set; } = 10086;
+}
+
+public enum DatabaseType
+{
+ Sqlite,
+ MySQL
+}
+
+public record DatabaseSettings
+{
+ public DatabaseType Type { get; set; } = DatabaseType.Sqlite;
+ public string? Host { get; set; }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/Configuration/ConfigurationHelper.cs b/OptixServe.Api/Configuration/ConfigurationHelper.cs
new file mode 100644
index 0000000..4c222e3
--- /dev/null
+++ b/OptixServe.Api/Configuration/ConfigurationHelper.cs
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/Dtos/Error.cs b/OptixServe.Api/Dtos/Error.cs
new file mode 100644
index 0000000..eb1d505
--- /dev/null
+++ b/OptixServe.Api/Dtos/Error.cs
@@ -0,0 +1,6 @@
+namespace OptixServe.Api.Dtos;
+
+public record CommonErrorDto
+{
+ public string? Message { get; set; }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/Endpoints/VersionEndpoint.cs b/OptixServe.Api/Endpoints/VersionEndpoint.cs
new file mode 100644
index 0000000..6eb01ec
--- /dev/null
+++ b/OptixServe.Api/Endpoints/VersionEndpoint.cs
@@ -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 { }
+
+
+///
+/// This is a endpoint ONLY FOR TEST!
+/// Should not expect ANY stable behavior on it!
+///
+public static class VersionEndpoint
+{
+ public static void Register(RouteGroupBuilder parentGroup)
+ {
+ var group = parentGroup.MapGroup("/version");
+
+ group.MapGet("/", () => "v1");
+ group.MapGet("/test/dbconfig", (IOptions 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}"
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/OptixServe.Api.csproj b/OptixServe.Api/OptixServe.Api.csproj
index 3815467..f69bfae 100644
--- a/OptixServe.Api/OptixServe.Api.csproj
+++ b/OptixServe.Api/OptixServe.Api.csproj
@@ -1,11 +1,16 @@
-
-
+
+
-
-
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
@@ -13,7 +18,7 @@
enable
enable
true
- true
+ false
diff --git a/OptixServe.Api/Program.cs b/OptixServe.Api/Program.cs
index 4952dd1..8da4d29 100644
--- a/OptixServe.Api/Program.cs
+++ b/OptixServe.Api/Program.cs
@@ -1,6 +1,9 @@
using System.CommandLine;
+using OptixServe.Api.Configuration;
using OptixServe.Api.Endpoints;
+using OptixServe.Core.Data;
using OptixServe.Core.Services;
+using OptixServe.Api.Utilites;
class Program
{
@@ -46,8 +49,15 @@ class Program
builder.RegiserJsonContext();
var app = builder.Build();
+
+ using (var scope = app.Services.CreateScope())
+ {
+ var initializer = scope.ServiceProvider.GetRequiredService();
+ initializer.Initialize();
+ }
+
var apiGroup = app.MapGroup("api/v1");
- ExtensionMethods.RegisterEndpoints(apiGroup);
+ StartupHelper.RegisterEndpoints(apiGroup);
app.Run();
});
@@ -62,7 +72,7 @@ class Program
///
/// Contains extension methods for WebApplicationBuilder and WebApplication
///
-static class ExtensionMethods
+static class StartupHelper
{
///
/// Adds configuration sources to the application builder
@@ -88,6 +98,37 @@ static class ExtensionMethods
builder.Configuration.AddConfiguration(configurationBuilder.Build());
}
+ ///
+ /// Configures DbContext services
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddAppDatabase(this IServiceCollection services, DatabaseSettings dbSettings)
+ {
+ services.AddDbContext(options => DatabaseHelper.ConfigureDbContext(options, dbSettings));
+ return services;
+ }
+
+ ///
+ /// Configures services for DI
+ ///
+ /// WebApplicationBuilder instance
+ public static void RegisterServices(this WebApplicationBuilder builder)
+ {
+ // Add configuration class
+ var optixSettigns = builder.Configuration.GetSection("OptixServe");
+ var onConfigSettings = optixSettigns.Get();
+ builder.Services.Configure(optixSettigns);
+
+ // Add DBContext class
+ builder.Services.AddAppDatabase(onConfigSettings?.Database!);
+ builder.Services.AddScoped();
+
+ // Application services
+ builder.Services.AddScoped();
+ }
+
///
/// Configures JSON serialization options with custom context
///
@@ -97,26 +138,18 @@ static class ExtensionMethods
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Add(UserJsonContext.Default);
+ options.SerializerOptions.TypeInfoResolverChain.Add(VersionJsonContext.Default);
});
}
- ///
- /// Configures services for DI
- ///
- ///
- public static void RegisterServices(this WebApplicationBuilder builder)
- {
- // Application services
- builder.Services.AddScoped();
- }
-
///
/// Registers all API endpoints
///
- /// WebApplication instance
+ /// Root RouteGroupBuilder instance
public static void RegisterEndpoints(RouteGroupBuilder rootGroup)
{
UserEndpoint.Register(rootGroup);
+ VersionEndpoint.Register(rootGroup);
}
}
\ No newline at end of file
diff --git a/OptixServe.Api/Utilites/DatabaseHelper.cs b/OptixServe.Api/Utilites/DatabaseHelper.cs
new file mode 100644
index 0000000..3107d66
--- /dev/null
+++ b/OptixServe.Api/Utilites/DatabaseHelper.cs
@@ -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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/Utilites/DesignTimeDbContextFactory.cs b/OptixServe.Api/Utilites/DesignTimeDbContextFactory.cs
new file mode 100644
index 0000000..f9d01d8
--- /dev/null
+++ b/OptixServe.Api/Utilites/DesignTimeDbContextFactory.cs
@@ -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
+{
+ public AppDbContext CreateDbContext(string[] args)
+ {
+ var configuration = ConfigurationHelper.CreateDefaultBuilder().Build();
+
+ var dbSettings = configuration.GetSection("OptixServe:Database").Get()!;
+ var optionsBuilder = new DbContextOptionsBuilder();
+ DatabaseHelper.ConfigureDbContext(optionsBuilder, dbSettings);
+
+ return new AppDbContext(optionsBuilder.Options);
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Api/appsettings.json b/OptixServe.Api/appsettings.json
index 4d56694..0d75ce5 100644
--- a/OptixServe.Api/appsettings.json
+++ b/OptixServe.Api/appsettings.json
@@ -5,5 +5,15 @@
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "OptixServe": {
+ "Api": {
+ "Listen": "0.0.0.0",
+ "Port": "54321"
+ },
+ "Database": {
+ "Type": "Sqlite",
+ "Host": "optixserve.db"
+ }
+ }
}
diff --git a/OptixServe.Core/Data/AppDbContext.cs b/OptixServe.Core/Data/AppDbContext.cs
new file mode 100644
index 0000000..2116fc3
--- /dev/null
+++ b/OptixServe.Core/Data/AppDbContext.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using OptixServe.Core.Models;
+
+namespace OptixServe.Core.Data;
+
+public class AppDbContext(DbContextOptions options) : DbContext(options)
+{
+ public DbSet Users { get; set; } = null!;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity(user =>
+ {
+ user.HasKey(u => u.Id);
+ });
+
+ modelBuilder.Entity().HasData([
+ new() {Id = "1", UserName = "admin", Password = "admin12345"}
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Core/Data/DbInitializer.cs b/OptixServe.Core/Data/DbInitializer.cs
new file mode 100644
index 0000000..bec11cc
--- /dev/null
+++ b/OptixServe.Core/Data/DbInitializer.cs
@@ -0,0 +1,11 @@
+namespace OptixServe.Core.Data;
+
+public class DbInitializer(AppDbContext dbContext)
+{
+ private readonly AppDbContext _context = dbContext;
+
+ public void Initialize()
+ {
+ _context.Database.EnsureCreated();
+ }
+}
\ No newline at end of file
diff --git a/OptixServe.Core/Models/User.cs b/OptixServe.Core/Models/User.cs
index 671e88e..3dbd0b4 100644
--- a/OptixServe.Core/Models/User.cs
+++ b/OptixServe.Core/Models/User.cs
@@ -1,8 +1,15 @@
namespace OptixServe.Core.Models;
+public enum PrivilegeGroup
+{
+ Admin,
+ User,
+}
+
public record User
{
public required string Id { 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;
}
\ No newline at end of file
diff --git a/OptixServe.Core/OptixServe.Core.csproj b/OptixServe.Core/OptixServe.Core.csproj
index 125f4c9..354caa7 100644
--- a/OptixServe.Core/OptixServe.Core.csproj
+++ b/OptixServe.Core/OptixServe.Core.csproj
@@ -6,4 +6,8 @@
enable
+
+
+
+
diff --git a/OptixServe.Core/Services/UserService.cs b/OptixServe.Core/Services/UserService.cs
index 8d9d292..01c4d7d 100644
--- a/OptixServe.Core/Services/UserService.cs
+++ b/OptixServe.Core/Services/UserService.cs
@@ -1,3 +1,4 @@
+using OptixServe.Core.Data;
using OptixServe.Core.Models;
namespace OptixServe.Core.Services;
@@ -5,21 +6,20 @@ namespace OptixServe.Core.Services;
public interface IUserService
{
IEnumerable GetUsers();
- User? GetUserById(string Id);
+ User? GetUserById(string id);
}
-public class UserService : IUserService
+public class UserService(AppDbContext dbContext) : IUserService
{
- public User? GetUserById(string Id)
+ private readonly AppDbContext _dbContext = dbContext;
+
+ public User? GetUserById(string id)
{
- throw new NotImplementedException();
+ return _dbContext.Users.FirstOrDefault(u => u.Id == id);
}
public IEnumerable GetUsers()
{
- return [
- new() { Id = "1234", UserName = "xxx", Password = "pass1" },
- new() { Id = "5678", UserName = "yyy", Password = "pass2" }
- ];
+ return _dbContext.Users.AsEnumerable();
}
}
\ No newline at end of file