#Aide Erreur "should be awaited before using its properties."

1 messages · Page 1 of 1 (latest)

sharp falconBOT
#

Hi @uncut vale. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Dans Next.js 15, les APIs dynamiques comme params et searchParams sont devenues asynchrones. L'erreur que vous rencontrez indique que vous accédez directement à params.id sans attendre la résolution de la promesse.

Voici comment corriger votre code :

// app/api/shortlinks/[id]/route.ts
import { NextRequest, NextResponse } from "next/server";
import shortlinksService from "@/services/shortlinksService";

export async function GET(
  req: NextRequest, 
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  
  if (!id) {
    return NextResponse.json({ message: 'Missing ID' }, { status: 400 });
  }
  
  const numId = Number(id);
  if (isNaN(numId)) {
    return NextResponse.json({ message: 'Invalid ID' }, { status: 400 });
  }
  
  const shortlink = shortlinksService.findOne(numId);
  if (!shortlink) {
    return NextResponse.json({ message: 'Shortlink not found' }, { status: 404 });
  }
  
  return NextResponse.json(shortlink, { status: 200 });
}

Les changements principaux sont :

  1. Modifier le type de params pour indiquer qu'il s'agit d'une promesse : { params: Promise<{ id: string }> }
  2. Attendre la résolution de la promesse avec await params avant d'accéder à la propriété id

Cette modification est nécessaire en raison d'un changement majeur dans Next.js 15 où les APIs dynamiques sont devenues asynchrones, comme expliqué dans la documentation de mise à niveau vers la version 15.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Pour les méthodes PUT et DELETE, vous devez également modifier le code pour gérer les paramètres asynchrones, comme vous l'avez fait pour la méthode GET. Voici comment corriger ces méthodes :

export async function PUT(
  req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const data = await req.json();
  const updatedShortlink = shortlinksService.update(Number(id), data);
  if (!updatedShortlink) {
    return NextResponse.json(
      { message: "Shortlink not found" },
      { status: 404 }
    );
  }
  return NextResponse.json(updatedShortlink, { status: 200 });
}

export async function DELETE(
  req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const removedShortlink = shortlinksService.remove(Number(id));
  if (!removedShortlink) {
    return NextResponse.json(
      { message: "Shortlink not found" },
      { status: 404 }
    );
  }
  return NextResponse.json(removedShortlink, { status: 200 });
}

Les modifications principales sont :

  1. Changer le type de context: { params: { id: string } } à { params: Promise<{ id: string }> }
  2. Extraire l'ID avec const { id } = await params au lieu de const { params } = context

Cette modification est nécessaire car dans Next.js 15, tous les gestionnaires de route (route handlers) reçoivent les paramètres dynamiques sous forme de promesses qui doivent être attendues avant d'être utilisées, comme indiqué dans la documentation de mise à niveau vers la version 15.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

sharp falconBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Pour connecter votre API Next.js à une base de données Neon Postgres afin que vos données persistent, voici les étapes à suivre :

Intégration avec Neon Postgres

Neon est l'une des options recommandées par Vercel pour les bases de données PostgreSQL. Voici comment l'intégrer à votre application Next.js :

1. Configurer Neon via le Marketplace Vercel

La façon la plus simple est d'utiliser le Marketplace Vercel pour connecter Neon à votre projet :

  1. Accédez au Marketplace Vercel et sélectionnez Neon
  2. Suivez les étapes d'intégration pour provisionner une base de données
  3. Vercel configurera automatiquement les variables d'environnement nécessaires

2. Configuration manuelle

Si vous préférez configurer manuellement :

  1. Créez un compte sur Neon
  2. Créez un nouveau projet et obtenez votre chaîne de connexion
  3. Ajoutez la chaîne de connexion à votre fichier .env.local :
DATABASE_URL="postgres://user:pass@host/db"

3. Installer les dépendances

Vous pouvez utiliser un ORM comme Prisma ou Drizzle pour faciliter l'interaction avec la base de données :

npm install @neondatabase/serverless
# Si vous utilisez Prisma
npm install prisma @prisma/client
# OU si vous préférez Drizzle
npm install drizzle-orm pg

4. Configurer votre schéma de base de données

Pour Prisma, créez un fichier prisma/schema.prisma :

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Shortlink {
  id       Int    @id @default(autoincrement())
  url      String
  shortUrl String @unique
}

Puis exécutez :

npx prisma db push
npx prisma generate

5. Modifier votre service pour utiliser la base de données

#

Voici comment vous pourriez modifier votre service de shortlinks pour utiliser Prisma :

