Files
bookorbit/client/src/features/reader/composables/useBookmarks.ts
T
neon 51dd3d30f3 chore: backend infrastructure improvements
- add URI versioning: global prefix changed to api/v1, all frontend call sites updated
- add AbortSignal.timeout to all outbound fetch calls (10s API, 15s scrapers)
- add X-Request-Id response header and requestId field in error payloads
- add response compression via @fastify/compress (gzip + brotli)
- add security headers via @fastify/helmet with CSP for the Vite SPA
- tune pg pool: max 20, idle/connection/statement timeouts on app and migration pools
- add GET /api/v1/health endpoint via @nestjs/terminus with DB connectivity check
2026-02-28 16:02:15 -06:00

73 lines
1.8 KiB
TypeScript

import { computed, ref, watch } from 'vue'
import { api } from '@/lib/api'
export interface Bookmark {
id: number
bookId: number
cfi: string
title: string
createdAt: string
}
export function useBookmarks() {
const bookmarks = ref<Bookmark[]>([])
const currentCfi = ref<string | null>(null)
const loadError = ref<string | null>(null)
const isCurrentCfiBookmarked = computed(() => {
if (!currentCfi.value) return false
return bookmarks.value.some((b) => b.cfi === currentCfi.value)
})
function setCfi(cfi: string | null) {
currentCfi.value = cfi
}
async function load(bookId: number) {
loadError.value = null
const res = await api(`/api/v1/books/${bookId}/bookmarks`)
if (!res.ok) {
loadError.value = 'Failed to load'
return
}
bookmarks.value = await res.json()
}
async function toggle(bookId: number, cfi: string, title: string) {
const existing = bookmarks.value.find((b) => b.cfi === cfi)
if (existing) {
await remove(bookId, existing.id)
} else {
const res = await api(`/api/v1/books/${bookId}/bookmarks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cfi, title }),
})
if (res.ok) {
const created: Bookmark = await res.json()
bookmarks.value = [...bookmarks.value, created]
}
}
}
async function remove(bookId: number, bookmarkId: number) {
const res = await api(`/api/v1/books/${bookId}/bookmarks/${bookmarkId}`, {
method: 'DELETE',
})
if (res.ok) {
bookmarks.value = bookmarks.value.filter((b) => b.id !== bookmarkId)
}
}
return {
bookmarks,
isCurrentCfiBookmarked,
currentCfi,
loadError,
setCfi,
load,
toggle,
remove,
}
}