Some checks failed
ci/woodpecker/push/web Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
374 lines
8.2 KiB
TypeScript
374 lines
8.2 KiB
TypeScript
/**
|
|
* Knowledge API Client
|
|
* Handles knowledge entry-related API requests
|
|
*/
|
|
|
|
import type {
|
|
KnowledgeEntryWithTags,
|
|
KnowledgeTag,
|
|
KnowledgeEntryVersionWithAuthor,
|
|
PaginatedResponse,
|
|
EntryStatus,
|
|
Visibility,
|
|
} from "@mosaic/shared";
|
|
import { apiGet, apiPost, apiPatch, apiDelete, type ApiResponse } from "./client";
|
|
|
|
export interface EntryFilters {
|
|
status?: EntryStatus;
|
|
visibility?: Visibility;
|
|
tag?: string;
|
|
tags?: string[];
|
|
page?: number;
|
|
limit?: number;
|
|
search?: string;
|
|
sortBy?: "updatedAt" | "createdAt" | "title";
|
|
sortOrder?: "asc" | "desc";
|
|
}
|
|
|
|
export interface EntriesResponse {
|
|
data: KnowledgeEntryWithTags[];
|
|
meta?: {
|
|
total?: number;
|
|
page?: number;
|
|
limit?: number;
|
|
};
|
|
}
|
|
|
|
export interface CreateEntryData {
|
|
title: string;
|
|
content: string;
|
|
summary?: string;
|
|
status?: EntryStatus;
|
|
visibility?: Visibility;
|
|
tags?: string[];
|
|
}
|
|
|
|
export interface UpdateEntryData {
|
|
title?: string;
|
|
content?: string;
|
|
summary?: string;
|
|
status?: EntryStatus;
|
|
visibility?: Visibility;
|
|
tags?: string[];
|
|
changeNote?: string;
|
|
}
|
|
|
|
export interface RestoreVersionData {
|
|
changeNote?: string;
|
|
}
|
|
|
|
/**
|
|
* Fetch knowledge entries with optional filters
|
|
*/
|
|
export async function fetchEntries(filters?: EntryFilters): Promise<EntriesResponse> {
|
|
const params = new URLSearchParams();
|
|
|
|
if (filters?.status) {
|
|
params.append("status", filters.status);
|
|
}
|
|
if (filters?.visibility) {
|
|
params.append("visibility", filters.visibility);
|
|
}
|
|
if (filters?.tag) {
|
|
params.append("tag", filters.tag);
|
|
}
|
|
if (filters?.tags && filters.tags.length > 0) {
|
|
filters.tags.forEach((tag) => {
|
|
params.append("tags", tag);
|
|
});
|
|
}
|
|
if (filters?.page) {
|
|
params.append("page", filters.page.toString());
|
|
}
|
|
if (filters?.limit) {
|
|
params.append("limit", filters.limit.toString());
|
|
}
|
|
if (filters?.search) {
|
|
params.append("search", filters.search);
|
|
}
|
|
if (filters?.sortBy) {
|
|
params.append("sortBy", filters.sortBy);
|
|
}
|
|
if (filters?.sortOrder) {
|
|
params.append("sortOrder", filters.sortOrder);
|
|
}
|
|
|
|
const queryString = params.toString();
|
|
const endpoint = queryString ? `/api/knowledge/entries?${queryString}` : "/api/knowledge/entries";
|
|
|
|
const response = await apiGet<EntriesResponse>(endpoint);
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Fetch a single knowledge entry by slug
|
|
*/
|
|
export async function fetchEntry(slug: string): Promise<KnowledgeEntryWithTags> {
|
|
return apiGet<KnowledgeEntryWithTags>(`/api/knowledge/entries/${slug}`);
|
|
}
|
|
|
|
/**
|
|
* Create a new knowledge entry
|
|
*/
|
|
export async function createEntry(data: CreateEntryData): Promise<KnowledgeEntryWithTags> {
|
|
return apiPost<KnowledgeEntryWithTags>("/api/knowledge/entries", data);
|
|
}
|
|
|
|
/**
|
|
* Update an existing knowledge entry
|
|
*/
|
|
export async function updateEntry(
|
|
slug: string,
|
|
data: UpdateEntryData
|
|
): Promise<KnowledgeEntryWithTags> {
|
|
return apiPatch<KnowledgeEntryWithTags>(`/api/knowledge/entries/${slug}`, data);
|
|
}
|
|
|
|
/**
|
|
* Delete (archive) a knowledge entry
|
|
*/
|
|
export async function deleteEntry(slug: string): Promise<void> {
|
|
await apiDelete(`/api/knowledge/entries/${slug}`);
|
|
}
|
|
|
|
/**
|
|
* Fetch all knowledge tags
|
|
*/
|
|
export async function fetchTags(): Promise<KnowledgeTag[]> {
|
|
const response = await apiGet<ApiResponse<KnowledgeTag[]>>("/api/knowledge/tags");
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Fetch version history for an entry
|
|
*/
|
|
export async function fetchVersions(
|
|
slug: string,
|
|
page = 1,
|
|
limit = 20
|
|
): Promise<PaginatedResponse<KnowledgeEntryVersionWithAuthor>> {
|
|
const params = new URLSearchParams();
|
|
params.append("page", page.toString());
|
|
params.append("limit", limit.toString());
|
|
|
|
return apiGet<PaginatedResponse<KnowledgeEntryVersionWithAuthor>>(
|
|
`/api/knowledge/entries/${slug}/versions?${params.toString()}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Fetch a specific version of an entry
|
|
*/
|
|
export async function fetchVersion(
|
|
slug: string,
|
|
version: number
|
|
): Promise<KnowledgeEntryVersionWithAuthor> {
|
|
return apiGet<KnowledgeEntryVersionWithAuthor>(
|
|
`/api/knowledge/entries/${slug}/versions/${version.toString()}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Restore a previous version of an entry
|
|
*/
|
|
export async function restoreVersion(
|
|
slug: string,
|
|
version: number,
|
|
data?: RestoreVersionData
|
|
): Promise<KnowledgeEntryWithTags> {
|
|
return apiPost<KnowledgeEntryWithTags>(
|
|
`/api/knowledge/entries/${slug}/restore/${version.toString()}`,
|
|
data ?? {}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Fetch backlinks for an entry (entries that link to this entry)
|
|
*/
|
|
export async function fetchBacklinks(slug: string): Promise<{
|
|
entry: { id: string; slug: string; title: string };
|
|
backlinks: {
|
|
id: string;
|
|
sourceId: string;
|
|
targetId: string;
|
|
linkText: string;
|
|
displayText: string;
|
|
positionStart: number;
|
|
positionEnd: number;
|
|
resolved: boolean;
|
|
context: string | null;
|
|
createdAt: Date;
|
|
source: {
|
|
id: string;
|
|
title: string;
|
|
slug: string;
|
|
summary?: string | null;
|
|
};
|
|
}[];
|
|
count: number;
|
|
}> {
|
|
return apiGet<{
|
|
entry: { id: string; slug: string; title: string };
|
|
backlinks: {
|
|
id: string;
|
|
sourceId: string;
|
|
targetId: string;
|
|
linkText: string;
|
|
displayText: string;
|
|
positionStart: number;
|
|
positionEnd: number;
|
|
resolved: boolean;
|
|
context: string | null;
|
|
createdAt: Date;
|
|
source: {
|
|
id: string;
|
|
title: string;
|
|
slug: string;
|
|
summary?: string | null;
|
|
};
|
|
}[];
|
|
count: number;
|
|
}>(`/api/knowledge/entries/${slug}/backlinks`);
|
|
}
|
|
|
|
/**
|
|
* Fetch knowledge base statistics
|
|
*/
|
|
export async function fetchKnowledgeStats(): Promise<{
|
|
overview: {
|
|
totalEntries: number;
|
|
totalTags: number;
|
|
totalLinks: number;
|
|
publishedEntries: number;
|
|
draftEntries: number;
|
|
archivedEntries: number;
|
|
};
|
|
mostConnected: {
|
|
id: string;
|
|
slug: string;
|
|
title: string;
|
|
incomingLinks: number;
|
|
outgoingLinks: number;
|
|
totalConnections: number;
|
|
}[];
|
|
recentActivity: {
|
|
id: string;
|
|
slug: string;
|
|
title: string;
|
|
updatedAt: string;
|
|
status: string;
|
|
}[];
|
|
tagDistribution: {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
color: string | null;
|
|
entryCount: number;
|
|
}[];
|
|
}> {
|
|
return apiGet(`/api/knowledge/stats`);
|
|
}
|
|
|
|
/**
|
|
* Fetch entry graph (network of connected entries)
|
|
*/
|
|
export async function fetchEntryGraph(
|
|
slug: string,
|
|
depth = 1
|
|
): Promise<{
|
|
centerNode: {
|
|
id: string;
|
|
slug: string;
|
|
title: string;
|
|
summary: string | null;
|
|
tags: {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
color: string | null;
|
|
}[];
|
|
depth: number;
|
|
};
|
|
nodes: {
|
|
id: string;
|
|
slug: string;
|
|
title: string;
|
|
summary: string | null;
|
|
tags: {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
color: string | null;
|
|
}[];
|
|
depth: number;
|
|
}[];
|
|
edges: {
|
|
id: string;
|
|
sourceId: string;
|
|
targetId: string;
|
|
linkText: string;
|
|
}[];
|
|
stats: {
|
|
totalNodes: number;
|
|
totalEdges: number;
|
|
maxDepth: number;
|
|
};
|
|
}> {
|
|
const params = new URLSearchParams();
|
|
params.append("depth", depth.toString());
|
|
return apiGet(`/api/knowledge/entries/${slug}/graph?${params.toString()}`);
|
|
}
|
|
|
|
/**
|
|
* Fetch full knowledge graph
|
|
*/
|
|
export async function fetchKnowledgeGraph(filters?: {
|
|
tags?: string[];
|
|
status?: string;
|
|
limit?: number;
|
|
}): Promise<{
|
|
nodes: {
|
|
id: string;
|
|
slug: string;
|
|
title: string;
|
|
summary: string | null;
|
|
status?: string;
|
|
tags: {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
color: string | null;
|
|
}[];
|
|
depth: number;
|
|
isOrphan?: boolean;
|
|
}[];
|
|
edges: {
|
|
id: string;
|
|
sourceId: string;
|
|
targetId: string;
|
|
linkText: string;
|
|
}[];
|
|
stats: {
|
|
totalNodes: number;
|
|
totalEdges: number;
|
|
orphanCount: number;
|
|
};
|
|
}> {
|
|
const params = new URLSearchParams();
|
|
if (filters?.tags && filters.tags.length > 0) {
|
|
filters.tags.forEach((tag) => {
|
|
params.append("tags", tag);
|
|
});
|
|
}
|
|
if (filters?.status) {
|
|
params.append("status", filters.status);
|
|
}
|
|
if (filters?.limit !== undefined) {
|
|
params.append("limit", filters.limit.toString());
|
|
}
|
|
|
|
const queryString = params.toString();
|
|
const endpoint = queryString ? `/api/knowledge/graph?${queryString}` : "/api/knowledge/graph";
|
|
return apiGet(endpoint);
|
|
}
|