CI/CD: Add GitLab CI/CD pipeline for Hostinger deployment

- Updated .gitlab-ci.yml with complete build, test, and deploy stages
- Added authentication redirect fix in Program.cs (302 redirect for admin routes)
- Fixed Cookie vs Bearer authentication conflict for admin panel
- Configure pipeline to build from .NET 9.0 source
- Deploy to Hostinger VPS with proper environment variables
- Include rollback capability for production deployments

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-01 13:10:48 +01:00
parent e61b055512
commit d31c0b4aeb
21 changed files with 5828 additions and 826 deletions

View File

@@ -1,3 +1,4 @@
using System.Net;
using LittleShop.Client.Configuration;
using LittleShop.Client.Http;
using LittleShop.Client.Services;
@@ -11,7 +12,9 @@ public static class ServiceCollectionExtensions
{
public static IServiceCollection AddLittleShopClient(
this IServiceCollection services,
Action<LittleShopClientOptions>? configureOptions = null)
Action<LittleShopClientOptions>? configureOptions = null,
bool useTorProxy = false,
int torSocksPort = 9050)
{
// Configure options
if (configureOptions != null)
@@ -26,7 +29,37 @@ public static class ServiceCollectionExtensions
// Register HTTP handlers
services.AddTransient<RetryPolicyHandler>();
services.AddTransient<ErrorHandlingMiddleware>();
// Helper function to configure SOCKS5 proxy if TOR is enabled
Func<IServiceProvider, HttpMessageHandler> createHandler = (serviceProvider) =>
{
if (useTorProxy)
{
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("LittleShop.Client.TorProxy");
var proxyUri = $"socks5://127.0.0.1:{torSocksPort}";
logger.LogInformation("LittleShop.Client: Configuring SOCKS5 proxy at {ProxyUri}", proxyUri);
return new SocketsHttpHandler
{
Proxy = new WebProxy(proxyUri)
{
BypassProxyOnLocal = false,
UseDefaultCredentials = false
},
UseProxy = true,
AllowAutoRedirect = false,
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2)
};
}
else
{
return new SocketsHttpHandler();
}
};
// Register main HTTP client
services.AddHttpClient<IAuthenticationService, AuthenticationService>((serviceProvider, client) =>
{
@@ -35,6 +68,7 @@ public static class ServiceCollectionExtensions
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(createHandler)
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{
@@ -50,6 +84,7 @@ public static class ServiceCollectionExtensions
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(createHandler)
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{
@@ -65,6 +100,7 @@ public static class ServiceCollectionExtensions
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(createHandler)
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{
@@ -72,7 +108,7 @@ public static class ServiceCollectionExtensions
var options = serviceProvider.GetRequiredService<IOptions<LittleShopClientOptions>>().Value;
return new RetryPolicyHandler(logger, options.MaxRetryAttempts);
});
services.AddHttpClient<ICustomerService, CustomerService>((serviceProvider, client) =>
{
var options = serviceProvider.GetRequiredService<IOptions<LittleShopClientOptions>>().Value;
@@ -80,6 +116,7 @@ public static class ServiceCollectionExtensions
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(createHandler)
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{
@@ -95,6 +132,7 @@ public static class ServiceCollectionExtensions
client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.ConfigurePrimaryHttpMessageHandler(createHandler)
.AddHttpMessageHandler<ErrorHandlingMiddleware>()
.AddHttpMessageHandler(serviceProvider =>
{