345ae0a4b5
CI/CD / CI · Admin API (dotnet build) (push) Successful in 41s
CI/CD / CI · Admin Web (tsc) (push) Failing after 5s
CI/CD / CI · Website (tsc) (push) Failing after 4s
CI/CD / CI · Koja (tsc) (push) Failing after 5s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m13s
CI/CD / CI · Dashboard (tsc) (push) Failing after 2m32s
CI/CD / Deploy · all services (push) Has been skipped
249 lines
11 KiB
C#
249 lines
11 KiB
C#
using System.Text;
|
|
using System.Text.Json.Serialization;
|
|
using FluentValidation;
|
|
using Hangfire;
|
|
using Hangfire.MemoryStorage;
|
|
using Hangfire.PostgreSql;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using Meezi.API.Hubs;
|
|
using Meezi.API.Configuration;
|
|
using Meezi.API.Jobs;
|
|
using Meezi.API.Services;
|
|
using Meezi.API.Services.Delivery;
|
|
using Meezi.Infrastructure.Services.Platform;
|
|
using Meezi.API.Services.Printing;
|
|
using Meezi.Core.Interfaces;
|
|
using Meezi.Infrastructure;
|
|
using Serilog;
|
|
using StackExchange.Redis;
|
|
|
|
namespace Meezi.API.Extensions;
|
|
|
|
public static class ServiceCollectionExtensions
|
|
{
|
|
public static IServiceCollection AddMeeziServices(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
services.AddHttpContextAccessor();
|
|
services.AddMeeziSecurity(configuration);
|
|
services.AddInfrastructure(configuration);
|
|
services.AddScoped<IAuthService, AuthService>();
|
|
services.AddScoped<IAuditLogService, AuditLogService>();
|
|
services.AddScoped<IConsumerAuthService, ConsumerAuthService>();
|
|
services.AddScoped<IConsumerOrdersService, ConsumerOrdersService>();
|
|
services.AddScoped<IKitchenStationService, KitchenStationService>();
|
|
services.AddScoped<INotificationInboxService, NotificationInboxService>();
|
|
services.AddScoped<IOrderNotificationService, OrderNotificationService>();
|
|
services.AddScoped<IWebsiteService, Meezi.Infrastructure.Services.WebsiteService>();
|
|
services.AddScoped<IJwtTokenService, JwtTokenService>();
|
|
services.AddSingleton<IRefreshTokenStore, RedisRefreshTokenStore>();
|
|
services.AddScoped<IPlanLimitChecker, PlanLimitChecker>();
|
|
services.AddScoped<IMenuService, MenuService>();
|
|
services.AddScoped<IBranchMenuService, BranchMenuService>();
|
|
services.AddScoped<IBranchIdentityService, BranchIdentityService>();
|
|
services.AddScoped<ITableService, TableService>();
|
|
services.AddScoped<IOrderService, OrderService>();
|
|
services.AddScoped<IKdsNotifier, KdsNotifier>();
|
|
services.AddScoped<ICustomerService, CustomerService>();
|
|
services.AddScoped<ICouponService, CouponService>();
|
|
services.AddScoped<ITaxService, TaxService>();
|
|
services.AddSingleton<IMediaStorageService, MediaStorageService>();
|
|
services.Configure<MenuAi3dOptions>(configuration.GetSection(MenuAi3dOptions.SectionName));
|
|
services.AddHttpClient("MenuAi3d");
|
|
services.AddHttpClient("OpenAi");
|
|
services.AddScoped<IOpenAiChatService, OpenAiChatService>();
|
|
services.AddScoped<ICoffeeAdvisorService, CoffeeAdvisorService>();
|
|
services.AddScoped<IMenuAi3dGenerationService, MenuAi3dGenerationService>();
|
|
services.AddScoped<IBranchLifecycleService, BranchLifecycleService>();
|
|
services.AddSingleton<ITerminalRegistryService, TerminalRegistryService>();
|
|
services.AddScoped<IInventoryService, InventoryService>();
|
|
services.AddScoped<ILoyaltyService, LoyaltyService>();
|
|
services.AddScoped<ISmsMarketingService, SmsMarketingService>();
|
|
services.AddScoped<IHrService, HrService>();
|
|
services.AddScoped<IReportService, ReportService>();
|
|
services.AddScoped<IDailyReportService, DailyReportService>();
|
|
services.AddScoped<IPublicService, PublicService>();
|
|
services.AddScoped<IReviewService, ReviewService>();
|
|
services.AddScoped<IPushDeviceService, PushDeviceService>();
|
|
services.AddScoped<IBillingService, BillingService>();
|
|
services.AddScoped<IBillingPaymentOrchestrator, BillingPaymentOrchestrator>();
|
|
services.Configure<DeliveryPlatformsOptions>(configuration.GetSection(DeliveryPlatformsOptions.SectionName));
|
|
services.PostConfigure<DeliveryPlatformsOptions>(opts =>
|
|
{
|
|
if (string.IsNullOrEmpty(opts.Snappfood.WebhookSecret))
|
|
opts.Snappfood.WebhookSecret = configuration["Snappfood:WebhookSecret"] ?? "";
|
|
if (string.IsNullOrEmpty(opts.Snappfood.ApiKey))
|
|
opts.Snappfood.ApiKey = configuration["Snappfood:ApiKey"] ?? "";
|
|
if (string.IsNullOrEmpty(opts.Snappfood.ApiBaseUrl))
|
|
opts.Snappfood.ApiBaseUrl = configuration["Snappfood:ApiBaseUrl"] ?? "";
|
|
});
|
|
|
|
services.AddScoped<IWebhookSignatureService, WebhookSignatureService>();
|
|
services.AddScoped<IOrderNormalizer, OrderNormalizer>();
|
|
services.AddScoped<ICommissionCalculator, CommissionCalculator>();
|
|
services.AddScoped<IDeliveryOrderProcessor, DeliveryOrderProcessor>();
|
|
services.AddScoped<IDeliveryWebhookIngressService, DeliveryWebhookIngressService>();
|
|
services.AddScoped<IDeliveryStatusSyncService, DeliveryStatusSyncService>();
|
|
services.AddScoped<IDeliveryFinanceReportService, DeliveryFinanceReportService>();
|
|
services.AddScoped<ProcessDeliveryOrderJob>();
|
|
services.AddScoped<ISnappfoodWebhookService, SnappfoodWebhookService>();
|
|
services.AddScoped<IReservationService, ReservationService>();
|
|
services.AddScoped<IQueueService, QueueService>();
|
|
services.AddScoped<IShiftService, ShiftService>();
|
|
services.AddScoped<IExpenseService, ExpenseService>();
|
|
services.AddScoped<ReceiptBuilder>();
|
|
services.AddScoped<IPrinterService, NetworkPrinterService>();
|
|
services.AddHttpClient(nameof(PosDeviceService));
|
|
services.AddScoped<IPosDeviceService, PosDeviceService>();
|
|
services.AddScoped<SubscriptionRenewalReminderJob>();
|
|
|
|
services.AddControllers()
|
|
.AddJsonOptions(options =>
|
|
{
|
|
options.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
|
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
|
});
|
|
services.AddEndpointsApiExplorer();
|
|
services.AddSwaggerGen();
|
|
services.AddSignalR();
|
|
|
|
services.AddValidatorsFromAssemblyContaining<Program>();
|
|
|
|
var jwtKey = configuration["Jwt:Key"] ?? "meezi-dev-secret-key-min-32-chars!!";
|
|
var jwtIssuer = configuration["Jwt:Issuer"] ?? "meezi";
|
|
var jwtAudience = configuration["Jwt:Audience"] ?? "meezi";
|
|
|
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = true,
|
|
ValidateAudience = true,
|
|
ValidateLifetime = true,
|
|
ValidateIssuerSigningKey = true,
|
|
ValidIssuer = jwtIssuer,
|
|
ValidAudience = jwtAudience,
|
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
|
|
};
|
|
options.Events = new JwtBearerEvents
|
|
{
|
|
OnMessageReceived = context =>
|
|
{
|
|
var accessToken = context.Request.Query["access_token"];
|
|
var path = context.HttpContext.Request.Path;
|
|
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
|
|
context.Token = accessToken;
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
});
|
|
|
|
services.AddAuthorization();
|
|
|
|
var isTesting = configuration.GetValue<bool>("Testing:Enabled");
|
|
var redisConnection = configuration.GetConnectionString("Redis") ?? "localhost:6379";
|
|
if (isTesting)
|
|
{
|
|
services.AddSingleton<IConnectionMultiplexer>(_ =>
|
|
ConnectionMultiplexer.Connect($"{redisConnection},abortConnect=false,connectTimeout=500"));
|
|
}
|
|
else
|
|
{
|
|
services.AddSingleton<IConnectionMultiplexer>(_ =>
|
|
ConnectionMultiplexer.Connect(redisConnection));
|
|
}
|
|
|
|
var connectionString = configuration.GetConnectionString("DefaultConnection")
|
|
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
|
|
|
services.AddHangfire(config =>
|
|
{
|
|
config
|
|
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
|
.UseSimpleAssemblyNameTypeSerializer()
|
|
.UseRecommendedSerializerSettings();
|
|
|
|
if (isTesting)
|
|
config.UseMemoryStorage();
|
|
else
|
|
config.UsePostgreSqlStorage(options => options.UseNpgsqlConnection(connectionString));
|
|
});
|
|
|
|
if (!isTesting)
|
|
services.AddHangfireServer();
|
|
|
|
services.AddCors(options =>
|
|
{
|
|
options.AddPolicy("MeeziCors", policy =>
|
|
{
|
|
var origins = configuration.GetSection("Cors:Origins").Get<string[]>()
|
|
?? ["http://localhost:3000"];
|
|
policy.WithOrigins(origins)
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod()
|
|
.AllowCredentials();
|
|
});
|
|
});
|
|
|
|
return services;
|
|
}
|
|
|
|
public static WebApplication ConfigureMeeziPipeline(this WebApplication app)
|
|
{
|
|
app.UseSerilogRequestLogging();
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
|
|
var uploadsPath = Path.Combine(app.Environment.ContentRootPath, "uploads");
|
|
Directory.CreateDirectory(uploadsPath);
|
|
app.UseStaticFiles(new StaticFileOptions
|
|
{
|
|
FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(uploadsPath),
|
|
RequestPath = "/uploads"
|
|
});
|
|
|
|
app.UseCors("MeeziCors");
|
|
app.UseRouting();
|
|
app.UseMeeziSecurity();
|
|
app.UseAuthentication();
|
|
app.UseMiddleware<Middleware.TenantMiddleware>();
|
|
app.UseMiddleware<Middleware.PlanLimitMiddleware>();
|
|
app.UseAuthorization();
|
|
|
|
app.MapControllers();
|
|
app.MapHub<KdsHub>("/hubs/kds");
|
|
app.MapHub<GuestOrderHub>("/hubs/guest-order");
|
|
app.MapGet("/health", () => Results.Ok(new { status = "healthy" }));
|
|
|
|
if (!app.Configuration.GetValue<bool>("Testing:Enabled"))
|
|
{
|
|
app.UseHangfireDashboard("/hangfire");
|
|
|
|
RecurringJob.AddOrUpdate<SubscriptionRenewalReminderJob>(
|
|
"subscription-renewal-reminder",
|
|
job => job.ExecuteAsync(),
|
|
Cron.Daily(6));
|
|
|
|
RecurringJob.AddOrUpdate<GenerateYesterdayReportsJob>(
|
|
"daily-reports-yesterday",
|
|
job => job.ExecuteAsync(),
|
|
"5 0 * * *",
|
|
new RecurringJobOptions { TimeZone = IranCalendar.TimeZone });
|
|
|
|
RecurringJob.AddOrUpdate<BranchPermanentDeleteJob>(
|
|
"branch-permanent-delete",
|
|
job => job.ExecuteAsync(),
|
|
Cron.Hourly);
|
|
}
|
|
|
|
return app;
|
|
}
|
|
}
|