// services/shortlinksService.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

const shortlinksService = {
  create: async (data: { url: string; shortUrl: string }) => {
    return await prisma.shortlink.create({
      data
    })
  },

  findOne: async (id: number) => {
    return await prisma.shortlink.findUnique({
      where: { id }
    })
  },

  update: async (id: number, data: { url?: string; shortUrl?: string }) => {
    return await prisma.shortlink.update({
      where: { id },
      data
    })
  },

  remove: async (id: number) => {
    return await prisma.shortlink.delete({
      where: { id }
    })
  }
}

export default shortlinksService

6. Mettre à jour vos gestionnaires de route

Modifiez vos gestionnaires de route pour utiliser les méthodes asynchrones :

// app/api/shortlinks/[id]/route.ts
export async function GET(
  req: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  
  if (!id) {
    return NextResponse.json({ message: 'Missing ID' }, { status: 400 });
  }
  
  const numId = Number(id);
  if (isNaN(numId)) {
    return NextResponse.json({ message: 'Invalid ID' }, { status: 400 });
  }
  
  // Notez l'ajout de await ici
  const shortlink = await shortlinksService.findOne(numId);
  if (!shortlink) {
    return NextResponse.json({ message: 'Shortlink not found' }, { status: 404 });
  }
  
  return NextResponse.json(shortlink, { status: 200 });
}

Plusieurs templates Next.js sont disponibles pour vous aider à démarrer avec Neon Postgres :

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

sharp falconBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez l'erreur concernant l'utilisation de params dans Next.js 15.2. Cette erreur est liée au changement introduit dans Next.js 15 où les APIs dynamiques comme params sont devenues asynchrones.

Pour corriger cette erreur dans votre fichier src\app\dashboard\shortlinks\edit\[id]\page.tsx, vous devez modifier la façon dont vous accédez à params.id. Voici comment corriger votre code :

export default async function EditShortlinkPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const session = await getSession();

  if (!session?.user?.id) {
    return <p className="text-red-600 font-medium">❌ Non connecté</p>;
  }

  const shortlink = await prisma.shortlink.findFirst({
    where: {
      id: id,
      userId: session.user.id,
    },
  });

  if (!shortlink) {
    return (
      <p className="text-red-600 font-medium">
        ❌ Shortlink introuvable ou non autorisé.
      </p>
    );
  }

  const apiKey = await prisma.apiKey.findFirst({
    where: { userId: session.user.id },
  });

  return (
    <EditShortlinkForm
      shortlink={shortlink}
      apiKey={apiKey?.key || ""}
    />
  );
}

Les modifications principales sont :

  1. Changer le type de params de { id: string } à Promise<{ id: string }>
  2. Extraire l'ID avec const { id } = await params avant de l'utiliser

Cette approche est conforme à la documentation officielle de Next.js qui indique que les APIs dynamiques sont maintenant asynchrones et doivent être attendues avant d'utiliser leurs propriétés.

#

Si quelqu'un vous a dit de ne "jamais faire await params", cette information est incorrecte pour Next.js 15. C'est exactement ce que la documentation recommande de faire, comme vous l'avez correctement cité dans votre exemple.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez toujours l'erreur concernant l'utilisation de params dans votre gestionnaire de route PUT. L'erreur indique que vous accédez directement à context.params.id sans attendre la résolution de la promesse.

Voici comment corriger votre code pour la méthode PUT :

export async function PUT(
  req: NextRequest, 
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const apiKey = req.headers.get("x-api-key");

  if (!apiKey) {
    // Votre code pour gérer l'absence d'API key
  }
  
  // Reste de votre code...
}

Les modifications principales sont :

  1. Remplacer context par une déstructuration directe { params }
  2. Spécifier le type correct pour params : Promise<{ id: string }>
  3. Attendre la résolution de la promesse avec const { id } = await params au lieu d'accéder directement à context.params.id

Cette modification est nécessaire car dans Next.js 15, tous les paramètres dynamiques comme params sont asynchrones et doivent être attendus avant d'être utilisés, comme indiqué dans la documentation sur les APIs dynamiques asynchrones.

L'erreur se produit même si votre API semble fonctionner (comme indiqué par le statut 200), mais il est important de corriger ce problème pour éviter des comportements inattendus dans les futures versions de Next.js.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Pour corriger l'erreur concernant l'utilisation de params dans votre gestionnaire de route PUT, vous devez modifier votre code pour gérer les paramètres asynchrones conformément aux exigences de Next.js 15. Voici comment corriger votre fichier :

