new features, bookstack

This commit is contained in:
Matthias Hochmeister
2026-03-03 21:30:38 +01:00
parent 817329db70
commit d3561c1109
32 changed files with 1923 additions and 207 deletions

View File

@@ -0,0 +1,113 @@
import axios from 'axios';
import environment from '../config/environment';
import logger from '../utils/logger';
export interface BookStackPage {
id: number;
name: string;
slug: string;
book_id: number;
book_slug: string;
chapter_id: number;
draft: boolean;
template: boolean;
created_at: string;
updated_at: string;
url: string;
preview_html?: { content: string };
book?: { name: string };
tags?: { name: string; value: string; order: number }[];
createdBy?: { name: string };
updatedBy?: { name: string };
}
export interface BookStackSearchResult {
id: number;
name: string;
slug: string;
book_id: number;
book_slug: string;
url: string;
preview_html: { content: string };
tags: { name: string; value: string; order: number }[];
}
function buildHeaders(): Record<string, string> {
const { bookstack } = environment;
return {
'Authorization': `Token ${bookstack.tokenId}:${bookstack.tokenSecret}`,
'Content-Type': 'application/json',
};
}
async function getRecentPages(): Promise<BookStackPage[]> {
const { bookstack } = environment;
if (!bookstack.url) {
throw new Error('BOOKSTACK_URL is not configured');
}
try {
const response = await axios.get(
`${bookstack.url}/api/pages`,
{
params: { sort: '-updated_at', count: 5 },
headers: buildHeaders(),
},
);
const pages: BookStackPage[] = response.data?.data ?? [];
return pages.map((p) => ({
...p,
url: `${bookstack.url}/books/${p.book_slug}/page/${p.slug}`,
}));
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error('BookStack getRecentPages failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
}
logger.error('BookStackService.getRecentPages failed', { error });
throw new Error('Failed to fetch BookStack recent pages');
}
}
async function searchPages(query: string): Promise<BookStackSearchResult[]> {
const { bookstack } = environment;
if (!bookstack.url) {
throw new Error('BOOKSTACK_URL is not configured');
}
try {
const response = await axios.get(
`${bookstack.url}/api/search`,
{
params: { query, count: 8 },
headers: buildHeaders(),
},
);
const results: BookStackSearchResult[] = (response.data?.data ?? [])
.filter((item: any) => item.type === 'page')
.map((item: any) => ({
id: item.id,
name: item.name,
slug: item.slug,
book_id: item.book_id ?? 0,
book_slug: item.book_slug ?? '',
url: `${bookstack.url}/books/${item.book_slug}/page/${item.slug}`,
preview_html: item.preview_html ?? { content: '' },
tags: item.tags ?? [],
}));
return results;
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error('BookStack searchPages failed', {
status: error.response?.status,
statusText: error.response?.statusText,
});
}
logger.error('BookStackService.searchPages failed', { error });
throw new Error('Failed to search BookStack pages');
}
}
export default { getRecentPages, searchPages };