From a84f537dc155ef103d27ba0cc26832a290825e9a Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sun, 13 Aug 2023 15:24:24 +0100 Subject: [PATCH] refactor(blog): swap to using new API host for client-side fetch --- .../Controllers/BlogApiController.cs | 96 ------------------- src/ts/API.ts | 36 ++++--- src/ts/Author.ts | 8 +- src/ts/BlogPost.ts | 6 ++ src/ts/UI.ts | 2 +- src/ts/app.ts | 4 +- 6 files changed, 37 insertions(+), 115 deletions(-) delete mode 100644 OliverBooth.Blog/Controllers/BlogApiController.cs diff --git a/OliverBooth.Blog/Controllers/BlogApiController.cs b/OliverBooth.Blog/Controllers/BlogApiController.cs deleted file mode 100644 index 3f24192..0000000 --- a/OliverBooth.Blog/Controllers/BlogApiController.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Humanizer; -using Microsoft.AspNetCore.Cors; -using Microsoft.AspNetCore.Mvc; -using OliverBooth.Blog.Data; -using OliverBooth.Blog.Services; - -namespace OliverBooth.Blog.Controllers; - -/// -/// Represents a controller for the blog API. -/// -[ApiController] -[Route("api")] -[Produces("application/json")] -[EnableCors("OliverBooth")] -public sealed class BlogApiController : ControllerBase -{ - private readonly IBlogPostService _blogPostService; - private readonly IUserService _userService; - - /// - /// Initializes a new instance of the class. - /// - /// The . - /// The . - public BlogApiController(IBlogPostService blogPostService, IUserService userService) - { - _blogPostService = blogPostService; - _userService = userService; - } - - [Route("count")] - public IActionResult Count() - { - if (!ValidateReferer()) return NotFound(); - return Ok(new { count = _blogPostService.GetAllBlogPosts().Count }); - } - - [HttpGet("all/{skip:int?}/{take:int?}")] - public IActionResult GetAllBlogPosts(int skip = 0, int take = -1) - { - if (!ValidateReferer()) return NotFound(); - - // TODO yes I'm aware I can use the new pagination I wrote, this will be added soon. - IReadOnlyList allPosts = _blogPostService.GetAllBlogPosts(); - - if (take == -1) - { - take = allPosts.Count; - } - - return Ok(allPosts.Skip(skip).Take(take).Select(post => new - { - id = post.Id, - commentsEnabled = post.EnableComments, - identifier = post.GetDisqusIdentifier(), - author = post.Author.Id, - title = post.Title, - published = post.Published.ToUnixTimeSeconds(), - formattedDate = post.Published.ToString("dddd, d MMMM yyyy HH:mm"), - updated = post.Updated?.ToUnixTimeSeconds(), - humanizedTimestamp = post.Updated?.Humanize() ?? post.Published.Humanize(), - excerpt = _blogPostService.RenderExcerpt(post, out bool trimmed), - trimmed, - url = Url.Page("/Article", - new - { - area = "blog", - year = post.Published.ToString("yyyy"), - month = post.Published.ToString("MM"), - day = post.Published.ToString("dd"), - slug = post.Slug - }) - })); - } - - [HttpGet("author/{id:guid}")] - public IActionResult GetAuthor(Guid id) - { - if (!ValidateReferer()) return NotFound(); - if (!_userService.TryGetUser(id, out IUser? author)) return NotFound(); - - return Ok(new - { - id = author.Id, - name = author.DisplayName, - avatarUrl = author.AvatarUrl, - }); - } - - private bool ValidateReferer() - { - var referer = Request.Headers["Referer"].ToString(); - return referer.StartsWith(Url.PageLink("/index")!); - } -} diff --git a/src/ts/API.ts b/src/ts/API.ts index fdf81d0..cdcd965 100644 --- a/src/ts/API.ts +++ b/src/ts/API.ts @@ -2,25 +2,37 @@ import BlogPost from "./BlogPost"; import Author from "./Author"; class API { - private static readonly BASE_URL: string = "/api"; + private static readonly BASE_URL: string = "https://api.oliverbooth.dev"; private static readonly BLOG_URL: string = "/blog"; static async getBlogPostCount(): Promise { - const response = await fetch(`${API.BASE_URL + API.BLOG_URL}/count`); - const text = await response.text(); - return JSON.parse(text).count; + const response = await API.getResponse(`count`); + return response.count; } - - static async getBlogPosts(skip: number, take: number): Promise { - const response = await fetch(`${API.BASE_URL + API.BLOG_URL}/all/${skip}/${take}`); - const text = await response.text(); - return JSON.parse(text).map(obj => new BlogPost(obj)); + + static async getBlogPost(id: string): Promise { + const response = await API.getResponse(`post/${id}`); + return new BlogPost(response); } - + + static async getBlogPosts(page: number): Promise { + const response = await API.getResponse(`posts/${page}`); + return response.map(obj => new BlogPost(obj)); + } + static async getAuthor(id: string): Promise { - const response = await fetch(`${API.BASE_URL + API.BLOG_URL}/author/${id}`); + const response = await API.getResponse(`author/${id}`); + return new Author(response); + } + + private static async getResponse(url: string): Promise { + const response = await fetch(`${API.BASE_URL + API.BLOG_URL}/${url}`); + if (response.status !== 200) { + throw new Error("Invalid response from server"); + } + const text = await response.text(); - return new Author(JSON.parse(text)); + return JSON.parse(text); } } diff --git a/src/ts/Author.ts b/src/ts/Author.ts index 52ae7c4..8faf1a5 100644 --- a/src/ts/Author.ts +++ b/src/ts/Author.ts @@ -1,12 +1,12 @@ class Author { private readonly _id: string; private readonly _name: string; - private readonly _avatarHash: string; + private readonly _avatarUrl: string; constructor(json: any) { this._id = json.id; this._name = json.name; - this._avatarHash = json.avatarHash; + this._avatarUrl = json.avatarUrl; } get id(): string { @@ -17,8 +17,8 @@ class Author { return this._name; } - get avatarHash(): string { - return this._avatarHash; + get avatarUrl(): string { + return this._avatarUrl; } } diff --git a/src/ts/BlogPost.ts b/src/ts/BlogPost.ts index 7f493b3..c1d1f25 100644 --- a/src/ts/BlogPost.ts +++ b/src/ts/BlogPost.ts @@ -3,6 +3,7 @@ class BlogPost { private readonly _commentsEnabled: boolean; private readonly _title: string; private readonly _excerpt: string; + private readonly _content: string; private readonly _authorId: string; private readonly _published: Date; private readonly _updated?: Date; @@ -17,6 +18,7 @@ class BlogPost { this._commentsEnabled = json.commentsEnabled; this._title = json.title; this._excerpt = json.excerpt; + this._content = json.content; this._authorId = json.author; this._published = new Date(json.published * 1000); this._updated = (json.updated && new Date(json.updated * 1000)) || null; @@ -43,6 +45,10 @@ class BlogPost { return this._excerpt; } + get content(): string { + return this._content; + } + get authorId(): string { return this._authorId; } diff --git a/src/ts/UI.ts b/src/ts/UI.ts index 970218b..79ca54a 100644 --- a/src/ts/UI.ts +++ b/src/ts/UI.ts @@ -54,7 +54,7 @@ class UI { }, author: { name: author.name, - avatar: `https://gravatar.com/avatar/${author.avatarHash}?s=28`, + avatar: author.avatarUrl } }); card.innerHTML = body.trim(); diff --git a/src/ts/app.ts b/src/ts/app.ts index 56c7c24..10d6136 100644 --- a/src/ts/app.ts +++ b/src/ts/app.ts @@ -39,8 +39,8 @@ declare const Prism: any; const authors = []; const template = Handlebars.compile(UI.blogPostTemplate.innerHTML); API.getBlogPostCount().then(async (count) => { - for (let i = 0; i < count; i += 5) { - const posts = await API.getBlogPosts(i, 5); + for (let i = 0; i <= count / 10; i++) { + const posts = await API.getBlogPosts(i); for (const post of posts) { let author: Author; if (authors[post.authorId]) {