diff --git a/.env b/.env index 9275886..68c89d3 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ # MongoDB MONGO_USER=admin MONGO_PASSWORD=password -MONGODB_URI=mongodb://admin:password@localhost:27017/stripstream?authSource=admin +MONGODB_URI=mongodb://admin:password@mongodb.paniels.orb.local:27017/stripstream?authSource=admin # Komga NEXT_PUBLIC_API_URL=https://cloud.julienfroidefond.com \ No newline at end of file diff --git a/src/app/api/komga/favorites/route.ts b/src/app/api/komga/favorites/route.ts new file mode 100644 index 0000000..6299109 --- /dev/null +++ b/src/app/api/komga/favorites/route.ts @@ -0,0 +1,37 @@ +import { NextResponse } from "next/server"; +import { FavoriteService } from "@/lib/services/favorite.service"; + +export async function GET() { + try { + const favoriteIds = await FavoriteService.getAllFavoriteIds(); + return NextResponse.json(favoriteIds); + } catch (error) { + console.error("Erreur lors de la récupération des favoris:", error); + return NextResponse.json( + { error: "Erreur lors de la récupération des favoris" }, + { status: 500 } + ); + } +} + +export async function POST(request: Request) { + try { + const { seriesId } = await request.json(); + await FavoriteService.addToFavorites(seriesId); + return NextResponse.json({ message: "Favori ajouté avec succès" }); + } catch (error) { + console.error("Erreur lors de l'ajout du favori:", error); + return NextResponse.json({ error: "Erreur lors de l'ajout du favori" }, { status: 500 }); + } +} + +export async function DELETE(request: Request) { + try { + const { seriesId } = await request.json(); + await FavoriteService.removeFromFavorites(seriesId); + return NextResponse.json({ message: "Favori supprimé avec succès" }); + } catch (error) { + console.error("Erreur lors de la suppression du favori:", error); + return NextResponse.json({ error: "Erreur lors de la suppression du favori" }, { status: 500 }); + } +} diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx index 899ebfe..0c89f3f 100644 --- a/src/components/home/HeroSection.tsx +++ b/src/components/home/HeroSection.tsx @@ -11,10 +11,10 @@ interface HeroSectionProps { } export function HeroSection({ series }: HeroSectionProps) { - console.log("HeroSection - Séries reçues:", { - count: series?.length || 0, - firstSeries: series?.[0], - }); + // console.log("HeroSection - Séries reçues:", { + // count: series?.length || 0, + // firstSeries: series?.[0], + // }); return (
diff --git a/src/components/home/HomeContent.tsx b/src/components/home/HomeContent.tsx index 63cced1..8519c2d 100644 --- a/src/components/home/HomeContent.tsx +++ b/src/components/home/HomeContent.tsx @@ -26,11 +26,11 @@ export function HomeContent({ data }: HomeContentProps) { }; // Vérification des données pour le debug - console.log("HomeContent - Données reçues:", { - ongoingCount: data.ongoing?.length || 0, - recentlyReadCount: data.recentlyRead?.length || 0, - onDeckCount: data.onDeck?.length || 0, - }); + // console.log("HomeContent - Données reçues:", { + // ongoingCount: data.ongoing?.length || 0, + // recentlyReadCount: data.recentlyRead?.length || 0, + // onDeckCount: data.onDeck?.length || 0, + // }); return (
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 598c013..21145a2 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,3 +1,5 @@ +"use client"; + import { BookOpen, Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; @@ -6,7 +8,6 @@ import { authService } from "@/lib/services/auth.service"; import { useEffect, useState, useCallback } from "react"; import { KomgaLibrary, KomgaSeries } from "@/types/komga"; import { storageService } from "@/lib/services/storage.service"; -import { FavoriteService } from "@/lib/services/favorite.service"; interface SidebarProps { isOpen: boolean; @@ -43,13 +44,20 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { const fetchFavorites = useCallback(async () => { setIsLoadingFavorites(true); try { - const favoriteIds = FavoriteService.getAllFavoriteIds(); + // 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"); + } + const favoriteIds = await favoritesResponse.json(); + if (favoriteIds.length === 0) { setFavorites([]); return; } - const promises = favoriteIds.map(async (id) => { + // 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; return response.json(); @@ -69,7 +77,7 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { useEffect(() => { fetchLibraries(); fetchFavorites(); - }, []); // Suppression de la dépendance pathname + }, [fetchLibraries, fetchFavorites]); // Mettre à jour les favoris quand ils changent useEffect(() => { @@ -77,18 +85,10 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { fetchFavorites(); }; - // Écouter les changements de favoris dans la même fenêtre window.addEventListener("favoritesChanged", handleFavoritesChange); - // Écouter les changements de favoris dans d'autres fenêtres - window.addEventListener("storage", (e) => { - if (e.key === "stripstream_favorites") { - fetchFavorites(); - } - }); return () => { window.removeEventListener("favoritesChanged", handleFavoritesChange); - window.removeEventListener("storage", handleFavoritesChange); }; }, [fetchFavorites]); diff --git a/src/components/layout/SidebarWrapper.tsx b/src/components/layout/SidebarWrapper.tsx new file mode 100644 index 0000000..7a6415a --- /dev/null +++ b/src/components/layout/SidebarWrapper.tsx @@ -0,0 +1,26 @@ +import { FavoriteService } from "@/lib/services/favorite.service"; +import { LibraryService } from "@/lib/services/library.service"; +import { ClientSidebar } from "./ClientSidebar"; + +export async function SidebarWrapper() { + // Récupérer les favoris depuis le serveur + const favoriteIds = await FavoriteService.getAllFavoriteIds(); + + // Récupérer les détails des séries favorites + const favoritesPromises = favoriteIds.map(async (id) => { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/series/${id}`, { + headers: { + Accept: "application/json", + }, + }); + if (!response.ok) return null; + return response.json(); + }); + + // Récupérer les bibliothèques + const libraries = await LibraryService.getLibraries(); + + const favorites = (await Promise.all(favoritesPromises)).filter(Boolean); + + return { favorites, libraries }; +} diff --git a/src/components/series/SeriesHeader.tsx b/src/components/series/SeriesHeader.tsx index 85c05d8..b104966 100644 --- a/src/components/series/SeriesHeader.tsx +++ b/src/components/series/SeriesHeader.tsx @@ -1,11 +1,9 @@ "use client"; import Image from "next/image"; -import { ImageOff, Book, BookOpen, BookMarked } from "lucide-react"; +import { ImageOff, Book, BookOpen, BookMarked, Star, StarOff } from "lucide-react"; import { KomgaSeries } from "@/types/komga"; import { useState, useEffect } from "react"; -import { FavoriteService } from "@/lib/services/favorite.service"; -import { Star, StarOff } from "lucide-react"; import { Button } from "../ui/button"; import { useToast } from "@/components/ui/use-toast"; import { cn } from "@/lib/utils"; @@ -56,13 +54,27 @@ export const SeriesHeader = ({ series, onSeriesUpdate }: SeriesHeaderProps) => { const [readingStatus, setReadingStatus] = useState( getReadingStatusInfo(series) ); - const [isFavorite, setIsFavorite] = useState(FavoriteService.isFavorite(series.id)); + const [isFavorite, setIsFavorite] = useState(false); const [isLoading, setIsLoading] = useState(false); const [mounted, setMounted] = useState(false); + // Vérifier si la série est dans les favoris au chargement useEffect(() => { + const checkFavorite = async () => { + try { + const response = await fetch("/api/komga/favorites"); + if (response.ok) { + const favoriteIds = await response.json(); + setIsFavorite(favoriteIds.includes(series.id)); + } + } catch (error) { + console.error("Erreur lors de la vérification des favoris:", error); + } + }; + + checkFavorite(); setMounted(true); - }, []); + }, [series.id]); useEffect(() => { setReadingStatus(getReadingStatusInfo(series)); @@ -82,18 +94,29 @@ export const SeriesHeader = ({ series, onSeriesUpdate }: SeriesHeaderProps) => { } }, [series.metadata.language]); - const handleToggleFavorite = () => { + const handleToggleFavorite = async () => { try { setIsLoading(true); - if (isFavorite) { - FavoriteService.removeFromFavorites(series.id); - } else { - FavoriteService.addToFavorites(series.id); + const response = await fetch("/api/komga/favorites", { + method: isFavorite ? "DELETE" : "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ seriesId: series.id }), + }); + + if (!response.ok) { + throw new Error("Erreur lors de la modification des favoris"); } + setIsFavorite(!isFavorite); if (onSeriesUpdate) { onSeriesUpdate({ ...series, favorite: !isFavorite }); } + + // Dispatch l'événement pour notifier les autres composants + window.dispatchEvent(new Event("favoritesChanged")); + toast({ title: isFavorite ? "Retiré des favoris" : "Ajouté aux favoris", variant: "default", diff --git a/src/lib/models/favorite.model.ts b/src/lib/models/favorite.model.ts new file mode 100644 index 0000000..9e345e2 --- /dev/null +++ b/src/lib/models/favorite.model.ts @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; + +const favoriteSchema = new mongoose.Schema( + { + userId: { + type: String, + required: true, + index: true, + }, + seriesId: { + type: String, + required: true, + }, + }, + { + timestamps: true, + } +); + +// Index composé pour s'assurer qu'un utilisateur ne peut pas avoir deux fois le même favori +favoriteSchema.index({ userId: 1, seriesId: 1 }, { unique: true }); + +export const FavoriteModel = mongoose.models.Favorite || mongoose.model("Favorite", favoriteSchema); diff --git a/src/lib/services/favorite.service.ts b/src/lib/services/favorite.service.ts index bb079f9..ae18c64 100644 --- a/src/lib/services/favorite.service.ts +++ b/src/lib/services/favorite.service.ts @@ -1,40 +1,111 @@ -import { storageService } from "./storage.service"; +import { cookies } from "next/headers"; +import connectDB from "@/lib/mongodb"; +import { FavoriteModel } from "@/lib/models/favorite.model"; + +interface User { + id: string; + email: string; +} export class FavoriteService { private static readonly FAVORITES_CHANGE_EVENT = "favoritesChanged"; private static dispatchFavoritesChanged() { // Dispatch l'événement pour notifier les changements - window.dispatchEvent(new Event(FavoriteService.FAVORITES_CHANGE_EVENT)); + if (typeof window !== "undefined") { + window.dispatchEvent(new Event(FavoriteService.FAVORITES_CHANGE_EVENT)); + } + } + + private static async getCurrentUser(): Promise { + const userCookie = cookies().get("stripUser"); + + if (!userCookie) { + throw new Error("Utilisateur non authentifié"); + } + + try { + return JSON.parse(atob(userCookie.value)); + } catch (error) { + console.error("Erreur lors de la récupération de l'utilisateur depuis le cookie:", error); + throw new Error("Utilisateur non authentifié"); + } } /** * Vérifie si une série est dans les favoris */ - static isFavorite(seriesId: string): boolean { - return storageService.isFavorite(seriesId); + static async isFavorite(seriesId: string): Promise { + try { + const user = await this.getCurrentUser(); + await connectDB(); + + const favorite = await FavoriteModel.findOne({ + userId: user.id, + seriesId: seriesId, + }); + + return !!favorite; + } catch (error) { + console.error("Erreur lors de la vérification du favori:", error); + return false; + } } /** * Ajoute une série aux favoris */ - static addToFavorites(seriesId: string): void { - storageService.addFavorite(seriesId); - this.dispatchFavoritesChanged(); + static async addToFavorites(seriesId: string): Promise { + try { + const user = await this.getCurrentUser(); + await connectDB(); + + await FavoriteModel.findOneAndUpdate( + { userId: user.id, seriesId }, + { userId: user.id, seriesId }, + { upsert: true } + ); + + this.dispatchFavoritesChanged(); + } catch (error) { + console.error("Erreur lors de l'ajout aux favoris:", error); + throw new Error("Erreur lors de l'ajout aux favoris"); + } } /** * Retire une série des favoris */ - static removeFromFavorites(seriesId: string): void { - storageService.removeFavorite(seriesId); - this.dispatchFavoritesChanged(); + static async removeFromFavorites(seriesId: string): Promise { + try { + const user = await this.getCurrentUser(); + await connectDB(); + + await FavoriteModel.findOneAndDelete({ + userId: user.id, + seriesId, + }); + + this.dispatchFavoritesChanged(); + } catch (error) { + console.error("Erreur lors de la suppression des favoris:", error); + throw new Error("Erreur lors de la suppression des favoris"); + } } /** * Récupère tous les IDs des séries favorites */ - static getAllFavoriteIds(): string[] { - return storageService.getFavorites(); + static async getAllFavoriteIds(): Promise { + try { + const user = await this.getCurrentUser(); + await connectDB(); + + const favorites = await FavoriteModel.find({ userId: user.id }); + return favorites.map((favorite) => favorite.seriesId); + } catch (error) { + console.error("Erreur lors de la récupération des favoris:", error); + return []; + } } }