diff --git a/.eslintrc.json b/.eslintrc.json
index 8f5e4c0..9ccdc42 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,11 +1,38 @@
{
"extends": ["next/core-web-vitals", "prettier"],
"parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint"],
+ "plugins": ["@typescript-eslint", "unused-imports"],
"rules": {
- "@typescript-eslint/no-unused-vars": "off",
- "no-console": "off",
+ "@typescript-eslint/no-unused-vars": [
+ "warn",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_",
+ "caughtErrorsIgnorePattern": "^_"
+ }
+ ],
+ "no-console": [
+ "warn",
+ {
+ "allow": ["warn", "error"]
+ }
+ ],
"@next/next/no-html-link-for-pages": "off",
- "react/no-unescaped-entities": "off"
+ "react/no-unescaped-entities": "off",
+ "no-unreachable": "error",
+ "no-unused-expressions": "warn",
+ "no-unused-private-class-members": "warn",
+ "unused-imports/no-unused-imports": "warn",
+ "unused-imports/no-unused-vars": [
+ "warn",
+ {
+ "vars": "all",
+ "varsIgnorePattern": "^_",
+ "args": "after-used",
+ "argsIgnorePattern": "^_"
+ }
+ ],
+ "no-empty-function": "warn",
+ "no-empty": ["warn", { "allowEmptyCatch": true }]
}
}
diff --git a/package.json b/package.json
index 9e7694c..ce36e04 100644
--- a/package.json
+++ b/package.json
@@ -35,12 +35,13 @@
"@types/node": "20.11.16",
"@types/react": "18.2.52",
"@types/react-dom": "18.2.18",
- "@typescript-eslint/eslint-plugin": "6.21.0",
+ "@typescript-eslint/eslint-plugin": "^8.24.0",
"@typescript-eslint/parser": "6.21.0",
"autoprefixer": "10.4.17",
"eslint": "8.56.0",
"eslint-config-next": "14.1.0",
"eslint-config-prettier": "10.0.1",
+ "eslint-plugin-unused-imports": "^4.1.4",
"postcss": "8.4.33",
"tailwindcss": "3.4.1",
"typescript": "5.3.3"
diff --git a/src/app/api/komga/config/route.ts b/src/app/api/komga/config/route.ts
index c1e3a2c..003e47b 100644
--- a/src/app/api/komga/config/route.ts
+++ b/src/app/api/komga/config/route.ts
@@ -1,6 +1,8 @@
import { NextResponse } from "next/server";
import { ConfigDBService } from "@/lib/services/config-db.service";
+export const dynamic = "force-dynamic";
+
export async function POST(request: Request) {
try {
const data = await request.json();
diff --git a/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts b/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts
index 380cb50..851d3e2 100644
--- a/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts
+++ b/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts
@@ -10,7 +10,7 @@ export async function GET(
try {
// Convertir le numéro de page en nombre
const pageNumber = parseInt(params.pageNumber);
- if (isNaN(pageNumber) || pageNumber < 1) {
+ if (isNaN(pageNumber) || pageNumber < 0) {
return NextResponse.json({ error: "Numéro de page invalide" }, { status: 400 });
}
@@ -18,8 +18,11 @@ export async function GET(
return response;
} catch (error) {
console.error("API Book Page Thumbnail - Erreur:", error);
+ if (error instanceof Error) {
+ return NextResponse.json({ error: error.message }, { status: 500 });
+ }
return NextResponse.json(
- { error: "Erreur lors de la récupération de la miniature" },
+ { error: "Une erreur est survenue lors de la récupération de la miniature" },
{ status: 500 }
);
}
diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx
index ca930ca..47625f0 100644
--- a/src/app/settings/page.tsx
+++ b/src/app/settings/page.tsx
@@ -1,5 +1,15 @@
import { ConfigDBService } from "@/lib/services/config-db.service";
import { ClientSettings } from "@/components/settings/ClientSettings";
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Préférences",
+ description: "Configurez vos préférences StripStream",
+};
+
+export const viewport = {
+ colorScheme: "dark light",
+};
export default async function SettingsPage() {
let config = null;
diff --git a/src/components/home/HomeContent.tsx b/src/components/home/HomeContent.tsx
index 361aa2c..a6ea7e6 100644
--- a/src/components/home/HomeContent.tsx
+++ b/src/components/home/HomeContent.tsx
@@ -23,10 +23,6 @@ export function HomeContent({ data }: HomeContentProps) {
await router.push(path);
};
- const handleSeriesClick = (seriesId: string) => {
- router.push(`/series/${seriesId}`);
- };
-
// Vérification des données pour le debug
// console.log("HomeContent - Données reçues:", {
// ongoingCount: data.ongoing?.length || 0,
diff --git a/src/components/home/MediaRow.tsx b/src/components/home/MediaRow.tsx
index adfe2e1..6018ef8 100644
--- a/src/components/home/MediaRow.tsx
+++ b/src/components/home/MediaRow.tsx
@@ -86,18 +86,9 @@ function MediaCard({ item, onClick }: MediaCardProps) {
? item.metadata.title
: item.metadata.title || `Tome ${item.metadata.number}`;
- const handleClick = () => {
- console.log("MediaCard - handleClick:", {
- itemType: isSeries ? "series" : "book",
- itemId: item.id,
- itemTitle: title,
- });
- onClick?.();
- };
-
return (
{/* Image de couverture */}
diff --git a/src/components/layout/ClientLayout.tsx b/src/components/layout/ClientLayout.tsx
index db3670c..3b7bd75 100644
--- a/src/components/layout/ClientLayout.tsx
+++ b/src/components/layout/ClientLayout.tsx
@@ -6,8 +6,7 @@ import { Header } from "@/components/layout/Header";
import { Sidebar } from "@/components/layout/Sidebar";
import { InstallPWA } from "../ui/InstallPWA";
import { Toaster } from "@/components/ui/toaster";
-import { usePathname, useRouter } from "next/navigation";
-import { authService } from "@/lib/services/auth.service";
+import { usePathname } from "next/navigation";
import { PreferencesProvider } from "@/contexts/PreferencesContext";
// Routes qui ne nécessitent pas d'authentification
@@ -15,7 +14,6 @@ const publicRoutes = ["/login", "/register"];
export default function ClientLayout({ children }: { children: React.ReactNode }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
- const router = useRouter();
const pathname = usePathname();
const handleCloseSidebar = () => {
@@ -56,8 +54,8 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/sw.js")
- .then((registration) => {
- console.log("Service Worker enregistré avec succès:", registration);
+ .then(() => {
+ // Succès silencieux
})
.catch((error) => {
console.error("Erreur lors de l'enregistrement du Service Worker:", error);
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index 2076cb2..7e741c3 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -1,7 +1,6 @@
"use client";
-import { BookOpen, Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react";
-import Link from "next/link";
+import { Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react";
import { usePathname, useRouter } from "next/navigation";
import { cn } from "@/lib/utils";
import { authService } from "@/lib/services/auth.service";
@@ -32,7 +31,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const data = await response.json();
setLibraries(data);
} catch (error) {
- console.error("Erreur:", error);
+ if (error instanceof Error) {
+ console.error("Erreur de chargement des bibliothèques:", error.message);
+ }
setLibraries([]);
} finally {
setIsLoading(false);
@@ -43,7 +44,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const fetchFavorites = useCallback(async () => {
setIsLoadingFavorites(true);
try {
- // Récupérer les IDs des favoris depuis l'API
const favoritesResponse = await fetch("/api/komga/favorites");
if (!favoritesResponse.ok) {
throw new Error("Erreur lors de la récupération des favoris");
@@ -55,7 +55,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
return;
}
- // Récupérer les détails des séries pour chaque ID
const promises = favoriteIds.map(async (id: string) => {
const response = await fetch(`/api/komga/series/${id}`);
if (!response.ok) return null;
@@ -65,7 +64,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
const results = await Promise.all(promises);
setFavorites(results.filter((series): series is KomgaSeries => series !== null));
} catch (error) {
- console.error("Erreur lors de la récupération des favoris:", error);
+ if (error instanceof Error) {
+ console.error("Erreur de chargement des favoris:", error.message);
+ }
setFavorites([]);
} finally {
setIsLoadingFavorites(false);
diff --git a/src/components/library/PaginatedSeriesGrid.tsx b/src/components/library/PaginatedSeriesGrid.tsx
index 455d41c..6b807cd 100644
--- a/src/components/library/PaginatedSeriesGrid.tsx
+++ b/src/components/library/PaginatedSeriesGrid.tsx
@@ -6,9 +6,10 @@ import { useRouter, usePathname, useSearchParams } from "next/navigation";
import { useState, useEffect } from "react";
import { Loader2, Filter } from "lucide-react";
import { cn } from "@/lib/utils";
+import { KomgaSeries } from "@/types/komga";
interface PaginatedSeriesGridProps {
- series: any[];
+ series: KomgaSeries[];
currentPage: number;
totalPages: number;
totalElements: number;
@@ -61,10 +62,6 @@ export function PaginatedSeriesGrid({
await router.push(`${pathname}?${params.toString()}`);
};
- const handleSeriesClick = (seriesId: string) => {
- router.push(`/series/${seriesId}`);
- };
-
// Calcul des indices de début et de fin pour l'affichage
const startIndex = (currentPage - 1) * pageSize + 1;
const endIndex = Math.min(currentPage * pageSize, totalElements);
diff --git a/src/components/reader/BookReader.tsx b/src/components/reader/BookReader.tsx
index af45a5d..718ad8c 100644
--- a/src/components/reader/BookReader.tsx
+++ b/src/components/reader/BookReader.tsx
@@ -24,12 +24,9 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
setIsLoading,
secondPageLoading,
setSecondPageLoading,
- imageError,
- setImageError,
handlePreviousPage,
handleNextPage,
shouldShowDoublePage,
- syncReadProgress,
} = usePageNavigation({
book,
pages,
@@ -66,9 +63,12 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
}
}
} catch (error) {
- console.error("Erreur lors du chargement des URLs:", error);
- setImageError(true);
- } finally {
+ if (error instanceof Error) {
+ console.error(
+ `Erreur de chargement des URLs pour la page ${currentPage}:`,
+ error.message
+ );
+ }
// On s'assure que le chargement est terminé même en cas d'erreur
if (isMounted) {
setIsLoading(false);
@@ -91,7 +91,6 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
getPageUrl,
setIsLoading,
setSecondPageLoading,
- setImageError,
]);
// Effet pour précharger la page courante et les pages adjacentes
@@ -205,6 +204,13 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
{/* Pages */}
+ {/*
+ Note: Nous utilisons intentionnellement des balises
natives au lieu de next/image pour :
+ 1. Avoir un contrôle précis sur le chargement et le préchargement des pages
+ 2. Gérer efficacement le mode double page et les transitions
+ 3. Les images sont déjà optimisées côté serveur
+ 4. La performance est critique pour une lecture fluide
+ */}
{/* Page courante */}
{
+ e.stopPropagation();
+ onToggleControls();
+ }}
>
{
diff --git a/src/components/reader/components/NavigationBar.tsx b/src/components/reader/components/NavigationBar.tsx
index 57a1ca0..d36ef32 100644
--- a/src/components/reader/components/NavigationBar.tsx
+++ b/src/components/reader/components/NavigationBar.tsx
@@ -2,7 +2,7 @@ import { NavigationBarProps } from "../types";
import { cn } from "@/lib/utils";
import { Thumbnail } from "./Thumbnail";
import { useThumbnails } from "../hooks/useThumbnails";
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
export const NavigationBar = ({
currentPage,
@@ -11,16 +11,13 @@ export const NavigationBar = ({
showControls,
book,
}: NavigationBarProps) => {
- const {
- loadedThumbnails,
- handleThumbnailLoad,
- getThumbnailUrl,
- visibleThumbnails,
- scrollToActiveThumbnail,
- } = useThumbnails({
- book,
- currentPage,
- });
+ const { loadedThumbnails, handleThumbnailLoad, getThumbnailUrl, visibleThumbnails } =
+ useThumbnails({
+ book,
+ currentPage,
+ });
+
+ const thumbnailsContainerRef = useRef(null);
// Scroll à l'ouverture des contrôles et au changement de page
useEffect(() => {
@@ -53,6 +50,7 @@ export const NavigationBar = ({
onTouchStart={(e) => e.stopPropagation()}
onTouchMove={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
+ ref={thumbnailsContainerRef}
>
{pages.map((_, index) => {
diff --git a/src/components/reader/components/Thumbnail.tsx b/src/components/reader/components/Thumbnail.tsx
index 249b328..34b4a9c 100644
--- a/src/components/reader/components/Thumbnail.tsx
+++ b/src/components/reader/components/Thumbnail.tsx
@@ -6,15 +6,7 @@ import { forwardRef, useEffect, useState, useCallback, useRef } from "react";
export const Thumbnail = forwardRef(
(
- {
- pageNumber,
- currentPage,
- onPageChange,
- getThumbnailUrl,
- loadedThumbnails,
- onThumbnailLoad,
- isVisible,
- },
+ { pageNumber, currentPage, onPageChange, getThumbnailUrl, loadedThumbnails, onThumbnailLoad },
ref
) => {
const [imageUrl, setImageUrl] = useState(null);
@@ -59,9 +51,6 @@ export const Thumbnail = forwardRef(
if (loadAttempts.current < maxAttempts) {
// Réessayer avec un délai croissant
const delay = Math.min(1000 * Math.pow(2, loadAttempts.current - 1), 5000);
- console.log(
- `Réessai ${loadAttempts.current}/${maxAttempts} dans ${delay}ms pour la page ${pageNumber}`
- );
setTimeout(() => {
setImageUrl((prev) => (prev ? `${prev}?retry=${loadAttempts.current}` : null));
}, delay);
@@ -94,7 +83,7 @@ export const Thumbnail = forwardRef(
src={imageUrl}
alt={`Miniature page ${pageNumber}`}
className={cn(
- "object-cover transition-opacity duration-300",
+ "object-contain transition-opacity duration-300",
isLoading ? "opacity-0" : "opacity-100"
)}
fill
diff --git a/src/components/reader/hooks/usePageNavigation.ts b/src/components/reader/hooks/usePageNavigation.ts
index 36dd602..2df89f9 100644
--- a/src/components/reader/hooks/usePageNavigation.ts
+++ b/src/components/reader/hooks/usePageNavigation.ts
@@ -12,12 +12,11 @@ export const usePageNavigation = ({
book,
pages,
isDoublePage,
- onClose = () => {},
+ onClose,
}: UsePageNavigationProps) => {
const [currentPage, setCurrentPage] = useState(book.readProgress?.page || 1);
const [isLoading, setIsLoading] = useState(true);
const [secondPageLoading, setSecondPageLoading] = useState(true);
- const [imageError, setImageError] = useState(false);
const timeoutRef = useRef(null);
const touchStartXRef = useRef(null);
const touchStartYRef = useRef(null);
@@ -39,7 +38,12 @@ export const usePageNavigation = ({
body: JSON.stringify({ page, completed }),
});
} catch (error) {
- console.error("Erreur lors de la synchronisation de la progression:", error);
+ if (error instanceof Error) {
+ console.error(
+ `Erreur de synchronisation de la progression pour le livre ${book.id} à la page ${page}:`,
+ error.message
+ );
+ }
}
},
[book.id, pages.length]
@@ -73,7 +77,6 @@ export const usePageNavigation = ({
setCurrentPage(page);
setIsLoading(true);
setSecondPageLoading(true);
- setImageError(false);
debouncedSyncReadProgress(page);
},
[debouncedSyncReadProgress]
@@ -167,11 +170,8 @@ export const usePageNavigation = ({
setIsLoading,
secondPageLoading,
setSecondPageLoading,
- imageError,
- setImageError,
handlePreviousPage,
handleNextPage,
shouldShowDoublePage,
- syncReadProgress: debouncedSyncReadProgress,
};
};
diff --git a/src/components/reader/hooks/useThumbnails.ts b/src/components/reader/hooks/useThumbnails.ts
index c53a743..d1c64de 100644
--- a/src/components/reader/hooks/useThumbnails.ts
+++ b/src/components/reader/hooks/useThumbnails.ts
@@ -16,7 +16,8 @@ export const useThumbnails = ({ book, currentPage }: UseThumbnailsProps) => {
const getThumbnailUrl = useCallback(
(pageNumber: number) => {
- return `/api/komga/images/books/${book.id}/pages/${pageNumber}/thumbnail`;
+ const zeroBasedPage = pageNumber - 1;
+ return `/api/komga/images/books/${book.id}/pages/${zeroBasedPage}/thumbnail?zero_based=true`;
},
[book.id]
);
diff --git a/src/components/series/PaginatedBookGrid.tsx b/src/components/series/PaginatedBookGrid.tsx
index 1e1d333..048dd72 100644
--- a/src/components/series/PaginatedBookGrid.tsx
+++ b/src/components/series/PaginatedBookGrid.tsx
@@ -70,10 +70,6 @@ export function PaginatedBookGrid({
const startIndex = (currentPage - 1) * pageSize + 1;
const endIndex = Math.min(currentPage * pageSize, totalElements);
- const getBookThumbnailUrl = (bookId: string) => {
- return `/api/komga/images/books/${bookId}/thumbnail`;
- };
-
return (
@@ -115,11 +111,7 @@ export function PaginatedBookGrid({
isChangingPage ? "opacity-25" : "opacity-100"
)}
>
-
+
diff --git a/src/components/series/SeriesHeader.tsx b/src/components/series/SeriesHeader.tsx
index 19b7652..6e9dd0f 100644
--- a/src/components/series/SeriesHeader.tsx
+++ b/src/components/series/SeriesHeader.tsx
@@ -5,15 +5,13 @@ import { KomgaSeries } from "@/types/komga";
import { useState, useEffect } from "react";
import { Button } from "../ui/button";
import { useToast } from "@/components/ui/use-toast";
-import { cn } from "@/lib/utils";
import { Cover } from "@/components/ui/cover";
interface SeriesHeaderProps {
series: KomgaSeries;
- onSeriesUpdate?: (series: KomgaSeries) => void;
}
-export const SeriesHeader = ({ series, onSeriesUpdate }: SeriesHeaderProps) => {
+export const SeriesHeader = ({ series }: SeriesHeaderProps) => {
const { toast } = useToast();
const [isFavorite, setIsFavorite] = useState(false);
diff --git a/src/components/settings/ClientSettings.tsx b/src/components/settings/ClientSettings.tsx
index 228cbbb..228caf8 100644
--- a/src/components/settings/ClientSettings.tsx
+++ b/src/components/settings/ClientSettings.tsx
@@ -9,10 +9,6 @@ import { usePreferences } from "@/contexts/PreferencesContext";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
-interface ErrorMessage {
- message: string;
-}
-
interface KomgaConfig {
url: string;
username: string;
@@ -256,7 +252,10 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
toast({
variant: "destructive",
title: "Erreur",
- description: "Une erreur est survenue lors de la mise à jour des préférences",
+ description:
+ error instanceof Error
+ ? error.message
+ : "Une erreur est survenue lors de la mise à jour des préférences",
});
}
};
diff --git a/src/components/ui/image-loader.tsx b/src/components/ui/image-loader.tsx
index bad38c0..d5e4b27 100644
--- a/src/components/ui/image-loader.tsx
+++ b/src/components/ui/image-loader.tsx
@@ -1,6 +1,5 @@
"use client";
-import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
interface ImageLoaderProps {
diff --git a/src/components/ui/use-toast.ts b/src/components/ui/use-toast.ts
index 011d655..f6bcd67 100644
--- a/src/components/ui/use-toast.ts
+++ b/src/components/ui/use-toast.ts
@@ -12,7 +12,7 @@ type ToasterToast = ToastProps & {
action?: ToastActionElement;
};
-const actionTypes = {
+const _actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
@@ -26,7 +26,7 @@ function genId() {
return count.toString();
}
-type ActionType = typeof actionTypes;
+type ActionType = typeof _actionTypes;
type Action =
| {
diff --git a/src/lib/services/auth.service.ts b/src/lib/services/auth.service.ts
index ab8ccee..d1b7f96 100644
--- a/src/lib/services/auth.service.ts
+++ b/src/lib/services/auth.service.ts
@@ -2,16 +2,13 @@
import { AuthError } from "@/types/auth";
-interface AuthUser {
- id: string;
- email: string;
- roles: string[];
- authenticated: boolean;
-}
class AuthService {
private static instance: AuthService;
- private constructor() {}
+ // Constructeur privé pour le pattern Singleton
+ private constructor() {
+ // Pas d'initialisation nécessaire
+ }
public static getInstance(): AuthService {
if (!AuthService.instance) {
diff --git a/src/lib/services/base-api.service.ts b/src/lib/services/base-api.service.ts
index ec60c82..ae73ff7 100644
--- a/src/lib/services/base-api.service.ts
+++ b/src/lib/services/base-api.service.ts
@@ -1,4 +1,3 @@
-import { cookies } from "next/headers";
import { AuthConfig } from "@/types/auth";
import { serverCacheService } from "./server-cache.service";
import { ConfigDBService } from "./config-db.service";
@@ -71,18 +70,12 @@ export abstract class BaseApiService {
});
}
- // Log de l'URL finale
- console.log(`🔄 [Komga API] ${url.toString()}`);
-
return url.toString();
}
protected static async fetchFromApi(url: string, headers: Headers): Promise {
const response = await fetch(url, { headers });
- // Log du résultat de la requête
- console.log(`📡 [Komga API] ${response.status} ${response.statusText} - ${url}`);
-
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`);
}
diff --git a/src/lib/services/image.service.ts b/src/lib/services/image.service.ts
index db8ac4d..7e4b467 100644
--- a/src/lib/services/image.service.ts
+++ b/src/lib/services/image.service.ts
@@ -18,9 +18,6 @@ export class ImageService extends BaseApiService {
async () => {
const response = await fetch(url, { headers });
- // Log du résultat de la requête
- console.log(`📡 [Komga API] ${response.status} ${response.statusText} - ${url}`);
-
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`);
}
diff --git a/src/lib/services/server-cache.service.ts b/src/lib/services/server-cache.service.ts
index 65c341c..838bdf3 100644
--- a/src/lib/services/server-cache.service.ts
+++ b/src/lib/services/server-cache.service.ts
@@ -1,9 +1,3 @@
-type CacheEntry = {
- data: any;
- timestamp: number;
- ttl: number;
-};
-
class ServerCacheService {
private static instance: ServerCacheService;
private cache: Map = new Map();
@@ -94,14 +88,11 @@ class ServerCacheService {
type: keyof typeof ServerCacheService.DEFAULT_TTL = "DEFAULT"
): Promise {
const now = Date.now();
- console.log("👀 Getting or setting cache for key:", key);
const cached = this.cache.get(key);
if (cached && cached.expiry > now) {
- console.log("✅ Cache hit for key:", key);
return cached.data as T;
}
- console.log("❌ Cache not hit for key:", key);
try {
const data = await fetcher();
diff --git a/src/types/series.ts b/src/types/series.ts
index 9ed4ac8..9aac77a 100644
--- a/src/types/series.ts
+++ b/src/types/series.ts
@@ -11,13 +11,13 @@ export interface Series {
booksUnreadCount: number;
booksInProgressCount: number;
metadata: {
- status: string;
+ status: "ENDED" | "ONGOING" | "ABANDONED" | "HIATUS";
created: string;
lastModified: string;
title: string;
titleSort: string;
summary: string;
- readingDirection: string;
+ readingDirection: "LEFT_TO_RIGHT" | "RIGHT_TO_LEFT" | "VERTICAL" | "WEBTOON";
publisher: string;
ageRating: number;
language: string;
@@ -47,4 +47,6 @@ export interface Series {
}>;
};
deleted: boolean;
+ oneshot: boolean;
+ favorite: boolean;
}
diff --git a/yarn.lock b/yarn.lock
index 04194c6..832efe8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -28,7 +28,7 @@
dependencies:
eslint-visitor-keys "^3.4.3"
-"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
@@ -741,11 +741,6 @@
dependencies:
tslib "^2.4.0"
-"@types/json-schema@^7.0.12":
- version "7.0.15"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
- integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
-
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -798,11 +793,6 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09"
integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==
-"@types/semver@^7.5.0":
- version "7.5.8"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
- integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
-
"@types/webidl-conversions@*":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859"
@@ -815,22 +805,20 @@
dependencies:
"@types/webidl-conversions" "*"
-"@typescript-eslint/eslint-plugin@6.21.0":
- version "6.21.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3"
- integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==
+"@typescript-eslint/eslint-plugin@^8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz#574a95d67660a1e4544ae131d672867a5b40abb3"
+ integrity sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==
dependencies:
- "@eslint-community/regexpp" "^4.5.1"
- "@typescript-eslint/scope-manager" "6.21.0"
- "@typescript-eslint/type-utils" "6.21.0"
- "@typescript-eslint/utils" "6.21.0"
- "@typescript-eslint/visitor-keys" "6.21.0"
- debug "^4.3.4"
+ "@eslint-community/regexpp" "^4.10.0"
+ "@typescript-eslint/scope-manager" "8.24.0"
+ "@typescript-eslint/type-utils" "8.24.0"
+ "@typescript-eslint/utils" "8.24.0"
+ "@typescript-eslint/visitor-keys" "8.24.0"
graphemer "^1.4.0"
- ignore "^5.2.4"
+ ignore "^5.3.1"
natural-compare "^1.4.0"
- semver "^7.5.4"
- ts-api-utils "^1.0.1"
+ ts-api-utils "^2.0.1"
"@typescript-eslint/parser@6.21.0", "@typescript-eslint/parser@^5.4.2 || ^6.0.0":
version "6.21.0"
@@ -851,21 +839,34 @@
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
-"@typescript-eslint/type-utils@6.21.0":
- version "6.21.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e"
- integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==
+"@typescript-eslint/scope-manager@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz#2e34b3eb2ce768f2ffb109474174ced5417002b1"
+ integrity sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==
dependencies:
- "@typescript-eslint/typescript-estree" "6.21.0"
- "@typescript-eslint/utils" "6.21.0"
+ "@typescript-eslint/types" "8.24.0"
+ "@typescript-eslint/visitor-keys" "8.24.0"
+
+"@typescript-eslint/type-utils@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz#6ee3ec4db06f9e5e7b01ca6c2b5dd5843a9fd1e8"
+ integrity sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "8.24.0"
+ "@typescript-eslint/utils" "8.24.0"
debug "^4.3.4"
- ts-api-utils "^1.0.1"
+ ts-api-utils "^2.0.1"
"@typescript-eslint/types@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
+"@typescript-eslint/types@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.24.0.tgz#694e7fb18d70506c317b816de9521300b0f72c8e"
+ integrity sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==
+
"@typescript-eslint/typescript-estree@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
@@ -880,18 +881,29 @@
semver "^7.5.4"
ts-api-utils "^1.0.1"
-"@typescript-eslint/utils@6.21.0":
- version "6.21.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134"
- integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==
+"@typescript-eslint/typescript-estree@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz#0487349be174097bb329a58273100a9629e03c6c"
+ integrity sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==
+ dependencies:
+ "@typescript-eslint/types" "8.24.0"
+ "@typescript-eslint/visitor-keys" "8.24.0"
+ debug "^4.3.4"
+ fast-glob "^3.3.2"
+ is-glob "^4.0.3"
+ minimatch "^9.0.4"
+ semver "^7.6.0"
+ ts-api-utils "^2.0.1"
+
+"@typescript-eslint/utils@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.24.0.tgz#21cb1195ae79230af825bfeed59574f5cb70a749"
+ integrity sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
- "@types/json-schema" "^7.0.12"
- "@types/semver" "^7.5.0"
- "@typescript-eslint/scope-manager" "6.21.0"
- "@typescript-eslint/types" "6.21.0"
- "@typescript-eslint/typescript-estree" "6.21.0"
- semver "^7.5.4"
+ "@typescript-eslint/scope-manager" "8.24.0"
+ "@typescript-eslint/types" "8.24.0"
+ "@typescript-eslint/typescript-estree" "8.24.0"
"@typescript-eslint/visitor-keys@6.21.0":
version "6.21.0"
@@ -901,6 +913,14 @@
"@typescript-eslint/types" "6.21.0"
eslint-visitor-keys "^3.4.1"
+"@typescript-eslint/visitor-keys@8.24.0":
+ version "8.24.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz#36ecf0b9b1d819ad88a3bd4157ab7d594cb797c9"
+ integrity sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==
+ dependencies:
+ "@typescript-eslint/types" "8.24.0"
+ eslint-visitor-keys "^4.2.0"
+
"@ungap/structured-clone@^1.2.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
@@ -1720,6 +1740,11 @@ eslint-plugin-react@^7.33.2:
string.prototype.matchall "^4.0.12"
string.prototype.repeat "^1.0.0"
+eslint-plugin-unused-imports@^4.1.4:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738"
+ integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==
+
eslint-scope@^7.2.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
@@ -1733,6 +1758,11 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+eslint-visitor-keys@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
+ integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
+
eslint@8.56.0:
version "8.56.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15"
@@ -1815,7 +1845,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-glob@^3.2.9, fast-glob@^3.3.0:
+fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2:
version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
@@ -2115,7 +2145,7 @@ hasown@^2.0.2:
dependencies:
function-bind "^1.1.2"
-ignore@^5.2.0, ignore@^5.2.4:
+ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
@@ -3246,7 +3276,7 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.5.4, semver@^7.6.3:
+semver@^7.5.4, semver@^7.6.0, semver@^7.6.3:
version "7.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
@@ -3661,6 +3691,11 @@ ts-api-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
+ts-api-utils@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd"
+ integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==
+
ts-interface-checker@^0.1.9:
version "0.1.13"
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"