notebook_attachments — Tabla D1 (imágenes embebidas en notas)
Tabla creada en la migration 0032_notebook_sharing.sql (PR#56, 2026-05-20). Almacena los metadatos de las imágenes subidas y embebidas en notas del Cuaderno. El blob real reside en el bucket R2 TASK_ATTACHMENTS.
Schema
CREATE TABLE IF NOT EXISTS notebook_attachments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
note_id INTEGER NOT NULL,
r2_key TEXT NOT NULL,
filename TEXT NOT NULL,
content_type TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
uploaded_by TEXT NOT NULL,
uploaded_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (note_id) REFERENCES notebook_notes(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_notebook_attachments_note ON notebook_attachments(note_id);
Campos
| Campo | Tipo | Descripción |
|---|---|---|
id | INTEGER PK | Identificador del attachment; usado en la URL /api/notebook-images/:id |
note_id | INTEGER FK | Referencia a notebook_notes.id. ON DELETE CASCADE borra la row cuando se elimina la nota |
r2_key | TEXT | Clave en R2 con formato notebook/{noteId}/{uuid}.{ext} |
filename | TEXT | Nombre del fichero original (sanitizado, máx 180 chars) |
content_type | TEXT | MIME type validado: image/png, image/jpeg, image/webp, image/gif |
size_bytes | INTEGER | Tamaño en bytes (máx 5MB = 5.242.880) |
uploaded_by | TEXT | Email del miembro del staff que subió la imagen |
uploaded_at | TEXT | ISO 8601 UTC (valor por defecto: datetime('now')) |
R2 y ciclo de vida del blob
El bucket R2 TASK_ATTACHMENTS es compartido con los adjuntos de tareas. El prefijo notebook/ segrega las imágenes de notas:
notebook/{noteId}/{uuid}.{ext}
Borrado en cascada (R2): La FK ON DELETE CASCADE borra la row de notebook_attachments pero no toca R2. El handler DELETE /api/notebook/:id recupera todas las r2_key y las elimina de R2 explícitamente antes del DELETE SQL:
const attachments = await env.DB.prepare(
'SELECT r2_key FROM notebook_attachments WHERE note_id = ?'
).bind(id).all();
for (const att of attachments.results || []) {
await env.TASK_ATTACHMENTS.delete(att.r2_key);
}
⚠ Si en el futuro se implementa un borrado masivo de notas (ej.
DELETE FROM notebook_notes WHERE owner_id = ?directo), hay que replicar este cleanup de R2 o se generarán objetos huérfanos en el bucket.
Acceso / Control de permisos
Las imágenes heredan los permisos de la nota a la que pertenecen:
GET /api/notebook-images/:id→ compruebanotebook_notes.owner_idyshared_withantes de hacer stream del blob.POST /api/notebook/:id/images→ comprueba acceso a la nota (owner OR collaborator autorizado).
Solo el owner puede borrar la nota (y por tanto las imágenes). Los collaborators pueden subir imágenes a notas compartidas pero no borrarlas.
Véase también
- [[feature—notebook—multiusuario-sharing]]
- [[entity—notebook—endpoint—notebook-images]]