From c9127cecce821fbb99bfdb2d946e7b5c8b4c0ed5 Mon Sep 17 00:00:00 2001 From: Jordan Harrison Date: Sat, 31 Oct 2026 13:30:21 +0000 Subject: [PATCH] fix(drafts): preserve reply drafts across forum moves --- docs/members/member-guide.md | 3 +- docs/operations/upgrading.md | 6 + .../db/migrations/0073_reply_draft_forums.sql | 4 + .../db/migrations/meta/0073_snapshot.json | 9892 +++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/src/client.pg.test.ts | 47 + packages/db/src/draft-repo.test.ts | 84 + packages/db/src/draft-repo.ts | 47 +- packages/db/src/thread-tools.ts | 3 + 9 files changed, 10076 insertions(+), 17 deletions(-) create mode 100644 packages/db/migrations/0073_reply_draft_forums.sql create mode 100644 packages/db/migrations/meta/0073_snapshot.json diff --git a/docs/members/member-guide.md b/docs/members/member-guide.md index 6447a9b3..d1b90eae 100644 --- a/docs/members/member-guide.md +++ b/docs/members/member-guide.md @@ -17,7 +17,8 @@ Open a forum or use **New posts**, **Unanswered**, **My posts** or **Search**. S 3. Add permitted attachments or a poll. 4. Submit. Posts held for approval appear after moderator review. -See [Formatting](formatting.md) for Markdown. Resume saved drafts at `/usercp/drafts`. Editing and deletion may have permission or time limits. +See [Formatting](formatting.md) for Markdown. Resume saved drafts at `/usercp/drafts`. Reply drafts follow a thread when it moves +to another forum, provided you can still read and reply there. Editing and deletion may have permission or time limits. ## Account and activity diff --git a/docs/operations/upgrading.md b/docs/operations/upgrading.md index 2a66d9ca..0a1a2330 100644 --- a/docs/operations/upgrading.md +++ b/docs/operations/upgrading.md @@ -40,3 +40,9 @@ Do not point a new PostgreSQL major at an old data volume. Restore a dump into a ## Verify Check version, pending migrations, sign-in, posting, private forums, attachments, mail, scheduled work and logs before reopening. If checks fail, use the restore plan rather than older code against the changed schema. + +Migration `0073_reply_draft_forums` repairs reply drafts' stored forum references +from their live threads without changing saved content or timestamps. Updated +thread moves and autosaves maintain these references under the thread lock. +Apply this forward-only migration before deleting an old forum after an upgrade; +drafts already removed by an earlier forum deletion cannot be recovered by it. diff --git a/packages/db/migrations/0073_reply_draft_forums.sql b/packages/db/migrations/0073_reply_draft_forums.sql new file mode 100644 index 00000000..f78b4d9e --- /dev/null +++ b/packages/db/migrations/0073_reply_draft_forums.sql @@ -0,0 +1,4 @@ +UPDATE post_drafts d + SET forum_id = t.forum_id + FROM threads t + WHERE d.thread_id = t.id AND d.forum_id <> t.forum_id; diff --git a/packages/db/migrations/meta/0073_snapshot.json b/packages/db/migrations/meta/0073_snapshot.json new file mode 100644 index 00000000..75bbc8a6 --- /dev/null +++ b/packages/db/migrations/meta/0073_snapshot.json @@ -0,0 +1,9892 @@ +{ + "id": "6d8fed8e-804f-4fc9-8ec0-ed6e28b45323", + "prevId": "db1f8366-07bd-4983-a26c-5e5d47fb3e56", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.announcements": { + "name": "announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "announcements_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_format": { + "name": "body_format", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "author_user_id": { + "name": "author_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_username": { + "name": "author_username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "announcements_live_idx": { + "name": "announcements_live_idx", + "columns": [ + { + "expression": "starts_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"announcements\".\"enabled\"", + "concurrently": false + }, + "announcements_forum_idx": { + "name": "announcements_forum_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "announcements_forum_id_forums_id_fk": { + "name": "announcements_forum_id_forums_id_fk", + "tableFrom": "announcements", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "announcements_author_user_id_users_id_fk": { + "name": "announcements_author_user_id_users_id_fk", + "tableFrom": "announcements", + "columnsFrom": ["author_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.attachment_orphans": { + "name": "attachment_orphans", + "schema": "", + "columns": { + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "attachment_orphans_age_idx": { + "name": "attachment_orphans_age_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.attachments": { + "name": "attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "attachments_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "uploader_user_id": { + "name": "uploader_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_key": { + "name": "thumbnail_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ready_at": { + "name": "ready_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "attachments_legacy_id_key": { + "name": "attachments_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"attachments\".\"legacy_id\" is not null", + "concurrently": false + }, + "attachments_post_idx": { + "name": "attachments_post_idx", + "columns": [ + { + "expression": "post_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "attachments_uploader_idx": { + "name": "attachments_uploader_idx", + "columns": [ + { + "expression": "uploader_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "attachments_pending_idx": { + "name": "attachments_pending_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"attachments\".\"status\" = 'pending'", + "concurrently": false + }, + "attachments_orphan_idx": { + "name": "attachments_orphan_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"attachments\".\"post_id\" is null", + "concurrently": false + }, + "attachments_storage_key_key": { + "name": "attachments_storage_key_key", + "columns": [ + { + "expression": "storage_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"attachments\".\"storage_key\" is not null", + "concurrently": false + }, + "attachments_source_key_key": { + "name": "attachments_source_key_key", + "columns": [ + { + "expression": "source_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"attachments\".\"source_key\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "attachments_post_id_posts_id_fk": { + "name": "attachments_post_id_posts_id_fk", + "tableFrom": "attachments", + "columnsFrom": ["post_id"], + "tableTo": "posts", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "attachments_forum_id_forums_id_fk": { + "name": "attachments_forum_id_forums_id_fk", + "tableFrom": "attachments", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "attachments_uploader_user_id_users_id_fk": { + "name": "attachments_uploader_user_id_users_id_fk", + "tableFrom": "attachments", + "columnsFrom": ["uploader_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_directives": { + "name": "custom_directives", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "custom_directives_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block": { + "name": "block", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_directives_name_key": { + "name": "custom_directives_name_key", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.digest_runs": { + "name": "digest_runs", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_sent_at": { + "name": "last_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "digest_runs_user_id_users_id_fk": { + "name": "digest_runs_user_id_users_id_fk", + "tableFrom": "digest_runs", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "digest_runs_pkey": { + "name": "digest_runs_pkey", + "columns": ["user_id", "cadence"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.post_revisions": { + "name": "post_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "post_revisions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "edited_by_user_id": { + "name": "edited_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "edit_reason": { + "name": "edit_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "post_revisions_post_revision_key": { + "name": "post_revisions_post_revision_key", + "columns": [ + { + "expression": "post_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "post_revisions_post_id_posts_id_fk": { + "name": "post_revisions_post_id_posts_id_fk", + "tableFrom": "post_revisions", + "columnsFrom": ["post_id"], + "tableTo": "posts", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "post_revisions_edited_by_user_id_users_id_fk": { + "name": "post_revisions_edited_by_user_id_users_id_fk", + "tableFrom": "post_revisions", + "columnsFrom": ["edited_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts": { + "name": "posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "posts_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "thread_id": { + "name": "thread_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "author_user_id": { + "name": "author_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_username": { + "name": "author_username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_format": { + "name": "body_format", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "message_html": { + "name": "message_html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "render_version": { + "name": "render_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vocab_version": { + "name": "vocab_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'visible'" + }, + "is_first_post": { + "name": "is_first_post", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "ip_prefix": { + "name": "ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "edited_by_user_id": { + "name": "edited_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "edit_reason": { + "name": "edit_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision_count": { + "name": "revision_count", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "search_vector": { + "name": "search_vector", + "type": "tsvector", + "primaryKey": false, + "notNull": false + }, + "search_version": { + "name": "search_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "posts_thread_visible_idx": { + "name": "posts_thread_visible_idx", + "columns": [ + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"posts\".\"visibility\" = 'visible'", + "concurrently": false + }, + "posts_thread_all_idx": { + "name": "posts_thread_all_idx", + "columns": [ + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "posts_author_idx": { + "name": "posts_author_idx", + "columns": [ + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "posts_render_version_idx": { + "name": "posts_render_version_idx", + "columns": [ + { + "expression": "render_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "posts_vocab_version_idx": { + "name": "posts_vocab_version_idx", + "columns": [ + { + "expression": "vocab_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "posts_search_version_idx": { + "name": "posts_search_version_idx", + "columns": [ + { + "expression": "search_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "posts_forum_visibility_idx": { + "name": "posts_forum_visibility_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"posts\".\"visibility\" <> 'visible'", + "concurrently": false + }, + "posts_legacy_id_key": { + "name": "posts_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"posts\".\"legacy_id\" is not null", + "concurrently": false + }, + "posts_search_vector_idx": { + "name": "posts_search_vector_idx", + "columns": [ + { + "expression": "search_vector", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": { + "posts_thread_id_threads_id_fk": { + "name": "posts_thread_id_threads_id_fk", + "tableFrom": "posts", + "columnsFrom": ["thread_id"], + "tableTo": "threads", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "posts_forum_id_forums_id_fk": { + "name": "posts_forum_id_forums_id_fk", + "tableFrom": "posts", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "posts_author_user_id_users_id_fk": { + "name": "posts_author_user_id_users_id_fk", + "tableFrom": "posts", + "columnsFrom": ["author_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "posts_edited_by_user_id_users_id_fk": { + "name": "posts_edited_by_user_id_users_id_fk", + "tableFrom": "posts", + "columnsFrom": ["edited_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_events": { + "name": "report_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "report_events_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "report_id": { + "name": "report_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_events_report_idx": { + "name": "report_events_report_idx", + "columns": [ + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "report_events_report_id_reports_id_fk": { + "name": "report_events_report_id_reports_id_fk", + "tableFrom": "report_events", + "columnsFrom": ["report_id"], + "tableTo": "reports", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "report_events_actor_user_id_users_id_fk": { + "name": "report_events_actor_user_id_users_id_fk", + "tableFrom": "report_events", + "columnsFrom": ["actor_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reports": { + "name": "reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "reports_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "thread_id": { + "name": "thread_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_label": { + "name": "target_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "reporter_user_id": { + "name": "reporter_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "assigned_to_user_id": { + "name": "assigned_to_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "resolved_by_user_id": { + "name": "resolved_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reports_open_idx": { + "name": "reports_open_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"reports\".\"status\" = 'open'", + "concurrently": false + }, + "reports_open_global_idx": { + "name": "reports_open_global_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"reports\".\"status\" = 'open' and \"reports\".\"forum_id\" is null", + "concurrently": false + }, + "reports_one_open_per_reporter_key": { + "name": "reports_one_open_per_reporter_key", + "columns": [ + { + "expression": "reporter_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"reports\".\"status\" = 'open' and \"reports\".\"reporter_user_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "reports_forum_id_forums_id_fk": { + "name": "reports_forum_id_forums_id_fk", + "tableFrom": "reports", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "reports_thread_id_threads_id_fk": { + "name": "reports_thread_id_threads_id_fk", + "tableFrom": "reports", + "columnsFrom": ["thread_id"], + "tableTo": "threads", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "reports_reporter_user_id_users_id_fk": { + "name": "reports_reporter_user_id_users_id_fk", + "tableFrom": "reports", + "columnsFrom": ["reporter_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "reports_assigned_to_user_id_users_id_fk": { + "name": "reports_assigned_to_user_id_users_id_fk", + "tableFrom": "reports", + "columnsFrom": ["assigned_to_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "reports_resolved_by_user_id_users_id_fk": { + "name": "reports_resolved_by_user_id_users_id_fk", + "tableFrom": "reports", + "columnsFrom": ["resolved_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reputation": { + "name": "reputation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "reputation_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "given_by_user_id": { + "name": "given_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "points": { + "name": "points", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reputation_legacy_id_key": { + "name": "reputation_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"reputation\".\"legacy_id\" is not null", + "concurrently": false + }, + "reputation_profile_unique": { + "name": "reputation_profile_unique", + "columns": [ + { + "expression": "given_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"reputation\".\"post_id\" is null", + "concurrently": false + }, + "reputation_post_unique": { + "name": "reputation_post_unique", + "columns": [ + { + "expression": "given_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "post_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"reputation\".\"post_id\" is not null", + "concurrently": false + }, + "reputation_user_idx": { + "name": "reputation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reputation_given_idx": { + "name": "reputation_given_idx", + "columns": [ + { + "expression": "given_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "reputation_user_id_users_id_fk": { + "name": "reputation_user_id_users_id_fk", + "tableFrom": "reputation", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "reputation_given_by_user_id_users_id_fk": { + "name": "reputation_given_by_user_id_users_id_fk", + "tableFrom": "reputation", + "columnsFrom": ["given_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "reputation_post_id_posts_id_fk": { + "name": "reputation_post_id_posts_id_fk", + "tableFrom": "reputation", + "columnsFrom": ["post_id"], + "tableTo": "posts", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.smilies": { + "name": "smilies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "smilies_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "src": { + "name": "src", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "smilies_code_key": { + "name": "smilies_code_key", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_prefixes": { + "name": "thread_prefixes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "thread_prefixes_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "forum_path_prefix": { + "name": "forum_path_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_prefixes_order_idx": { + "name": "thread_prefixes_order_idx", + "columns": [ + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_subscriptions": { + "name": "thread_subscriptions", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'instant'" + }, + "last_notified_post_id": { + "name": "last_notified_post_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_subscriptions_pkey": { + "name": "thread_subscriptions_pkey", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "thread_subscriptions_thread_idx": { + "name": "thread_subscriptions_thread_idx", + "columns": [ + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "thread_subscriptions_user_idx": { + "name": "thread_subscriptions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "thread_subscriptions_mode_idx": { + "name": "thread_subscriptions_mode_idx", + "columns": [ + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "thread_subscriptions_user_id_users_id_fk": { + "name": "thread_subscriptions_user_id_users_id_fk", + "tableFrom": "thread_subscriptions", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "thread_subscriptions_thread_id_threads_id_fk": { + "name": "thread_subscriptions_thread_id_threads_id_fk", + "tableFrom": "thread_subscriptions", + "columnsFrom": ["thread_id"], + "tableTo": "threads", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_view_buffer": { + "name": "thread_view_buffer", + "schema": "", + "columns": { + "thread_id": { + "name": "thread_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "pending": { + "name": "pending", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_view_buffer_updated_idx": { + "name": "thread_view_buffer_updated_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "thread_view_buffer_thread_id_threads_id_fk": { + "name": "thread_view_buffer_thread_id_threads_id_fk", + "tableFrom": "thread_view_buffer", + "columnsFrom": ["thread_id"], + "tableTo": "threads", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.threads": { + "name": "threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "threads_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix_id": { + "name": "prefix_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_user_id": { + "name": "author_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_username": { + "name": "author_username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'visible'" + }, + "is_sticky": { + "name": "is_sticky", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "first_post_id": { + "name": "first_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "view_count": { + "name": "view_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "rating_total": { + "name": "rating_total", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "rating_count": { + "name": "rating_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_post_id": { + "name": "last_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_post_user_id": { + "name": "last_post_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_post_username": { + "name": "last_post_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_post_at": { + "name": "last_post_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "moved_to_thread_id": { + "name": "moved_to_thread_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "threads_forum_listing_idx": { + "name": "threads_forum_listing_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_sticky", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "last_post_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"threads\".\"visibility\" = 'visible'", + "concurrently": false + }, + "threads_forum_listing_all_idx": { + "name": "threads_forum_listing_all_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_sticky", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "last_post_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "threads_author_idx": { + "name": "threads_author_idx", + "columns": [ + { + "expression": "author_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "threads_legacy_id_key": { + "name": "threads_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"threads\".\"legacy_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "threads_forum_id_forums_id_fk": { + "name": "threads_forum_id_forums_id_fk", + "tableFrom": "threads", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "threads_prefix_id_thread_prefixes_id_fk": { + "name": "threads_prefix_id_thread_prefixes_id_fk", + "tableFrom": "threads", + "columnsFrom": ["prefix_id"], + "tableTo": "thread_prefixes", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "threads_author_user_id_users_id_fk": { + "name": "threads_author_user_id_users_id_fk", + "tableFrom": "threads", + "columnsFrom": ["author_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.threads_read": { + "name": "threads_read", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "last_read_post_id": { + "name": "last_read_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "threads_read_pkey": { + "name": "threads_read_pkey", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "threads_read_read_at_idx": { + "name": "threads_read_read_at_idx", + "columns": [ + { + "expression": "read_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "threads_read_user_id_users_id_fk": { + "name": "threads_read_user_id_users_id_fk", + "tableFrom": "threads_read", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "threads_read_thread_id_threads_id_fk": { + "name": "threads_read_thread_id_threads_id_fk", + "tableFrom": "threads_read", + "columnsFrom": ["thread_id"], + "tableTo": "threads", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.word_filters": { + "name": "word_filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "word_filters_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "pattern": { + "name": "pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin_sessions": { + "name": "admin_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "admin_sessions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip_prefix": { + "name": "ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authenticated_at": { + "name": "authenticated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "admin_sessions_token_hash_key": { + "name": "admin_sessions_token_hash_key", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "admin_sessions_user_idx": { + "name": "admin_sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "admin_sessions_expiry_idx": { + "name": "admin_sessions_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "admin_sessions_user_id_users_id_fk": { + "name": "admin_sessions_user_id_users_id_fk", + "tableFrom": "admin_sessions", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_events": { + "name": "auth_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "auth_events_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip_prefix": { + "name": "ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "detail": { + "name": "detail", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "at": { + "name": "at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_events_user_idx": { + "name": "auth_events_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auth_events_recent_idx": { + "name": "auth_events_recent_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auth_events_kind_idx": { + "name": "auth_events_kind_idx", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "auth_events_user_id_users_id_fk": { + "name": "auth_events_user_id_users_id_fk", + "tableFrom": "auth_events", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ban_filters": { + "name": "ban_filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "ban_filters_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pattern": { + "name": "pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ban_filters_type_idx": { + "name": "ban_filters_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "ban_filters_type_pattern_key": { + "name": "ban_filters_type_pattern_key", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pattern", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "ban_filters_created_by_user_id_users_id_fk": { + "name": "ban_filters_created_by_user_id_users_id_fk", + "tableFrom": "ban_filters", + "columnsFrom": ["created_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bans": { + "name": "bans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "bans_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "banned_by_user_id": { + "name": "banned_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_reason": { + "name": "public_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_primary_group_id": { + "name": "previous_primary_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lifted_at": { + "name": "lifted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bans_active_user_key": { + "name": "bans_active_user_key", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"bans\".\"lifted_at\" is null", + "concurrently": false + }, + "bans_expires_idx": { + "name": "bans_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"bans\".\"lifted_at\" is null", + "concurrently": false + }, + "bans_legacy_id_key": { + "name": "bans_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"bans\".\"legacy_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "bans_user_id_users_id_fk": { + "name": "bans_user_id_users_id_fk", + "tableFrom": "bans", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "bans_banned_by_user_id_users_id_fk": { + "name": "bans_banned_by_user_id_users_id_fk", + "tableFrom": "bans", + "columnsFrom": ["banned_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "bans_previous_primary_group_id_usergroups_id_fk": { + "name": "bans_previous_primary_group_id_usergroups_id_fk", + "tableFrom": "bans", + "columnsFrom": ["previous_primary_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credential_tokens": { + "name": "credential_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "credential_tokens_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_tokens_hash_key": { + "name": "credential_tokens_hash_key", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_tokens_user_purpose_idx": { + "name": "credential_tokens_user_purpose_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purpose", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_tokens_expires_idx": { + "name": "credential_tokens_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "credential_tokens_user_id_users_id_fk": { + "name": "credential_tokens_user_id_users_id_fk", + "tableFrom": "credential_tokens", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feed_tokens": { + "name": "feed_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "feed_tokens_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "lookup": { + "name": "lookup", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_hash": { + "name": "secret_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "feed_tokens_user_key": { + "name": "feed_tokens_user_key", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "feed_tokens_lookup_key": { + "name": "feed_tokens_lookup_key", + "columns": [ + { + "expression": "lookup", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "feed_tokens_user_id_users_id_fk": { + "name": "feed_tokens_user_id_users_id_fk", + "tableFrom": "feed_tokens", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.group_promotions": { + "name": "group_promotions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "group_promotions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "min_post_count": { + "name": "min_post_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_reputation": { + "name": "min_reputation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "min_days_registered": { + "name": "min_days_registered", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "from_primary_group_id": { + "name": "from_primary_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "to_primary_group_id": { + "name": "to_primary_group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "group_promotions_order_idx": { + "name": "group_promotions_order_idx", + "columns": [ + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "group_promotions_from_primary_group_id_usergroups_id_fk": { + "name": "group_promotions_from_primary_group_id_usergroups_id_fk", + "tableFrom": "group_promotions", + "columnsFrom": ["from_primary_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "group_promotions_to_primary_group_id_usergroups_id_fk": { + "name": "group_promotions_to_primary_group_id_usergroups_id_fk", + "tableFrom": "group_promotions", + "columnsFrom": ["to_primary_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.login_attempts": { + "name": "login_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "login_attempts_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "succeeded": { + "name": "succeeded", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "login_attempts_bucket_idx": { + "name": "login_attempts_bucket_idx", + "columns": [ + { + "expression": "bucket", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_preferences": { + "name": "notification_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "push": { + "name": "push", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notification_preferences_kind_email_idx": { + "name": "notification_preferences_kind_email_idx", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"notification_preferences\".\"email\" = true", + "concurrently": false + } + }, + "foreignKeys": { + "notification_preferences_user_id_users_id_fk": { + "name": "notification_preferences_user_id_users_id_fk", + "tableFrom": "notification_preferences", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "notification_preferences_pkey": { + "name": "notification_preferences_pkey", + "columns": ["user_id", "kind"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "notifications_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "href": { + "name": "href", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurrences": { + "name": "occurrences", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email_sent_at": { + "name": "email_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "push_sent_at": { + "name": "push_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "notifications_user_idx": { + "name": "notifications_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "notifications_unread_idx": { + "name": "notifications_unread_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"notifications\".\"read_at\" is null", + "concurrently": false + }, + "notifications_dedupe_idx": { + "name": "notifications_dedupe_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"notifications\".\"read_at\" is null and \"notifications\".\"dedupe_key\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkeys": { + "name": "passkeys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "passkeys_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sign_count": { + "name": "sign_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "passkeys_credential_id_key": { + "name": "passkeys_credential_id_key", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "passkeys_user_idx": { + "name": "passkeys_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "passkeys_user_id_users_id_fk": { + "name": "passkeys_user_id_users_id_fk", + "tableFrom": "passkeys", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profile_field_groups": { + "name": "profile_field_groups", + "schema": "", + "columns": { + "field_id": { + "name": "field_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "can_view": { + "name": "can_view", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_edit": { + "name": "can_edit", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "profile_field_groups_field_id_profile_fields_id_fk": { + "name": "profile_field_groups_field_id_profile_fields_id_fk", + "tableFrom": "profile_field_groups", + "columnsFrom": ["field_id"], + "tableTo": "profile_fields", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "profile_field_groups_group_id_usergroups_id_fk": { + "name": "profile_field_groups_group_id_usergroups_id_fk", + "tableFrom": "profile_field_groups", + "columnsFrom": ["group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "profile_field_groups_pkey": { + "name": "profile_field_groups_pkey", + "columns": ["field_id", "group_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profile_field_values": { + "name": "profile_field_values", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "field_id": { + "name": "field_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "profile_field_values_user_idx": { + "name": "profile_field_values_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "profile_field_values_user_id_users_id_fk": { + "name": "profile_field_values_user_id_users_id_fk", + "tableFrom": "profile_field_values", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "profile_field_values_field_id_profile_fields_id_fk": { + "name": "profile_field_values_field_id_profile_fields_id_fk", + "tableFrom": "profile_field_values", + "columnsFrom": ["field_id"], + "tableTo": "profile_fields", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "profile_field_values_pkey": { + "name": "profile_field_values_pkey", + "columns": ["user_id", "field_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profile_fields": { + "name": "profile_fields", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "profile_fields_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text'" + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "max_length": { + "name": "max_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "required_at_registration": { + "name": "required_at_registration", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_visible": { + "name": "default_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "default_editable": { + "name": "default_editable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "show_in_postbit": { + "name": "show_in_postbit", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "profile_fields_key_unique": { + "name": "profile_fields_key_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "profile_fields_order_idx": { + "name": "profile_fields_order_idx", + "columns": [ + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.push_subscriptions": { + "name": "push_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "push_subscriptions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "p256dh": { + "name": "p256dh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth": { + "name": "auth", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "push_subscriptions_endpoint_key": { + "name": "push_subscriptions_endpoint_key", + "columns": [ + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "push_subscriptions_user_idx": { + "name": "push_subscriptions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "push_subscriptions_user_id_users_id_fk": { + "name": "push_subscriptions_user_id_users_id_fk", + "tableFrom": "push_subscriptions", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recovery_codes": { + "name": "recovery_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "recovery_codes_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code_hash": { + "name": "code_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recovery_codes_user_hash_key": { + "name": "recovery_codes_user_hash_key", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "recovery_codes_unused_idx": { + "name": "recovery_codes_unused_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"recovery_codes\".\"used_at\" is null", + "concurrently": false + } + }, + "foreignKeys": { + "recovery_codes_user_id_users_id_fk": { + "name": "recovery_codes_user_id_users_id_fk", + "tableFrom": "recovery_codes", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.remember_tokens": { + "name": "remember_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "remember_tokens_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "family_id": { + "name": "family_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_reason": { + "name": "revoked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "remember_tokens_hash_key": { + "name": "remember_tokens_hash_key", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "remember_tokens_family_idx": { + "name": "remember_tokens_family_idx", + "columns": [ + { + "expression": "family_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "remember_tokens_user_idx": { + "name": "remember_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "remember_tokens_expires_idx": { + "name": "remember_tokens_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "remember_tokens_user_id_users_id_fk": { + "name": "remember_tokens_user_id_users_id_fk", + "tableFrom": "remember_tokens", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "sessions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "location_path": { + "name": "location_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location_forum_id": { + "name": "location_forum_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "location_thread_id": { + "name": "location_thread_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ip_prefix": { + "name": "ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "superseded_by_session_id": { + "name": "superseded_by_session_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "credential_proved_at": { + "name": "credential_proved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sessions_token_hash_key": { + "name": "sessions_token_hash_key", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sessions_last_seen_idx": { + "name": "sessions_last_seen_idx", + "columns": [ + { + "expression": "last_seen_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sessions_user_idx": { + "name": "sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sessions_expires_idx": { + "name": "sessions_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_group_memberships": { + "name": "user_group_memberships", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_display_group": { + "name": "is_display_group", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "granted_by_user_id": { + "name": "granted_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "granted_by_plugin": { + "name": "granted_by_plugin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_reason": { + "name": "grant_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_primary_group_id": { + "name": "previous_primary_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_group_memberships_pkey": { + "name": "user_group_memberships_pkey", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_group_memberships_user_idx": { + "name": "user_group_memberships_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_group_memberships_group_idx": { + "name": "user_group_memberships_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_group_memberships_expiry_idx": { + "name": "user_group_memberships_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"user_group_memberships\".\"expires_at\" is not null", + "concurrently": false + }, + "user_group_memberships_previous_primary_idx": { + "name": "user_group_memberships_previous_primary_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"user_group_memberships\".\"previous_primary_group_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "user_group_memberships_user_id_users_id_fk": { + "name": "user_group_memberships_user_id_users_id_fk", + "tableFrom": "user_group_memberships", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_group_memberships_group_id_usergroups_id_fk": { + "name": "user_group_memberships_group_id_usergroups_id_fk", + "tableFrom": "user_group_memberships", + "columnsFrom": ["group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_group_memberships_granted_by_user_id_users_id_fk": { + "name": "user_group_memberships_granted_by_user_id_users_id_fk", + "tableFrom": "user_group_memberships", + "columnsFrom": ["granted_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "user_group_memberships_previous_primary_group_id_usergroups_id_fk": { + "name": "user_group_memberships_previous_primary_group_id_usergroups_id_fk", + "tableFrom": "user_group_memberships", + "columnsFrom": ["previous_primary_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_identities": { + "name": "user_identities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_identities_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "linked_at": { + "name": "linked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_identities_provider_subject_key": { + "name": "user_identities_provider_subject_key", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_identities_user_idx": { + "name": "user_identities_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_identities_user_id_users_id_fk": { + "name": "user_identities_user_id_users_id_fk", + "tableFrom": "user_identities", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_relations": { + "name": "user_relations", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "other_user_id": { + "name": "other_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_relations_kind_idx": { + "name": "user_relations_kind_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "other_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_relations_reverse_idx": { + "name": "user_relations_reverse_idx", + "columns": [ + { + "expression": "other_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_relations_user_id_users_id_fk": { + "name": "user_relations_user_id_users_id_fk", + "tableFrom": "user_relations", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_relations_other_user_id_users_id_fk": { + "name": "user_relations_other_user_id_users_id_fk", + "tableFrom": "user_relations", + "columnsFrom": ["other_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "user_relations_pkey": { + "name": "user_relations_pkey", + "columns": ["user_id", "other_user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_two_factor": { + "name": "user_two_factor", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "sealed_secret": { + "name": "sealed_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_step": { + "name": "last_step", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_two_factor_user_id_users_id_fk": { + "name": "user_two_factor_user_id_users_id_fk", + "tableFrom": "user_two_factor", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usergroups": { + "name": "usergroups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "usergroups_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "badge_token": { + "name": "badge_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name_color_light": { + "name": "name_color_light", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name_color_dark": { + "name": "name_color_dark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "badge_image_light": { + "name": "badge_image_light", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "badge_image_dark": { + "name": "badge_image_dark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_staff_group": { + "name": "is_staff_group", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "plugin_grantable": { + "name": "plugin_grantable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view": { + "name": "can_view", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_threads": { + "name": "can_view_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_others_threads": { + "name": "can_view_others_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_search": { + "name": "can_search", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_post_threads": { + "name": "can_post_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_post_replies": { + "name": "can_post_replies", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_post_polls": { + "name": "can_post_polls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_vote_polls": { + "name": "can_vote_polls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_rate_threads": { + "name": "can_rate_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_edit_own_posts": { + "name": "can_edit_own_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_delete_own_posts": { + "name": "can_delete_own_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_delete_own_threads": { + "name": "can_delete_own_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_edit_others_posts": { + "name": "can_edit_others_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_soft_delete_posts": { + "name": "can_soft_delete_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_unapproved": { + "name": "can_view_unapproved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_deleted": { + "name": "can_view_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_approve_content": { + "name": "can_approve_content", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_upload_attachments": { + "name": "can_upload_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_download_attachments": { + "name": "can_download_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_subscribe": { + "name": "can_subscribe", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_profiles": { + "name": "can_view_profiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_member_list": { + "name": "can_view_member_list", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_use_private_messages": { + "name": "can_use_private_messages", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_use_signature": { + "name": "can_use_signature", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_upload_avatar": { + "name": "can_upload_avatar", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_report_content": { + "name": "can_report_content", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_view_board_offline": { + "name": "can_view_board_offline", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_bypass_flood_check": { + "name": "can_bypass_flood_check", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_access_mod_cp": { + "name": "can_access_mod_cp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_warn_users": { + "name": "can_warn_users", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_super_moderator": { + "name": "is_super_moderator", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_administrator": { + "name": "is_administrator", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_access_admin_cp": { + "name": "can_access_admin_cp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "max_posts_per_day": { + "name": "max_posts_per_day", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "edit_time_limit_minutes": { + "name": "edit_time_limit_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attachments_per_post": { + "name": "max_attachments_per_post", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attachment_size_kb": { + "name": "max_attachment_size_kb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_private_messages_per_day": { + "name": "max_private_messages_per_day", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "can_give_reputation": { + "name": "can_give_reputation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "max_reputation_per_day": { + "name": "max_reputation_per_day", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "private_message_quota": { + "name": "private_message_quota", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_signature_length": { + "name": "max_signature_length", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "requires_thread_approval": { + "name": "requires_thread_approval", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "requires_post_approval": { + "name": "requires_post_approval", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "requires_approval_on_edit": { + "name": "requires_approval_on_edit", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "usergroups_key_key": { + "name": "usergroups_key_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "usergroups_display_order_idx": { + "name": "usergroups_display_order_idx", + "columns": [ + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "users_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "username_lower": { + "name": "username_lower", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_lower": { + "name": "email_lower", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password_algo": { + "name": "password_algo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password_changed_at": { + "name": "password_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "primary_group_id": { + "name": "primary_group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "display_group_id": { + "name": "display_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "post_count": { + "name": "post_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "thread_count": { + "name": "thread_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reputation": { + "name": "reputation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "warning_points": { + "name": "warning_points", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "signature_html": { + "name": "signature_html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature_render_version": { + "name": "signature_render_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "signature_format": { + "name": "signature_format", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "signature_locked": { + "name": "signature_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "signature_locked_reason": { + "name": "signature_locked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_status": { + "name": "avatar_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "avatar_key": { + "name": "avatar_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_source_key": { + "name": "avatar_source_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_width": { + "name": "avatar_width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avatar_height": { + "name": "avatar_height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avatar_failure_reason": { + "name": "avatar_failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_updated_at": { + "name": "avatar_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "avatar_locked": { + "name": "avatar_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "avatar_locked_reason": { + "name": "avatar_locked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_ip_prefix": { + "name": "registration_ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_ip_prefix": { + "name": "last_ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email_verified_at": { + "name": "email_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "mass_mail_opt_in_at": { + "name": "mass_mail_opt_in_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "board_digest_cadence": { + "name": "board_digest_cadence", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'weekly'" + }, + "board_digest_sent_at": { + "name": "board_digest_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "suspended_posting_until": { + "name": "suspended_posting_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "moderated_posting_until": { + "name": "moderated_posting_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "posts_per_page": { + "name": "posts_per_page", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "threads_per_page": { + "name": "threads_per_page", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "invisible": { + "name": "invisible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_watch_own_threads": { + "name": "auto_watch_own_threads", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "auto_watch_replied_threads": { + "name": "auto_watch_replied_threads", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_username_lower_key": { + "name": "users_username_lower_key", + "columns": [ + { + "expression": "username_lower", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_email_lower_key": { + "name": "users_email_lower_key", + "columns": [ + { + "expression": "email_lower", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_legacy_id_key": { + "name": "users_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"users\".\"legacy_id\" is not null", + "concurrently": false + }, + "users_primary_group_idx": { + "name": "users_primary_group_idx", + "columns": [ + { + "expression": "primary_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_active_created_idx": { + "name": "users_active_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"users\".\"state\" = 'active'", + "concurrently": false + }, + "users_last_active_idx": { + "name": "users_last_active_idx", + "columns": [ + { + "expression": "last_active_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_signature_render_version_idx": { + "name": "users_signature_render_version_idx", + "columns": [ + { + "expression": "signature_render_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"users\".\"signature\" <> ''", + "concurrently": false + }, + "users_mass_mail_opt_in_idx": { + "name": "users_mass_mail_opt_in_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"users\".\"mass_mail_opt_in_at\" is not null", + "concurrently": false + }, + "users_board_digest_due_idx": { + "name": "users_board_digest_due_idx", + "columns": [ + { + "expression": "board_digest_cadence", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_active_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"users\".\"state\" = 'active'", + "concurrently": false + } + }, + "foreignKeys": { + "users_primary_group_id_usergroups_id_fk": { + "name": "users_primary_group_id_usergroups_id_fk", + "tableFrom": "users", + "columnsFrom": ["primary_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "users_display_group_id_usergroups_id_fk": { + "name": "users_display_group_id_usergroups_id_fk", + "tableFrom": "users", + "columnsFrom": ["display_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.warning_levels": { + "name": "warning_levels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "warning_levels_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "points": { + "name": "points", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "duration_days": { + "name": "duration_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "warning_levels_points_unique": { + "name": "warning_levels_points_unique", + "columns": [ + { + "expression": "points", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.warning_types": { + "name": "warning_types", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "warning_types_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "points": { + "name": "points", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "expiry_days": { + "name": "expiry_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "warning_types_order_idx": { + "name": "warning_types_order_idx", + "columns": [ + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.warnings": { + "name": "warnings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "warnings_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issued_by_user_id": { + "name": "issued_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "type_id": { + "name": "type_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "points": { + "name": "points", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "report_id": { + "name": "report_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_by_user_id": { + "name": "revoked_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "revoke_reason": { + "name": "revoke_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "warnings_user_idx": { + "name": "warnings_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "warnings_legacy_id_key": { + "name": "warnings_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"warnings\".\"legacy_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "warnings_user_id_users_id_fk": { + "name": "warnings_user_id_users_id_fk", + "tableFrom": "warnings", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "warnings_issued_by_user_id_users_id_fk": { + "name": "warnings_issued_by_user_id_users_id_fk", + "tableFrom": "warnings", + "columnsFrom": ["issued_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "warnings_type_id_warning_types_id_fk": { + "name": "warnings_type_id_warning_types_id_fk", + "tableFrom": "warnings", + "columnsFrom": ["type_id"], + "tableTo": "warning_types", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "warnings_revoked_by_user_id_users_id_fk": { + "name": "warnings_revoked_by_user_id_users_id_fk", + "tableFrom": "warnings", + "columnsFrom": ["revoked_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.private_message_copies": { + "name": "private_message_copies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "private_message_copies_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "message_id": { + "name": "message_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "folder": { + "name": "folder", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inbox'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'to'" + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "private_message_copies_folder_idx": { + "name": "private_message_copies_folder_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "folder", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "private_message_copies_unread_idx": { + "name": "private_message_copies_unread_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"private_message_copies\".\"read_at\" is null and \"private_message_copies\".\"folder\" = 'inbox'", + "concurrently": false + }, + "private_message_copies_message_idx": { + "name": "private_message_copies_message_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "private_message_copies_message_id_private_messages_id_fk": { + "name": "private_message_copies_message_id_private_messages_id_fk", + "tableFrom": "private_message_copies", + "columnsFrom": ["message_id"], + "tableTo": "private_messages", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "private_message_copies_owner_user_id_users_id_fk": { + "name": "private_message_copies_owner_user_id_users_id_fk", + "tableFrom": "private_message_copies", + "columnsFrom": ["owner_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "private_message_copies_unique": { + "name": "private_message_copies_unique", + "columns": ["message_id", "owner_user_id"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.private_messages": { + "name": "private_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "private_messages_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "author_user_id": { + "name": "author_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_username": { + "name": "author_username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_format": { + "name": "body_format", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "message_html": { + "name": "message_html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "render_version": { + "name": "render_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vocab_version": { + "name": "vocab_version", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reply_to_id": { + "name": "reply_to_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "receipt_requested": { + "name": "receipt_requested", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "private_messages_render_version_idx": { + "name": "private_messages_render_version_idx", + "columns": [ + { + "expression": "render_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "private_messages_vocab_version_idx": { + "name": "private_messages_vocab_version_idx", + "columns": [ + { + "expression": "vocab_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "private_messages_legacy_id_key": { + "name": "private_messages_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"private_messages\".\"legacy_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "private_messages_author_user_id_users_id_fk": { + "name": "private_messages_author_user_id_users_id_fk", + "tableFrom": "private_messages", + "columnsFrom": ["author_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "private_messages_reply_to_id_private_messages_id_fk": { + "name": "private_messages_reply_to_id_private_messages_id_fk", + "tableFrom": "private_messages", + "columnsFrom": ["reply_to_id"], + "tableTo": "private_messages", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin_log": { + "name": "admin_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "admin_log_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detail": { + "name": "detail", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "ip_prefix": { + "name": "ip_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "admin_log_user_idx": { + "name": "admin_log_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "admin_log_action_idx": { + "name": "admin_log_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "admin_log_forum_scope_idx": { + "name": "admin_log_forum_scope_idx", + "columns": [ + { + "expression": "(\"detail\" -> 'forumIds') jsonb_path_ops", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": { + "admin_log_user_id_users_id_fk": { + "name": "admin_log_user_id_users_id_fk", + "tableFrom": "admin_log", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin_undo_operations": { + "name": "admin_undo_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "admin_undo_operations_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "admin_undo_operations_token_key": { + "name": "admin_undo_operations_token_key", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "admin_undo_operations_live_idx": { + "name": "admin_undo_operations_live_idx", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "admin_undo_operations_actor_user_id_users_id_fk": { + "name": "admin_undo_operations_actor_user_id_users_id_fk", + "tableFrom": "admin_undo_operations", + "columnsFrom": ["actor_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup_runs": { + "name": "backup_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "backup_runs_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "requested_by_user_id": { + "name": "requested_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "bundle_name": { + "name": "bundle_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "uploads": { + "name": "uploads", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shipped": { + "name": "shipped", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skipped_keys": { + "name": "skipped_keys", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "backup_runs_status_idx": { + "name": "backup_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "backup_runs_recent_idx": { + "name": "backup_runs_recent_idx", + "columns": [ + { + "expression": "requested_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "backup_runs_active_idx": { + "name": "backup_runs_active_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"backup_runs\".\"status\" in ('queued', 'running')", + "concurrently": false + } + }, + "foreignKeys": { + "backup_runs_requested_by_user_id_users_id_fk": { + "name": "backup_runs_requested_by_user_id_users_id_fk", + "tableFrom": "backup_runs", + "columnsFrom": ["requested_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "backup_runs_trigger_check": { + "name": "backup_runs_trigger_check", + "value": "\"backup_runs\".\"trigger\" in ('manual', 'schedule', 'upgrade', 'cli')" + }, + "backup_runs_status_check": { + "name": "backup_runs_status_check", + "value": "\"backup_runs\".\"status\" in ('queued', 'running', 'done', 'incomplete', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.board_stats": { + "name": "board_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "smallint", + "primaryKey": true, + "notNull": true, + "default": 1 + }, + "thread_count": { + "name": "thread_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "post_count": { + "name": "post_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "member_count": { + "name": "member_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "newest_user_id": { + "name": "newest_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "newest_username": { + "name": "newest_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "most_online_count": { + "name": "most_online_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "most_online_at": { + "name": "most_online_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "computed_at": { + "name": "computed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "board_stats_singleton": { + "name": "board_stats_singleton", + "value": "\"board_stats\".\"id\" = 1" + } + }, + "isRLSEnabled": false + }, + "public.cache_versions": { + "name": "cache_versions", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "bumped_at": { + "name": "bumped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.captcha_questions": { + "name": "captcha_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "captcha_questions_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "answers": { + "name": "answers", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.content_counter_rollups": { + "name": "content_counter_rollups", + "schema": "", + "columns": { + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.counter_recount_state": { + "name": "counter_recount_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'threads'" + }, + "cursor": { + "name": "cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "passes": { + "name": "passes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "corrected": { + "name": "corrected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jobs": { + "name": "jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "jobs_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "run_at": { + "name": "run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "attempts": { + "name": "attempts", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "locked_by": { + "name": "locked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "jobs_pending_idx": { + "name": "jobs_pending_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"jobs\".\"status\" = 'pending'", + "concurrently": false + }, + "jobs_idempotency_key": { + "name": "jobs_idempotency_key", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"jobs\".\"idempotency_key\" is not null", + "concurrently": false + }, + "jobs_status_idx": { + "name": "jobs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.marketplace_catalog": { + "name": "marketplace_catalog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "smallint", + "primaryKey": true, + "notNull": true, + "default": 1 + }, + "feed": { + "name": "feed", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fetched_at": { + "name": "fetched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_at": { + "name": "error_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "notified_updates": { + "name": "notified_updates", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "marketplace_catalog_single_row": { + "name": "marketplace_catalog_single_row", + "value": "\"marketplace_catalog\".\"id\" = 1" + } + }, + "isRLSEnabled": false + }, + "public.mass_mails": { + "name": "mass_mails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "mass_mails_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_group_id": { + "name": "target_group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_user_id": { + "name": "last_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "queued_count": { + "name": "queued_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sending'" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mass_mails_target_group_id_usergroups_id_fk": { + "name": "mass_mails_target_group_id_usergroups_id_fk", + "tableFrom": "mass_mails", + "columnsFrom": ["target_group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "mass_mails_created_by_user_id_users_id_fk": { + "name": "mass_mails_created_by_user_id_users_id_fk", + "tableFrom": "mass_mails", + "columnsFrom": ["created_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.navigation_item_groups": { + "name": "navigation_item_groups", + "schema": "", + "columns": { + "item_id": { + "name": "item_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "navigation_item_groups_group_idx": { + "name": "navigation_item_groups_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "navigation_item_groups_item_id_navigation_items_id_fk": { + "name": "navigation_item_groups_item_id_navigation_items_id_fk", + "tableFrom": "navigation_item_groups", + "columnsFrom": ["item_id"], + "tableTo": "navigation_items", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "navigation_item_groups_group_id_usergroups_id_fk": { + "name": "navigation_item_groups_group_id_usergroups_id_fk", + "tableFrom": "navigation_item_groups", + "columnsFrom": ["group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "navigation_item_groups_item_id_group_id_pk": { + "name": "navigation_item_groups_item_id_group_id_pk", + "columns": ["item_id", "group_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.navigation_items": { + "name": "navigation_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "navigation_items_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "href": { + "name": "href", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "new_tab": { + "name": "new_tab", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "navigation_items_key_key": { + "name": "navigation_items_key_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"navigation_items\".\"key\" is not null", + "concurrently": false + }, + "navigation_items_order_idx": { + "name": "navigation_items_order_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "navigation_items_parent_id_fk": { + "name": "navigation_items_parent_id_fk", + "tableFrom": "navigation_items", + "columnsFrom": ["parent_id"], + "tableTo": "navigation_items", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "navigation_items_audience_check": { + "name": "navigation_items_audience_check", + "value": "\"navigation_items\".\"audience\" in ('all', 'guests', 'members', 'staff')" + } + }, + "isRLSEnabled": false + }, + "public.outbox": { + "name": "outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "outbox_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claim_token": { + "name": "claim_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "outbox_pending_idx": { + "name": "outbox_pending_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"outbox\".\"dispatched_at\" is null", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plugin_health": { + "name": "plugin_health", + "schema": "", + "columns": { + "plugin_key": { + "name": "plugin_key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "failures": { + "name": "failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.promotion_scan_state": { + "name": "promotion_scan_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "cursor": { + "name": "cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "passes": { + "name": "passes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limits": { + "name": "rate_limits", + "schema": "", + "columns": { + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_start": { + "name": "window_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "rate_limits_window_idx": { + "name": "rate_limits_window_idx", + "columns": [ + { + "expression": "window_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "rate_limits_scope_subject_window_start_pk": { + "name": "rate_limits_scope_subject_window_start_pk", + "columns": ["scope", "subject", "window_start"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.searches": { + "name": "searches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "searches_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_key": { + "name": "session_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terms": { + "name": "terms", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filters": { + "name": "filters", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "hit_count": { + "name": "hit_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "searches_token_key": { + "name": "searches_token_key", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "searches_user_recent_idx": { + "name": "searches_user_recent_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "searches_session_recent_idx": { + "name": "searches_session_recent_idx", + "columns": [ + { + "expression": "session_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "searches_user_id_users_id_fk": { + "name": "searches_user_id_users_id_fk", + "tableFrom": "searches", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.setting_groups": { + "name": "setting_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "setting_groups_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "setting_groups_key_key": { + "name": "setting_groups_key_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_key": { + "name": "group_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "settings_group_idx": { + "name": "settings_group_idx", + "columns": [ + { + "expression": "group_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "settings_updated_by_user_id_users_id_fk": { + "name": "settings_updated_by_user_id_users_id_fk", + "tableFrom": "settings", + "columnsFrom": ["updated_by_user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.task_log": { + "name": "task_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "task_log_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "task_key": { + "name": "task_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "succeeded": { + "name": "succeeded", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ran_at": { + "name": "ran_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "task_log_key_ran_idx": { + "name": "task_log_key_ran_idx", + "columns": [ + { + "expression": "task_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ran_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "interval_seconds": { + "name": "interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.themes": { + "name": "themes", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_overrides": { + "name": "token_overrides", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "branding": { + "name": "branding", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "layout_options": { + "name": "layout_options", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "custom_css": { + "name": "custom_css", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "themes_single_default_key": { + "name": "themes_single_default_key", + "columns": [ + { + "expression": "is_default", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"themes\".\"is_default\"", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_moderators": { + "name": "forum_moderators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "forum_moderators_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cascade_to_subforums": { + "name": "cascade_to_subforums", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_edit_posts": { + "name": "can_edit_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_soft_delete_posts": { + "name": "can_soft_delete_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_restore_posts": { + "name": "can_restore_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_approve_content": { + "name": "can_approve_content", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_open_close_threads": { + "name": "can_open_close_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_stick_threads": { + "name": "can_stick_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_move_threads": { + "name": "can_move_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_merge_threads": { + "name": "can_merge_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "can_split_threads": { + "name": "can_split_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forum_moderators_user_key": { + "name": "forum_moderators_user_key", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"forum_moderators\".\"user_id\" is not null", + "concurrently": false + }, + "forum_moderators_group_key": { + "name": "forum_moderators_group_key", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"forum_moderators\".\"group_id\" is not null", + "concurrently": false + }, + "forum_moderators_user_idx": { + "name": "forum_moderators_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forum_moderators_group_idx": { + "name": "forum_moderators_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forum_moderators_forum_id_forums_id_fk": { + "name": "forum_moderators_forum_id_forums_id_fk", + "tableFrom": "forum_moderators", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_moderators_user_id_users_id_fk": { + "name": "forum_moderators_user_id_users_id_fk", + "tableFrom": "forum_moderators", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_moderators_group_id_usergroups_id_fk": { + "name": "forum_moderators_group_id_usergroups_id_fk", + "tableFrom": "forum_moderators", + "columnsFrom": ["group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_permissions": { + "name": "forum_permissions", + "schema": "", + "columns": { + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "can_view": { + "name": "can_view", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_view_threads": { + "name": "can_view_threads", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_view_others_threads": { + "name": "can_view_others_threads", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_search": { + "name": "can_search", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_post_threads": { + "name": "can_post_threads", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_post_replies": { + "name": "can_post_replies", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_post_polls": { + "name": "can_post_polls", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_vote_polls": { + "name": "can_vote_polls", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_rate_threads": { + "name": "can_rate_threads", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_edit_own_posts": { + "name": "can_edit_own_posts", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_delete_own_posts": { + "name": "can_delete_own_posts", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_delete_own_threads": { + "name": "can_delete_own_threads", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_edit_others_posts": { + "name": "can_edit_others_posts", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_soft_delete_posts": { + "name": "can_soft_delete_posts", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_view_unapproved": { + "name": "can_view_unapproved", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_view_deleted": { + "name": "can_view_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_approve_content": { + "name": "can_approve_content", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_upload_attachments": { + "name": "can_upload_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_download_attachments": { + "name": "can_download_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "can_subscribe": { + "name": "can_subscribe", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "edit_time_limit_minutes": { + "name": "edit_time_limit_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_attachments_per_post": { + "name": "max_attachments_per_post", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_attachment_size_kb": { + "name": "max_attachment_size_kb", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "requires_thread_approval": { + "name": "requires_thread_approval", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "requires_post_approval": { + "name": "requires_post_approval", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "requires_approval_on_edit": { + "name": "requires_approval_on_edit", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forum_permissions_pkey": { + "name": "forum_permissions_pkey", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forum_permissions_group_idx": { + "name": "forum_permissions_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forum_permissions_forum_id_forums_id_fk": { + "name": "forum_permissions_forum_id_forums_id_fk", + "tableFrom": "forum_permissions", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_permissions_group_id_usergroups_id_fk": { + "name": "forum_permissions_group_id_usergroups_id_fk", + "tableFrom": "forum_permissions", + "columnsFrom": ["group_id"], + "tableTo": "usergroups", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_subscriptions": { + "name": "forum_subscriptions", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'instant'" + }, + "last_notified_post_id": { + "name": "last_notified_post_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forum_subscriptions_pkey": { + "name": "forum_subscriptions_pkey", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forum_subscriptions_forum_idx": { + "name": "forum_subscriptions_forum_idx", + "columns": [ + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forum_subscriptions_user_idx": { + "name": "forum_subscriptions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forum_subscriptions_mode_idx": { + "name": "forum_subscriptions_mode_idx", + "columns": [ + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forum_subscriptions_user_id_users_id_fk": { + "name": "forum_subscriptions_user_id_users_id_fk", + "tableFrom": "forum_subscriptions", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_subscriptions_forum_id_forums_id_fk": { + "name": "forum_subscriptions_forum_id_forums_id_fk", + "tableFrom": "forum_subscriptions", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forums": { + "name": "forums", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "forums_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'forum'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "depth": { + "name": "depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "link_url": { + "name": "link_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "link_hits": { + "name": "link_hits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_open": { + "name": "is_open", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_threads": { + "name": "allow_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_replies": { + "name": "allow_replies", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_polls": { + "name": "allow_polls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_attachments": { + "name": "allow_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "requires_prefix": { + "name": "requires_prefix", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "moderate_new_threads": { + "name": "moderate_new_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "moderate_new_posts": { + "name": "moderate_new_posts", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thread_count": { + "name": "thread_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "post_count": { + "name": "post_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_post_id": { + "name": "last_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_post_thread_id": { + "name": "last_post_thread_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_post_thread_title": { + "name": "last_post_thread_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_post_user_id": { + "name": "last_post_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_post_username": { + "name": "last_post_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_post_at": { + "name": "last_post_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "legacy_id": { + "name": "legacy_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forums_parent_slug_key": { + "name": "forums_parent_slug_key", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"forums\".\"parent_id\" is not null", + "concurrently": false + }, + "forums_root_slug_key": { + "name": "forums_root_slug_key", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"forums\".\"parent_id\" is null", + "concurrently": false + }, + "forums_parent_order_idx": { + "name": "forums_parent_order_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forums_path_idx": { + "name": "forums_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "forums_legacy_id_key": { + "name": "forums_legacy_id_key", + "columns": [ + { + "expression": "legacy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"forums\".\"legacy_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forums_read": { + "name": "forums_read", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "forum_id": { + "name": "forum_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forums_read_pkey": { + "name": "forums_read_pkey", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "forum_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forums_read_user_id_users_id_fk": { + "name": "forums_read_user_id_users_id_fk", + "tableFrom": "forums_read", + "columnsFrom": ["user_id"], + "tableTo": "users", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forums_read_forum_id_forums_id_fk": { + "name": "forums_read_forum_id_forums_id_fk", + "tableFrom": "forums_read", + "columnsFrom": ["forum_id"], + "tableTo": "forums", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index d2207022..5c6ce45e 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -512,6 +512,13 @@ "when": 1793377160357, "tag": "0072_job_idempotency", "breakpoints": true + }, + { + "idx": 73, + "version": "7", + "when": 1793453353033, + "tag": "0073_reply_draft_forums", + "breakpoints": true } ] } diff --git a/packages/db/src/client.pg.test.ts b/packages/db/src/client.pg.test.ts index d8b8fc35..69fa998f 100644 --- a/packages/db/src/client.pg.test.ts +++ b/packages/db/src/client.pg.test.ts @@ -1,5 +1,6 @@ import { readFileSync } from 'node:fs' import path from 'node:path' +import { setTimeout as delay } from 'node:timers/promises' import { fileURLToPath } from 'node:url' import { sql } from 'drizzle-orm' @@ -9,6 +10,7 @@ import { isScope } from '@meith/api' import { PostgresApiTokenRepository } from './api-repo' import { createIsolatedDb, schema } from './client' +import { PostgresDraftRepository } from './draft-repo' import { PostgresReputationRepository } from './reputation-repo' import { resultRows } from './result-rows' @@ -195,6 +197,51 @@ describeIfPg('against real Postgres', () => { } }) + it('serializes an autosave from an old composer with a forum move', async () => { + await harness.db.execute(sql`insert into users (id, username, username_lower, email, email_lower, primary_group_id) + values (1403, 'draft-owner', 'draft-owner', 'draft@example.test', 'draft@example.test', 2)`) + await harness.db.execute(sql`insert into forums (id, title, slug, path) + values (1400, 'Before', 'before', '1400'), (1401, 'After', 'after', '1401')`) + await harness.db.execute(sql`insert into threads (id, forum_id, title, slug, author_username) + values (1402, 1400, 'Moving', 'moving', 'draft-owner')`) + const other = createIsolatedDb(scratchUrl(URL!)) + let saving: Promise | undefined + try { + const [connection] = await other.sql<{ pid: number }[]>`select pg_backend_pid() as pid` + const draft = { + forumId: 1400, + threadId: 1402, + title: '', + message: 'Preserved', + prefixId: null, + } + await new PostgresDraftRepository(harness.db).save(1403, draft) + await harness.db.transaction(async (tx) => { + await tx.execute(sql`select id from threads where id = 1402 for update`) + saving = new PostgresDraftRepository(other.db).save(1403, draft) + let blockers: unknown[] = [] + for (let attempt = 0; attempt < 100 && blockers.length === 0; attempt++) { + blockers = resultRows<{ blockers: unknown[] }>( + await tx.execute(sql`select pg_blocking_pids(${connection!.pid}) as blockers`), + )[0]!.blockers + if (blockers.length === 0) await delay(25) + } + expect(blockers.length).toBeGreaterThan(0) + await tx.execute(sql`update threads set forum_id = 1401 where id = 1402`) + await tx.execute(sql`update post_drafts set forum_id = 1401 where thread_id = 1402`) + }) + await saving + await harness.db.execute(sql`delete from forums where id = 1400`) + expect(await new PostgresDraftRepository(harness.db).find(1403, 1401, 1402)).toMatchObject({ + forumId: 1401, + message: 'Preserved', + }) + } finally { + await saving?.catch(() => {}) + await other.close() + } + }) + describe('the date serialiser getDb() and createIsolatedDb() install', () => { it('binds a Date into a timestamp column through drizzle and reads it back as a Date', async () => { const runAt = new Date('2026-07-31T12:34:56.000Z') diff --git a/packages/db/src/draft-repo.test.ts b/packages/db/src/draft-repo.test.ts index 867114da..ceff37ea 100644 --- a/packages/db/src/draft-repo.test.ts +++ b/packages/db/src/draft-repo.test.ts @@ -1,3 +1,5 @@ +import { readFileSync } from 'node:fs' + import { sql } from 'drizzle-orm' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' @@ -7,6 +9,7 @@ import type { Database } from './client' import { PostgresDraftRepository } from './draft-repo' import { createTestDb, type TestDb } from './pglite.fixture' import { resultRows } from './result-rows' +import { PostgresThreadToolsRepository } from './thread-tools' let harness: TestDb let db: Database @@ -196,3 +199,84 @@ describe('draft target confidentiality', () => { expect(await drafts.listByUser(901, unrestrictedAudience([], 901))).toEqual([]) }) }) + +describe('reply drafts across forum moves', () => { + const reply = { forumId: 900, threadId: 950, title: '', message: 'Keep my reply', prefixId: null } + beforeEach(async () => { + await db.execute(sql`delete from forums where id = 999`) + await db.execute( + sql`insert into forums (id, title, slug, path) values (999, 'Destination', 'destination', '999')`, + ) + await db.execute( + sql`insert into threads (id, forum_id, title, slug, author_user_id, author_username) values (950, 900, 'Moved', 'moved', 901, 'one')`, + ) + await drafts.save(901, reply) + }) + + it('finds and removes a legacy reply draft using its thread identity', async () => { + await db.execute(sql`update threads set forum_id = 999 where id = 950`) + expect(await drafts.find(901, 999, 950)).toMatchObject({ + forumId: 999, + message: 'Keep my reply', + }) + expect(await drafts.listByUser(901, unrestrictedAudience([999], 901))).toMatchObject([ + { forumId: 999 }, + ]) + await drafts.remove(902, 999, 950) + expect(await drafts.find(901, 999, 950)).not.toBeNull() + await drafts.remove(901, 999, 950) + expect(await drafts.find(901, 900, 950)).toBeNull() + }) + + it('saves with the locked current forum even when the composer supplies an old forum', async () => { + await db.execute(sql`update threads set forum_id = 999 where id = 950`) + await drafts.save(901, { ...reply, message: 'Updated reply' }) + expect( + resultRows(await db.execute(sql`select forum_id from post_drafts where thread_id = 950`)), + ).toEqual([{ forum_id: 999 }]) + await db.execute(sql`delete from forums where id = 900`) + expect(await drafts.find(901, 999, 950)).toMatchObject({ message: 'Updated reply' }) + }) + + it('moves stored references before the old forum can delete an untouched reply draft', async () => { + const tools = new PostgresThreadToolsRepository(db) + expect( + await tools.move({ + threadId: 950, + fromForumId: 900, + toForumId: 999, + actorUserId: 901, + at: new Date(), + }), + ).toBe(true) + await db.execute(sql`delete from forums where id = 900`) + expect(await drafts.find(901, 999, 950)).toMatchObject({ + message: 'Keep my reply', + forumId: 999, + }) + }) + + it('repairs existing references without changing saved content or timestamps', async () => { + await db.execute(sql`update threads set forum_id = 999 where id = 950`) + const before = await drafts.find(901, 999, 950) + const migration = readFileSync( + new URL('../migrations/0073_reply_draft_forums.sql', import.meta.url), + 'utf8', + ) + await db.execute(sql.raw(migration)) + expect( + resultRows(await db.execute(sql`select forum_id from post_drafts where thread_id = 950`)), + ).toEqual([{ forum_id: 999 }]) + expect(await drafts.find(901, 999, 950)).toEqual(before) + await db.execute(sql`delete from forums where id = 900`) + expect(await drafts.find(901, 999, 950)).toEqual(before) + }) + + it('keeps new-thread drafts scoped to their forum', async () => { + await drafts.save(901, { ...reply, threadId: null, message: 'Original forum' }) + await drafts.save(901, { ...reply, forumId: 999, threadId: null, message: 'Other forum' }) + await drafts.remove(901, 999, null) + expect(await drafts.find(901, 900, null)).toMatchObject({ message: 'Original forum' }) + expect(await drafts.find(901, 999, null)).toBeNull() + }) +}) diff --git a/packages/db/src/draft-repo.ts b/packages/db/src/draft-repo.ts index f8bca2a1..7d5c7750 100644 --- a/packages/db/src/draft-repo.ts +++ b/packages/db/src/draft-repo.ts @@ -1,7 +1,8 @@ import { sql } from 'drizzle-orm' -import { PUBLIC_CONTENT, type ThreadAudience } from '@meith/core' +import { PUBLIC_CONTENT, type ThreadAudience, ValidationError } from '@meith/core' import type { Draft, DraftRepository, DraftSummary } from '@meith/drafts' +import { msg } from '@meith/i18n' import { BodyFormat, sourceAsMarkdown } from '@meith/markdown' import type { Database } from './client' @@ -15,9 +16,11 @@ export class PostgresDraftRepository implements DraftRepository { async find(userId: number, forumId: number, threadId: number | null): Promise { const rows = resultRows( await this.db.execute(sql` - select forum_id, thread_id, title, message, body_format, prefix_id, updated_at from post_drafts - where user_id = ${userId} and forum_id = ${forumId} - and thread_id is not distinct from ${threadId} + select coalesce(t.forum_id, d.forum_id) as forum_id, d.thread_id, + d.title, d.message, d.body_format, d.prefix_id, d.updated_at + from post_drafts d left join threads t on t.id = d.thread_id + where d.user_id = ${userId} + and ${threadId === null ? sql`d.forum_id = ${forumId} and d.thread_id is null` : sql`d.thread_id = ${threadId}`} `), ) as Array<{ forum_id: number @@ -42,28 +45,40 @@ export class PostgresDraftRepository implements DraftRepository { } async save(userId: number, draft: Draft): Promise { - await this.db.execute(sql` - insert into post_drafts (user_id, forum_id, thread_id, title, message, body_format, prefix_id) - values (${userId}, ${draft.forumId}, ${draft.threadId}, ${draft.title}, ${draft.message}, ${BodyFormat.Markdown}, ${draft.prefixId}) - on conflict (${sql.raw(draft.threadId === null ? 'user_id, forum_id' : 'user_id, thread_id')}) - where ${sql.raw(draft.threadId === null ? 'thread_id is null' : 'thread_id is not null')} - do update set title = excluded.title, message = excluded.message, - body_format = excluded.body_format, prefix_id = excluded.prefix_id, - updated_at = now() - `) + await this.db.transaction(async (tx) => { + let forumId = draft.forumId + if (draft.threadId !== null) { + const rows = resultRows<{ forum_id: number }>( + await tx.execute( + sql`select forum_id from threads where id = ${draft.threadId} for update`, + ), + ) + if (rows[0] === undefined) throw new ValidationError(msg('error.db.thread-exist')) + forumId = Number(rows[0].forum_id) + } + await tx.execute(sql` + insert into post_drafts (user_id, forum_id, thread_id, title, message, body_format, prefix_id) + values (${userId}, ${forumId}, ${draft.threadId}, ${draft.title}, ${draft.message}, ${BodyFormat.Markdown}, ${draft.prefixId}) + on conflict (${sql.raw(draft.threadId === null ? 'user_id, forum_id' : 'user_id, thread_id')}) + where ${sql.raw(draft.threadId === null ? 'thread_id is null' : 'thread_id is not null')} + do update set forum_id = excluded.forum_id, title = excluded.title, message = excluded.message, + body_format = excluded.body_format, prefix_id = excluded.prefix_id, + updated_at = now() + `) + }) } async remove(userId: number, forumId: number, threadId: number | null): Promise { await this.db.execute(sql` - delete from post_drafts where user_id = ${userId} and forum_id = ${forumId} - and thread_id is not distinct from ${threadId} + delete from post_drafts where user_id = ${userId} + and ${threadId === null ? sql`forum_id = ${forumId} and thread_id is null` : sql`thread_id = ${threadId}`} `) } async listByUser(userId: number, audience: ThreadAudience): Promise { const rows = resultRows( await this.db.execute(sql` - select d.forum_id, f.title as forum_title, f.slug as forum_slug, + select coalesce(t.forum_id, d.forum_id) as forum_id, f.title as forum_title, f.slug as forum_slug, d.thread_id, t.title as thread_title, t.slug as thread_slug, d.title, d.message, d.body_format, d.updated_at from post_drafts d diff --git a/packages/db/src/thread-tools.ts b/packages/db/src/thread-tools.ts index 29c54a34..a4c7659e 100644 --- a/packages/db/src/thread-tools.ts +++ b/packages/db/src/thread-tools.ts @@ -192,6 +192,9 @@ export class PostgresThreadToolsRepository implements ThreadToolsRepository { await tx.execute(sql` update posts set forum_id = ${input.toForumId} where thread_id = ${input.threadId} `) + await tx.execute(sql` + update post_drafts set forum_id = ${input.toForumId} where thread_id = ${input.threadId} + `) await tx.execute(sql` update attachments set forum_id = ${input.toForumId} where post_id in (select id from posts where thread_id = ${input.threadId})