// app/api/shortlinks/[id]/route.ts
import { NextRequest, NextResponse } from "next/server";
import shortlinksService from "@/services/shortlinksService";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function PUT(
  req: NextRequest, 
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  const apiKey = req.headers.get("x-api-key");

  if (!apiKey) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  const apiKeyRecord = await prisma.apiKey.findUnique({
    where: { key: apiKey },
  });

  if (!apiKeyRecord) {
    return NextResponse.json({ message: "Invalid API Key" }, { status: 403 });
  }

  const userId = apiKeyRecord.userId;
  const data = await req.json();

  const updated = await prisma.shortlink.updateMany({
    where: { id, userId },
    data: {
      url: data.url,
      shortUrl: data.code, // ✅ mapping correct
    },
  });

  if (updated.count === 0) {
    return NextResponse.json({ message: "Non trouvé ou non autorisé" }, { status: 404 });
  }

  return NextResponse.json({ message: "Mis à jour" }, { status: 200 });
}

Les modifications principales sont :

  1. Remplacer context par une déstructuration directe { params }: { params: Promise<{ id: string }> }
  2. Attendre la résolution de la promesse avec const { id } = await params au lieu d'accéder directement à context.params.id
#

Cette modification est nécessaire car dans Next.js 15, les paramètres dynamiques comme params sont asynchrones et doivent être attendus avant d'être utilisés, comme indiqué dans la documentation de mise à niveau vers la version 15.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur de type dans votre gestionnaire de route POST. L'erreur indique que le type ContextParams n'est pas valide pour le second argument de la fonction.

Dans Next.js 15, les paramètres dynamiques comme params sont devenus asynchrones et doivent être traités comme des promesses. Voici comment corriger votre code :

