package user import ( "database/sql" "fmt" ) // Initial PostgreSQL schema const ( postgresCreateTablesQueries = ` CREATE TABLE IF NOT EXISTS tier ( id TEXT PRIMARY KEY, code TEXT NOT NULL, name TEXT NOT NULL, messages_limit BIGINT NOT NULL, messages_expiry_duration BIGINT NOT NULL, emails_limit BIGINT NOT NULL, calls_limit BIGINT NOT NULL, reservations_limit BIGINT NOT NULL, attachment_file_size_limit BIGINT NOT NULL, attachment_total_size_limit BIGINT NOT NULL, attachment_expiry_duration BIGINT NOT NULL, attachment_bandwidth_limit BIGINT NOT NULL, stripe_monthly_price_id TEXT, stripe_yearly_price_id TEXT, UNIQUE(code), UNIQUE(stripe_monthly_price_id), UNIQUE(stripe_yearly_price_id) ); CREATE TABLE IF NOT EXISTS "user" ( id TEXT PRIMARY KEY, tier_id TEXT REFERENCES tier(id), user_name TEXT NOT NULL UNIQUE, pass TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN ('anonymous', 'admin', 'user')), prefs JSONB NOT NULL DEFAULT '{}', sync_topic TEXT NOT NULL, provisioned BOOLEAN NOT NULL, stats_messages BIGINT NOT NULL DEFAULT 0, stats_emails BIGINT NOT NULL DEFAULT 0, stats_calls BIGINT NOT NULL DEFAULT 0, stripe_customer_id TEXT UNIQUE, stripe_subscription_id TEXT UNIQUE, stripe_subscription_status TEXT, stripe_subscription_interval TEXT, stripe_subscription_paid_until BIGINT, stripe_subscription_cancel_at BIGINT, created BIGINT NOT NULL, deleted BIGINT ); CREATE TABLE IF NOT EXISTS user_access ( user_id TEXT NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, topic TEXT NOT NULL, read BOOLEAN NOT NULL, write BOOLEAN NOT NULL, owner_user_id TEXT REFERENCES "user"(id) ON DELETE CASCADE, provisioned BOOLEAN NOT NULL, PRIMARY KEY (user_id, topic) ); CREATE TABLE IF NOT EXISTS user_token ( user_id TEXT NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, token TEXT NOT NULL UNIQUE, label TEXT NOT NULL, last_access BIGINT NOT NULL, last_origin TEXT NOT NULL, expires BIGINT NOT NULL, provisioned BOOLEAN NOT NULL, PRIMARY KEY (user_id, token) ); CREATE TABLE IF NOT EXISTS user_phone ( user_id TEXT NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, phone_number TEXT NOT NULL, PRIMARY KEY (user_id, phone_number) ); CREATE TABLE IF NOT EXISTS schema_version ( store TEXT PRIMARY KEY, version INT NOT NULL ); INSERT INTO "user" (id, user_name, pass, role, sync_topic, provisioned, created) VALUES ('` + everyoneID + `', '*', '', 'anonymous', '', false, EXTRACT(EPOCH FROM NOW())::BIGINT) ON CONFLICT (id) DO NOTHING; ` ) // Schema table management queries for Postgres const ( postgresCurrentSchemaVersion = 6 postgresSelectSchemaVersionQuery = `SELECT version FROM schema_version WHERE store = 'user'` postgresInsertSchemaVersionQuery = `INSERT INTO schema_version (store, version) VALUES ('user', $1)` ) func setupPostgres(db *sql.DB) error { var schemaVersion int err := db.QueryRow(postgresSelectSchemaVersionQuery).Scan(&schemaVersion) if err != nil { return setupNewPostgres(db) } if schemaVersion > postgresCurrentSchemaVersion { return fmt.Errorf("unexpected schema version: version %d is higher than current version %d", schemaVersion, postgresCurrentSchemaVersion) } // Note: PostgreSQL migrations will be added when needed return nil } func setupNewPostgres(db *sql.DB) error { if _, err := db.Exec(postgresCreateTablesQueries); err != nil { return err } if _, err := db.Exec(postgresInsertSchemaVersionQuery, postgresCurrentSchemaVersion); err != nil { return err } return nil }