CreaRack-SL

notebook_attachments — Tabla D1 (imágenes embebidas en notas)

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

CampoTipoDescripción
idINTEGER PKIdentificador del attachment; usado en la URL /api/notebook-images/:id
note_idINTEGER FKReferencia a notebook_notes.id. ON DELETE CASCADE borra la row cuando se elimina la nota
r2_keyTEXTClave en R2 con formato notebook/{noteId}/{uuid}.{ext}
filenameTEXTNombre del fichero original (sanitizado, máx 180 chars)
content_typeTEXTMIME type validado: image/png, image/jpeg, image/webp, image/gif
size_bytesINTEGERTamaño en bytes (máx 5MB = 5.242.880)
uploaded_byTEXTEmail del miembro del staff que subió la imagen
uploaded_atTEXTISO 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 → comprueba notebook_notes.owner_id y shared_with antes 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]]