import { NextRequest, NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";
import { getSession } from "@/lib/auth";

const prisma = new PrismaClient();

export async function POST(
  req: NextRequest, 
  { params }: { params: Promise<{ id: string }> }
) {
  const session = await getSession();

  if (!session?.user?.id) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  const { id } = await params;

  await prisma.shortlink.delete({
    where: {
      id,
      userId: session.user.id,
    },
  });

  return NextResponse.redirect("/dashboard/shortlinks");
}

Les modifications principales sont :

  1. Suppression de l'interface ContextParams qui n'est plus nécessaire
  2. Modification du type du second paramètre pour indiquer que params est une promesse : { params: Promise<{ id: string }> }
  3. Utilisation de await params pour attendre la résolution de la promesse avant d'accéder à la propriété id

Cette modification est nécessaire en raison d'un changement majeur dans Next.js 15 où les APIs dynamiques sont devenues asynchrones, comme expliqué dans la documentation de mise à niveau vers la version 15.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur de type concernant l'importation de PageProps depuis le module "next". Cette erreur indique que le module "next" n'exporte pas de membre nommé PageProps.

Pour corriger cette erreur dans Next.js 15, vous devez modifier votre code pour utiliser le bon typage des paramètres de page. Voici comment corriger votre fichier :

import { redirect } from "next/navigation";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default async function RedirectPage({
  params,
}: {
  params: Promise<{ code: string }>;
}) {
  const { code } = await params;

  const shortlink = await prisma.shortlink.findUnique({
    where: { shortUrl: code },
  });

  if (!shortlink) {
    return (
      <div className="h-screen flex items-center justify-center">
        <p className="text-lg text-red-600 font-semibold">
          :mag: Shortlink introuvable.
        </p>
      </div>
    );
  }

  redirect(shortlink.url);
}

Les modifications principales sont :

  1. Suppression de l'importation import { type PageProps } from "next"; qui n'existe pas
  2. Remplacement du type PageProps<{ code: string }> par le type correct { params: Promise<{ code: string }> }
  3. Utilisation de await params pour attendre la résolution de la promesse avant d'accéder à la propriété code

Cette modification est nécessaire car dans Next.js 15, les paramètres dynamiques comme params sont asynchrones et doivent être attendus avant d'être utilisés, comme indiqué dans la documentation sur les routes dynamiques.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur TypeScript concernant le paramètre context qui a implicitement un type any. Cette erreur est liée au fait que vous n'avez pas spécifié le type du paramètre context dans votre gestionnaire de route DELETE.

Dans Next.js 15, les paramètres dynamiques comme params sont asynchrones et doivent être typés correctement. Voici comment corriger votre code :

import { NextRequest, NextResponse } from "next/server";
import { getSession } from "@/lib/auth";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function DELETE(
  req: NextRequest, 
  { params }: { params: Promise<{ id: string }> }
) {
  const session = await getSession();

  if (!session?.user?.id) {
    return NextResponse.json({ message: "Non autorisé" }, { status: 401 });
  }

  const { id } = await params;

  const email = await prisma.secondaryEmail.findUnique({
    where: { id },
  });

  if (!email || email.userId !== session.user.id) {
    return NextResponse.json(
      { message: "Non trouvé ou non autorisé" },
      { status: 404 }
    );
  }

  await prisma.secondaryEmail.delete({ where: { id } });

  return NextResponse.json({ message: "Supprimé" }, { status: 200 });
}

Les modifications principales sont :

  1. Remplacer context par une déstructuration directe { params }: { params: Promise<{ id: string }> }
  2. Attendre la résolution de la promesse avec const { id } = await params au lieu d'accéder directement à context.params.id
#

Cette modification est nécessaire car dans Next.js 15, les paramètres dynamiques comme params sont asynchrones et doivent être attendus avant d'être utilisés, comme indiqué dans la documentation de mise à niveau vers la version 15.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur de type concernant les props passées au composant DeleteAccountForm. L'erreur indique que le type { userId: string; } n'est pas assignable au type IntrinsicAttributes.

Cette erreur se produit généralement lorsque vous passez des props à un composant qui n'a pas défini ces props dans son interface ou type. Dans ce cas, le composant DeleteAccountForm ne semble pas avoir défini de type pour la prop userId.

Pour résoudre ce problème, vous devez définir correctement les props pour votre composant DeleteAccountForm. Voici comment vous pourriez corriger votre code :

  1. D'abord, assurez-vous que votre composant DeleteAccountForm définit correctement ses props :
// Dans DeleteAccountForm.tsx
"use client";

import { useState } from "react";

interface DeleteAccountFormProps {
  userId: string;
}

export default function DeleteAccountForm({ userId }: DeleteAccountFormProps) {
  // Votre code ici
  return (
    // Votre JSX ici
  );
}
  1. Si vous utilisez déjà une interface pour les props dans DeleteAccountForm, assurez-vous qu'elle inclut la propriété userId.

Cette erreur n'est pas liée aux changements de Next.js 15 concernant les paramètres asynchrones, mais plutôt à un problème de typage TypeScript standard. Assurez-vous que tous vos composants définissent correctement les types des props qu'ils acceptent.

Si le composant DeleteAccountForm est un composant client (avec la directive "use client"), assurez-vous également que les props que vous lui passez sont sérialisables, car elles doivent être transmises du serveur au client.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois le problème. Votre composant DeleteAccountForm ne définit pas de prop userId, mais dans votre page précédente, vous essayez de lui passer cette prop : <DeleteAccountForm userId={session.user.id} />.

Pour résoudre cette erreur de type, vous avez deux options :

Option 1 : Modifier le composant DeleteAccountForm pour accepter la prop userId

"use client";

import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";

interface DeleteAccountFormProps {
  userId: string;
}

export default function DeleteAccountForm({ userId }: DeleteAccountFormProps) {
  const [confirmText, setConfirmText] = useState("");
  const [isDeleting, startTransition] = useTransition();
  const router = useRouter();

  const deleteAccount = () => {
    if (confirmText !== "SUPPRIMER MON COMPTE") {
      toast.error(":x: Vous devez recopier le texte");
      return;
    }

    startTransition(async () => {
      // Vous pouvez utiliser userId ici si nécessaire
      const res = await fetch(`/api/account/delete`, {
        method: "POST",
      });

      if (res.ok) {
        toast.success(":white_check_mark: Compte supprimé.");
        router.push("/account-deleted");
      } else {
        toast.error(":x: Une erreur est survenue.");
      }
    });
  };

  // Reste du code inchangé...
}

Option 2 : Supprimer la prop userId dans la page parent

Si vous n'avez pas besoin de la prop userId dans le composant DeleteAccountForm (ce qui semble être le cas puisque vous n'utilisez pas cette prop dans le code actuel), vous pouvez simplement modifier votre page parent pour ne pas passer cette prop :

#
// Dans src/app/dashboard/settings/account/delete/page.tsx
return (
  <main className="max-w-xl mx-auto py-10 px-4">
    <h1 className="text-2xl font-bold mb-6">:x: Supprimer mon compte</h1>
    <DeleteAccountForm />
  </main>
);

