Skip to main content

Database Schema

PostgreSQL database schema for TMA Cloud.

Tables

users

User accounts.

ColumnTypeDescription
idVARCHAR(255)Primary key
emailVARCHAR(255)Unique, not null
passwordVARCHAR(255)Hashed (nullable for OAuth)
nameVARCHAR(255)Display name
google_idVARCHAR(255)Unique (optional)
mfa_enabledBOOLEANDefault false
mfa_secretTEXTTOTP secret (nullable)
token_versionINTEGERToken version for revocation
last_token_invalidationTIMESTAMPLast token invalidation time
storage_limitBIGINTStorage limit (nullable, bytes)
created_atTIMESTAMPTZDefault now()
updated_atTIMESTAMPTZDefault now()

files

Files and folders.

ColumnTypeDescription
idVARCHAR(255)Primary key
nameVARCHAR(255)Not null
typeVARCHAR(50)'file' or 'folder'
sizeBIGINTFile size in bytes
mime_typeVARCHAR(255)MIME type
user_idVARCHAR(255)FK → users.id
parent_idVARCHAR(255)FK → files.id (null for root)
pathTEXTFull path
starredBOOLEANDefault false
deleted_atTIMESTAMPTZSoft delete timestamp
modifiedTIMESTAMPTZLast modification time

Indexes: user_id, parent_id, path, deleted_at, full-text on name

Share link metadata.

ColumnTypeDescription
idVARCHAR(255)Primary key (used as token)
file_idVARCHAR(255)FK → files.id
user_idVARCHAR(255)FK → users.id
expires_atTIMESTAMPTZExpiration (null = no expiration)
created_atTIMESTAMPTZDefault now()

Indexes: Partial index on expires_at where expires_at IS NOT NULL

Junction table linking share links to files.

ColumnTypeDescription
share_link_idVARCHAR(255)FK → share_links.id
file_idVARCHAR(255)FK → files.id
created_atTIMESTAMPTZDefault now()

Primary Key: (share_link_id, file_id)

app_settings

Application-wide settings.

ColumnTypeDescription
idTEXTPrimary key (always 'app_settings')
signup_enabledBOOLEANDefault true
first_user_idTEXTFK → users.id (immutable)
share_base_urlTEXTCustom share link base URL (null = use request origin)
max_upload_size_bytesBIGINTMax single-file upload size in bytes (default 10737418240 = 10 GB)
hide_file_extensionsBOOLEANWhen true, file names are shown without extensions (default false)
require_electron_clientBOOLEANWhen true, only desktop app is allowed to use (default false)
updated_atTIMESTAMPTZDefault now()

sessions

Active user sessions.

ColumnTypeDescription
idTEXTPrimary key
user_idTEXTFK → users.id
token_versionINTEGERToken version when created
user_agentTEXTBrowser user agent
ip_addressINETClient IP
created_atTIMESTAMPTZDefault now()
last_activityTIMESTAMPTZDefault now() (updates on each request)

Indexes: (user_id, created_at DESC), (user_id, token_version), last_activity

audit_logs

Audit trail events.

ColumnTypeDescription
idSERIALPrimary key
event_typeVARCHAR(100)Event type
user_idVARCHAR(16)FK → users.id (nullable)
statusVARCHAR(20)'success' or 'failure'
resource_typeVARCHAR(50)Resource type
resource_idVARCHAR(255)Resource ID
ip_addressINETClient IP
user_agentTEXTBrowser user agent
metadataJSONBEvent-specific data
created_atTIMESTAMPDefault CURRENT_TIMESTAMP

Indexes: user_id, event_type, created_at, (resource_type, resource_id)

pgboss.*

pg-boss job queue tables (managed automatically).

migrations

Migration tracking.

ColumnTypeDescription
versionVARCHAR(255)Primary key
applied_atTIMESTAMPTZDefault now()

Relationships

  • User → Files (one-to-many, CASCADE)
  • File → Files (parent-child, self-referential, CASCADE)
  • User → Share Links (one-to-many, CASCADE)
  • Share Link → Files (many-to-many via share_link_files)
  • User → Sessions (one-to-many, CASCADE)
  • User → Audit Logs (one-to-many, SET NULL)

Common Queries

List user files:

SELECT * FROM files
WHERE user_id = $1 AND parent_id = $2 AND deleted_at IS NULL
ORDER BY type, name;

Search files:

SELECT * FROM files
WHERE user_id = $1 AND deleted_at IS NULL
AND (
lower(name) LIKE lower($2) || '%'
OR (lower(name) LIKE '%' || lower($2) || '%'
AND similarity(lower(name), lower($2)) > 0.15)
)
ORDER BY
CASE
WHEN lower(name) = lower($2) THEN 1
WHEN lower(name) LIKE lower($2) || '%' THEN 2
ELSE 3
END ASC,
similarity(lower(name), lower($2)) DESC NULLS LAST,
modified DESC;

Query audit logs:

SELECT event_type, status, metadata, created_at
FROM audit_logs
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT 100;