diff --git a/src/RestAPI.sln b/src/RestAPI.sln new file mode 100644 index 0000000..5a3dd9a --- /dev/null +++ b/src/RestAPI.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30615.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestAPI", "RestAPI\RestAPI.csproj", "{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{197287CE-CDEF-4FF8-9846-EFF913496E1A}" + ProjectSection(SolutionItems) = preProject + ..\.env.default = ..\.env.default + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docker", "Docker", "{98D984C1-F9F9-4D41-8CDA-4D87F5C0A774}" + ProjectSection(SolutionItems) = preProject + ..\docker-compose.yml = ..\docker-compose.yml + RestAPI\Dockerfile = RestAPI\Dockerfile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github", "Github", "{766907EE-8F38-477E-BD1D-F34EA54AC784}" + ProjectSection(SolutionItems) = preProject + ..\.github\LICENSE = ..\.github\LICENSE + ..\.github\README = ..\.github\README + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {98D984C1-F9F9-4D41-8CDA-4D87F5C0A774} = {197287CE-CDEF-4FF8-9846-EFF913496E1A} + {766907EE-8F38-477E-BD1D-F34EA54AC784} = {197287CE-CDEF-4FF8-9846-EFF913496E1A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {972DCCF9-29C3-427D-82B0-1DA966964D24} + EndGlobalSection +EndGlobal diff --git a/src/RestAPI/RestAPI/Clients/FirebirdClient.cs b/src/RestAPI/Clients/FirebirdClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Clients/FirebirdClient.cs rename to src/RestAPI/Clients/FirebirdClient.cs diff --git a/src/RestAPI/RestAPI/Clients/MongoDbClient.cs b/src/RestAPI/Clients/MongoDbClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Clients/MongoDbClient.cs rename to src/RestAPI/Clients/MongoDbClient.cs diff --git a/src/RestAPI/RestAPI/Clients/MySqlClient.cs b/src/RestAPI/Clients/MySqlClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Clients/MySqlClient.cs rename to src/RestAPI/Clients/MySqlClient.cs diff --git a/src/RestAPI/RestAPI/Clients/NpgsqlClient.cs b/src/RestAPI/Clients/NpgsqlClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Clients/NpgsqlClient.cs rename to src/RestAPI/Clients/NpgsqlClient.cs diff --git a/src/RestAPI/RestAPI/Clients/SQLiteClient.cs b/src/RestAPI/Clients/SQLiteClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Clients/SQLiteClient.cs rename to src/RestAPI/Clients/SQLiteClient.cs diff --git a/src/RestAPI/RestAPI/Controllers/TableController.cs b/src/RestAPI/Controllers/TableController.cs similarity index 100% rename from src/RestAPI/RestAPI/Controllers/TableController.cs rename to src/RestAPI/Controllers/TableController.cs diff --git a/src/RestAPI/RestAPI/ExceptionFilters/HttpResponseExceptionFilter.cs b/src/RestAPI/ExceptionFilters/HttpResponseExceptionFilter.cs similarity index 100% rename from src/RestAPI/RestAPI/ExceptionFilters/HttpResponseExceptionFilter.cs rename to src/RestAPI/ExceptionFilters/HttpResponseExceptionFilter.cs diff --git a/src/RestAPI/RestAPI/Exceptions/HttpResponseException.cs b/src/RestAPI/Exceptions/HttpResponseException.cs similarity index 100% rename from src/RestAPI/RestAPI/Exceptions/HttpResponseException.cs rename to src/RestAPI/Exceptions/HttpResponseException.cs diff --git a/src/RestAPI/RestAPI/Interfaces/IDatabaseClient.cs b/src/RestAPI/Interfaces/IDatabaseClient.cs similarity index 100% rename from src/RestAPI/RestAPI/Interfaces/IDatabaseClient.cs rename to src/RestAPI/Interfaces/IDatabaseClient.cs diff --git a/src/RestAPI/RestAPI/OutputFormatters/HtmlOutputFormatter.cs b/src/RestAPI/OutputFormatters/HtmlOutputFormatter.cs similarity index 100% rename from src/RestAPI/RestAPI/OutputFormatters/HtmlOutputFormatter.cs rename to src/RestAPI/OutputFormatters/HtmlOutputFormatter.cs diff --git a/src/RestAPI/Program.cs b/src/RestAPI/Program.cs new file mode 100644 index 0000000..b02f0e4 --- /dev/null +++ b/src/RestAPI/Program.cs @@ -0,0 +1,54 @@ +using dotenv.net; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.IO; + +namespace RestAPI +{ + public class Program + { + public static void Main(string[] args) + { + string envFile = Path.Combine(AppContext.BaseDirectory, ".env"); + + DotEnv.Config(false, filePath: envFile); + + CreateHostBuilder(args).Build().Run(); + } + + public static string[] GetUrls(IWebHostBuilder webBuilder) + { + List urls = new() + { + "http://0.0.0.0:80" + }; + + var httpsPort = Environment.GetEnvironmentVariable("HTTPSPORT"); + + if (!string.IsNullOrEmpty(httpsPort)) + { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + if (!string.IsNullOrEmpty(environment) && !environment.ToLowerInvariant().StartsWith("dev")) + { + throw new InvalidOperationException("HTTPS not supported yet, a work around is to use a reverse proxy"); + } + + if (int.TryParse(httpsPort, out int port)) + { + urls.Add($"https://0.0.0.0:{port}"); + } + } + + return urls.ToArray(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup().UseUrls(GetUrls(webBuilder)); + }); + } +} diff --git a/src/RestAPI/RestAPI/Properties/launchSettings.json b/src/RestAPI/Properties/launchSettings.json similarity index 100% rename from src/RestAPI/RestAPI/Properties/launchSettings.json rename to src/RestAPI/Properties/launchSettings.json diff --git a/src/RestAPI/RestAPI.csproj b/src/RestAPI/RestAPI.csproj new file mode 100644 index 0000000..88f8bc6 --- /dev/null +++ b/src/RestAPI/RestAPI.csproj @@ -0,0 +1,23 @@ + + + + net5.0 + preview + + + + + + + + + + + + + + + + + + diff --git a/src/RestAPI/RestAPI.sln b/src/RestAPI/RestAPI.sln deleted file mode 100644 index 6803ca0..0000000 --- a/src/RestAPI/RestAPI.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30615.102 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestAPI", "RestAPI\RestAPI.csproj", "{3929B199-3AEB-41BA-993B-DA1AF2A73CA3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3929B199-3AEB-41BA-993B-DA1AF2A73CA3}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {972DCCF9-29C3-427D-82B0-1DA966964D24} - EndGlobalSection -EndGlobal diff --git a/src/RestAPI/RestAPI/.env.default b/src/RestAPI/RestAPI/.env.default deleted file mode 100644 index 15b040d..0000000 --- a/src/RestAPI/RestAPI/.env.default +++ /dev/null @@ -1,7 +0,0 @@ -DATASOURCE= -HOST= -HOSTPORT= -USERNAME= -PASSWORD= -DATABASE= -BLACKLISTEDFIELDS= \ No newline at end of file diff --git a/src/RestAPI/RestAPI/Program.cs b/src/RestAPI/RestAPI/Program.cs deleted file mode 100644 index 71c4b39..0000000 --- a/src/RestAPI/RestAPI/Program.cs +++ /dev/null @@ -1,27 +0,0 @@ -using dotenv.net; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using System; -using System.IO; - -namespace RestAPI -{ - public class Program - { - public static void Main(string[] args) - { - string envFile = Path.Combine(AppContext.BaseDirectory, ".env"); - - DotEnv.Config(false, filePath: envFile); - - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} diff --git a/src/RestAPI/RestAPI/RestAPI.csproj b/src/RestAPI/RestAPI/RestAPI.csproj deleted file mode 100644 index 748f6a5..0000000 --- a/src/RestAPI/RestAPI/RestAPI.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - - - - - - - - diff --git a/src/RestAPI/RestAPI/Startup.cs b/src/RestAPI/RestAPI/Startup.cs deleted file mode 100644 index be1a045..0000000 --- a/src/RestAPI/RestAPI/Startup.cs +++ /dev/null @@ -1,205 +0,0 @@ -using FirebirdSql.Data.FirebirdClient; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Data.Sqlite; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using MongoDB.Driver; -using MySqlConnector; -using Npgsql; -using RestAPI.Clients; -using RestAPI.ExceptionFilters; -using RestAPI.Interfaces; -using RestAPI.OutputFormatters; -using System; -using System.IO; -using System.Linq; - -namespace RestAPI -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - string dataSource = Environment.GetEnvironmentVariable("DATASOURCE"); - - if (dataSource != null) - { - string serverHost = Environment.GetEnvironmentVariable("HOST"); - string serverHostPort = Environment.GetEnvironmentVariable("HOSTPORT"); - string username = Environment.GetEnvironmentVariable("USERNAME"); - string password = Environment.GetEnvironmentVariable("PASSWORD"); - string database = Environment.GetEnvironmentVariable("DATABASE"); - - ushort hostPort = 0; - - if (!ushort.TryParse(serverHostPort, out hostPort)) - { - - } - - switch (dataSource.ToLowerInvariant()) - { - case "mysql": - case "mariadb": - { - if (hostPort == 0) - { - hostPort = 3306; - } - - services.AddSingleton(new MySqlClient(new MySqlConnectionStringBuilder - { - Server = serverHost, - Port = hostPort, - UserID = username, - Password = password, - Database = database, - AllowUserVariables = true - }.ConnectionString)); - } - break; - case "mongo": - case "mongodb": - { - if (hostPort == 0) - { - hostPort = 27017; - } - - MongoClientSettings builder = new MongoClientSettings(); - - string prefix = "mongodb"; - - if (!System.Net.IPAddress.TryParse(serverHost, out _)) - { - prefix += "+srv"; - } - - string connString; - - if (!string.IsNullOrEmpty(username)) - { - connString = string.Format( - "{0}://{1}:{2}@{3}:{4}/", - prefix, - username, - password, - serverHost, - hostPort - ); - } - else - { - connString = string.Format( - "{0}://{1}:{2}/", - prefix, - serverHost, - hostPort - ); - } - - services.AddSingleton(new MongoDbClient(connString, database)); - } - break; - case "firebird": - case "interbase": - { - //TODO: TEST FIREBASE - throw new NotImplementedException(); - - if (hostPort == 0) - { - hostPort = 3050; - } - - services.AddSingleton(new FirebirdClient(new FbConnectionStringBuilder - { - DataSource = serverHost, - Port = hostPort, - UserID = username, - Password = password, - ServerType = FbServerType.Default - }.ConnectionString)); - } - break; - case "pgsql": - case "postgresql": - { - if (hostPort == 0) - { - hostPort = 5432; - } - - services.AddSingleton(new NpgsqlClient(new NpgsqlConnectionStringBuilder - { - Host = serverHost, - Port = hostPort, - Database = database, - Username = username, - Password = password - }.ConnectionString)); - } - break; - case "sqlite": - { - //TODO: TEST SQLite - throw new NotImplementedException(); - - SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder(); - - var sqliteFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.sqlite", SearchOption.AllDirectories); - - if (File.Exists(database)) - { - builder.DataSource = database; - } - else if (!database.Contains(".sqlite")) - { - builder.DataSource = sqliteFiles.FirstOrDefault(x => x.Contains($"{database}.sqlite")); - } - - services.AddSingleton(new SQLiteClient(builder.ConnectionString)); - } - break; - } - } - - services.AddControllers(options => - { - options.Filters.Add(new HttpResponseExceptionFilter()); - options.OutputFormatters.Add(new HtmlOutputFormatter()); - }) - .AddXmlSerializerFormatters(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/src/RestAPI/Startup.cs b/src/RestAPI/Startup.cs new file mode 100644 index 0000000..83971cc --- /dev/null +++ b/src/RestAPI/Startup.cs @@ -0,0 +1,223 @@ +using FirebirdSql.Data.FirebirdClient; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MongoDB.Driver; +using MySqlConnector; +using Npgsql; +using RestAPI.Clients; +using RestAPI.ExceptionFilters; +using RestAPI.Interfaces; +using RestAPI.OutputFormatters; +using System; +using System.IO; +using System.Linq; + +namespace RestAPI +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + IDatabaseClient GetDatabase() + { + string dataSource = Environment.GetEnvironmentVariable("DATASOURCE"); + + string serverHost = Environment.GetEnvironmentVariable("HOST"); + string serverHostPort = Environment.GetEnvironmentVariable("HOSTPORT"); + string username = Environment.GetEnvironmentVariable("USERNAME"); + string password = Environment.GetEnvironmentVariable("PASSWORD"); + string database = Environment.GetEnvironmentVariable("DATABASE"); + + if (!ushort.TryParse(serverHostPort, out ushort hostPort)) + { + switch (dataSource.ToLowerInvariant()) + { + case "mysql": + case "mariadb": + hostPort = 3306; + break; + case "mongo": + case "mongodb": + hostPort = 27017; + break; + case "firebird": + case "interbase": + hostPort = 3050; + break; + case "pgsql": + case "postgresql": + hostPort = 5432; + break; + } + } + + switch (dataSource.ToLowerInvariant()) + { + case "mysql": + case "mariadb": + return new MySqlClient(new MySqlConnectionStringBuilder + { + Server = serverHost, + Port = hostPort, + UserID = username, + Password = password, + Database = database, + AllowUserVariables = true + }.ConnectionString); + case "mongo": + case "mongodb": + MongoClientSettings mongoBuilder = new(); + + string prefix = "mongodb"; + + if (!System.Net.IPAddress.TryParse(serverHost, out _)) + { + prefix += "+srv"; + } + + string connString; + + if (!string.IsNullOrEmpty(username)) + { + connString = string.Format( + "{0}://{1}:{2}@{3}:{4}/", + prefix, + username, + password, + serverHost, + (ushort)hostPort + ); + } + else + { + connString = string.Format( + "{0}://{1}:{2}/", + prefix, + serverHost, + (ushort)hostPort + ); + } + + return new MongoDbClient(connString, database); + case "firebird": + case "interbase": + //TODO: TEST FIREBIRD + throw new NotImplementedException(); + + return new FirebirdClient(new FbConnectionStringBuilder + { + DataSource = serverHost, + Port = hostPort, + UserID = username, + Password = password, + ServerType = FbServerType.Default + }.ConnectionString); + case "pgsql": + case "postgresql": + return new NpgsqlClient(new NpgsqlConnectionStringBuilder + { + Host = serverHost, + Port = hostPort, + Database = database, + Username = username, + Password = password + }.ConnectionString); + case "sqlite": + //TODO: TEST SQLite + throw new NotImplementedException(); + + SqliteConnectionStringBuilder sqliteBuilder = new(); + + var sqliteFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.sqlite", SearchOption.AllDirectories); + + if (File.Exists(database)) + { + sqliteBuilder.DataSource = database; + } + else if (!database.Contains(".sqlite")) + { + sqliteBuilder.DataSource = sqliteFiles.FirstOrDefault(x => x.Contains($"{database}.sqlite")); + } + + return new SQLiteClient(sqliteBuilder.ConnectionString); + } + + return null; + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + string dataSource = Environment.GetEnvironmentVariable("DATASOURCE"); + string httpsPort = Environment.GetEnvironmentVariable("HTTPSPORT"); + + if (!string.IsNullOrEmpty(dataSource)) + { + var database = GetDatabase(); + services.AddSingleton(database); + } + + if (!string.IsNullOrEmpty(httpsPort)) + { + throw new InvalidOperationException("HTTPS not supported yet, a work around is to use a reverse proxy"); + + if (int.TryParse(httpsPort, out int port)) + { + services.AddHsts(options => + { + options.Preload = true; + options.IncludeSubDomains = true; + options.MaxAge = TimeSpan.FromDays(60); + }); + + services.AddHttpsRedirection(options => + { + options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect; + options.HttpsPort = port; + }); + } + } + + services.AddControllers(options => + { + options.Filters.Add(new HttpResponseExceptionFilter()); + options.OutputFormatters.Add(new HtmlOutputFormatter()); + }) + .AddXmlSerializerFormatters(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + string httpsPort = Environment.GetEnvironmentVariable("HTTPSPORT"); + if (!string.IsNullOrEmpty(httpsPort)) + { + app.UseHttpsRedirection(); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/src/RestAPI/RestAPI/appsettings.Development.json b/src/RestAPI/appsettings.Development.json similarity index 100% rename from src/RestAPI/RestAPI/appsettings.Development.json rename to src/RestAPI/appsettings.Development.json diff --git a/src/RestAPI/RestAPI/appsettings.json b/src/RestAPI/appsettings.json similarity index 100% rename from src/RestAPI/RestAPI/appsettings.json rename to src/RestAPI/appsettings.json