Schema do Banco D1
O banco de dados do Neemias é um SQLite gerenciado pelo Cloudflare D1. As migrações são aplicadas sequencialmente na pasta migrations/. Abaixo, o schema completo após todas as 7 migrações.
Convenções
- UUIDs são armazenados como
TEXT PRIMARY KEY(gerados viacrypto.randomUUID()) - Timestamps usam formato ISO 8601 (
TEXT) - Booleanos são
INTEGER(0 = false, 1 = true) - JSON é armazenado como
TEXT(serializado) - Soft-delete usa coluna
status(ACTIVE|DELETED)
Tabelas
users
Usuários do sistema com autenticação PBKDF2.
| Coluna | Tipo | Restrições |
|---|---|---|
user_id | TEXT | PRIMARY KEY |
email | TEXT | UNIQUE NOT NULL |
display_name | TEXT | NOT NULL |
role | TEXT | NOT NULL |
status | TEXT | NOT NULL, DEFAULT 'ACTIVE' |
password_hash | TEXT | |
created_at | TEXT | NOT NULL |
updated_at | TEXT | NOT NULL |
students
Alunos cadastrados com dados completos de cadastro.
| Coluna | Tipo | Restrições |
|---|---|---|
student_id | TEXT | PRIMARY KEY |
display_name | TEXT | NOT NULL |
photo_ref | TEXT | |
status | TEXT | NOT NULL, DEFAULT 'ACTIVE' |
created_at | TEXT | NOT NULL |
updated_at | TEXT | NOT NULL |
deleted_justification | TEXT | |
guardian_name | TEXT | NOT NULL, DEFAULT '' |
guardian_name_alt | TEXT | |
birth_date | TEXT | Formato YYYY-MM-DD |
phones | TEXT | NOT NULL, DEFAULT '[]' (JSON array) |
address_street | TEXT | |
address_number | TEXT | |
address_complement | TEXT | |
address_neighborhood | TEXT | |
address_city | TEXT | |
address_state | TEXT | |
address_zip | TEXT | |
class_id | TEXT | REFERENCES classes(class_id) |
nucleus_participates | INTEGER | NOT NULL, DEFAULT 0 |
nucleus_region | TEXT | |
nucleus_name | TEXT | |
allergies | TEXT | |
special_needs | TEXT | |
family_membership_status | TEXT | NOT NULL, DEFAULT 'DESCONHECIDO' |
Índices:
idx_students_status_updated_at—(status, updated_at DESC)
Valores de family_membership_status: MEMBRO, NAO_MEMBRO, DESCONHECIDO
classes
Turmas por faixa etária, com seed de 7 turmas padrão.
| Coluna | Tipo | Restrições |
|---|---|---|
class_id | TEXT | PRIMARY KEY |
name | TEXT | NOT NULL UNIQUE |
age_min | INTEGER | |
age_max | INTEGER | |
status | TEXT | NOT NULL, DEFAULT 'ACTIVE' |
created_at | TEXT | NOT NULL |
updated_at | TEXT | NOT NULL |
Índices:
idx_classes_status—(status, name)
Seed: Berçário (1-2), Maternal Infantil (3), Jardim de Infância (4-5), Infantil (6), Juniores 1 (7), Juniores 2 (8), Pré-Adolescentes (9-11)
nuclei
Núcleos (células) organizados por região.
| Coluna | Tipo | Restrições |
|---|---|---|
nucleus_id | TEXT | PRIMARY KEY |
region | TEXT | NOT NULL |
name | TEXT | NOT NULL |
status | TEXT | NOT NULL, DEFAULT 'ACTIVE' |
created_at | TEXT | NOT NULL |
updated_at | TEXT | NOT NULL |
Índices:
idx_nuclei_region—(region, status)
Regiões fixas: Azul Celeste, Azul, Amarela, Branca, Vermelha, Laranja, Verde
attendance_events
Eventos imutáveis de presença (event sourcing). Cada chamada gera um evento.
| Coluna | Tipo | Restrições |
|---|---|---|
event_id | TEXT | PRIMARY KEY |
student_id | TEXT | NOT NULL, REFERENCES students(student_id) |
action_type | TEXT | NOT NULL (MARK_PRESENT ou MARK_ABSENT) |
happened_at | TEXT | NOT NULL |
actor_user_id | TEXT | NOT NULL |
actor_role | TEXT | NOT NULL |
server_timestamp | TEXT | NOT NULL |
is_conflict_loser | INTEGER | NOT NULL, DEFAULT 0 |
created_at | TEXT | NOT NULL |
Índices:
idx_attendance_events_student_id—(student_id, server_timestamp DESC)
student_events
Eventos imutáveis de mutação de alunos (CREATE, UPDATE, DELETE).
| Coluna | Tipo | Restrições |
|---|---|---|
event_id | TEXT | PRIMARY KEY |
student_id | TEXT | NOT NULL, REFERENCES students(student_id) |
event_type | TEXT | NOT NULL |
actor_user_id | TEXT | NOT NULL |
actor_role | TEXT | NOT NULL |
payload | TEXT | NOT NULL (JSON) |
server_timestamp | TEXT | NOT NULL |
is_conflict_loser | INTEGER | NOT NULL, DEFAULT 0 |
created_at | TEXT | NOT NULL |
Índices:
idx_student_events_student_id—(student_id, server_timestamp DESC)
user_events
Eventos imutáveis de mutação de usuários.
| Coluna | Tipo | Restrições |
|---|---|---|
event_id | TEXT | PRIMARY KEY |
target_user_id | TEXT | NOT NULL, REFERENCES users(user_id) |
event_type | TEXT | NOT NULL |
actor_user_id | TEXT | NOT NULL |
actor_role | TEXT | NOT NULL |
payload | TEXT | NOT NULL (JSON) |
server_timestamp | TEXT | NOT NULL |
created_at | TEXT | NOT NULL |
auth_refresh_sessions
Sessões de refresh token com rotação e detecção de comprometimento.
| Coluna | Tipo | Restrições |
|---|---|---|
session_id | TEXT | PRIMARY KEY |
user_id | TEXT | NOT NULL, REFERENCES users(user_id) ON DELETE CASCADE |
refresh_token_hash | TEXT | NOT NULL UNIQUE |
csrf_token | TEXT | NOT NULL |
expires_at | TEXT | NOT NULL |
revoked_at | TEXT | |
replaced_by | TEXT | REFERENCES auth_refresh_sessions(session_id) |
rotated_at | TEXT | |
suspected_compromise_at | TEXT | |
created_at | TEXT | NOT NULL |
Índices:
idx_auth_refresh_sessions_user_id—(user_id, created_at DESC)idx_auth_refresh_sessions_expires_at—(expires_at)
idempotency_ledger
Registro de idempotência para detectar replay de mutações.
| Coluna | Tipo | Restrições |
|---|---|---|
key | TEXT | PRIMARY KEY (composta com user_id) |
user_id | TEXT | PRIMARY KEY (composta com key) |
payload_hash | TEXT | NOT NULL |
status_code | INTEGER | NOT NULL |
response_body | TEXT | NOT NULL (JSON) |
created_at | TEXT | NOT NULL |
Índices:
idx_idempotency_ledger_created_at—(created_at DESC)
audit_logs
Trilha de auditoria para conformidade LGPD.
| Coluna | Tipo | Restrições |
|---|---|---|
audit_id | TEXT | PRIMARY KEY |
correlation_id | TEXT | NOT NULL |
actor_user_id | TEXT | NOT NULL |
actor_role | TEXT | NOT NULL |
endpoint | TEXT | NOT NULL |
outcome | TEXT | NOT NULL |
details | TEXT | NOT NULL (JSON) |
created_at | TEXT | NOT NULL |
roles
Catálogo de roles com permissões granulares.
| Coluna | Tipo | Restrições |
|---|---|---|
name | TEXT | PRIMARY KEY |
display_name | TEXT | NOT NULL |
permissions | TEXT | NOT NULL, DEFAULT '[]' (JSON array) |
is_system | INTEGER | NOT NULL, DEFAULT 0 |
created_at | TEXT | NOT NULL |
updated_at | TEXT | NOT NULL |
Seed (4 roles de sistema, is_system=1):
| Role | Permissões |
|---|---|
ADMIN | attendance, reports, students.*, users.*, settings, import-export, sessions.*, nuclei.manage, classes.manage |
CHAMADOR | attendance, students.search |
RELATORIOS | reports, students.search |
CADASTRO | students.add, students.search, sessions.add, sessions.edit, nuclei.manage, classes.manage |
rate_limits
Controle de rate limiting baseado em D1.
| Coluna | Tipo | Restrições |
|---|---|---|
key | TEXT | PRIMARY KEY |
count | INTEGER | NOT NULL, DEFAULT 1 |
window_start | INTEGER | NOT NULL |
expires_at | INTEGER | NOT NULL |
Histórico de Migrações
| Migração | Arquivo | Descrição |
|---|---|---|
| 0001 | 0001_init.sql | Schema core: users, students, attendance_events, student_events, user_events, idempotency_ledger, audit_logs + índices |
| 0002 | 0002_auth_sessions.sql | Tabela auth_refresh_sessions com rotação e revogação |
| 0003 | 0003_student_fields.sql | Expansão de students (guardião, telefones, endereço) + novas tabelas classes e nuclei + seed de turmas |
| 0004 | 0004_family_membership.sql | Coluna family_membership_status em students |
| 0005 | 0005_compromise_detection.sql | Coluna suspected_compromise_at em auth_refresh_sessions |
| 0006 | 0006_roles.sql | Tabela roles + seed dos 4 papéis do sistema |
| 0007 | 0007_rate_limits.sql | Tabela rate_limits para controle de taxa |
Fonte: migrations/0001_init.sql até 0007_rate_limits.sql