433 lines
11 KiB
Plaintext
433 lines
11 KiB
Plaintext
@startuml db-orm
|
||
' Schema source: Flyway V1–V60 (excl. V37, V43 — intentionally removed)
|
||
' Schema as of: V60 (2026-05-06)
|
||
' ⚠ This is a versioned snapshot. Update when the schema changes significantly.
|
||
|
||
hide circle
|
||
skinparam linetype ortho
|
||
|
||
' ── Auth ──
|
||
package "Auth" {
|
||
|
||
entity app_users {
|
||
id : UUID <<PK>>
|
||
--
|
||
email : VARCHAR(255) NOT NULL UNIQUE
|
||
password : VARCHAR(255) NOT NULL
|
||
first_name : VARCHAR(100)
|
||
last_name : VARCHAR(100)
|
||
birth_date : DATE
|
||
contact : TEXT
|
||
enabled : BOOLEAN NOT NULL
|
||
color : VARCHAR(20) NOT NULL
|
||
notify_on_reply : BOOLEAN NOT NULL
|
||
notify_on_mention : BOOLEAN NOT NULL
|
||
created_at : TIMESTAMP
|
||
}
|
||
|
||
entity user_groups {
|
||
id : UUID <<PK>>
|
||
--
|
||
name : VARCHAR(255) NOT NULL UNIQUE
|
||
}
|
||
|
||
entity app_users_groups {
|
||
app_user_id : UUID <<FK>>
|
||
group_id : UUID <<FK>>
|
||
}
|
||
|
||
entity group_permissions {
|
||
group_id : UUID <<FK>>
|
||
--
|
||
permission : VARCHAR(255)
|
||
}
|
||
|
||
entity password_reset_tokens {
|
||
id : UUID <<PK>>
|
||
--
|
||
app_user_id : UUID <<FK>>
|
||
token : VARCHAR(64) NOT NULL UNIQUE
|
||
expires_at : TIMESTAMP NOT NULL
|
||
used : BOOLEAN NOT NULL
|
||
created_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity invite_tokens {
|
||
id : UUID <<PK>>
|
||
--
|
||
code : VARCHAR(10) NOT NULL UNIQUE
|
||
label : VARCHAR(255)
|
||
max_uses : INTEGER
|
||
use_count : INTEGER NOT NULL
|
||
prefill_first_name : VARCHAR(255)
|
||
prefill_last_name : VARCHAR(255)
|
||
prefill_email : VARCHAR(255)
|
||
expires_at : TIMESTAMP
|
||
created_by : UUID <<FK>>
|
||
created_at : TIMESTAMP NOT NULL
|
||
revoked : BOOLEAN NOT NULL
|
||
}
|
||
|
||
entity invite_token_group_ids {
|
||
invite_token_id : UUID <<FK>>
|
||
group_id : UUID <<FK>>
|
||
}
|
||
}
|
||
|
||
' ── Documents ──
|
||
package "Documents" {
|
||
|
||
entity documents {
|
||
id : UUID <<PK>>
|
||
--
|
||
title : VARCHAR(255) NOT NULL
|
||
original_filename : VARCHAR(255) NOT NULL
|
||
status : VARCHAR(255) NOT NULL
|
||
file_path : VARCHAR(255)
|
||
file_hash : VARCHAR(64)
|
||
summary : TEXT
|
||
transcription : TEXT
|
||
meta_date : DATE
|
||
meta_location : VARCHAR(255)
|
||
meta_document_location : VARCHAR(255)
|
||
archive_box : VARCHAR(255)
|
||
archive_folder : VARCHAR(255)
|
||
sender_id : UUID <<FK>>
|
||
metadata_complete : BOOLEAN NOT NULL
|
||
script_type : VARCHAR(30) NOT NULL
|
||
thumbnail_key : VARCHAR(255)
|
||
thumbnail_generated_at : TIMESTAMP
|
||
thumbnail_aspect : VARCHAR(16)
|
||
page_count : INTEGER
|
||
search_vector : tsvector <<computed>>
|
||
created_at : TIMESTAMP
|
||
updated_at : TIMESTAMP
|
||
}
|
||
|
||
entity document_receivers {
|
||
document_id : UUID <<FK>>
|
||
person_id : UUID <<FK>>
|
||
}
|
||
|
||
entity document_tags {
|
||
document_id : UUID <<FK>>
|
||
tag_id : UUID <<FK>>
|
||
}
|
||
|
||
entity document_versions {
|
||
id : UUID <<PK>>
|
||
--
|
||
document_id : UUID <<FK>>
|
||
editor_id : UUID <<FK>>
|
||
editor_name : VARCHAR(200) NOT NULL
|
||
saved_at : TIMESTAMP NOT NULL
|
||
snapshot : JSONB NOT NULL
|
||
changed_fields : JSONB NOT NULL
|
||
}
|
||
|
||
entity document_annotations {
|
||
id : UUID <<PK>>
|
||
--
|
||
document_id : UUID <<FK>>
|
||
page_number : INTEGER NOT NULL
|
||
x : DOUBLE PRECISION NOT NULL
|
||
y : DOUBLE PRECISION NOT NULL
|
||
width : DOUBLE PRECISION NOT NULL
|
||
height : DOUBLE PRECISION NOT NULL
|
||
color : VARCHAR(20) NOT NULL
|
||
polygon : JSONB
|
||
file_hash : VARCHAR(64)
|
||
created_by : UUID <<FK>>
|
||
created_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity document_comments {
|
||
id : UUID <<PK>>
|
||
--
|
||
document_id : UUID <<FK>>
|
||
annotation_id : UUID <<FK>>
|
||
block_id : UUID <<FK>>
|
||
parent_id : UUID <<FK>>
|
||
author_id : UUID <<FK>>
|
||
author_name : VARCHAR(200) NOT NULL
|
||
content : TEXT NOT NULL
|
||
created_at : TIMESTAMP NOT NULL
|
||
updated_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity document_training_labels {
|
||
document_id : UUID <<FK>>
|
||
--
|
||
label : VARCHAR(50) NOT NULL
|
||
}
|
||
|
||
entity comment_mentions {
|
||
comment_id : UUID <<FK>>
|
||
app_user_id : UUID <<FK>>
|
||
}
|
||
}
|
||
|
||
' ── Persons ──
|
||
package "Persons" {
|
||
|
||
entity persons {
|
||
id : UUID <<PK>>
|
||
--
|
||
first_name : VARCHAR(255)
|
||
last_name : VARCHAR(255) NOT NULL
|
||
alias : VARCHAR(255)
|
||
title : VARCHAR(50)
|
||
person_type : VARCHAR(20) NOT NULL
|
||
notes : TEXT
|
||
birth_year : INTEGER
|
||
death_year : INTEGER
|
||
family_member : BOOLEAN NOT NULL
|
||
}
|
||
|
||
entity person_name_aliases {
|
||
id : UUID <<PK>>
|
||
--
|
||
person_id : UUID <<FK>>
|
||
last_name : VARCHAR(255) NOT NULL
|
||
first_name : VARCHAR(255)
|
||
type : VARCHAR(50) NOT NULL
|
||
sort_order : INTEGER NOT NULL
|
||
created_at : TIMESTAMPTZ
|
||
}
|
||
|
||
entity person_relationships {
|
||
id : UUID <<PK>>
|
||
--
|
||
person_id : UUID <<FK>>
|
||
related_person_id : UUID <<FK>>
|
||
relation_type : VARCHAR(30) NOT NULL
|
||
from_year : INTEGER
|
||
to_year : INTEGER
|
||
notes : VARCHAR(2000)
|
||
created_at : TIMESTAMPTZ NOT NULL
|
||
}
|
||
}
|
||
|
||
' ── Tags ──
|
||
package "Tags" {
|
||
|
||
entity tag {
|
||
id : UUID <<PK>>
|
||
--
|
||
name : VARCHAR(255) NOT NULL UNIQUE
|
||
parent_id : UUID <<FK>>
|
||
color : VARCHAR(20)
|
||
}
|
||
}
|
||
|
||
' ── Transcription ──
|
||
package "Transcription" {
|
||
|
||
entity transcription_blocks {
|
||
id : UUID <<PK>>
|
||
--
|
||
annotation_id : UUID <<FK>>
|
||
document_id : UUID <<FK>>
|
||
text : TEXT
|
||
label : VARCHAR(200)
|
||
sort_order : INTEGER NOT NULL
|
||
version : INTEGER NOT NULL
|
||
source : VARCHAR(10) NOT NULL
|
||
reviewed : BOOLEAN NOT NULL
|
||
created_by : UUID <<FK>>
|
||
updated_by : UUID <<FK>>
|
||
created_at : TIMESTAMP NOT NULL
|
||
updated_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity transcription_block_versions {
|
||
id : UUID <<PK>>
|
||
--
|
||
block_id : UUID <<FK>>
|
||
text : TEXT NOT NULL
|
||
changed_by : UUID <<FK>>
|
||
changed_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity transcription_block_mentioned_persons {
|
||
block_id : UUID <<FK>>
|
||
person_id : UUID NOT NULL
|
||
--
|
||
display_name : VARCHAR(200) NOT NULL
|
||
}
|
||
}
|
||
|
||
' ── OCR ──
|
||
package "OCR" {
|
||
|
||
entity ocr_jobs {
|
||
id : UUID <<PK>>
|
||
--
|
||
status : VARCHAR(20) NOT NULL
|
||
total_documents : INT NOT NULL
|
||
processed_documents : INT NOT NULL
|
||
error_count : INT NOT NULL
|
||
skipped_count : INT NOT NULL
|
||
created_by : UUID
|
||
progress_message : TEXT
|
||
created_at : TIMESTAMPTZ NOT NULL
|
||
updated_at : TIMESTAMPTZ NOT NULL
|
||
}
|
||
|
||
entity ocr_job_documents {
|
||
id : UUID <<PK>>
|
||
--
|
||
job_id : UUID <<FK>>
|
||
document_id : UUID <<FK>>
|
||
status : VARCHAR(20) NOT NULL
|
||
error_message : TEXT
|
||
current_page : INT
|
||
total_pages : INT
|
||
created_at : TIMESTAMPTZ NOT NULL
|
||
updated_at : TIMESTAMPTZ NOT NULL
|
||
}
|
||
|
||
entity ocr_training_runs {
|
||
id : UUID <<PK>>
|
||
--
|
||
status : VARCHAR(20) NOT NULL
|
||
block_count : INT NOT NULL
|
||
document_count : INT NOT NULL
|
||
model_name : VARCHAR(100) NOT NULL
|
||
error_message : TEXT
|
||
triggered_by : UUID <<FK>>
|
||
person_id : UUID <<FK>>
|
||
cer : DOUBLE PRECISION
|
||
loss : DOUBLE PRECISION
|
||
accuracy : DOUBLE PRECISION
|
||
epochs : INT
|
||
created_at : TIMESTAMPTZ NOT NULL
|
||
completed_at : TIMESTAMPTZ
|
||
}
|
||
|
||
entity sender_models {
|
||
id : UUID <<PK>>
|
||
--
|
||
person_id : UUID <<FK>> UNIQUE
|
||
model_path : TEXT NOT NULL
|
||
accuracy : DOUBLE PRECISION
|
||
cer : DOUBLE PRECISION
|
||
corrected_lines_at_training : INT NOT NULL
|
||
created_at : TIMESTAMPTZ NOT NULL
|
||
updated_at : TIMESTAMPTZ NOT NULL
|
||
}
|
||
}
|
||
|
||
' ── Supporting ──
|
||
package "Supporting" {
|
||
|
||
entity notifications {
|
||
id : UUID <<PK>>
|
||
--
|
||
recipient_id : UUID <<FK>>
|
||
type : VARCHAR(32) NOT NULL
|
||
document_id : UUID
|
||
reference_id : UUID
|
||
annotation_id : UUID
|
||
actor_name : VARCHAR(255)
|
||
read : BOOLEAN NOT NULL
|
||
created_at : TIMESTAMP NOT NULL
|
||
}
|
||
|
||
entity audit_log {
|
||
id : UUID <<PK>>
|
||
--
|
||
happened_at : TIMESTAMPTZ NOT NULL
|
||
actor_id : UUID <<FK>>
|
||
kind : VARCHAR(50) NOT NULL
|
||
document_id : UUID <<FK>>
|
||
payload : JSONB
|
||
}
|
||
|
||
entity geschichten {
|
||
id : UUID <<PK>>
|
||
--
|
||
title : VARCHAR(255) NOT NULL
|
||
body : TEXT
|
||
status : VARCHAR(32) NOT NULL
|
||
author_id : UUID <<FK>>
|
||
created_at : TIMESTAMP NOT NULL
|
||
updated_at : TIMESTAMP NOT NULL
|
||
published_at : TIMESTAMP
|
||
}
|
||
|
||
entity geschichten_persons {
|
||
geschichte_id : UUID <<FK>>
|
||
person_id : UUID <<FK>>
|
||
}
|
||
|
||
entity geschichten_documents {
|
||
geschichte_id : UUID <<FK>>
|
||
document_id : UUID <<FK>>
|
||
}
|
||
}
|
||
|
||
' Auth relationships
|
||
app_users_groups }o--|| app_users : app_user_id
|
||
app_users_groups }o--|| user_groups : group_id
|
||
group_permissions }o--|| user_groups : group_id
|
||
password_reset_tokens }o--|| app_users : app_user_id
|
||
invite_tokens }o--|| app_users : created_by
|
||
invite_token_group_ids }o--|| invite_tokens : invite_token_id
|
||
invite_token_group_ids }o--|| user_groups : group_id
|
||
|
||
' Document relationships
|
||
documents }o--o| persons : sender_id
|
||
document_receivers }o--|| documents : document_id
|
||
document_receivers }o--|| persons : person_id
|
||
document_tags }o--|| documents : document_id
|
||
document_tags }o--|| tag : tag_id
|
||
document_versions }o--|| documents : document_id
|
||
document_versions }o--o| app_users : editor_id
|
||
document_annotations }o--|| documents : document_id
|
||
document_annotations }o--o| app_users : created_by
|
||
document_comments }o--|| documents : document_id
|
||
document_comments }o--o| document_annotations : annotation_id
|
||
document_comments }o--o| transcription_blocks : block_id
|
||
document_comments }o--o| app_users : author_id
|
||
document_comments }o--o| document_comments : parent_id
|
||
document_training_labels }o--|| documents : document_id
|
||
comment_mentions }o--|| document_comments : comment_id
|
||
comment_mentions }o--|| app_users : app_user_id
|
||
|
||
' Person relationships
|
||
person_name_aliases }o--|| persons : person_id
|
||
person_relationships }o--|| persons : person_id
|
||
person_relationships }o--|| persons : related_person_id
|
||
|
||
' Tag self-reference
|
||
tag }o--o| tag : parent_id
|
||
|
||
' Transcription relationships
|
||
transcription_blocks }o--|| document_annotations : annotation_id
|
||
transcription_blocks }o--|| documents : document_id
|
||
transcription_blocks }o--o| app_users : created_by
|
||
transcription_blocks }o--o| app_users : updated_by
|
||
transcription_block_versions }o--|| transcription_blocks : block_id
|
||
transcription_block_versions }o--o| app_users : changed_by
|
||
transcription_block_mentioned_persons }o--|| transcription_blocks : block_id
|
||
|
||
' OCR relationships
|
||
ocr_job_documents }o--|| ocr_jobs : job_id
|
||
ocr_job_documents }o--|| documents : document_id
|
||
ocr_training_runs }o--o| app_users : triggered_by
|
||
ocr_training_runs }o--o| persons : person_id
|
||
sender_models ||--|| persons : person_id
|
||
|
||
' Supporting relationships
|
||
notifications }o--|| app_users : recipient_id
|
||
audit_log }o--o| app_users : actor_id
|
||
audit_log }o--o| documents : document_id
|
||
geschichten }o--o| app_users : author_id
|
||
geschichten_persons }o--|| geschichten : geschichte_id
|
||
geschichten_persons }o--|| persons : person_id
|
||
geschichten_documents }o--|| geschichten : geschichte_id
|
||
geschichten_documents }o--|| documents : document_id
|
||
|
||
@enduml
|