Cette deuxième option est plus simple si vous n'avez pas besoin de l'ID utilisateur dans le formulaire de suppression. Votre API /api/account/delete semble déjà récupérer l'utilisateur à partir de la session côté serveur, donc il n'est probablement pas nécessaire de passer l'ID utilisateur au composant client.

Cette erreur est un problème de typage TypeScript standard et n'est pas liée aux changements de Next.js 15 concernant les paramètres asynchrones.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

sharp falconBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je comprends que vous développez un service de raccourcissement d'URL avec Next.js 15.2 où chaque utilisateur peut ajouter son propre domaine personnalisé. Vous avez réussi à configurer les domaines avec CNAME, mais vous rencontrez un problème : lorsqu'un utilisateur accède à un domaine personnalisé, il peut voir tous les liens courts de tous les utilisateurs, au lieu de voir uniquement ses propres liens.

Ce problème est typique dans les applications multi-tenant avec des domaines personnalisés. Vous avez besoin d'implémenter une logique de filtrage basée sur le domaine dans votre middleware.

Voici comment vous pourriez résoudre ce problème :

1. Modifier votre middleware pour filtrer par domaine

Vous devez adapter votre middleware pour qu'il détecte le domaine de la requête et ne serve que les liens courts appartenant à l'utilisateur propriétaire de ce domaine.

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();
#

export async function middleware(request: NextRequest) {
const url = request.nextUrl.clone();
const { pathname, hostname } = url;

// Ignorer les routes d'API, d'authentification, etc.
if (
pathname.startsWith('/api') ||
pathname.startsWith('/sign-in') ||
pathname.startsWith('/sign-up') ||
pathname.startsWith('/dashboard') ||
pathname.startsWith('/_next')
) {
return NextResponse.next();
}

// Extraire le code court du chemin
const shortCode = pathname.slice(1);

if (!shortCode) {
return NextResponse.next();
}

try {
// Trouver le domaine dans la base de données
const domain = await prisma.domain.findFirst({
where: {
name: hostname,
verified: true,
},
include: {
user: true,
},
});

if (!domain) {
  // Si ce n'est pas un domaine personnalisé, continuer normalement
  return NextResponse.next();
}

// Chercher le lien court qui appartient à l'utilisateur propriétaire du domaine
const shortlink = await prisma.shortlink.findFirst({
  where: {
    shortUrl: shortCode,
    userId: domain.userId,
  },
});

if (shortlink) {
  return NextResponse.redirect(shortlink.url);
} else {
  // Lien non trouvé pour cet utilisateur
  return NextResponse.rewrite(new URL('/not-found', request.url));
}

} catch (error) {
console.error('Middleware error:', error);
return NextResponse.next();
}
}

export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};


### 2. Créer une page d'erreur personnalisée
#
// app/not-found.tsx
export default function NotFound() {
  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-100">
      <div className="text-center p-8 bg-white rounded-lg shadow-md">
        <h1 className="text-2xl font-bold text-red-600 mb-4">Lien non trouvé</h1>
        <p className="text-gray-600">
          Le lien court que vous avez demandé n'existe pas ou n'est pas disponible sur ce domaine.
        </p>
      </div>
    </div>
  );
}

3. Optimiser avec le hostname rewrites

Pour les applications multi-tenant avec des domaines personnalisés, vous pouvez également utiliser la fonctionnalité de "hostname rewrites" de Next.js, comme décrit dans le template Hostname Rewrites de Vercel :

// middleware.ts (version alternative avec hostname rewrites)
import { NextRequest, NextResponse } from 'next/server';

export async function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();
  const { hostname, pathname } = url;
  
  // Ignorer les routes d'API, etc.
  if (pathname.startsWith('/api') || pathname.startsWith('/_next')) {
    return NextResponse.next();
  }
  
  // Vérifier si c'est un domaine personnalisé
  const customDomain = hostname !== 'sbsb.fr' && 
                       hostname !== 'www.sbsb.fr' && 
                       !hostname.endsWith('.vercel.app');
  
  if (customDomain) {
    // Réécrire la requête pour inclure le domaine dans le chemin
    // Cela permettra à votre application de savoir quel utilisateur servir
    url.pathname = `/domains/${hostname}${pathname}`;
    return NextResponse.rewrite(url);
  }
  
  return NextResponse.next();
}

Puis créez une route dynamique pour gérer ces requêtes :

