feat(backend): ✨ implement news fetching from git.cohabit
This commit is contained in:
parent
ec90d92f46
commit
5593878c66
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -37,7 +37,9 @@
|
||||||
"ux",
|
"ux",
|
||||||
"route",
|
"route",
|
||||||
"frontend",
|
"frontend",
|
||||||
"components"
|
"components",
|
||||||
|
"island",
|
||||||
|
"backend"
|
||||||
],
|
],
|
||||||
"[ignore]": {
|
"[ignore]": {
|
||||||
"editor.defaultFormatter": "foxundermoon.shell-format"
|
"editor.defaultFormatter": "foxundermoon.shell-format"
|
||||||
|
|
104
src/blog/mod.ts
Normal file
104
src/blog/mod.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import { BlogProps } from ':components/BlogCard.tsx'
|
||||||
|
import { NewsFrontMatter } from ':src/blog/types.ts'
|
||||||
|
import { base64ToString } from ':src/utils.ts'
|
||||||
|
import { extract } from '@std/front-matter/yaml'
|
||||||
|
|
||||||
|
export async function fetchNews(
|
||||||
|
publisher: string,
|
||||||
|
name: string,
|
||||||
|
): Promise<BlogProps> {
|
||||||
|
const apiUrl = 'https://git.cohabit.fr/api/v1/'
|
||||||
|
const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
|
||||||
|
const endpoint = new URL('contents/', baseEndpoint)
|
||||||
|
|
||||||
|
// Get readme content api url
|
||||||
|
const readmePath = encodeURIComponent(`${name}/README.md`)
|
||||||
|
const contentUrl = new URL(readmePath, endpoint)
|
||||||
|
|
||||||
|
// Fetch readme content, commit hash and raw url for relative links
|
||||||
|
const file = await getCommitAndContent(contentUrl)
|
||||||
|
// Get commit infos (author + date) and get readme content from base64 source
|
||||||
|
const { raw, url, lastUpdate, author } = await getAuthorAndParseContent(
|
||||||
|
file,
|
||||||
|
baseEndpoint,
|
||||||
|
)
|
||||||
|
// Extract frontmatter
|
||||||
|
const { attrs, body } = extract<NewsFrontMatter>(raw)
|
||||||
|
|
||||||
|
// Transform API responses into BlogProps for BlogCard and BlogPost components
|
||||||
|
return {
|
||||||
|
author,
|
||||||
|
publisher,
|
||||||
|
lastUpdate,
|
||||||
|
options: attrs['x-cohabit'],
|
||||||
|
title: attrs.title,
|
||||||
|
hash: file.sha,
|
||||||
|
description: attrs.description,
|
||||||
|
body,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
tags: attrs.tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function* fetchNewsList(
|
||||||
|
publisher: string,
|
||||||
|
): AsyncGenerator<BlogProps, void, void> {
|
||||||
|
const apiUrl = 'https://git.cohabit.fr/api/v1/'
|
||||||
|
const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
|
||||||
|
const endpoint = new URL('contents/', baseEndpoint)
|
||||||
|
|
||||||
|
// Fetch repo content
|
||||||
|
const root = await fetch(endpoint).then((response) => response.json()) as {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
}[]
|
||||||
|
|
||||||
|
// Fetch `README.md` in sub directories
|
||||||
|
const blogPropsList = root
|
||||||
|
// Remove file and dir starting with "."
|
||||||
|
.filter(isNewsDirectory)
|
||||||
|
// Fetch single news and return BlogProps
|
||||||
|
.map(({ name }) => fetchNews(publisher, name))
|
||||||
|
|
||||||
|
// Yield each news
|
||||||
|
for (const blogProps of blogPropsList) {
|
||||||
|
yield blogProps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAuthorAndParseContent(
|
||||||
|
file: { download_url: string; content: string; last_commit_sha: string },
|
||||||
|
baseEndpoint: URL,
|
||||||
|
) {
|
||||||
|
const commitUrl = new URL(
|
||||||
|
`git/commits/${file.last_commit_sha}?stat=false&verification=false&files=false`,
|
||||||
|
baseEndpoint,
|
||||||
|
)
|
||||||
|
const infos = await fetch(commitUrl).then((response) =>
|
||||||
|
response.json()
|
||||||
|
) as {
|
||||||
|
created: string
|
||||||
|
author: { login: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
raw: base64ToString(file.content),
|
||||||
|
url: file.download_url,
|
||||||
|
lastUpdate: new Date(infos.created),
|
||||||
|
author: infos.author.login,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCommitAndContent(contentUrl: URL) {
|
||||||
|
return await fetch(contentUrl).then((response) => response.json()) as {
|
||||||
|
download_url: string
|
||||||
|
content: string
|
||||||
|
sha: string
|
||||||
|
last_commit_sha: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNewsDirectory(entry: { name: string; type: string }): boolean {
|
||||||
|
return entry.type === 'dir' && entry.name.startsWith('.') === false
|
||||||
|
}
|
12
src/blog/types.ts
Normal file
12
src/blog/types.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export type NewsFrontMatter = {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
tags?: string[]
|
||||||
|
'x-cohabit': {
|
||||||
|
links?: Record<string, string>[]
|
||||||
|
status: 'canceled' | 'futur' | 'current' | 'finished'
|
||||||
|
visibility?: 'public' | 'internal'
|
||||||
|
thumbnail: string
|
||||||
|
dueDate: string
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue