import { addDays, subDays } from "date-fns"; import { Book, Chapter, Editor, Announcement, Glossary, Report } from "./types"; const API_URL = process.env.NEXT_PUBLIC_API_URL as string; const API_TOKEN = process.env.STRAPI_API_TOKEN as string; export async function createFromAPI( endpoint: string, payload: string, options: RequestInit = {}, ): Promise { const headers: HeadersInit = { Authorization: `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }; const config: RequestInit = { method: "POST", headers, body: payload, ...options, } const response = await fetch(`${API_URL}${endpoint}`, config) if(!response.ok) { throw new Error(`API request failed with status ${response.status}`) } return response.json() } export async function fetchFromAPI( endpoint: string, options: RequestInit = {} ): Promise { const headers: HeadersInit = { Authorization: `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }; const config: RequestInit = { method: "GET", headers, ...options, }; let results: T[] = []; let currentPage = 1; let totalPages = 1; try { do { const url = `${API_URL}${endpoint}&pagination[page]=${currentPage}&pagination[pageSize]=25`; const response = await fetch(url, { ...config, next: { revalidate: 30 } }); if (!response.ok) { const errorData = await response.json(); console.error(`Error fetching ${url}:`, errorData); throw new Error(errorData.message || `API fetch error (status: ${response.status})`); } const responseJson = await response.json(); results = results.concat(responseJson.data); totalPages = responseJson.meta?.pagination?.pageCount; currentPage += 1; } while (currentPage <= totalPages) } catch (error) { console.error("Fetch error:", error); throw error; } return results; } /** * Fetches all books from the API. * Populates optional fields like Chapters or Editors based on requirements. */ export async function fetchBooks(): Promise { const data = await fetchFromAPI("/api/books?populate=cover&sort[title]=asc"); return data; } export async function fetchAnnouncements(): Promise { const data = await fetchFromAPI("/api/announcements?"); return data; } export async function fetchChaptersRSS(): Promise { const currentDateTime = new Date() const yesterday = subDays(currentDateTime, 1); const data = await fetchFromAPI (`/api/chapters?populate=book&filters[release_datetime][$lte]=${currentDateTime.toISOString()}&filters[release_datetime][$gte]=${yesterday.toISOString()}`); return data; } export async function fetchBookChapterLinks(bookId: string): Promise { const currentDateTime = new Date().toISOString(); const data = await fetchFromAPI(`/api/books/${bookId}?populate[chapters][filters][release_datetime][$lte]=${currentDateTime}`); return data[0] } /** * Fetches a specific book by ID with its chapters. * Filters chapters by release date to include only valid ones. */ export async function fetchBookById(bookId: string): Promise { const currentDateTime = new Date().toISOString(); //[chapters][filters][release_datetime][$lte]=${currentDateTime} const data = await fetchFromAPI( `/api/books/${bookId}?populate[chapters][filters][release_datetime][$lte]=${currentDateTime}&populate=cover` ); //I do not know why the hell it refuse to populate glossary only 1 field is allow to be populated after ???????? const glossary = await fetchGlossaryByBookId(bookId); data[0].glossary = glossary; data[0].chapters = data[0].chapters.sort((a, b) => a.number - b.number); return data[0]; } /** * Fetches a specific chapter by ID. */ export async function fetchChapterByBookId(bookId: string, chapterId: string): Promise { const currentChapter = await fetchFromAPI(`/api/chapters/${chapterId}?populate[book][fields][0]=title&filters[book][documentId]=${bookId}`); const bookWithAllChapters = await fetchFromAPI(`/api/books/${bookId}?populate[chapters][filters][number][$gte]=${currentChapter[0].number - 1 }&populate[chapters][filters][number][$lte]=${currentChapter[0].number + 1 }`); //const nextChapter = await fetchFromAPI(`/api/chapters?populate[book]&filters[book][id]=${bookId}&sort[number]=asc`); return bookWithAllChapters[0].chapters; } /** * Fetches all editors. */ export async function fetchEditors(): Promise { const data = await fetchFromAPI("/api/editors"); return data; } export type ChapterRelease = { current_chapters: Chapter[], future_chapters: Chapter[] } export async function fetchReleases(): Promise<{ current_chapters: Chapter[], future_chapters: Chapter[] }> { const current_datetime = new Date() const previous_week = subDays(current_datetime, 3); const next_week = addDays(current_datetime, 3); const data = await fetchFromAPI(`/api/chapters/?populate[book][fields][0]=title&fields[0]=number&fields[1]=title&fields[2]=release_datetime&filters[release_datetime][$gte]=${previous_week.toISOString()}&filters[release_datetime][$lte]=${next_week.toISOString()}`); const chapters: Chapter[] = data; const future_chapters = chapters.filter(chapter => new Date(chapter.release_datetime) > new Date()); const current_chapters = chapters.filter(chapter => new Date(chapter.release_datetime) <= new Date()); return { current_chapters, future_chapters } } export async function fetchAnnouncementById(announcementId: string): Promise { const data = await fetchFromAPI(`/api/announcements/${announcementId}?`); return data[0]; } export async function fetchGlossaryByBookId(bookId: string): Promise { const data = await fetchFromAPI(`/api/glossaries?filters[book][documentId]=${bookId}`); return data[0]; } export async function createReport( error_type: string, details: string, book_id: string, chapter_id: string ): Promise { const payload = { data: { error_type, details, book: book_id, chapter: chapter_id } } const data = await createFromAPI(`/api/reports`, JSON.stringify(payload)) return data }