#
// app/domains/[domain]/[code]/page.tsx
export default async function CustomDomainRedirect({
  params,
}: {
  params: Promise<{ domain: string; code: string }>;
}) {
  const { domain, code } = await params;
  
  // Rechercher le domaine et l'utilisateur associé
  const domainRecord = await prisma.domain.findUnique({
    where: { name: domain },
    include: { user: true },
  });
  
  if (!domainRecord) {
    return <NotFound />;
  }
  
  // Rechercher le lien court appartenant à cet utilisateur
  const shortlink = await prisma.shortlink.findFirst({
    where: {
      shortUrl: code,
      userId: domainRecord.userId,
    },
  });
  
  if (!shortlink) {
    return <NotFound />;
  }
  
  // Rediriger vers l'URL cible
  redirect(shortlink.url);
}

Cette approche est particulièrement utile pour les applications multi-tenant comme la vôtre, car elle permet de gérer efficacement les domaines personnalisés tout en maintenant une séparation claire entre les données des différents utilisateurs.

Si vous souhaitez approfondir cette implémentation, je vous recommande de consulter le guide officiel de Vercel sur les applications multi-tenant et le template Platforms Starter Kit qui contient des exemples complets pour ce type d'application.

Souhaitez-vous que j'analyse votre middleware actuel ou d'autres fichiers spécifiques pour vous aider à résoudre ce problème?

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois votre middleware actuel et comprends que vous souhaitez permettre à chaque domaine personnalisé d'accéder uniquement aux routes API de shortlinks de l'utilisateur propriétaire de ce domaine.

Votre middleware actuel protège bien les routes API avec une clé API et les routes du dashboard avec une session, mais il ne gère pas encore la logique de filtrage par domaine pour les domaines personnalisés.

Voici comment vous pourriez modifier votre middleware pour implémenter cette fonctionnalité :

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function middleware(request: NextRequest) {
  const url = request.nextUrl.pathname;
  const hostname = request.headers.get("host") || "";

  // Vérifier si c'est un domaine personnalisé (différent du domaine principal)
  const isCustomDomain = !hostname.includes("sbsb.fr");
#

// Si c'est un domaine personnalisé et que c'est une route de redirection (pas /api, /dashboard, etc.)
if (isCustomDomain && !url.startsWith("/api") && !url.startsWith("/dashboard") && !url.startsWith("/_next")) {
// Extraire le code court du chemin (enlever le premier slash)
const shortCode = url.slice(1);

if (shortCode) {
  try {
    // Trouver le domaine dans la base de données
    const domain = await prisma.domain.findFirst({
      where: {
        name: hostname,
        verified: true,
      },
    });
    
    if (!domain) {
      // Domaine non trouvé ou non vérifié
      return NextResponse.redirect(new URL("/domain-not-found", request.url));
    }
    
    // Chercher le shortlink qui appartient à l'utilisateur propriétaire du domaine
    const shortlink = await prisma.shortlink.findFirst({
      where: {
        shortUrl: shortCode,
        userId: domain.userId,
      },
    });
    
    if (shortlink) {
      // Rediriger vers l'URL cible
      return NextResponse.redirect(new URL(shortlink.url));
    } else {
      // Shortlink non trouvé pour cet utilisateur
      return NextResponse.redirect(new URL("/shortlink-not-found", request.url));
    }
  } catch (error) {
    console.error("Middleware error:", error);
    return NextResponse.redirect(new URL("/error", request.url));
  }
}

}

// :closed_lock_with_key: PROTECTION DES ROUTES API PAR CLÉ
if (url.startsWith("/api/shortlinks")) {
const apiKey = request.headers.get("x-api-key");

if (!apiKey) {
  return NextResponse.json({ message: "API Key requise" }, { status: 401 });
}

const validKey = await prisma.apiKey.findUnique({ where: { key: apiKey } });
#

if (!validKey) {
return NextResponse.json({ message: "Clé API invalide" }, { status: 403 });
}

// :white_check_mark: Autorisé : on continue sans injecter de header
return NextResponse.next();

}

// :closed_lock_with_key: PROTECTION DASHBOARD VIA SESSION
if (url.startsWith("/dashboard")) {
const cookies = getSessionCookie(request);
if (!cookies) {
return NextResponse.redirect(new URL("/sign-in", request.url));
}
}

return NextResponse.next();
}

// Modifier le matcher pour inclure toutes les routes
export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico).*)",
],
};


Les modifications principales sont :

1. Ajout de la détection des domaines personnalisés
2. Ajout de la logique pour extraire le code court du chemin
3. Recherche du domaine dans la base de données
4. Recherche du shortlink appartenant à l'utilisateur propriétaire du domaine
5. Redirection vers l'URL cible si le shortlink est trouvé
6. Modification du matcher pour inclure toutes les routes (pas seulement dashboard et api)

