using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Kurs.Platform.DynamicServices; using Kurs.Platform.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; using static Kurs.Platform.PlatformConsts; namespace Kurs.Platform.DeveloperKit; [Authorize] public class DynamicAppServiceAppService : PlatformAppService, IDynamicServiceAppService { private readonly IRepository _dynamicAppServiceRepository; private readonly DynamicServiceCompiler _compiler; public DynamicAppServiceAppService( IRepository dynamicAppServiceRepository, DynamicServiceCompiler compiler ) { _dynamicAppServiceRepository = dynamicAppServiceRepository; _compiler = compiler; } [Authorize(AppCodes.DeveloperKits.DynamicServices.TestCompile)] public virtual async Task TestCompileAsync(TestCompileRequestDto request) { Logger.LogInformation("Test compile başlatıldı. Tenant: {TenantId}", CurrentTenant.Id); try { var result = await _compiler.CompileAndValidateAsync(request.Code, CurrentTenant.Id); Logger.LogInformation("Test compile tamamlandı. Başarılı: {Success}, Tenant: {TenantId}", result.Success, CurrentTenant.Id); return result; } catch (Exception ex) { Logger.LogError(ex, "Test compile sırasında hata. Tenant: {TenantId}", CurrentTenant.Id); throw; } } [Authorize(AppCodes.DeveloperKits.DynamicServices.Publish)] public virtual async Task PublishAsync(PublishAppServiceRequestDto request) { Logger.LogInformation("AppService yayınlama başlatıldı. Ad: {Name}, Tenant: {TenantId}", request.Name, CurrentTenant.Id); try { // Önce kodu test compile et var compileResult = await _compiler.CompileAndValidateAsync(request.Code, CurrentTenant.Id); if (!compileResult.Success) { return new PublishResultDto { Success = false, ErrorMessage = compileResult.ErrorMessage }; } // Aynı isimde AppService var mı kontrol et var existingService = await _dynamicAppServiceRepository .FirstOrDefaultAsync(x => x.Name == request.Name && x.TenantId == CurrentTenant.Id); DynamicService appService; if (existingService != null) { // Mevcut servisi güncelle existingService.UpdateCode(request.Code); existingService.DisplayName = request.DisplayName; existingService.Description = request.Description; existingService.PrimaryEntityType = request.PrimaryEntityType; appService = await _dynamicAppServiceRepository.UpdateAsync(existingService); Logger.LogInformation("Mevcut AppService güncellendi. ID: {Id}", appService.Id); } else { // Yeni servis oluştur appService = new DynamicService( GuidGenerator.Create(), request.Name, request.Code, CurrentTenant.Id) { DisplayName = request.DisplayName, Description = request.Description, PrimaryEntityType = request.PrimaryEntityType, ControllerName = GenerateControllerName(request.Name) }; appService = await _dynamicAppServiceRepository.InsertAsync(appService); Logger.LogInformation("Yeni AppService oluşturuldu. ID: {Id}", appService.Id); } // Runtime'da derle ve yükle var assemblyName = $"DynamicAppService_{appService.Id}_{appService.Version}"; var loadResult = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, request.Code, assemblyName); if (loadResult.Success) { // Başarılı derleme durumunu kaydet appService.MarkCompilationSuccess(); await _dynamicAppServiceRepository.UpdateAsync(appService); var result = new PublishResultDto { Success = true, AppServiceId = appService.Id, ControllerName = appService.ControllerName, SwaggerUrl = "/swagger/index.html", GeneratedEndpoints = GenerateEndpointList(request.Name) }; Logger.LogInformation("AppService başarıyla yayınlandı. ID: {Id}, Controller: {Controller}", appService.Id, appService.ControllerName); return result; } else { // Derleme hatası durumunu kaydet appService.MarkCompilationError(loadResult.ErrorMessage); await _dynamicAppServiceRepository.UpdateAsync(appService); return new PublishResultDto { Success = false, ErrorMessage = loadResult.ErrorMessage, AppServiceId = appService.Id }; } } catch (Exception ex) { Logger.LogError(ex, "AppService yayınlama sırasında hata. Ad: {Name}, Tenant: {TenantId}", request.Name, CurrentTenant.Id); return new PublishResultDto { Success = false, ErrorMessage = $"Yayınlama sırasında beklenmeyen hata: {ex.Message}" }; } } [Authorize(AppCodes.DeveloperKits.DynamicServices.DynamicService)] public virtual async Task> GetListAsync(PagedAndSortedResultRequestDto input) { Logger.LogDebug("AppService listesi istendi. Tenant: {TenantId}", CurrentTenant.Id); var queryable = await _dynamicAppServiceRepository.GetQueryableAsync(); // Tenant filtresi otomatik uygulanır (IMultiTenant) var query = queryable.WhereIf(!string.IsNullOrEmpty(input.Sorting), x => x.CreationTime.ToString().Contains(input.Sorting ?? "")); var totalCount = await AsyncExecuter.CountAsync(query); var items = await AsyncExecuter.ToListAsync( query.OrderByDescending(x => x.CreationTime) .Skip(input.SkipCount) .Take(input.MaxResultCount) ); var dtos = ObjectMapper.Map, List>(items); Logger.LogDebug("AppService listesi döndürüldü. Toplam: {Total}, Dönen: {Count}", totalCount, dtos.Count); return new PagedResultDto(totalCount, dtos); } [Authorize(AppCodes.DeveloperKits.DynamicServices.ViewCode)] public virtual async Task GetAsync(Guid id) { Logger.LogDebug("AppService detayı istendi. ID: {Id}, Tenant: {TenantId}", id, CurrentTenant.Id); var appService = await _dynamicAppServiceRepository.GetAsync(id); var dto = ObjectMapper.Map(appService); return dto; } [Authorize(AppCodes.DeveloperKits.DynamicServices.Delete)] public virtual async Task DeleteAsync(Guid id) { Logger.LogInformation("AppService silme işlemi başlatıldı. ID: {Id}, Tenant: {TenantId}", id, CurrentTenant.Id); var appService = await _dynamicAppServiceRepository.GetAsync(id); // TODO: Runtime'dan assembly'yi kaldırma işlemi // (AssemblyLoadContext.Unload() çağrısı) await _dynamicAppServiceRepository.DeleteAsync(id); Logger.LogInformation("AppService silindi. ID: {Id}", id); } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task SetActiveAsync(Guid id, bool isActive) { Logger.LogInformation("AppService aktiflik durumu değiştiriliyor. ID: {Id}, Aktif: {Active}", id, isActive); var appService = await _dynamicAppServiceRepository.GetAsync(id); appService.IsActive = isActive; await _dynamicAppServiceRepository.UpdateAsync(appService); Logger.LogInformation("AppService aktiflik durumu güncellendi. ID: {Id}, Aktif: {Active}", id, isActive); } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task ReloadAllActiveServicesAsync() { Logger.LogInformation("Tüm aktif AppService'ler yeniden yükleniyor. Tenant: {TenantId}", CurrentTenant.Id); var activeServices = await _dynamicAppServiceRepository .GetListAsync(x => x.IsActive && x.CompilationStatus == CompilationStatus.Success); var successCount = 0; var errorCount = 0; foreach (var service in activeServices) { try { var assemblyName = $"DynamicAppService_{service.Id}_{service.Version}"; var result = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, service.Code, assemblyName); if (result.Success) { successCount++; Logger.LogDebug("AppService yeniden yüklendi. ID: {Id}, Ad: {Name}", service.Id, service.Name); } else { errorCount++; service.MarkCompilationError(result.ErrorMessage); await _dynamicAppServiceRepository.UpdateAsync(service); Logger.LogWarning("AppService yeniden yüklenemedi. ID: {Id}, Hata: {Error}", service.Id, result.ErrorMessage); } } catch (Exception ex) { errorCount++; Logger.LogError(ex, "AppService yeniden yükleme hatası. ID: {Id}", service.Id); service.MarkCompilationError(ex.Message); await _dynamicAppServiceRepository.UpdateAsync(service); } } Logger.LogInformation("AppService yeniden yükleme tamamlandı. Başarılı: {Success}, Hatalı: {Error}, Toplam: {Total}", successCount, errorCount, activeServices.Count); } [Authorize(AppCodes.DeveloperKits.DynamicServices.Manage)] public virtual async Task RecompileAsync(Guid id) { Logger.LogInformation("AppService yeniden derleniyor. ID: {Id}", id); var appService = await _dynamicAppServiceRepository.GetAsync(id); var assemblyName = $"DynamicAppService_{appService.Id}_{appService.Version + 1}"; var result = await _compiler.CompileAndRegisterForTenantAsync( CurrentTenant.Id ?? Guid.Empty, appService.Code, assemblyName); if (result.Success) { appService.MarkCompilationSuccess(); appService.Version++; } else { appService.MarkCompilationError(result.ErrorMessage); } await _dynamicAppServiceRepository.UpdateAsync(appService); Logger.LogInformation("AppService yeniden derleme tamamlandı. ID: {Id}, Başarılı: {Success}", id, result.Success); return result; } private string GenerateControllerName(string serviceName) { // DynamicCustomerAppService -> DynamicCustomer var controllerName = serviceName; if (controllerName.EndsWith("AppService")) { controllerName = controllerName.Substring(0, controllerName.Length - "AppService".Length); } else if (controllerName.EndsWith("ApplicationService")) { controllerName = controllerName.Substring(0, controllerName.Length - "ApplicationService".Length); } return controllerName; } private List GenerateEndpointList(string serviceName) { var controllerName = GenerateControllerName(serviceName); return [ $"/api/app/{controllerName.ToLowerInvariant()}", $"/api/app/{controllerName.ToLowerInvariant()}/{{id}}", $"/api/app/{controllerName.ToLowerInvariant()}/list" ]; } }