Files
blogflare/packages/obsidian-plugin/src/api.ts
T
2026-09-24 11:09:56 +08:00

170 lines
4.6 KiB
TypeScript

import { requestUrl } from 'obsidian';
import type { BlogFlareSettings } from './settings';
export interface MetaItem {
id: number;
name: string;
slug?: string;
}
export interface PostPayload {
title: string;
slug: string;
content: string;
category_slug?: string;
tags?: string[];
}
export interface PostMeta {
id: number;
slug: string;
title: string;
published_at: string;
category_slug?: string;
category_name?: string;
tags?: string[];
}
export interface PostDetail extends PostMeta {
content: string;
updated_at: string;
}
export class BlogFlareAPI {
private settings: BlogFlareSettings;
constructor(settings: BlogFlareSettings) {
this.settings = settings;
}
private get headers() {
return {
'Authorization': `Bearer ${this.settings.apiToken}`,
'Content-Type': 'application/json'
};
}
private getBaseUrl(): string {
let url = this.settings.apiEndpoint.trim();
if (url.endsWith('/')) {
url = url.slice(0, -1);
}
return url;
}
async getCategories(): Promise<MetaItem[]> {
if (!this.getBaseUrl()) return [];
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/categories`,
headers: this.headers
});
return (res.json as { data: MetaItem[] }).data || [];
}
async getTags(): Promise<MetaItem[]> {
if (!this.getBaseUrl()) return [];
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/tags`,
headers: this.headers
});
return (res.json as { data: MetaItem[] }).data || [];
}
async getSeries(): Promise<MetaItem[]> {
if (!this.getBaseUrl()) return [];
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/series`,
headers: this.headers
});
return (res.json as { data: MetaItem[] }).data || [];
}
async createPost(data: PostPayload): Promise<{ success: boolean; slug?: string; error?: string }> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/posts`,
method: 'POST',
headers: this.headers,
body: JSON.stringify(data)
});
return res.json as { success: boolean; slug?: string; error?: string };
}
async updatePost(slug: string, data: Partial<PostPayload>): Promise<{ success: boolean; slug?: string; error?: string }> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/posts/${slug}`,
method: 'PUT',
headers: this.headers,
body: JSON.stringify(data)
});
return res.json as { success: boolean; slug?: string; error?: string };
}
async getPosts(): Promise<PostMeta[]> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
let allPosts: PostMeta[] = [];
let offset = 0;
const limit = 100;
let total = 0;
do {
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/posts?limit=${limit}&offset=${offset}`,
headers: this.headers
});
const json = res.json as { data: PostMeta[], meta: { total: number } };
const data = json.data || [];
if (data.length === 0) break; // 防止死循环
allPosts = allPosts.concat(data);
total = json.meta?.total || 0;
offset += limit;
} while (offset < total);
return allPosts;
}
async getPost(slug: string): Promise<PostDetail> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/posts/${slug}`,
headers: this.headers
});
return (res.json as { data: PostDetail }).data;
}
async createCategory(name: string, slug?: string): Promise<{ success: boolean; error?: string }> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/categories`,
method: 'POST',
headers: this.headers,
body: JSON.stringify({ name, slug: slug || name })
});
return res.json as { success: boolean; error?: string };
}
async createTag(name: string): Promise<{ success: boolean; error?: string }> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/tags`,
method: 'POST',
headers: this.headers,
body: JSON.stringify({ name })
});
return res.json as { success: boolean; error?: string };
}
async deletePost(slug: string): Promise<{ success: boolean; error?: string }> {
if (!this.getBaseUrl()) throw new Error('API Endpoint is not configured');
const res = await requestUrl({
url: `${this.getBaseUrl()}/api/v1/posts/${slug}`,
method: 'DELETE',
headers: this.headers
});
return res.json as { success: boolean; error?: string };
}
}