summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/blog/query.ts
blob: 91c2a5141d67def2abb18dc021b1ee36ebfcec46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import type {
	MarbleAuthorList,
	MarbleCategoryList,
	Pagination,
	MarblePost,
	MarblePostList,
	MarbleTagList,
} from "@/lib/blog/types";
import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeStringify from "rehype-stringify";
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeSanitize from "rehype-sanitize";

const url =
	process.env.NEXT_PUBLIC_MARBLE_API_URL ?? "https://api.marblecms.com";
const key = process.env.MARBLE_WORKSPACE_KEY ?? "cmd4iw9mm0006l804kwqv0k46";

const EMPTY_PAGINATION: Pagination = {
	limit: 0,
	currpage: 1,
	nextPage: null,
	prevPage: null,
	totalItems: 0,
	totalPages: 0,
};

const EMPTY_POSTS: MarblePostList = {
	posts: [],
	pagination: EMPTY_PAGINATION,
};

const EMPTY_TAGS: MarbleTagList = {
	tags: [],
	pagination: EMPTY_PAGINATION,
};

const EMPTY_CATEGORIES: MarbleCategoryList = {
	categories: [],
	pagination: EMPTY_PAGINATION,
};

const EMPTY_AUTHORS: MarbleAuthorList = {
	authors: [],
	pagination: EMPTY_PAGINATION,
};

function isMarbleConfigured() {
	return ![
		"",
		"placeholder",
		"build-placeholder",
		"your_workspace_key_here",
	].includes(key);
}

async function fetchFromMarble<T>({
	endpoint,
	fallback,
}: {
	endpoint: string;
	fallback: T;
}): Promise<T> {
	if (!isMarbleConfigured()) {
		return fallback;
	}

	try {
		const response = await fetch(`${url}/${key}/${endpoint}`);
		if (!response.ok) {
			console.warn(
				`Failed to fetch ${endpoint}: ${response.status} ${response.statusText}`,
			);
			return fallback;
		}
		return (await response.json()) as T;
	} catch (error) {
		console.error(`Error fetching ${endpoint}:`, error);
		return fallback;
	}
}

export async function getPosts() {
	return fetchFromMarble<MarblePostList>({
		endpoint: "posts",
		fallback: EMPTY_POSTS,
	});
}

export async function getTags() {
	return fetchFromMarble<MarbleTagList>({
		endpoint: "tags",
		fallback: EMPTY_TAGS,
	});
}

export async function getSinglePost({ slug }: { slug: string }) {
	return fetchFromMarble<MarblePost | null>({
		endpoint: `posts/${slug}`,
		fallback: null,
	});
}

export async function getCategories() {
	return fetchFromMarble<MarbleCategoryList>({
		endpoint: "categories",
		fallback: EMPTY_CATEGORIES,
	});
}

export async function getAuthors() {
	return fetchFromMarble<MarbleAuthorList>({
		endpoint: "authors",
		fallback: EMPTY_AUTHORS,
	});
}

export async function processHtmlContent({
	html,
}: {
	html: string;
}): Promise<string> {
	const processor = unified()
		.use(rehypeSanitize)
		.use(rehypeParse, { fragment: true })
		.use(rehypeSlug)
		.use(rehypeAutolinkHeadings, { behavior: "append" })
		.use(rehypeStringify);

	const file = await processor.process({ value: html, type: "html" });
	return String(file);
}