using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using GhostImageManager.Models; using HtmlAgilityPack; namespace GhostImageManager.Services; public class GhostApiService { private readonly HttpClient _httpClient; public GhostApiService(HttpClient httpClient) { _httpClient = httpClient; } public async Task> GetPostsAsync(GhostInstance instance, string filter = "", int limit = 100) { var request = CreateRequest(HttpMethod.Get, instance, $"posts/?limit={limit}&filter={Uri.EscapeDataString(filter)}&fields=id,title,slug,feature_image,html,created_at,updated_at,status"); var response = await _httpClient.SendAsync(request); 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") }); } } return posts; } public async Task GetPostAsync(GhostInstance instance, string postId) { 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) 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) { 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); return response.IsSuccessStatusCode; } public async Task UploadImageAsync(GhostInstance instance, Stream fileStream, string fileName, string? reference = null) { 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) return null; var json = await response.Content.ReadAsStringAsync(); var doc = JsonDocument.Parse(json); if (doc.RootElement.TryGetProperty("images", out var images) && images.GetArrayLength() > 0) { return GetString(images[0], "url"); } 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; } } public class GhostImageReference { [JsonPropertyName("url")] public string Url { get; set; } = ""; [JsonPropertyName("ref")] public string? Ref { get; set; } }