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,88 +1,88 @@
|
||||
#!/usr/bin/env bun
|
||||
import { spawn } from "bun";
|
||||
import { resolve } from "path";
|
||||
|
||||
const ROOT = resolve(import.meta.dir, "..");
|
||||
const BACKEND = resolve(ROOT, "backend");
|
||||
const FRONTEND = resolve(ROOT, "frontend");
|
||||
|
||||
function run(cmd: string[], cwd = ROOT): Promise<void> {
|
||||
console.log(` → ${cmd.join(" ")}`);
|
||||
const proc = spawn(cmd, { cwd, stdout: "inherit", stderr: "inherit" });
|
||||
return proc.exited.then((code) => {
|
||||
if (code !== 0) throw new Error(`Command failed (exit ${code}): ${cmd.join(" ")}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function waitForService(
|
||||
label: string,
|
||||
check: string[],
|
||||
retries = 30,
|
||||
delayMs = 2000
|
||||
): Promise<void> {
|
||||
for (let i = 1; i <= retries; i++) {
|
||||
const proc = spawn(check, { cwd: ROOT, stdout: "pipe", stderr: "pipe" });
|
||||
if ((await proc.exited) === 0) {
|
||||
console.log(`✅ ${label} is ready`);
|
||||
return;
|
||||
}
|
||||
console.log(`⏳ Waiting for ${label}... (${i}/${retries})`);
|
||||
await Bun.sleep(delayMs);
|
||||
}
|
||||
throw new Error(`${label} did not become ready in time`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("\n🐳 Starting Docker containers...");
|
||||
await run(["docker", "compose", "up", "-d"]);
|
||||
|
||||
console.log("\n⏳ Waiting for services...");
|
||||
await waitForService("PostgreSQL", [
|
||||
"docker", "compose", "exec", "postgres", "pg_isready", "-U", "xip",
|
||||
]);
|
||||
await waitForService("Redis", [
|
||||
"docker", "compose", "exec", "redis", "redis-cli", "ping",
|
||||
]);
|
||||
|
||||
console.log("\n🔧 Generating Prisma client...");
|
||||
await run(["bunx", "prisma", "generate"], BACKEND);
|
||||
|
||||
console.log("\n🗄️ Applying database migrations...");
|
||||
await run(
|
||||
["bunx", "prisma", "migrate", "dev", "--name", "init", "--skip-seed"],
|
||||
BACKEND
|
||||
);
|
||||
|
||||
console.log("\n🌱 Seeding database...");
|
||||
await run(["bun", "run", "prisma/seed.ts"], BACKEND);
|
||||
|
||||
console.log("\n✅ Stack ready! Starting dev servers...\n");
|
||||
console.log(" Backend → http://localhost:3000");
|
||||
console.log(" Frontend → http://localhost:5173\n");
|
||||
|
||||
const backend = spawn(["bun", "run", "dev"], {
|
||||
cwd: BACKEND,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
const frontend = spawn(["bun", "run", "dev"], {
|
||||
cwd: FRONTEND,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
|
||||
const cleanup = () => {
|
||||
backend.kill();
|
||||
frontend.kill();
|
||||
process.exit(0);
|
||||
};
|
||||
process.on("SIGINT", cleanup);
|
||||
process.on("SIGTERM", cleanup);
|
||||
|
||||
await Promise.all([backend.exited, frontend.exited]);
|
||||
}
|
||||
|
||||
main().catch((err: Error) => {
|
||||
console.error("\n❌ ", err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
#!/usr/bin/env bun
|
||||
import { spawn } from "bun";
|
||||
import { resolve } from "path";
|
||||
|
||||
const ROOT = resolve(import.meta.dir, "..");
|
||||
const BACKEND = resolve(ROOT, "backend");
|
||||
const FRONTEND = resolve(ROOT, "frontend");
|
||||
|
||||
function run(cmd: string[], cwd = ROOT): Promise<void> {
|
||||
console.log(` → ${cmd.join(" ")}`);
|
||||
const proc = spawn(cmd, { cwd, stdout: "inherit", stderr: "inherit" });
|
||||
return proc.exited.then((code) => {
|
||||
if (code !== 0) throw new Error(`Command failed (exit ${code}): ${cmd.join(" ")}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function waitForService(
|
||||
label: string,
|
||||
check: string[],
|
||||
retries = 30,
|
||||
delayMs = 2000
|
||||
): Promise<void> {
|
||||
for (let i = 1; i <= retries; i++) {
|
||||
const proc = spawn(check, { cwd: ROOT, stdout: "pipe", stderr: "pipe" });
|
||||
if ((await proc.exited) === 0) {
|
||||
console.log(`✅ ${label} is ready`);
|
||||
return;
|
||||
}
|
||||
console.log(`⏳ Waiting for ${label}... (${i}/${retries})`);
|
||||
await Bun.sleep(delayMs);
|
||||
}
|
||||
throw new Error(`${label} did not become ready in time`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("\n🐳 Starting Docker containers...");
|
||||
await run(["docker", "compose", "up", "-d"]);
|
||||
|
||||
console.log("\n⏳ Waiting for services...");
|
||||
await waitForService("PostgreSQL", [
|
||||
"docker", "compose", "exec", "postgres", "pg_isready", "-U", "xip",
|
||||
]);
|
||||
await waitForService("Redis", [
|
||||
"docker", "compose", "exec", "redis", "redis-cli", "ping",
|
||||
]);
|
||||
|
||||
console.log("\n🔧 Generating Prisma client...");
|
||||
await run(["bunx", "prisma", "generate"], BACKEND);
|
||||
|
||||
console.log("\n🗄️ Applying database migrations...");
|
||||
await run(
|
||||
["bunx", "prisma", "migrate", "dev", "--name", "init", "--skip-seed"],
|
||||
BACKEND
|
||||
);
|
||||
|
||||
console.log("\n🌱 Seeding database...");
|
||||
await run(["bun", "run", "prisma/seed.ts"], BACKEND);
|
||||
|
||||
console.log("\n✅ Stack ready! Starting dev servers...\n");
|
||||
console.log(" Backend → http://localhost:3000");
|
||||
console.log(" Frontend → http://localhost:5173\n");
|
||||
|
||||
const backend = spawn(["bun", "run", "dev"], {
|
||||
cwd: BACKEND,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
const frontend = spawn(["bun", "run", "dev"], {
|
||||
cwd: FRONTEND,
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
|
||||
const cleanup = () => {
|
||||
backend.kill();
|
||||
frontend.kill();
|
||||
process.exit(0);
|
||||
};
|
||||
process.on("SIGINT", cleanup);
|
||||
process.on("SIGTERM", cleanup);
|
||||
|
||||
await Promise.all([backend.exited, frontend.exited]);
|
||||
}
|
||||
|
||||
main().catch((err: Error) => {
|
||||
console.error("\n❌ ", err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user