-- +goose Up -- Consolidated baseline schema for the Scrabble backend. It squashes the former -- incremental migrations (00001-00014) into one starting point; there is no -- production data, so the squash carries no data migration. The per-feature -- narrative that lived in the squashed migrations is preserved in git history and -- in docs/ARCHITECTURE.md. Every object lives in the `backend` schema. SET search_path = backend, pg_catalog; -- Name: backend; Type: SCHEMA; Schema: -; Owner: - -- CREATE SCHEMA IF NOT EXISTS backend; SET default_tablespace = ''; SET default_table_access_method = heap; -- -- Name: account_best_move; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.account_best_move ( account_id uuid NOT NULL, variant text NOT NULL, score integer NOT NULL, tiles jsonb NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: account_roles; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.account_roles ( account_id uuid NOT NULL, role text NOT NULL, granted_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: account_stats; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.account_stats ( account_id uuid NOT NULL, wins integer DEFAULT 0 NOT NULL, losses integer DEFAULT 0 NOT NULL, draws integer DEFAULT 0 NOT NULL, max_game_points integer DEFAULT 0 NOT NULL, max_word_points integer DEFAULT 0 NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, moves integer DEFAULT 0 NOT NULL, hints_used integer DEFAULT 0 NOT NULL ); -- -- Name: account_suspensions; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.account_suspensions ( suspension_id uuid NOT NULL, account_id uuid NOT NULL, blocked_at timestamp with time zone DEFAULT now() NOT NULL, blocked_until timestamp with time zone, reason_en text, reason_ru text, reason_id uuid, lifted_at timestamp with time zone ); -- -- Name: accounts; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.accounts ( account_id uuid NOT NULL, display_name text DEFAULT ''::text NOT NULL, preferred_language text DEFAULT 'en'::text NOT NULL, time_zone text DEFAULT 'UTC'::text NOT NULL, block_chat boolean DEFAULT false NOT NULL, block_friend_requests boolean DEFAULT false NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, away_start time without time zone DEFAULT '00:00:00'::time without time zone NOT NULL, away_end time without time zone DEFAULT '07:00:00'::time without time zone NOT NULL, hint_balance integer DEFAULT 0 NOT NULL, is_guest boolean DEFAULT false NOT NULL, notifications_in_app_only boolean DEFAULT true NOT NULL, paid_account boolean DEFAULT false NOT NULL, merged_into uuid, merged_at timestamp with time zone, flagged_high_rate_at timestamp with time zone, variant_preferences text[] DEFAULT ARRAY['erudit_ru'::text] NOT NULL, CONSTRAINT accounts_hint_balance_chk CHECK ((hint_balance >= 0)), CONSTRAINT accounts_preferred_language_chk CHECK ((preferred_language = ANY (ARRAY['en'::text, 'ru'::text]))), CONSTRAINT accounts_variant_preferences_nonempty_chk CHECK ((cardinality(variant_preferences) >= 1)), CONSTRAINT accounts_variant_preferences_subset_chk CHECK ((variant_preferences <@ ARRAY['scrabble_en'::text, 'scrabble_ru'::text, 'erudit_ru'::text])) ); -- -- Name: ad_campaigns; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.ad_campaigns ( campaign_id uuid NOT NULL, name text NOT NULL, weight integer NOT NULL, is_default boolean DEFAULT false NOT NULL, enabled boolean DEFAULT true NOT NULL, starts_at timestamp with time zone, ends_at timestamp with time zone, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT ad_campaigns_default_window_chk CHECK (((NOT is_default) OR ((starts_at IS NULL) AND (ends_at IS NULL)))), CONSTRAINT ad_campaigns_weight_chk CHECK (((weight >= 1) AND (weight <= 100))), CONSTRAINT ad_campaigns_window_chk CHECK (((starts_at IS NULL) OR (ends_at IS NULL) OR (starts_at <= ends_at))) ); -- -- Name: ad_messages; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.ad_messages ( message_id uuid NOT NULL, campaign_id uuid NOT NULL, "position" integer DEFAULT 0 NOT NULL, body_en text NOT NULL, body_ru text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: ad_settings; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.ad_settings ( id boolean DEFAULT true NOT NULL, hold_ms integer NOT NULL, edge_pause_ms integer NOT NULL, scroll_px_per_sec integer NOT NULL, fade_out_ms integer NOT NULL, gap_ms integer NOT NULL, fade_in_ms integer NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT ad_settings_singleton_chk CHECK (id) ); -- -- Name: blocks; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.blocks ( blocker_id uuid NOT NULL, blocked_id uuid NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT blocks_distinct_chk CHECK ((blocker_id <> blocked_id)) ); -- -- Name: chat_messages; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.chat_messages ( message_id uuid NOT NULL, game_id uuid NOT NULL, sender_id uuid NOT NULL, kind text DEFAULT 'message'::text NOT NULL, body text DEFAULT ''::text NOT NULL, sender_ip text, created_at timestamp with time zone DEFAULT now() NOT NULL, unread_seats smallint DEFAULT 0 NOT NULL, CONSTRAINT chat_messages_body_len_chk CHECK ((char_length(body) <= 60)), CONSTRAINT chat_messages_kind_chk CHECK ((kind = ANY (ARRAY['message'::text, 'nudge'::text]))), CONSTRAINT chat_messages_nudge_empty_chk CHECK (((kind <> 'nudge'::text) OR (body = ''::text))) ); -- -- Name: complaints; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.complaints ( complaint_id uuid NOT NULL, complainant_id uuid NOT NULL, game_id uuid NOT NULL, variant text NOT NULL, dict_version text NOT NULL, word text NOT NULL, was_valid boolean NOT NULL, note text DEFAULT ''::text NOT NULL, status text DEFAULT 'open'::text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, disposition text DEFAULT ''::text NOT NULL, resolution_note text DEFAULT ''::text NOT NULL, resolved_at timestamp with time zone, applied_in_version text DEFAULT ''::text NOT NULL, CONSTRAINT complaints_disposition_chk CHECK ((disposition = ANY (ARRAY[''::text, 'reject'::text, 'accept_add'::text, 'accept_remove'::text]))), CONSTRAINT complaints_status_chk CHECK ((status = ANY (ARRAY['open'::text, 'resolved'::text]))) ); -- -- Name: dictionary_state; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.dictionary_state ( id boolean DEFAULT true NOT NULL, active_version text NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT dictionary_state_singleton_chk CHECK (id) ); -- -- Name: email_confirmations; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.email_confirmations ( confirmation_id uuid NOT NULL, account_id uuid NOT NULL, email text NOT NULL, code_hash text NOT NULL, expires_at timestamp with time zone NOT NULL, attempts smallint DEFAULT 0 NOT NULL, consumed_at timestamp with time zone, created_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT email_confirmations_attempts_chk CHECK ((attempts >= 0)) ); -- -- Name: feedback_messages; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.feedback_messages ( message_id uuid NOT NULL, account_id uuid NOT NULL, body text NOT NULL, attachment bytea, attachment_name text, sender_ip text, channel text NOT NULL, read_at timestamp with time zone, archived_at timestamp with time zone, reply_body text, replied_at timestamp with time zone, reply_read_at timestamp with time zone, created_at timestamp with time zone DEFAULT now() NOT NULL, lang text ); -- -- Name: friend_codes; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.friend_codes ( code_id uuid NOT NULL, account_id uuid NOT NULL, code_hash text NOT NULL, expires_at timestamp with time zone NOT NULL, consumed_at timestamp with time zone, created_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: friendships; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.friendships ( requester_id uuid NOT NULL, addressee_id uuid NOT NULL, status text DEFAULT 'pending'::text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, responded_at timestamp with time zone, CONSTRAINT friendships_distinct_chk CHECK ((requester_id <> addressee_id)), CONSTRAINT friendships_status_chk CHECK ((status = ANY (ARRAY['pending'::text, 'accepted'::text, 'declined'::text]))) ); -- -- Name: game_drafts; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_drafts ( game_id uuid NOT NULL, account_id uuid NOT NULL, rack_order text DEFAULT ''::text NOT NULL, board_tiles jsonb DEFAULT '[]'::jsonb NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: game_hidden; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_hidden ( account_id uuid NOT NULL, game_id uuid NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: game_invitation_invitees; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_invitation_invitees ( invitation_id uuid NOT NULL, account_id uuid NOT NULL, seat smallint NOT NULL, response text DEFAULT 'pending'::text NOT NULL, responded_at timestamp with time zone, CONSTRAINT game_invitation_invitees_response_chk CHECK ((response = ANY (ARRAY['pending'::text, 'accepted'::text, 'declined'::text]))), CONSTRAINT game_invitation_invitees_seat_chk CHECK (((seat >= 1) AND (seat <= 3))) ); -- -- Name: game_invitations; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_invitations ( invitation_id uuid NOT NULL, inviter_id uuid NOT NULL, variant text NOT NULL, turn_timeout_secs integer NOT NULL, hints_allowed boolean DEFAULT true NOT NULL, hints_per_player smallint DEFAULT 1 NOT NULL, dropout_tiles text DEFAULT 'remove'::text NOT NULL, multiple_words_per_turn boolean DEFAULT true NOT NULL, status text DEFAULT 'pending'::text NOT NULL, game_id uuid, expires_at timestamp with time zone NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT game_invitations_dropout_tiles_chk CHECK ((dropout_tiles = ANY (ARRAY['remove'::text, 'return'::text]))), CONSTRAINT game_invitations_hints_per_player_chk CHECK ((hints_per_player >= 0)), CONSTRAINT game_invitations_status_chk CHECK ((status = ANY (ARRAY['pending'::text, 'declined'::text, 'cancelled'::text, 'expired'::text, 'started'::text]))), CONSTRAINT game_invitations_turn_timeout_chk CHECK ((turn_timeout_secs > 0)), CONSTRAINT game_invitations_variant_chk CHECK ((variant = ANY (ARRAY['scrabble_en'::text, 'scrabble_ru'::text, 'erudit_ru'::text]))) ); -- -- Name: game_moves; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_moves ( game_id uuid NOT NULL, seq integer NOT NULL, seat smallint NOT NULL, action text NOT NULL, score integer DEFAULT 0 NOT NULL, running_total integer DEFAULT 0 NOT NULL, exchanged_count smallint DEFAULT 0 NOT NULL, payload text DEFAULT '{}'::text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT game_moves_action_chk CHECK ((action = ANY (ARRAY['play'::text, 'pass'::text, 'exchange'::text, 'resign'::text, 'timeout'::text]))) ); -- -- Name: game_players; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_players ( game_id uuid NOT NULL, seat smallint NOT NULL, account_id uuid, score integer DEFAULT 0 NOT NULL, hints_used smallint DEFAULT 0 NOT NULL, is_winner boolean DEFAULT false NOT NULL, display_name text DEFAULT ''::text NOT NULL ); -- -- Name: game_setup_draws; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.game_setup_draws ( game_id uuid NOT NULL, round smallint NOT NULL, pick_no smallint NOT NULL, account_id uuid, letter text NOT NULL, is_blank boolean DEFAULT false NOT NULL, draw_rank smallint NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: games; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.games ( game_id uuid NOT NULL, variant text NOT NULL, dict_version text NOT NULL, seed bigint NOT NULL, status text DEFAULT 'active'::text NOT NULL, players smallint NOT NULL, to_move smallint DEFAULT 0 NOT NULL, turn_started_at timestamp with time zone DEFAULT now() NOT NULL, turn_timeout_secs integer NOT NULL, hints_allowed boolean DEFAULT true NOT NULL, hints_per_player smallint DEFAULT 1 NOT NULL, move_count integer DEFAULT 0 NOT NULL, end_reason text, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, finished_at timestamp with time zone, open_deadline_at timestamp with time zone, dropout_tiles text DEFAULT 'remove'::text NOT NULL, multiple_words_per_turn boolean DEFAULT true NOT NULL, vs_ai boolean DEFAULT false NOT NULL, CONSTRAINT games_dropout_tiles_chk CHECK ((dropout_tiles = ANY (ARRAY['remove'::text, 'return'::text]))), CONSTRAINT games_end_reason_chk CHECK (((end_reason IS NULL) OR (end_reason = ANY (ARRAY['out_of_tiles'::text, 'scoreless'::text, 'resign'::text, 'timeout'::text, 'aborted'::text])))), CONSTRAINT games_hints_per_player_chk CHECK ((hints_per_player >= 0)), CONSTRAINT games_players_chk CHECK (((players >= 2) AND (players <= 4))), CONSTRAINT games_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'finished'::text, 'open'::text]))), CONSTRAINT games_to_move_chk CHECK (((to_move >= 0) AND (to_move < players))), CONSTRAINT games_turn_timeout_chk CHECK ((turn_timeout_secs > 0)), CONSTRAINT games_variant_chk CHECK ((variant = ANY (ARRAY['scrabble_en'::text, 'scrabble_ru'::text, 'erudit_ru'::text]))) ); -- -- Name: identities; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.identities ( identity_id uuid NOT NULL, account_id uuid NOT NULL, kind text NOT NULL, external_id text NOT NULL, confirmed boolean DEFAULT false NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT identities_kind_chk CHECK ((kind = ANY (ARRAY['telegram'::text, 'email'::text, 'robot'::text]))) ); -- -- Name: robot_blocks; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.robot_blocks ( id uuid NOT NULL, blocker_id uuid NOT NULL, game_id uuid NOT NULL, seat smallint NOT NULL, robot_id uuid NOT NULL, display_name text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: robot_friend_requests; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.robot_friend_requests ( id uuid NOT NULL, requester_id uuid NOT NULL, game_id uuid NOT NULL, seat smallint NOT NULL, robot_id uuid NOT NULL, display_name text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: sessions; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.sessions ( session_id uuid NOT NULL, account_id uuid NOT NULL, token_hash text NOT NULL, status text DEFAULT 'active'::text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, last_seen_at timestamp with time zone, revoked_at timestamp with time zone, CONSTRAINT sessions_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'revoked'::text]))) ); -- -- Name: suspension_reasons; Type: TABLE; Schema: backend; Owner: - -- CREATE TABLE backend.suspension_reasons ( reason_id uuid NOT NULL, text_en text NOT NULL, text_ru text NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL ); -- -- Name: account_best_move account_best_move_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_best_move ADD CONSTRAINT account_best_move_pkey PRIMARY KEY (account_id, variant); -- -- Name: account_roles account_roles_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_roles ADD CONSTRAINT account_roles_pkey PRIMARY KEY (account_id, role); -- -- Name: account_stats account_stats_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_stats ADD CONSTRAINT account_stats_pkey PRIMARY KEY (account_id); -- -- Name: account_suspensions account_suspensions_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_suspensions ADD CONSTRAINT account_suspensions_pkey PRIMARY KEY (suspension_id); -- -- Name: accounts accounts_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.accounts ADD CONSTRAINT accounts_pkey PRIMARY KEY (account_id); -- -- Name: ad_campaigns ad_campaigns_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.ad_campaigns ADD CONSTRAINT ad_campaigns_pkey PRIMARY KEY (campaign_id); -- -- Name: ad_messages ad_messages_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.ad_messages ADD CONSTRAINT ad_messages_pkey PRIMARY KEY (message_id); -- -- Name: ad_settings ad_settings_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.ad_settings ADD CONSTRAINT ad_settings_pkey PRIMARY KEY (id); -- -- Name: blocks blocks_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.blocks ADD CONSTRAINT blocks_pkey PRIMARY KEY (blocker_id, blocked_id); -- -- Name: chat_messages chat_messages_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.chat_messages ADD CONSTRAINT chat_messages_pkey PRIMARY KEY (message_id); -- -- Name: complaints complaints_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.complaints ADD CONSTRAINT complaints_pkey PRIMARY KEY (complaint_id); -- -- Name: dictionary_state dictionary_state_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.dictionary_state ADD CONSTRAINT dictionary_state_pkey PRIMARY KEY (id); -- -- Name: email_confirmations email_confirmations_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.email_confirmations ADD CONSTRAINT email_confirmations_pkey PRIMARY KEY (confirmation_id); -- -- Name: feedback_messages feedback_messages_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.feedback_messages ADD CONSTRAINT feedback_messages_pkey PRIMARY KEY (message_id); -- -- Name: friend_codes friend_codes_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.friend_codes ADD CONSTRAINT friend_codes_pkey PRIMARY KEY (code_id); -- -- Name: friendships friendships_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.friendships ADD CONSTRAINT friendships_pkey PRIMARY KEY (requester_id, addressee_id); -- -- Name: game_drafts game_drafts_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_drafts ADD CONSTRAINT game_drafts_pkey PRIMARY KEY (game_id, account_id); -- -- Name: game_hidden game_hidden_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_hidden ADD CONSTRAINT game_hidden_pkey PRIMARY KEY (account_id, game_id); -- -- Name: game_invitation_invitees game_invitation_invitees_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitation_invitees ADD CONSTRAINT game_invitation_invitees_pkey PRIMARY KEY (invitation_id, account_id); -- -- Name: game_invitations game_invitations_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitations ADD CONSTRAINT game_invitations_pkey PRIMARY KEY (invitation_id); -- -- Name: game_moves game_moves_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_moves ADD CONSTRAINT game_moves_pkey PRIMARY KEY (game_id, seq); -- -- Name: game_players game_players_account_key; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_players ADD CONSTRAINT game_players_account_key UNIQUE (game_id, account_id); -- -- Name: game_players game_players_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_players ADD CONSTRAINT game_players_pkey PRIMARY KEY (game_id, seat); -- -- Name: game_setup_draws game_setup_draws_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_setup_draws ADD CONSTRAINT game_setup_draws_pkey PRIMARY KEY (game_id, round, pick_no); -- -- Name: games games_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.games ADD CONSTRAINT games_pkey PRIMARY KEY (game_id); -- -- Name: identities identities_kind_external_id_key; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.identities ADD CONSTRAINT identities_kind_external_id_key UNIQUE (kind, external_id); -- -- Name: identities identities_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.identities ADD CONSTRAINT identities_pkey PRIMARY KEY (identity_id); -- -- Name: robot_blocks robot_blocks_blocker_id_game_id_seat_key; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_blocks ADD CONSTRAINT robot_blocks_blocker_id_game_id_seat_key UNIQUE (blocker_id, game_id, seat); -- -- Name: robot_blocks robot_blocks_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_blocks ADD CONSTRAINT robot_blocks_pkey PRIMARY KEY (id); -- -- Name: robot_friend_requests robot_friend_requests_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_friend_requests ADD CONSTRAINT robot_friend_requests_pkey PRIMARY KEY (id); -- -- Name: robot_friend_requests robot_friend_requests_requester_id_game_id_seat_key; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_friend_requests ADD CONSTRAINT robot_friend_requests_requester_id_game_id_seat_key UNIQUE (requester_id, game_id, seat); -- -- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.sessions ADD CONSTRAINT sessions_pkey PRIMARY KEY (session_id); -- -- Name: sessions sessions_token_hash_key; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.sessions ADD CONSTRAINT sessions_token_hash_key UNIQUE (token_hash); -- -- Name: suspension_reasons suspension_reasons_pkey; Type: CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.suspension_reasons ADD CONSTRAINT suspension_reasons_pkey PRIMARY KEY (reason_id); -- -- Name: account_suspensions_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX account_suspensions_account_idx ON backend.account_suspensions USING btree (account_id, blocked_at DESC); -- -- Name: ad_campaigns_default_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE UNIQUE INDEX ad_campaigns_default_idx ON backend.ad_campaigns USING btree (is_default) WHERE is_default; -- -- Name: ad_messages_campaign_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX ad_messages_campaign_idx ON backend.ad_messages USING btree (campaign_id, "position"); -- -- Name: blocks_blocked_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX blocks_blocked_idx ON backend.blocks USING btree (blocked_id); -- -- Name: chat_messages_game_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX chat_messages_game_idx ON backend.chat_messages USING btree (game_id, created_at); -- -- Name: chat_messages_nudge_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX chat_messages_nudge_idx ON backend.chat_messages USING btree (game_id, sender_id, created_at) WHERE (kind = 'nudge'::text); -- -- Name: chat_messages_unread_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX chat_messages_unread_idx ON backend.chat_messages USING btree (game_id) WHERE (unread_seats <> 0); -- -- Name: complaints_status_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX complaints_status_idx ON backend.complaints USING btree (status); -- -- Name: email_confirmations_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX email_confirmations_account_idx ON backend.email_confirmations USING btree (account_id); -- -- Name: feedback_messages_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX feedback_messages_account_idx ON backend.feedback_messages USING btree (account_id, created_at DESC); -- -- Name: feedback_messages_created_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX feedback_messages_created_idx ON backend.feedback_messages USING btree (created_at DESC); -- -- Name: friend_codes_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX friend_codes_account_idx ON backend.friend_codes USING btree (account_id); -- -- Name: friend_codes_code_hash_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX friend_codes_code_hash_idx ON backend.friend_codes USING btree (code_hash); -- -- Name: friendships_addressee_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX friendships_addressee_idx ON backend.friendships USING btree (addressee_id); -- -- Name: game_invitation_invitees_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX game_invitation_invitees_account_idx ON backend.game_invitation_invitees USING btree (account_id); -- -- Name: game_invitations_inviter_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX game_invitations_inviter_idx ON backend.game_invitations USING btree (inviter_id); -- -- Name: game_players_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX game_players_account_idx ON backend.game_players USING btree (account_id); -- -- Name: games_active_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX games_active_idx ON backend.games USING btree (turn_started_at) WHERE (status = 'active'::text); -- -- Name: games_open_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX games_open_idx ON backend.games USING btree (open_deadline_at) WHERE (status = 'open'::text); -- -- Name: games_open_match_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX games_open_match_idx ON backend.games USING btree (variant, multiple_words_per_turn, created_at) WHERE (status = 'open'::text); -- -- Name: identities_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX identities_account_idx ON backend.identities USING btree (account_id); -- -- Name: robot_blocks_blocker_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX robot_blocks_blocker_idx ON backend.robot_blocks USING btree (blocker_id); -- -- Name: robot_friend_requests_requester_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX robot_friend_requests_requester_idx ON backend.robot_friend_requests USING btree (requester_id); -- -- Name: sessions_account_idx; Type: INDEX; Schema: backend; Owner: - -- CREATE INDEX sessions_account_idx ON backend.sessions USING btree (account_id); -- -- Name: account_best_move account_best_move_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_best_move ADD CONSTRAINT account_best_move_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: account_roles account_roles_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_roles ADD CONSTRAINT account_roles_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: account_stats account_stats_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_stats ADD CONSTRAINT account_stats_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: account_suspensions account_suspensions_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_suspensions ADD CONSTRAINT account_suspensions_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: account_suspensions account_suspensions_reason_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.account_suspensions ADD CONSTRAINT account_suspensions_reason_id_fkey FOREIGN KEY (reason_id) REFERENCES backend.suspension_reasons(reason_id) ON DELETE SET NULL; -- -- Name: accounts accounts_merged_into_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.accounts ADD CONSTRAINT accounts_merged_into_fkey FOREIGN KEY (merged_into) REFERENCES backend.accounts(account_id) ON DELETE SET NULL; -- -- Name: ad_messages ad_messages_campaign_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.ad_messages ADD CONSTRAINT ad_messages_campaign_id_fkey FOREIGN KEY (campaign_id) REFERENCES backend.ad_campaigns(campaign_id) ON DELETE CASCADE; -- -- Name: blocks blocks_blocked_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.blocks ADD CONSTRAINT blocks_blocked_id_fkey FOREIGN KEY (blocked_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: blocks blocks_blocker_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.blocks ADD CONSTRAINT blocks_blocker_id_fkey FOREIGN KEY (blocker_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: chat_messages chat_messages_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.chat_messages ADD CONSTRAINT chat_messages_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: chat_messages chat_messages_sender_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.chat_messages ADD CONSTRAINT chat_messages_sender_id_fkey FOREIGN KEY (sender_id) REFERENCES backend.accounts(account_id); -- -- Name: complaints complaints_complainant_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.complaints ADD CONSTRAINT complaints_complainant_id_fkey FOREIGN KEY (complainant_id) REFERENCES backend.accounts(account_id); -- -- Name: complaints complaints_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.complaints ADD CONSTRAINT complaints_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: email_confirmations email_confirmations_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.email_confirmations ADD CONSTRAINT email_confirmations_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: feedback_messages feedback_messages_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.feedback_messages ADD CONSTRAINT feedback_messages_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: friend_codes friend_codes_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.friend_codes ADD CONSTRAINT friend_codes_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: friendships friendships_addressee_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.friendships ADD CONSTRAINT friendships_addressee_id_fkey FOREIGN KEY (addressee_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: friendships friendships_requester_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.friendships ADD CONSTRAINT friendships_requester_id_fkey FOREIGN KEY (requester_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_drafts game_drafts_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_drafts ADD CONSTRAINT game_drafts_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_drafts game_drafts_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_drafts ADD CONSTRAINT game_drafts_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: game_hidden game_hidden_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_hidden ADD CONSTRAINT game_hidden_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_hidden game_hidden_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_hidden ADD CONSTRAINT game_hidden_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: game_invitation_invitees game_invitation_invitees_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitation_invitees ADD CONSTRAINT game_invitation_invitees_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_invitation_invitees game_invitation_invitees_invitation_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitation_invitees ADD CONSTRAINT game_invitation_invitees_invitation_id_fkey FOREIGN KEY (invitation_id) REFERENCES backend.game_invitations(invitation_id) ON DELETE CASCADE; -- -- Name: game_invitations game_invitations_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitations ADD CONSTRAINT game_invitations_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE SET NULL; -- -- Name: game_invitations game_invitations_inviter_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_invitations ADD CONSTRAINT game_invitations_inviter_id_fkey FOREIGN KEY (inviter_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_moves game_moves_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_moves ADD CONSTRAINT game_moves_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: game_players game_players_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_players ADD CONSTRAINT game_players_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id); -- -- Name: game_players game_players_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_players ADD CONSTRAINT game_players_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: game_setup_draws game_setup_draws_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_setup_draws ADD CONSTRAINT game_setup_draws_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: game_setup_draws game_setup_draws_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.game_setup_draws ADD CONSTRAINT game_setup_draws_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: identities identities_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.identities ADD CONSTRAINT identities_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: robot_blocks robot_blocks_blocker_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_blocks ADD CONSTRAINT robot_blocks_blocker_id_fkey FOREIGN KEY (blocker_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: robot_blocks robot_blocks_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_blocks ADD CONSTRAINT robot_blocks_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: robot_blocks robot_blocks_robot_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_blocks ADD CONSTRAINT robot_blocks_robot_id_fkey FOREIGN KEY (robot_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: robot_friend_requests robot_friend_requests_game_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_friend_requests ADD CONSTRAINT robot_friend_requests_game_id_fkey FOREIGN KEY (game_id) REFERENCES backend.games(game_id) ON DELETE CASCADE; -- -- Name: robot_friend_requests robot_friend_requests_requester_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_friend_requests ADD CONSTRAINT robot_friend_requests_requester_id_fkey FOREIGN KEY (requester_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: robot_friend_requests robot_friend_requests_robot_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.robot_friend_requests ADD CONSTRAINT robot_friend_requests_robot_id_fkey FOREIGN KEY (robot_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- -- Name: sessions sessions_account_id_fkey; Type: FK CONSTRAINT; Schema: backend; Owner: - -- ALTER TABLE ONLY backend.sessions ADD CONSTRAINT sessions_account_id_fkey FOREIGN KEY (account_id) REFERENCES backend.accounts(account_id) ON DELETE CASCADE; -- Seed data carried over from the squashed migrations (a schema-only dump omits it): -- the default house ad campaign, its message and the banner display timings. INSERT INTO ad_campaigns (campaign_id, name, weight, is_default, enabled) VALUES ('00000000-0000-0000-0000-0000000000ad', 'Default (house)', 100, true, true); INSERT INTO ad_messages (message_id, campaign_id, position, body_en, body_ru) VALUES ( '00000000-0000-0000-0000-0000000000a1', '00000000-0000-0000-0000-0000000000ad', 0, 'Tip: a play using all 7 tiles earns a +50 bonus.', 'Совет: ход всеми 7 фишками приносит бонус +50 очков.' ); INSERT INTO ad_settings (id, hold_ms, edge_pause_ms, scroll_px_per_sec, fade_out_ms, gap_ms, fade_in_ms) VALUES (true, 60000, 5000, 40, 1000, 250, 1000); -- +goose Down DROP SCHEMA IF EXISTS backend CASCADE;