class CreateCoreTables < ActiveRecord::Migration[8.1] def change enable_extension "citext" enable_extension "pgcrypto" create_table :users do |t| t.citext :email, null: false, index: { unique: true } t.string :display_name t.string :color t.datetime :last_seen_at t.timestamps end create_table :sessions do |t| t.references :user, null: false, foreign_key: { on_delete: :cascade } t.string :user_agent t.string :ip_address t.datetime :last_active_at t.timestamps end create_table :documents, id: :uuid do |t| t.references :owner, null: false, foreign_key: { to_table: :users, on_delete: :cascade } t.string :title, null: false t.text :content, null: false # the DocumentState JSON, same as inside a .num file t.integer :version, null: false, default: 1 t.references :updated_by, foreign_key: { to_table: :users, on_delete: :nullify } t.datetime :content_updated_at, null: false # simple edit lock until live collaboration exists (concept, phase 3) t.string :lock_holder t.string :lock_holder_name t.datetime :lock_expires_at t.timestamps end create_table :share_links do |t| t.references :document, type: :uuid, null: false, foreign_key: { on_delete: :cascade } t.integer :role, null: false t.string :token, null: false, index: { unique: true } t.references :created_by, null: false, foreign_key: { to_table: :users, on_delete: :cascade } t.datetime :revoked_at t.timestamps end create_table :document_memberships do |t| t.references :document, type: :uuid, null: false, foreign_key: { on_delete: :cascade } t.references :user, null: false, foreign_key: { on_delete: :cascade } t.integer :role, null: false t.integer :source, null: false t.references :share_link, foreign_key: { on_delete: :cascade } t.datetime :last_opened_at t.timestamps t.index [ :document_id, :user_id ], unique: true end create_table :magic_links do |t| t.citext :email, null: false, index: true t.string :token_digest, null: false, index: { unique: true } t.string :code_digest, null: false t.integer :purpose, null: false, default: 0 t.references :document, type: :uuid, foreign_key: { on_delete: :cascade } t.integer :role t.references :invited_by, foreign_key: { to_table: :users, on_delete: :cascade } t.string :return_to t.integer :failed_attempts, null: false, default: 0 t.datetime :expires_at, null: false t.datetime :used_at t.timestamps end end end