import { addDays, subDays } from "date-fns"; import { Book, Chapter, Editor, Announcement, Glossary } 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&filters[release_datetime][$lte]=${new Date().toISOString()}`); return data; } export async function fetchAnnouncements(): Promise { const data = await fetchFromAPI(`/api/announcements?filters[datetime][$lte]=${new Date().toISOString()}&sort[datetime]=desc`); 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(); const nextday = addDays(new Date(), 1).toISOString(); //[chapters][filters][release_datetime][$lte]=${currentDateTime} const data = await fetchFromAPI( `/api/books/${bookId}?populate[chapters][filters][release_datetime][$lte]=${nextday}&populate=cover` ); const stripped_data = [] for (const chapter of data[0].chapters) { const stripped_chapter = chapter; if (new Date(chapter.release_datetime).toISOString() > currentDateTime) { stripped_chapter.content = ""; stripped_chapter.documentId = ""; } stripped_data.push(stripped_chapter) } data[0].chapters = stripped_data; //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 async function fetchEarlyRelease(): Promise { const current_datetime = new Date() 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]=${current_datetime.toISOString()}&filters[release_datetime][$lte]=${addDays(current_datetime, 1).toISOString()}`); 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, 1); const next_week = addDays(current_datetime, 1); 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, email: string ) { const payload = { data: { error_type: error_type, details: details, book: book_id, chapter: chapter_id, report_email: email } } const response = await fetch( '/api/reports', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) } ) if (!response.ok) { throw new Error(`API request failed with status ${response.status}`) } return response }