Vous devrez également créer des pages d'erreur personnalisées :

- `/domain-not-found` : pour indiquer que le domaine n'est pas trouvé ou n'est pas vérifié
- `/shortlink-not-found` : pour indiquer que le shortlink n'existe pas pour cet utilisateur
- `/error` : pour les erreurs générales

Cette approche permet de s'assurer que chaque domaine personnalisé ne peut accéder qu'aux shortlinks de l'utilisateur propriétaire de ce domaine, tout en préservant le fonctionnement normal de votre domaine principal sbsb.fr.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Pour gérer les cas d'erreur dans votre middleware Next.js sans créer de routes spécifiques, vous pouvez simplement renvoyer une réponse HTML directement. Voici comment vous pourriez modifier votre middleware pour afficher des messages d'erreur au lieu de rediriger vers des routes d'erreur :

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function middleware(request: NextRequest) {
  const url = request.nextUrl.pathname;
  const hostname = request.headers.get("host") || "";

  // Vérifier si c'est un domaine personnalisé (différent du domaine principal)
  const isCustomDomain = !hostname.includes("sbsb.fr");
#

// Si c'est un domaine personnalisé et que c'est une route de redirection (pas /api, /dashboard, etc.)
if (isCustomDomain && !url.startsWith("/api") && !url.startsWith("/dashboard") && !url.startsWith("/_next")) {
// Extraire le code court du chemin (enlever le premier slash)
const shortCode = url.slice(1);

if (shortCode) {
  try {
    // Trouver le domaine dans la base de données
    const domain = await prisma.domain.findFirst({
      where: {
        name: hostname,
        verified: true,
      },
    });
    
    if (!domain) {
      // Domaine non trouvé ou non vérifié - renvoyer un message HTML
      return new NextResponse(
        `<!DOCTYPE html>
        <html>
          <head>
            <title>Domaine non vérifié</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <style>
              body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f5f5f5; }
              .container { max-width: 500px; padding: 2rem; background-color: white; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); text-align: center; }
              h1 { color: #e53e3e; margin-bottom: 1rem; }
              p { color: #4a5568; line-height: 1.5; }
            </style>
          </head>
          <body>
            <div class="container">
              <h1>Domaine non vérifié</h1>
              <p>Ce domaine n'est pas vérifié ou n'est pas enregistré dans notre système.</p>
            </div>
          </body>
        </html>`,
        { status: 404, headers: { "Content-Type": "text/html; charset=utf-8" } }
      );
    }
#

// Chercher le shortlink qui appartient à l'utilisateur propriétaire du domaine
const shortlink = await prisma.shortlink.findFirst({
where: {
shortUrl: shortCode,
userId: domain.userId,
},
});

    if (shortlink) {
      // Rediriger vers l'URL cible
      return NextResponse.redirect(new URL(shortlink.url));
    } else {
      // Shortlink non trouvé pour cet utilisateur - renvoyer un message HTML
      return new NextResponse(
        `<!DOCTYPE html>
        <html>
          <head>
            <title>Lien non trouvé</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <style>
              body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f5f5f5; }
              .container { max-width: 500px; padding: 2rem; background-color: white; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); text-align: center; }
              h1 { color: #e53e3e; margin-bottom: 1rem; }
              p { color: #4a5568; line-height: 1.5; }
            </style>
          </head>
          <body>
            <div class="container">
              <h1>Lien non trouvé</h1>
              <p>Le lien court que vous avez demandé n'existe pas ou n'est pas disponible sur ce domaine.</p>
            </div>
          </body>
        </html>`,
        { status: 404, headers: { "Content-Type": "text/html; charset=utf-8" } }
      );
    }
  } catch (error) {
    console.error("Middleware error:", error);
    // Erreur générale - renvoyer un message HTML
    return new NextResponse(
      `<!DOCTYPE html>
#

<html>
<head>
<title>Erreur serveur</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f5f5f5; }
.container { max-width: 500px; padding: 2rem; background-color: white; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); text-align: center; }
h1 { color: #e53e3e; margin-bottom: 1rem; }
p { color: #4a5568; line-height: 1.5; }
</style>
</head>
<body>
<div class="container">
<h1>Erreur serveur</h1>
<p>Une erreur s'est produite lors du traitement de votre demande. Veuillez réessayer ultérieurement.</p>
</div>
</body>
</html>`,
{ status: 500, headers: { "Content-Type": "text/html; charset=utf-8" } }
);
}
}
}

#

// Le reste de votre middleware reste inchangé...
// ...
}


