Servidor Fastify + Socket.IO + Drizzle/Postgres; front Vite+React (EN/ES, tema claro/oscuro). Tableros por columnas con plantillas, notas privadas/públicas, agrupación, votos configurables (multivoto), timer compartido con alarma, fases, action items, export Markdown/JSON e import, autodestrucción, gestión de columnas. Imagen única (front servido por el servidor) y CI que la construye y publica.
62 lines
2.8 KiB
TypeScript
62 lines
2.8 KiB
TypeScript
import postgres from "postgres";
|
|
|
|
// Crea la base de datos e2e y su esquema desde cero antes de arrancar los servidores.
|
|
const DDL = `
|
|
DROP TABLE IF EXISTS action_items, votes, notes, groups, columns, boards CASCADE;
|
|
CREATE TABLE boards (
|
|
id text PRIMARY KEY, title text NOT NULL, phase text NOT NULL DEFAULT 'writing',
|
|
revealed boolean NOT NULL DEFAULT false, votes_revealed boolean NOT NULL DEFAULT false,
|
|
votes_per_user integer NOT NULL DEFAULT 3, allow_multi_vote boolean NOT NULL DEFAULT true,
|
|
moderation_mode text NOT NULL DEFAULT 'everyone', show_card_creators boolean NOT NULL DEFAULT true,
|
|
locked boolean NOT NULL DEFAULT false,
|
|
timer_ends_at timestamptz, timer_running boolean NOT NULL DEFAULT false,
|
|
moderator_token text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz
|
|
);
|
|
CREATE TABLE columns (
|
|
id text PRIMARY KEY, board_id text NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
|
|
title text NOT NULL, position integer NOT NULL
|
|
);
|
|
CREATE TABLE groups (
|
|
id text PRIMARY KEY, board_id text NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
|
|
column_id text NOT NULL REFERENCES columns(id) ON DELETE CASCADE,
|
|
title text NOT NULL DEFAULT '', position double precision NOT NULL DEFAULT 0
|
|
);
|
|
CREATE TABLE notes (
|
|
id text PRIMARY KEY, board_id text NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
|
|
column_id text NOT NULL REFERENCES columns(id) ON DELETE CASCADE,
|
|
group_id text REFERENCES groups(id) ON DELETE SET NULL,
|
|
author_id text NOT NULL, author_name text NOT NULL, text text NOT NULL, color text,
|
|
revealed boolean NOT NULL DEFAULT false, position double precision NOT NULL DEFAULT 0,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE votes (
|
|
board_id text NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
|
|
target_type text NOT NULL, target_id text NOT NULL, participant_id text NOT NULL,
|
|
count integer NOT NULL DEFAULT 1
|
|
);
|
|
CREATE UNIQUE INDEX votes_target_participant_uniq ON votes (target_id, participant_id);
|
|
CREATE TABLE action_items (
|
|
id text PRIMARY KEY, board_id text NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
|
|
text text NOT NULL, owner text, done boolean NOT NULL DEFAULT false,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
`;
|
|
|
|
export default async function globalSetup() {
|
|
const url = process.env.E2E_DATABASE_URL ?? "postgres://retro:retro@localhost:5433/retro_e2e";
|
|
const dbName = new URL(url).pathname.slice(1);
|
|
const admin = postgres(url.replace(`/${dbName}`, "/postgres"), { max: 1 });
|
|
try {
|
|
const exists = await admin`SELECT 1 FROM pg_database WHERE datname = ${dbName}`;
|
|
if (exists.length === 0) await admin.unsafe(`CREATE DATABASE ${dbName}`);
|
|
} finally {
|
|
await admin.end();
|
|
}
|
|
const sql = postgres(url, { max: 1 });
|
|
try {
|
|
await sql.unsafe(DDL);
|
|
} finally {
|
|
await sql.end();
|
|
}
|
|
}
|