refactor(blog): swap to using new API host for client-side fetch
This commit is contained in:
parent
7495da56cb
commit
a84f537dc1
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a controller for the blog API.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api")]
|
||||
[Produces("application/json")]
|
||||
[EnableCors("OliverBooth")]
|
||||
public sealed class BlogApiController : ControllerBase
|
||||
{
|
||||
private readonly IBlogPostService _blogPostService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlogApiController" /> class.
|
||||
/// </summary>
|
||||
/// <param name="blogPostService">The <see cref="IBlogPostService" />.</param>
|
||||
/// <param name="userService">The <see cref="IUserService" />.</param>
|
||||
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<IBlogPost> 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")!);
|
||||
}
|
||||
}
|
|
@ -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<number> {
|
||||
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<BlogPost[]> {
|
||||
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<BlogPost> {
|
||||
const response = await API.getResponse(`post/${id}`);
|
||||
return new BlogPost(response);
|
||||
}
|
||||
|
||||
static async getBlogPosts(page: number): Promise<BlogPost[]> {
|
||||
const response = await API.getResponse(`posts/${page}`);
|
||||
return response.map(obj => new BlogPost(obj));
|
||||
}
|
||||
|
||||
static async getAuthor(id: string): Promise<Author> {
|
||||
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<any> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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]) {
|
||||
|
|
Loading…
Reference in New Issue