Cette approche présente plusieurs avantages :

1. Vous n'avez pas besoin de créer des routes d'erreur spécifiques
2. Les messages d'erreur sont directement renvoyés par le middleware
3. Vous pouvez personnaliser le style et le contenu des messages d'erreur

Notez que dans Next.js, il est généralement recommandé d'utiliser les pages d'erreur intégrées comme `not-found.js` pour une meilleure gestion des erreurs, mais cette approche directe via le middleware est parfaitement valide pour votre cas d'utilisation spécifique avec des domaines personnalisés.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur TypeScript concernant la propriété 'verified' qui n'existe pas dans le type 'DomainWhereInput'. Cette erreur est typique lorsque vous utilisez Prisma avec Next.js et que vous essayez d'utiliser une propriété qui n'est pas définie dans votre schéma Prisma.

L'erreur indique que vous essayez d'utiliser une propriété 'verified' dans une clause where de Prisma, mais cette propriété n'est pas reconnue par TypeScript car elle n'est probablement pas définie dans votre modèle de domaine.

Pour résoudre ce problème, vous avez deux options :

Option 1 : Ajouter la propriété 'verified' à votre modèle de domaine

Si vous souhaitez utiliser cette propriété, vous devez l'ajouter à votre schéma Prisma. Dans votre fichier schema.prisma, ajoutez la propriété au modèle Domain :

model Domain {
  id        String   @id @default(cuid())
  name      String   @unique
  verified  Boolean  @default(false)
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  // autres champs...
}

Après avoir modifié le schéma, exécutez npx prisma generate pour mettre à jour les types TypeScript générés.

Option 2 : Corriger la requête Prisma

Si la propriété existe déjà dans votre base de données mais n'est pas correctement définie dans votre schéma, vous devez vous assurer que le nom de la propriété correspond exactement à celui défini dans votre schéma.

Par exemple, si dans votre middleware vous avez :

const domain = await prisma.domain.findFirst({
  where: {
    name: hostname,
    verified: true, // Cette ligne cause l'erreur
  },
});

Vérifiez que la propriété s'appelle bien 'verified' dans votre schéma et non pas 'isVerified' ou un autre nom similaire.

Option 3 : Ignorer les erreurs TypeScript temporairement

#

Si vous êtes en phase de développement et que vous souhaitez ignorer temporairement cette erreur, vous pouvez configurer Next.js pour ignorer les erreurs TypeScript pendant la compilation :

// next.config.js
module.exports = {
  typescript: {
    // !! WARN !!
    // Dangerously allow production builds to successfully complete even if
    // your project has type errors.
    // !! WARN !!
    ignoreBuildErrors: true,
  },
}

Cependant, cette approche n'est pas recommandée pour la production car elle peut masquer des erreurs importantes.

Cette erreur est courante dans les projets Next.js utilisant Prisma et est généralement due à un décalage entre votre schéma Prisma et les requêtes que vous effectuez. Assurez-vous que votre schéma est à jour et que vous utilisez les propriétés correctes dans vos requêtes.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@uncut vale Kapa.ai is still learning and improving, please let me know how I did by reacting below.

sharp falconBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Je vois que vous rencontrez une erreur de type concernant les paramètres de page dans Next.js 15. L'erreur indique que le type { code: string } ne satisfait pas la contrainte PageProps car params devrait être une promesse.

Dans Next.js 15, les APIs dynamiques comme params et searchParams sont devenues asynchrones et doivent être traitées comme des promesses. Voici comment corriger votre code :

import { redirect } from "next/navigation";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default async function RedirectPage({
  params,
}: {
  params: Promise<{ code: string }>;
}) {
  const { code } = await params;

  const shortlink = await prisma.shortlink.findFirst({
    where: {
      shortUrl: code,
      domainId: null,
    },
  });

  if (!shortlink) {
    return (
      <div className="h-screen flex items-center justify-center">
        <p className="text-lg text-red-600 font-semibold">
          🔍 Shortlink introuvable.
        </p>
      </div>
    );
  }

  redirect(shortlink.url);
}

Les modifications principales sont :

  1. Changer le type de params de { code: string } à Promise<{ code: string }>
  2. Utiliser await params pour attendre la résolution de la promesse avant d'accéder à la propriété code

Cette modification est nécessaire car dans Next.js 15, les paramètres dynamiques comme params sont asynchrones et doivent être attendus avant d'être utilisés, comme indiqué dans la documentation sur les routes dynamiques.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: