feat: conformite enonce - explorer, favoris, stats perso, tests, slots
Some checks failed
Deploy XIP / deploy (push) Failing after 37s
Some checks failed
Deploy XIP / deploy (push) Failing after 37s
Fonctionnel
- Backend messages : GET /api/messages/:id (detail) + recherche (q),
pagination par curseur (before/limit) avec enveloppe { items, nextCursor,
hasMore } ; le flux temps reel garde l'ancien format quand aucun parametre.
- Explorer (/explorer) : catalogue distant, recherche debouncee + annulable
(AbortController), filtre, defilement infini, etat garde (keep-alive).
- Details par id : /message/:id et /shop/p/:id (consomment route.params).
- Favoris (/favoris) : liste perso persistee en localStorage, notation
(note/rating/statut) via modale, refletee partout (bouton favori).
- Mes stats (/mes-stats) : agregats derives des favoris (note moyenne, top
pays/auteurs, statuts), auto-mis a jour, route gardee si liste vide.
- Routeur : pages secondaires en lazy-load + repli, garde beforeEnter.
Technique
- Slots : PrefSection (slot defaut + slot nomme) enveloppe les 5 sections
"Mes Persos" ; Modal (Teleport + slots).
- v-model custom : SearchBox (defineModel + debounce).
- Directive custom : v-click-outside.
- Tests Vitest : 25 tests (etat, fonctions, composants), ~86% du code metier.
- Retrait d'Ionic (inutilise). Script typecheck backend ; tsconfig @types/bun.
- Correctif type : garde stockLimit nullable dans l'achat (catalog.ts).
- README complet (URL, stack, run, tests, secrets, deploiement, mention IA).
This commit is contained in:
@@ -1,282 +1,282 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// ── Marketplace catalogue (faithful to the shop mockups) ────────────────────
|
||||
// Prices are centi-credits (mockup € → credits): 9.99 → 999.
|
||||
const PRODUCTS = [
|
||||
{
|
||||
id: "cadre-pub",
|
||||
category: "publicite",
|
||||
name: "Cadre de Pub",
|
||||
subtitle: "1 000 impressions garanties · 130×180 px · lien cliquable",
|
||||
kind: "ad-frame",
|
||||
basePrice: 1500,
|
||||
promoPrice: 999,
|
||||
badge: "-33% FLASH PROMO",
|
||||
sortOrder: 10,
|
||||
metaJson: JSON.stringify({
|
||||
durations: [
|
||||
{ days: 7, extra: 0 },
|
||||
{ days: 14, extra: 800 },
|
||||
{ days: 30, extra: 2000 },
|
||||
],
|
||||
formats: [
|
||||
{ id: "static", label: "Image statique", extra: 0 },
|
||||
{ id: "gif", label: "GIF animé", extra: 300 },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "noads",
|
||||
category: "abonnements",
|
||||
name: "Abonnement NoAds",
|
||||
subtitle: "Supprime toutes les pubs du chat",
|
||||
kind: "subscription",
|
||||
basePrice: 499,
|
||||
badge: "POPULAIRE",
|
||||
sortOrder: 20,
|
||||
metaJson: JSON.stringify({
|
||||
plans: [
|
||||
{ id: "monthly", label: "Mensuel", price: 499 },
|
||||
{ id: "annual", label: "Annuel", price: 3999 },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "style-dore",
|
||||
category: "cosmetiques",
|
||||
name: "Style Doré",
|
||||
subtitle: "Ton IP en or brillant, visible de tous",
|
||||
kind: "ip-skin",
|
||||
basePrice: 999,
|
||||
badge: "LIMITÉ 50 ex.",
|
||||
stockLimit: 50,
|
||||
sortOrder: 30,
|
||||
metaJson: JSON.stringify({ variant: "gold" }),
|
||||
},
|
||||
{
|
||||
id: "pet",
|
||||
category: "cosmetiques",
|
||||
name: "Pet de Nom",
|
||||
subtitle: "Un petit élément décoratif autour de ton IP",
|
||||
kind: "pet",
|
||||
basePrice: 799,
|
||||
badge: "NOUVEAU",
|
||||
sortOrder: 40,
|
||||
metaJson: JSON.stringify({
|
||||
designs: [
|
||||
{ id: "coeur", char: "♥" },
|
||||
{ id: "etoile", char: "★" },
|
||||
{ id: "diamant", char: "♦" },
|
||||
{ id: "trefle", char: "♣" },
|
||||
{ id: "couronne", char: "♚" },
|
||||
{ id: "crane", char: "☠" },
|
||||
{ id: "eclair", char: "⚡" },
|
||||
{ id: "fleur", char: "✿" },
|
||||
{ id: "note", char: "♫" },
|
||||
{ id: "feu", char: "🔥" },
|
||||
],
|
||||
positions: ["left", "right", "both"],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "bundle-cosmetic",
|
||||
category: "promotions",
|
||||
name: "Pack Cosmétique",
|
||||
subtitle: "Style Doré + 1 Pet au choix",
|
||||
kind: "bundle",
|
||||
basePrice: 1798,
|
||||
promoPrice: 1499,
|
||||
badge: "-3 CR",
|
||||
sortOrder: 50,
|
||||
metaJson: JSON.stringify({ includes: ["style-dore", "pet"] }),
|
||||
},
|
||||
{
|
||||
// id == entitlement kind, so the "unlock" branch grants "element-skin".
|
||||
id: "element-skin",
|
||||
category: "cosmetiques",
|
||||
name: "Skin d'éléments",
|
||||
subtitle: "Relooke ta barre de saisie et ton bouton d'envoi",
|
||||
kind: "unlock",
|
||||
basePrice: 599,
|
||||
sortOrder: 45,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "rich-htmlcss",
|
||||
category: "premium",
|
||||
name: "Messages HTML / CSS",
|
||||
subtitle: "Mets en forme tes messages (sans script)",
|
||||
kind: "rich",
|
||||
basePrice: 2999,
|
||||
sortOrder: 60,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "rich-js",
|
||||
category: "premium",
|
||||
name: "Messages JavaScript",
|
||||
subtitle: "Scripts interactifs (isolés). TRÈS cher.",
|
||||
kind: "rich",
|
||||
basePrice: 19999,
|
||||
badge: "TRÈS TRÈS CHER",
|
||||
sortOrder: 70,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "no-file-limit",
|
||||
category: "premium",
|
||||
name: "Fichiers illimités",
|
||||
subtitle: "Plus de limite de 1 Mo sur tes pièces jointes",
|
||||
kind: "unlock",
|
||||
basePrice: 1499,
|
||||
sortOrder: 80,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "audio-alert",
|
||||
category: "premium",
|
||||
name: "Alerte audio générale",
|
||||
subtitle: "Fais hurler un son chez tout le monde (cooldown)",
|
||||
kind: "consumable",
|
||||
basePrice: 999,
|
||||
badge: "CONSOMMABLE",
|
||||
sortOrder: 90,
|
||||
metaJson: JSON.stringify({ cooldownMs: 60000, maxDurationMs: 5000 }),
|
||||
},
|
||||
// ── Cosmetics: IP color + send button skins ──────────────────────────────
|
||||
{
|
||||
id: "ip-colors",
|
||||
category: "cosmetiques",
|
||||
name: "Palette IP",
|
||||
subtitle: "Personnalise la couleur de ton adresse IP dans le chat",
|
||||
kind: "unlock",
|
||||
basePrice: 299,
|
||||
sortOrder: 46,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "send-skin-honker",
|
||||
category: "cosmetiques",
|
||||
name: "Doigt d'honneur",
|
||||
subtitle: "Bouton d'envoi qui exprime tout",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 47,
|
||||
metaJson: JSON.stringify({ char: "🖕", label: "Doigt d'honneur" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-skull",
|
||||
category: "cosmetiques",
|
||||
name: "Crâne",
|
||||
subtitle: "Envoyer avec style... macabre",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 48,
|
||||
metaJson: JSON.stringify({ char: "💀", label: "Crâne" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-rocket",
|
||||
category: "cosmetiques",
|
||||
name: "Fusée",
|
||||
subtitle: "Tes messages décollent",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 49,
|
||||
metaJson: JSON.stringify({ char: "🚀", label: "Fusée" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-ghost",
|
||||
category: "cosmetiques",
|
||||
name: "Fantôme",
|
||||
subtitle: "Boo !",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 50,
|
||||
metaJson: JSON.stringify({ char: "👻", label: "Fantôme" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-bomb",
|
||||
category: "cosmetiques",
|
||||
name: "Bombe",
|
||||
subtitle: "Message explosif",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 51,
|
||||
metaJson: JSON.stringify({ char: "💣", label: "Bombe" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-sword",
|
||||
category: "cosmetiques",
|
||||
name: "Épée",
|
||||
subtitle: "Tranche le silence",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 52,
|
||||
metaJson: JSON.stringify({ char: "⚔️", label: "Épée" }),
|
||||
},
|
||||
] as const;
|
||||
|
||||
// ── Ad inventory (the 4 hardcoded joke ads, now real data) ──────────────────
|
||||
const ADS = [
|
||||
{ brand: "NOVA", subtitle: "STORE 2026", url: "https://nova-store.io", cta: "DÉCOUVRIR", icon: "🛒", tone: "blue", kind: "band", weight: 1 },
|
||||
{ brand: "APEX GEAR", subtitle: "Gaming Setup", url: "https://apex-gear.com", cta: "ACHETER", icon: "🎮", tone: "green", kind: "band", weight: 1 },
|
||||
{ brand: "SHIELDVPN", subtitle: "Sécurité totale", url: "https://shieldvpn.net", cta: "ESSAI GRATUIT", icon: "🔒", tone: "purple", kind: "band", weight: 1 },
|
||||
{ brand: "CASINO LUCKY", subtitle: "OFFRE EXCLUSIVE · +200% · 500€ max", url: "https://casino-lucky.bet", cta: "JOUER MAINTENANT", icon: "♠", tone: "casino", kind: "casino", weight: 1 },
|
||||
] as const;
|
||||
|
||||
async function seedProducts() {
|
||||
for (const p of PRODUCTS) {
|
||||
await prisma.product.upsert({
|
||||
where: { id: p.id },
|
||||
create: p as any,
|
||||
update: p as any,
|
||||
});
|
||||
}
|
||||
console.log(`✅ ${PRODUCTS.length} produits upsertés.`);
|
||||
}
|
||||
|
||||
async function seedAds() {
|
||||
for (const a of ADS) {
|
||||
// Idempotent on brand: only seed the canonical (non-user) ads once.
|
||||
const existing = await prisma.ad.findFirst({ where: { brand: a.brand, ownerIp: null } });
|
||||
if (existing) {
|
||||
await prisma.ad.update({ where: { id: existing.id }, data: a as any });
|
||||
} else {
|
||||
await prisma.ad.create({ data: a as any });
|
||||
}
|
||||
}
|
||||
console.log(`✅ ${ADS.length} pubs upsertées.`);
|
||||
}
|
||||
|
||||
async function seedMessages() {
|
||||
const count = await prisma.message.count();
|
||||
if (count > 0) {
|
||||
console.log("⏭️ Messages déjà présents, seed messages ignoré.");
|
||||
return;
|
||||
}
|
||||
const root1 = await prisma.message.create({
|
||||
data: {
|
||||
content: "Bienvenue sur XIP — le réseau social sans filtre ni compte.",
|
||||
authorIp: "1.2.3.4",
|
||||
},
|
||||
});
|
||||
await prisma.message.create({
|
||||
data: { content: "Pas de compte, ton IP c'est toi.", authorIp: "5.6.7.8" },
|
||||
});
|
||||
await prisma.message.create({
|
||||
data: { content: "Réponse au premier message !", authorIp: "9.10.11.12", parentId: root1.id },
|
||||
});
|
||||
console.log("✅ 3 messages de démo créés.");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await seedProducts();
|
||||
await seedAds();
|
||||
await seedMessages();
|
||||
}
|
||||
|
||||
main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// ── Marketplace catalogue (faithful to the shop mockups) ────────────────────
|
||||
// Prices are centi-credits (mockup € → credits): 9.99 → 999.
|
||||
const PRODUCTS = [
|
||||
{
|
||||
id: "cadre-pub",
|
||||
category: "publicite",
|
||||
name: "Cadre de Pub",
|
||||
subtitle: "1 000 impressions garanties · 130×180 px · lien cliquable",
|
||||
kind: "ad-frame",
|
||||
basePrice: 1500,
|
||||
promoPrice: 999,
|
||||
badge: "-33% FLASH PROMO",
|
||||
sortOrder: 10,
|
||||
metaJson: JSON.stringify({
|
||||
durations: [
|
||||
{ days: 7, extra: 0 },
|
||||
{ days: 14, extra: 800 },
|
||||
{ days: 30, extra: 2000 },
|
||||
],
|
||||
formats: [
|
||||
{ id: "static", label: "Image statique", extra: 0 },
|
||||
{ id: "gif", label: "GIF animé", extra: 300 },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "noads",
|
||||
category: "abonnements",
|
||||
name: "Abonnement NoAds",
|
||||
subtitle: "Supprime toutes les pubs du chat",
|
||||
kind: "subscription",
|
||||
basePrice: 499,
|
||||
badge: "POPULAIRE",
|
||||
sortOrder: 20,
|
||||
metaJson: JSON.stringify({
|
||||
plans: [
|
||||
{ id: "monthly", label: "Mensuel", price: 499 },
|
||||
{ id: "annual", label: "Annuel", price: 3999 },
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "style-dore",
|
||||
category: "cosmetiques",
|
||||
name: "Style Doré",
|
||||
subtitle: "Ton IP en or brillant, visible de tous",
|
||||
kind: "ip-skin",
|
||||
basePrice: 999,
|
||||
badge: "LIMITÉ 50 ex.",
|
||||
stockLimit: 50,
|
||||
sortOrder: 30,
|
||||
metaJson: JSON.stringify({ variant: "gold" }),
|
||||
},
|
||||
{
|
||||
id: "pet",
|
||||
category: "cosmetiques",
|
||||
name: "Pet de Nom",
|
||||
subtitle: "Un petit élément décoratif autour de ton IP",
|
||||
kind: "pet",
|
||||
basePrice: 799,
|
||||
badge: "NOUVEAU",
|
||||
sortOrder: 40,
|
||||
metaJson: JSON.stringify({
|
||||
designs: [
|
||||
{ id: "coeur", char: "♥" },
|
||||
{ id: "etoile", char: "★" },
|
||||
{ id: "diamant", char: "♦" },
|
||||
{ id: "trefle", char: "♣" },
|
||||
{ id: "couronne", char: "♚" },
|
||||
{ id: "crane", char: "☠" },
|
||||
{ id: "eclair", char: "⚡" },
|
||||
{ id: "fleur", char: "✿" },
|
||||
{ id: "note", char: "♫" },
|
||||
{ id: "feu", char: "🔥" },
|
||||
],
|
||||
positions: ["left", "right", "both"],
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "bundle-cosmetic",
|
||||
category: "promotions",
|
||||
name: "Pack Cosmétique",
|
||||
subtitle: "Style Doré + 1 Pet au choix",
|
||||
kind: "bundle",
|
||||
basePrice: 1798,
|
||||
promoPrice: 1499,
|
||||
badge: "-3 CR",
|
||||
sortOrder: 50,
|
||||
metaJson: JSON.stringify({ includes: ["style-dore", "pet"] }),
|
||||
},
|
||||
{
|
||||
// id == entitlement kind, so the "unlock" branch grants "element-skin".
|
||||
id: "element-skin",
|
||||
category: "cosmetiques",
|
||||
name: "Skin d'éléments",
|
||||
subtitle: "Relooke ta barre de saisie et ton bouton d'envoi",
|
||||
kind: "unlock",
|
||||
basePrice: 599,
|
||||
sortOrder: 45,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "rich-htmlcss",
|
||||
category: "premium",
|
||||
name: "Messages HTML / CSS",
|
||||
subtitle: "Mets en forme tes messages (sans script)",
|
||||
kind: "rich",
|
||||
basePrice: 2999,
|
||||
sortOrder: 60,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "rich-js",
|
||||
category: "premium",
|
||||
name: "Messages JavaScript",
|
||||
subtitle: "Scripts interactifs (isolés). TRÈS cher.",
|
||||
kind: "rich",
|
||||
basePrice: 19999,
|
||||
badge: "TRÈS TRÈS CHER",
|
||||
sortOrder: 70,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "no-file-limit",
|
||||
category: "premium",
|
||||
name: "Fichiers illimités",
|
||||
subtitle: "Plus de limite de 1 Mo sur tes pièces jointes",
|
||||
kind: "unlock",
|
||||
basePrice: 1499,
|
||||
sortOrder: 80,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "audio-alert",
|
||||
category: "premium",
|
||||
name: "Alerte audio générale",
|
||||
subtitle: "Fais hurler un son chez tout le monde (cooldown)",
|
||||
kind: "consumable",
|
||||
basePrice: 999,
|
||||
badge: "CONSOMMABLE",
|
||||
sortOrder: 90,
|
||||
metaJson: JSON.stringify({ cooldownMs: 60000, maxDurationMs: 5000 }),
|
||||
},
|
||||
// ── Cosmetics: IP color + send button skins ──────────────────────────────
|
||||
{
|
||||
id: "ip-colors",
|
||||
category: "cosmetiques",
|
||||
name: "Palette IP",
|
||||
subtitle: "Personnalise la couleur de ton adresse IP dans le chat",
|
||||
kind: "unlock",
|
||||
basePrice: 299,
|
||||
sortOrder: 46,
|
||||
metaJson: JSON.stringify({}),
|
||||
},
|
||||
{
|
||||
id: "send-skin-honker",
|
||||
category: "cosmetiques",
|
||||
name: "Doigt d'honneur",
|
||||
subtitle: "Bouton d'envoi qui exprime tout",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 47,
|
||||
metaJson: JSON.stringify({ char: "🖕", label: "Doigt d'honneur" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-skull",
|
||||
category: "cosmetiques",
|
||||
name: "Crâne",
|
||||
subtitle: "Envoyer avec style... macabre",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 48,
|
||||
metaJson: JSON.stringify({ char: "💀", label: "Crâne" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-rocket",
|
||||
category: "cosmetiques",
|
||||
name: "Fusée",
|
||||
subtitle: "Tes messages décollent",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 49,
|
||||
metaJson: JSON.stringify({ char: "🚀", label: "Fusée" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-ghost",
|
||||
category: "cosmetiques",
|
||||
name: "Fantôme",
|
||||
subtitle: "Boo !",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 50,
|
||||
metaJson: JSON.stringify({ char: "👻", label: "Fantôme" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-bomb",
|
||||
category: "cosmetiques",
|
||||
name: "Bombe",
|
||||
subtitle: "Message explosif",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 51,
|
||||
metaJson: JSON.stringify({ char: "💣", label: "Bombe" }),
|
||||
},
|
||||
{
|
||||
id: "send-skin-sword",
|
||||
category: "cosmetiques",
|
||||
name: "Épée",
|
||||
subtitle: "Tranche le silence",
|
||||
kind: "send-skin",
|
||||
basePrice: 149,
|
||||
sortOrder: 52,
|
||||
metaJson: JSON.stringify({ char: "⚔️", label: "Épée" }),
|
||||
},
|
||||
] as const;
|
||||
|
||||
// ── Ad inventory (the 4 hardcoded joke ads, now real data) ──────────────────
|
||||
const ADS = [
|
||||
{ brand: "NOVA", subtitle: "STORE 2026", url: "https://nova-store.io", cta: "DÉCOUVRIR", icon: "🛒", tone: "blue", kind: "band", weight: 1 },
|
||||
{ brand: "APEX GEAR", subtitle: "Gaming Setup", url: "https://apex-gear.com", cta: "ACHETER", icon: "🎮", tone: "green", kind: "band", weight: 1 },
|
||||
{ brand: "SHIELDVPN", subtitle: "Sécurité totale", url: "https://shieldvpn.net", cta: "ESSAI GRATUIT", icon: "🔒", tone: "purple", kind: "band", weight: 1 },
|
||||
{ brand: "CASINO LUCKY", subtitle: "OFFRE EXCLUSIVE · +200% · 500€ max", url: "https://casino-lucky.bet", cta: "JOUER MAINTENANT", icon: "♠", tone: "casino", kind: "casino", weight: 1 },
|
||||
] as const;
|
||||
|
||||
async function seedProducts() {
|
||||
for (const p of PRODUCTS) {
|
||||
await prisma.product.upsert({
|
||||
where: { id: p.id },
|
||||
create: p as any,
|
||||
update: p as any,
|
||||
});
|
||||
}
|
||||
console.log(`✅ ${PRODUCTS.length} produits upsertés.`);
|
||||
}
|
||||
|
||||
async function seedAds() {
|
||||
for (const a of ADS) {
|
||||
// Idempotent on brand: only seed the canonical (non-user) ads once.
|
||||
const existing = await prisma.ad.findFirst({ where: { brand: a.brand, ownerIp: null } });
|
||||
if (existing) {
|
||||
await prisma.ad.update({ where: { id: existing.id }, data: a as any });
|
||||
} else {
|
||||
await prisma.ad.create({ data: a as any });
|
||||
}
|
||||
}
|
||||
console.log(`✅ ${ADS.length} pubs upsertées.`);
|
||||
}
|
||||
|
||||
async function seedMessages() {
|
||||
const count = await prisma.message.count();
|
||||
if (count > 0) {
|
||||
console.log("⏭️ Messages déjà présents, seed messages ignoré.");
|
||||
return;
|
||||
}
|
||||
const root1 = await prisma.message.create({
|
||||
data: {
|
||||
content: "Bienvenue sur XIP — le réseau social sans filtre ni compte.",
|
||||
authorIp: "1.2.3.4",
|
||||
},
|
||||
});
|
||||
await prisma.message.create({
|
||||
data: { content: "Pas de compte, ton IP c'est toi.", authorIp: "5.6.7.8" },
|
||||
});
|
||||
await prisma.message.create({
|
||||
data: { content: "Réponse au premier message !", authorIp: "9.10.11.12", parentId: root1.id },
|
||||
});
|
||||
console.log("✅ 3 messages de démo créés.");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await seedProducts();
|
||||
await seedAds();
|
||||
await seedMessages();
|
||||
}
|
||||
|
||||
main()
|
||||
.catch(console.error)
|
||||
.finally(() => prisma.$disconnect());
|
||||
|
||||
Reference in New Issue
Block a user