Saltar a contenido

Esquema de base de datos

Esquema actual post-migraciones de organizaciones (0001-0014). Todas las tablas usan PostgreSQL.

Diagrama de relaciones

erDiagram
    user_roles {
        int id PK
        varchar name
    }
    users {
        int id PK
        varchar username
        varchar hashed_password
        bool is_approved
        int role_id FK
        timestamptz created_at
        bool is_disabled
        timestamptz disabled_at
        int disabled_by_id FK
        varchar disabled_reason
        varchar recovery_code
        timestamptz recovery_code_created_at
        bool recovery_code_used
    }
    organizations {
        uuid id PK
        varchar name
        varchar slug
        timestamptz created_at
        int created_by_id FK
    }
    user_organizations {
        int user_id PK_FK
        uuid org_id PK_FK
        varchar role
    }
    guests {
        int mazmo_user_id PK
        varchar username
        varchar displayname
    }
    meetups {
        uuid id PK
        uuid org_id FK
        varchar mazmo_meetup_url
        varchar name
        timestamptz date
        bool is_finalized
        timestamptz finalized_at
    }
    meetup_rsvps {
        uuid meetup_id PK_FK
        int guest_id PK_FK
        timestamptz rsvp_time
        bool cancelled_rsvp
        bool has_arrived
        timestamptz arrival_time
        int arrival_order
        bool is_walkin
        int checked_in_by_id FK
    }
    organization_bans {
        int id PK
        uuid org_id FK
        int guest_id FK
        int banned_by_id FK
        timestamptz banned_at
        varchar reason
    }
    event_log {
        int id PK
        varchar event_type
        timestamptz timestamp
        uuid org_id FK
        int actor_id FK
        int guest_id FK
        uuid meetup_id FK
        varchar reason
    }

    user_roles ||--o{ users : "rol de"
    users ||--o{ user_organizations : "es miembro en"
    organizations ||--o{ user_organizations : "tiene miembros"
    organizations ||--o{ meetups : "tiene"
    organizations ||--o{ organization_bans : "tiene bans"
    guests ||--o{ meetup_rsvps : "tiene RSVPs"
    meetups ||--o{ meetup_rsvps : "tiene guests"
    guests ||--o{ organization_bans : "puede estar baneado en"
    users ||--o{ event_log : "realiza acciones"
    guests ||--o{ event_log : "es sujeto de"
    meetups ||--o{ event_log : "tiene eventos"
    organizations ||--o{ event_log : "scopea eventos"

Tablas

user_roles

Tabla de lookup para roles globales. Sembrada por migraciones; no editable por usuarios.

Columna Tipo Constraints Notas
id integer PK Auto-increment
name varchar(32) UNIQUE, NOT NULL USER o SITE_ADMIN

users

Cuentas de staff y admin. Las cuentas nunca se borran fisicamente; ver is_disabled.

Columna Tipo Constraints Notas
id integer PK Auto-increment
username varchar(64) UNIQUE, NOT NULL, indexed Case-sensitive
hashed_password varchar NOT NULL Hash Argon2
is_approved boolean NOT NULL, default false Debe ser true para poder entrar
role_id integer FK -> user_roles.id, NOT NULL Rol global
created_at timestamptz NOT NULL
is_disabled boolean NOT NULL, default false Soft-delete
disabled_at timestamptz nullable
disabled_by_id integer FK -> users.id, nullable Quien la deshabilito
disabled_reason varchar(500) nullable
recovery_code varchar(6) nullable String de 6 digitos; NULL cuando no hay codigo activo
recovery_code_created_at timestamptz nullable Para calcular el TTL de 72 horas
recovery_code_used boolean NOT NULL, default false Marcado como usado al completar un reset exitoso

organizations

Contenedores de nivel superior. Cada org posee sus propios meetups, bans y eventos del audit log.

Columna Tipo Constraints Notas
id uuid PK Generado al crear
name varchar(128) UNIQUE, NOT NULL Nombre de display
slug varchar(64) UNIQUE, NOT NULL Identificador para URLs
created_at timestamptz NOT NULL
created_by_id integer FK -> users.id, nullable

user_organizations

Membresia de un usuario en una organizacion con un rol especifico. PK compuesta en (user_id, org_id).

Columna Tipo Constraints Notas
user_id integer PK + FK -> users.id
org_id uuid PK + FK -> organizations.id
role varchar(32) NOT NULL OrgRole: STAFF o ADMIN

Un usuario puede ser miembro de multiples orgs con roles distintos en cada una.


guests

Identidades obtenidas desde Mazmo. Los guests son globales -- no pertenecen a ninguna org especifica. La PK es el propio ID de Mazmo, lo que garantiza upserts idempotentes.

Columna Tipo Constraints Notas
mazmo_user_id integer PK Asignado por Mazmo; nunca cambia
username varchar NOT NULL, indexed Handle de Mazmo
displayname varchar NOT NULL Cambia frecuentemente

Los bans se almacenan en organization_bans, no en esta tabla. No existe campo is_banned en guests.


meetups

Eventos trackeados por el sistema. Cada meetup pertenece a una org y esta vinculado a una URL de Mazmo.

Columna Tipo Constraints Notas
id uuid PK Generado al crear
org_id uuid FK -> organizations.id, NOT NULL, indexed
mazmo_meetup_url varchar UNIQUE, NOT NULL URL completa del evento en Mazmo
name varchar NOT NULL, indexed Obtenido de Mazmo al crear
date timestamptz NOT NULL Obtenido de Mazmo al crear
is_finalized boolean NOT NULL, default false Bloquea check-ins y syncs cuando es true
finalized_at timestamptz nullable Seteado al finalizar

meetup_rsvps

Asociacion entre un guest y un meetup, con datos de RSVP y check-in. PK compuesta en (meetup_id, guest_id).

Columna Tipo Constraints Notas
meetup_id uuid PK + FK -> meetups.id
guest_id integer PK + FK -> guests.mazmo_user_id
rsvp_time timestamptz NOT NULL Cuando se registro el RSVP en Mazmo
cancelled_rsvp boolean NOT NULL, default false
has_arrived boolean NOT NULL, default false, indexed Solo modificado por el flujo de check-in; el sync nunca lo toca
arrival_time timestamptz nullable Solo seteado por check-in
arrival_order integer nullable Asignado por trigger de DB; no por codigo de aplicacion
is_walkin boolean NOT NULL, default false True si fue agregado como walk-in
checked_in_by_id integer FK -> users.id, nullable Que staff hizo el check-in

Campos intocables por el sync

has_arrived, arrival_time, arrival_order, y checked_in_by_id solo son modificados por el flujo de check-in. El sync nunca los toca, aunque actualice otros campos del RSVP.

arrival_order es asignado por un trigger de base de datos en el momento del check-in -- no es calculado por el codigo de aplicacion. El trigger asigna el siguiente entero disponible dentro del meetup.


organization_bans

Bans activos de guests dentro de una organizacion. Una fila = un ban activo. Al desbanear se elimina la fila; el historial queda en event_log.

Columna Tipo Constraints Notas
id integer PK Auto-increment
org_id uuid FK -> organizations.id, NOT NULL, indexed
guest_id integer FK -> guests.mazmo_user_id, NOT NULL, indexed
banned_by_id integer FK -> users.id, nullable
banned_at timestamptz NOT NULL
reason varchar(500) NOT NULL Obligatorio; no puede estar vacio

Constraint UNIQUE en (org_id, guest_id): solo puede existir un ban activo por guest por org.


event_log

Audit log. Una fila por accion auditable. Las filas nunca se modifican ni eliminan.

Columna Tipo Constraints Notas
id integer PK Auto-increment
event_type varchar(32) NOT NULL, indexed Ver valores de EventType abajo
timestamp timestamptz NOT NULL, indexed Default: now()
org_id uuid FK -> organizations.id, nullable, indexed NULL solo para GUEST_CREATED
actor_id integer FK -> users.id, nullable El staff que realizo la accion
guest_id integer FK -> guests.mazmo_user_id, nullable, indexed
meetup_id uuid FK -> meetups.id, nullable, indexed
reason varchar(500) nullable Presente para UNDO_CHECK_IN y BAN

Valores de EventType: CHECK_IN, UNDO_CHECK_IN, BAN, UNBAN, MEETUP_FINALIZED, MEETUP_UNFINALIZED, WALKIN, GUEST_CREATED

Cada entrada del audit log se escribe en la misma transaccion de base de datos que la accion que registra. Si la accion se revierte, la entrada del log se revierte con ella.


Historial de migraciones

Migracion Resumen
0001 Schema inicial: user_roles, users, guests, meetups, meetup_rsvps
0002 Refactor de schema multi-meetup
0003 Campos de disable en users; campos de tracking de check-in en meetup_rsvps
0004 Campos de ban global en guests (removidos en 0013)
0005 Tabla event_log
0006 Campos de finalizacion en meetups
0007 Flag is_walkin en meetup_rsvps
0008 Tabla organizations
0009 Tabla user_organizations
0010 FK meetups.org_id; seed org "Club Vanta"; backfill de membresias de usuarios
0011 Tabla organization_bans; org_id en event_log; backfill
0012 Renombrar roles globales (STAFF -> USER, ADMIN -> SITE_ADMIN); migrar usuarios
0013 Remover campos de ban global de guests; migrar bans existentes a organization_bans
0014 Campos de recovery_code en users