using System.Text; using System.Text.Json; using GhostImageManager.Models; namespace GhostImageManager.Services; public class GhostApiService { private readonly HttpClient _httpClient; private readonly ILogger _logger; public GhostApiService(HttpClient httpClient, ILogger logger) { _httpClient = httpClient; _logger = logger; } public async Task> GetPostsAsync(GhostInstance instance, string filter = "", int limit = 100) { var endpoint = $"posts/?limit={limit}&filter={Uri.EscapeDataString(filter)}&formats=html,plaintext&fields=id,title,slug,feature_image,html,created_at,updated_at,status"; _logger.LogInformation("Hole Posts von {Url} (Filter: {Filter}, Limit: {Limit})", instance.Url, filter, limit); var request = CreateRequest(HttpMethod.Get, instance, endpoint); var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError("Ghost API Fehler beim Laden der Posts: {StatusCode} - {Error}", response.StatusCode, errorContent); response.EnsureSuccessStatusCode(); } var json = await response.Content.ReadAsStringAsync(); var doc = JsonDocument.Parse(json); var posts = new List(); if (doc.RootElement.TryGetProperty("posts", out var postsElement)) { foreach (var post in postsElement.EnumerateArray()) { posts.Add(new GhostPost { Id = GetString(post, "id"), Title = GetString(post, "title"), Slug = GetString(post, "slug"), Html = GetString(post, "html"), FeatureImage = GetString(post, "feature_image"), CreatedAt = GetDateTime(post, "created_at"), UpdatedAt = GetDateTime(post, "updated_at"), Status = GetString(post, "status") }); } } _logger.LogInformation("{Count} Posts von Ghost geladen", posts.Count); return posts; } public async Task GetPostAsync(GhostInstance instance, string postId) { _logger.LogInformation("Lade Post {PostId} von {Url}", postId, instance.Url); var request = CreateRequest(HttpMethod.Get, instance, $"posts/{postId}/?fields=id,title,slug,feature_image,html,created_at,updated_at,status"); var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError("Ghost API Fehler beim Laden von Post {PostId}: {StatusCode} - {Error}", postId, response.StatusCode, errorContent); return null; } var json = await response.Content.ReadAsStringAsync(); var doc = JsonDocument.Parse(json); if (doc.RootElement.TryGetProperty("posts", out var posts) && posts.GetArrayLength() > 0) { var post = posts[0]; return new GhostPost { Id = GetString(post, "id"), Title = GetString(post, "title"), Slug = GetString(post, "slug"), Html = GetString(post, "html"), FeatureImage = GetString(post, "feature_image"), CreatedAt = GetDateTime(post, "created_at"), UpdatedAt = GetDateTime(post, "updated_at"), Status = GetString(post, "status") }; } return null; } public async Task UpdatePostAsync(GhostInstance instance, GhostPost post) { _logger.LogInformation("Aktualisiere Post {PostId} ({Title}) in Ghost", post.Id, post.Title); var request = CreateRequest(HttpMethod.Put, instance, $"posts/{post.Id}/?source=html"); var body = new { posts = new[] { new { id = post.Id, title = post.Title, html = post.Html, status = post.Status, feature_image = post.FeatureImage, updated_at = post.UpdatedAt?.ToString("o") } } }; request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError("Ghost API Fehler beim Aktualisieren von Post {PostId}: {StatusCode} - {Error}", post.Id, response.StatusCode, errorContent); return false; } _logger.LogInformation("Post {PostId} erfolgreich aktualisiert", post.Id); return true; } public async Task> GetUsersAsync(GhostInstance instance) { _logger.LogInformation("Hole Benutzer von {Url}", instance.Url); var request = CreateRequest(HttpMethod.Get, instance, "users/?limit=100&fields=id,name,slug,email,profile_image"); var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError("Ghost API Fehler beim Laden der Benutzer: {StatusCode} - {Error}", response.StatusCode, errorContent); return new List(); } var json = await response.Content.ReadAsStringAsync(); var doc = JsonDocument.Parse(json); var users = new List(); if (doc.RootElement.TryGetProperty("users", out var usersElement)) { foreach (var user in usersElement.EnumerateArray()) { users.Add(new GhostUser { Id = GetString(user, "id"), Name = GetString(user, "name"), Slug = GetString(user, "slug"), Email = GetString(user, "email"), ProfileImage = GetString(user, "profile_image") }); } } _logger.LogInformation("{Count} Benutzer von Ghost geladen", users.Count); return users; } public async Task UploadImageAsync(GhostInstance instance, Stream fileStream, string fileName, string? reference = null) { _logger.LogInformation("Lade Bild {FileName} zu Ghost hoch", fileName); var token = JwtTokenService.GenerateToken(instance.AdminApiKey); var request = new HttpRequestMessage(HttpMethod.Post, $"{instance.Url.TrimEnd('/')}/ghost/api/admin/images/upload/"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Ghost", token); request.Headers.Add("Accept-Version", "v5.0"); var content = new MultipartFormDataContent(); var streamContent = new StreamContent(fileStream); var ext = Path.GetExtension(fileName).ToLowerInvariant(); var mimeType = ext switch { ".jpg" or ".jpeg" => "image/jpeg", ".png" => "image/png", ".gif" => "image/gif", ".webp" => "image/webp", ".svg" => "image/svg+xml", _ => "application/octet-stream" }; streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mimeType); content.Add(streamContent, "file", fileName); if (!string.IsNullOrEmpty(reference)) content.Add(new StringContent(reference), "ref"); request.Content = content; var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError("Ghost API Fehler beim Hochladen von {FileName}: {StatusCode} - {Error}", fileName, response.StatusCode, errorContent); return null; } var json = await response.Content.ReadAsStringAsync(); var doc = JsonDocument.Parse(json); if (doc.RootElement.TryGetProperty("images", out var images) && images.GetArrayLength() > 0) { var url = GetString(images[0], "url"); _logger.LogInformation("Bild {FileName} hochgeladen: {Url}", fileName, url); return url; } _logger.LogWarning("Keine URL im Upload-Response für {FileName} gefunden", fileName); return null; } private HttpRequestMessage CreateRequest(HttpMethod method, GhostInstance instance, string endpoint) { var token = JwtTokenService.GenerateToken(instance.AdminApiKey); var request = new HttpRequestMessage(method, $"{instance.Url.TrimEnd('/')}/ghost/api/admin/{endpoint}"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Ghost", token); request.Headers.Add("Accept-Version", "v5.0"); return request; } private static string GetString(JsonElement element, string property) { if (element.TryGetProperty(property, out var prop) && prop.ValueKind != JsonValueKind.Null) return prop.GetString() ?? ""; return ""; } private static DateTime? GetDateTime(JsonElement element, string property) { if (element.TryGetProperty(property, out var prop) && prop.ValueKind != JsonValueKind.Null) if (DateTime.TryParse(prop.GetString(), out var dt)) return dt; return null; } }