chore: resolve lint warnings with targeted type and rule fixes
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { AuthServerService } from "@/lib/services/auth-server.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { ConfigDBService } from "@/lib/services/config-db.service";
|
||||
import { TestService } from "@/lib/services/test.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
import type { KomgaConfig, KomgaConfigData, KomgaLibrary } from "@/types/komga";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { FavoriteService } from "@/lib/services/favorite.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { LibraryService } from "@/lib/services/library.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { UserService } from "@/lib/services/user.service";
|
||||
import { AuthServerService } from "@/lib/services/auth-server.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { PreferencesService } from "@/lib/services/preferences.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
import type { UserPreferences } from "@/types/preferences";
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { BookService } from "@/lib/services/book.service";
|
||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||
import { AppError } from "@/utils/errors";
|
||||
|
||||
const HOME_CACHE_TAG = "home-data";
|
||||
|
||||
@@ -7,6 +7,8 @@ import type { KomgaBookWithPages } from "@/types/komga";
|
||||
import type { NextRequest } from "next/server";
|
||||
import logger from "@/lib/logger";
|
||||
|
||||
type ErrorWithStatusParams = AppError & { params?: { status?: number } };
|
||||
|
||||
// Cache handled in service via fetchFromApi options
|
||||
|
||||
export async function GET(
|
||||
@@ -25,7 +27,8 @@ export async function GET(
|
||||
if (error instanceof AppError) {
|
||||
const isNotFound =
|
||||
error.code === ERROR_CODES.BOOK.NOT_FOUND ||
|
||||
(error.code === ERROR_CODES.KOMGA.HTTP_ERROR && (error as any).params?.status === 404);
|
||||
(error.code === ERROR_CODES.KOMGA.HTTP_ERROR &&
|
||||
(error as ErrorWithStatusParams).params?.status === 404);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: {
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function GET(
|
||||
|
||||
if (httpStatus === 404) {
|
||||
const { bookId, pageNumber } = await params;
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
logger.info(`📷 Page ${pageNumber} not found for book: ${bookId}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function GET(
|
||||
if (httpStatus === 404) {
|
||||
const { bookId, pageNumber: pageNumberParam } = await params;
|
||||
const pageNumber: number = parseInt(pageNumberParam);
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
logger.info(`📷 Page ${pageNumber} thumbnail not found for book: ${bookId}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function GET(
|
||||
|
||||
if (httpStatus === 404) {
|
||||
const bookId: string = (await params).bookId;
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
logger.info(`📷 Thumbnail not found for book: ${bookId}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function GET(
|
||||
|
||||
if (httpStatus === 404) {
|
||||
const seriesId: string = (await params).seriesId;
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
logger.info(`📷 First page image not found for series: ${seriesId}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AuthProvider } from "@/components/providers/AuthProvider";
|
||||
import { cookies } from "next/headers";
|
||||
import { defaultPreferences } from "@/types/preferences";
|
||||
import type { UserPreferences } from "@/types/preferences";
|
||||
import type { KomgaLibrary, KomgaSeries } from "@/types/komga";
|
||||
import logger from "@/lib/logger";
|
||||
|
||||
const inter = Inter({
|
||||
@@ -73,8 +74,8 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
|
||||
let preferences: UserPreferences = defaultPreferences;
|
||||
let userIsAdmin = false;
|
||||
let libraries: any[] = [];
|
||||
let favorites: any[] = [];
|
||||
let libraries: KomgaLibrary[] = [];
|
||||
let favorites: KomgaSeries[] = [];
|
||||
|
||||
try {
|
||||
const [preferencesData, isAdminCheck, librariesData, favoritesData] = await Promise.allSettled([
|
||||
|
||||
@@ -6,7 +6,6 @@ import { RefreshButton } from "@/components/library/RefreshButton";
|
||||
import { PullToRefreshIndicator } from "@/components/common/PullToRefreshIndicator";
|
||||
import { usePullToRefresh } from "@/hooks/usePullToRefresh";
|
||||
import { useTranslate } from "@/hooks/useTranslate";
|
||||
import logger from "@/lib/logger";
|
||||
|
||||
interface HomeClientWrapperProps {
|
||||
children: ReactNode;
|
||||
@@ -20,22 +19,10 @@ export function HomeClientWrapper({ children }: HomeClientWrapperProps) {
|
||||
const handleRefresh = async () => {
|
||||
try {
|
||||
setIsRefreshing(true);
|
||||
|
||||
// Fetch fresh data from network with cache bypass
|
||||
const response = await fetch("/api/komga/home", {
|
||||
cache: "no-store",
|
||||
headers: { "Cache-Control": "no-cache" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to refresh home");
|
||||
}
|
||||
|
||||
// Trigger Next.js revalidation to update the UI
|
||||
// Re-fetch server-side data
|
||||
router.refresh();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error({ err: error }, "Erreur lors du rafraîchissement:");
|
||||
} catch (_error) {
|
||||
return { success: false, error: "Erreur lors du rafraîchissement de la page d'accueil" };
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
|
||||
@@ -12,7 +12,10 @@ interface SeriesGridProps {
|
||||
}
|
||||
|
||||
// Utility function to get reading status info
|
||||
const getReadingStatusInfo = (series: KomgaSeries, t: (key: string, options?: any) => string) => {
|
||||
const getReadingStatusInfo = (
|
||||
series: KomgaSeries,
|
||||
t: (key: string, options?: { [key: string]: string | number }) => string
|
||||
) => {
|
||||
if (series.booksCount === 0) {
|
||||
return {
|
||||
label: t("series.status.noBooks"),
|
||||
|
||||
@@ -20,7 +20,10 @@ interface SeriesListItemProps {
|
||||
}
|
||||
|
||||
// Utility function to get reading status info
|
||||
const getReadingStatusInfo = (series: KomgaSeries, t: (key: string, options?: any) => string) => {
|
||||
const getReadingStatusInfo = (
|
||||
series: KomgaSeries,
|
||||
t: (key: string, options?: { [key: string]: string | number }) => string
|
||||
) => {
|
||||
if (series.booksCount === 0) {
|
||||
return {
|
||||
label: t("series.status.noBooks"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useCallback, useRef } from "react";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useReadingDirection } from "./useReadingDirection";
|
||||
interface UseTouchNavigationProps {
|
||||
onPreviousPage: () => void;
|
||||
onNextPage: () => void;
|
||||
pswpRef: React.MutableRefObject<any>;
|
||||
pswpRef: React.MutableRefObject<unknown>;
|
||||
}
|
||||
|
||||
export function useTouchNavigation({
|
||||
|
||||
@@ -9,6 +9,10 @@ interface BeforeInstallPromptEvent extends Event {
|
||||
userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
|
||||
}
|
||||
|
||||
interface NavigatorStandalone extends Navigator {
|
||||
standalone?: boolean;
|
||||
}
|
||||
|
||||
const DISMISS_KEY = "pwa-install-dismissed";
|
||||
const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 jours en millisecondes
|
||||
|
||||
@@ -24,7 +28,7 @@ export function InstallPWA() {
|
||||
const checkStandalone = () => {
|
||||
return (
|
||||
window.matchMedia("(display-mode: standalone)").matches ||
|
||||
(window.navigator as any).standalone ||
|
||||
(window.navigator as NavigatorStandalone).standalone ||
|
||||
document.referrer.includes("android-app://")
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,10 @@ import { useBookOfflineStatus } from "@/hooks/useBookOfflineStatus";
|
||||
import { WifiOff } from "lucide-react";
|
||||
|
||||
// Fonction utilitaire pour obtenir les informations de statut de lecture
|
||||
const getReadingStatusInfo = (book: KomgaBook, t: (key: string, options?: any) => string) => {
|
||||
const getReadingStatusInfo = (
|
||||
book: KomgaBook,
|
||||
t: (key: string, options?: { [key: string]: string | number }) => string
|
||||
) => {
|
||||
if (!book.readProgress) {
|
||||
return {
|
||||
label: t("books.status.unread"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ interface TabsProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onValueChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
interface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
type TabsListProps = React.HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
interface TabsTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
value: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
|
||||
export const ERROR_CODES = {
|
||||
MONGODB: {
|
||||
|
||||
@@ -159,7 +159,7 @@ export function ServiceWorkerProvider({ children }: { children: ReactNode }) {
|
||||
// Silently ignore message handling errors to prevent app crashes
|
||||
// This can happen with malformed messages or during SW reinstall
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
console.warn("[SW Context] Error handling message:", error, event.data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,13 @@ interface KomgaUrlBuilder {
|
||||
params?: Record<string, string | string[]>;
|
||||
}
|
||||
|
||||
interface FetchErrorLike {
|
||||
code?: string;
|
||||
cause?: {
|
||||
code?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class BaseApiService {
|
||||
protected static async getKomgaConfig(): Promise<AuthConfig> {
|
||||
try {
|
||||
@@ -146,9 +153,10 @@ export abstract class BaseApiService {
|
||||
? { revalidate: options.revalidate }
|
||||
: undefined,
|
||||
});
|
||||
} catch (fetchError: any) {
|
||||
} catch (fetchError: unknown) {
|
||||
const normalizedError = fetchError as FetchErrorLike;
|
||||
// Gestion spécifique des erreurs DNS
|
||||
if (fetchError?.cause?.code === "EAI_AGAIN" || fetchError?.code === "EAI_AGAIN") {
|
||||
if (normalizedError.cause?.code === "EAI_AGAIN" || normalizedError.code === "EAI_AGAIN") {
|
||||
logger.error(`DNS resolution failed for ${url}. Retrying with different DNS settings...`);
|
||||
|
||||
response = await fetch(url, {
|
||||
@@ -168,7 +176,7 @@ export abstract class BaseApiService {
|
||||
? { revalidate: options.revalidate }
|
||||
: undefined,
|
||||
});
|
||||
} else if (fetchError?.cause?.code === "UND_ERR_CONNECT_TIMEOUT") {
|
||||
} else if (normalizedError.cause?.code === "UND_ERR_CONNECT_TIMEOUT") {
|
||||
// Retry automatique sur timeout de connexion (cold start)
|
||||
logger.info(`⏱️ Connection timeout for ${url}. Retrying once (cold start)...`);
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import { PreferencesService } from "./preferences.service";
|
||||
import { ERROR_CODES } from "../../constants/errorCodes";
|
||||
import { AppError } from "../../utils/errors";
|
||||
|
||||
type ErrorWithStatusParams = AppError & { params?: { status?: number } };
|
||||
|
||||
export class BookService extends BaseApiService {
|
||||
private static readonly CACHE_TTL = 60; // 1 minute
|
||||
|
||||
@@ -26,7 +28,7 @@ export class BookService extends BaseApiService {
|
||||
|
||||
return {
|
||||
book,
|
||||
pages: pages.map((page: any) => page.number),
|
||||
pages: pages.map((page) => page.number),
|
||||
};
|
||||
} catch (error) {
|
||||
throw new AppError(ERROR_CODES.BOOK.NOT_FOUND, {}, error);
|
||||
@@ -43,7 +45,7 @@ export class BookService extends BaseApiService {
|
||||
if (
|
||||
error instanceof AppError &&
|
||||
error.code === ERROR_CODES.KOMGA.HTTP_ERROR &&
|
||||
(error as any).params?.status === 404
|
||||
(error as ErrorWithStatusParams).params?.status === 404
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ interface KomgaLibraryRaw {
|
||||
unavailable: boolean;
|
||||
}
|
||||
|
||||
type KomgaCondition = Record<string, unknown>;
|
||||
|
||||
export class LibraryService extends BaseApiService {
|
||||
private static readonly CACHE_TTL = 300; // 5 minutes
|
||||
|
||||
@@ -83,7 +85,7 @@ export class LibraryService extends BaseApiService {
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
|
||||
// Construction du body de recherche pour Komga
|
||||
let condition: any;
|
||||
let condition: KomgaCondition;
|
||||
|
||||
if (unreadOnly) {
|
||||
condition = {
|
||||
@@ -101,7 +103,7 @@ export class LibraryService extends BaseApiService {
|
||||
condition = { libraryId: { operator: "is", value: libraryId } };
|
||||
}
|
||||
|
||||
const searchBody: { condition: any; fullTextSearch?: string } = { condition };
|
||||
const searchBody: { condition: KomgaCondition; fullTextSearch?: string } = { condition };
|
||||
|
||||
const params: Record<string, string | string[]> = {
|
||||
page: String(page),
|
||||
|
||||
@@ -55,13 +55,15 @@ export class PreferencesService {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const updateData: Record<string, any> = {};
|
||||
const updateData: Prisma.PreferencesUpdateInput = {};
|
||||
if (preferences.showThumbnails !== undefined)
|
||||
updateData.showThumbnails = preferences.showThumbnails;
|
||||
if (preferences.showOnlyUnread !== undefined)
|
||||
updateData.showOnlyUnread = preferences.showOnlyUnread;
|
||||
if (preferences.displayMode !== undefined) updateData.displayMode = preferences.displayMode;
|
||||
if (preferences.background !== undefined) updateData.background = preferences.background;
|
||||
if (preferences.background !== undefined) {
|
||||
updateData.background = preferences.background as unknown as Prisma.InputJsonValue;
|
||||
}
|
||||
if (preferences.readerPrefetchCount !== undefined)
|
||||
updateData.readerPrefetchCount = preferences.readerPrefetchCount;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type PendingRequest<T> = Promise<T>;
|
||||
|
||||
class RequestDeduplicationService {
|
||||
// Map pour tracker les requêtes en cours par clé unique
|
||||
private pendingRequests = new Map<string, PendingRequest<any>>();
|
||||
private pendingRequests = new Map<string, PendingRequest<unknown>>();
|
||||
|
||||
/**
|
||||
* Exécute une requête de manière dédupliquée
|
||||
|
||||
@@ -9,6 +9,8 @@ import { AppError } from "../../utils/errors";
|
||||
import type { UserPreferences } from "@/types/preferences";
|
||||
import logger from "@/lib/logger";
|
||||
|
||||
type KomgaCondition = Record<string, unknown>;
|
||||
|
||||
export class SeriesService extends BaseApiService {
|
||||
private static readonly CACHE_TTL = 120; // 2 minutes
|
||||
|
||||
@@ -34,7 +36,7 @@ export class SeriesService extends BaseApiService {
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
|
||||
// Construction du body de recherche pour Komga
|
||||
let condition: any;
|
||||
let condition: KomgaCondition;
|
||||
|
||||
if (unreadOnly) {
|
||||
// Utiliser allOf pour combiner seriesId avec anyOf pour UNREAD ou IN_PROGRESS
|
||||
|
||||
@@ -14,13 +14,13 @@ export function formatDate(date: string | Date): string {
|
||||
});
|
||||
}
|
||||
|
||||
export function debounce<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
export function debounce<TArgs extends unknown[]>(
|
||||
func: (...args: TArgs) => void,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
): (...args: TArgs) => void {
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
return function executedFunction(...args: Parameters<T>) {
|
||||
return function executedFunction(...args: TArgs) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
|
||||
2
src/types/json.d.ts
vendored
2
src/types/json.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare module "*.json" {
|
||||
const value: any;
|
||||
const value: unknown;
|
||||
export default value;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ export function findHttpStatus(error: unknown): number | null {
|
||||
|
||||
// Si c'est une erreur HTTP, récupérer le status
|
||||
if (error.code === ERROR_CODES.KOMGA.HTTP_ERROR) {
|
||||
return (error.params as any)?.status || null;
|
||||
const params = error.params as { status?: number } | undefined;
|
||||
return params?.status || null;
|
||||
}
|
||||
|
||||
// Sinon, chercher récursivement dans originalError
|
||||
|
||||
Reference in New Issue
Block a user