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 [];
+ }
}
}