From 56abcc4f0be6acc10a7158dd5987d8a10c3c6bca Mon Sep 17 00:00:00 2001 From: Sherif Lawal Date: Wed, 6 May 2026 19:15:26 +0100 Subject: [PATCH 1/4] add migration 0015: chat_messages object_uri and bookmark uniqueness indexes --- api/drizzle/0015_lethal_master_mold.sql | 4 + api/drizzle/meta/0015_snapshot.json | 2334 +++++++++++++++++++++++ api/drizzle/meta/_journal.json | 9 +- 3 files changed, 2346 insertions(+), 1 deletion(-) create mode 100644 api/drizzle/0015_lethal_master_mold.sql create mode 100644 api/drizzle/meta/0015_snapshot.json diff --git a/api/drizzle/0015_lethal_master_mold.sql b/api/drizzle/0015_lethal_master_mold.sql new file mode 100644 index 000000000..bc35cec81 --- /dev/null +++ b/api/drizzle/0015_lethal_master_mold.sql @@ -0,0 +1,4 @@ +ALTER TABLE "chat_messages" ADD COLUMN "object_uri" text;--> statement-breakpoint +CREATE UNIQUE INDEX "bookmarks_user_at_uri_unique_idx" ON "bookmarks" USING btree ("user_id","at_uri") WHERE "bookmarks"."at_uri" IS NOT NULL;--> statement-breakpoint +CREATE UNIQUE INDEX "bookmarks_user_object_uri_unique_idx" ON "bookmarks" USING btree ("user_id","object_uri") WHERE "bookmarks"."object_uri" IS NOT NULL;--> statement-breakpoint +CREATE UNIQUE INDEX "chat_messages_object_uri_unique_idx" ON "chat_messages" USING btree ("object_uri") WHERE "chat_messages"."object_uri" IS NOT NULL; \ No newline at end of file diff --git a/api/drizzle/meta/0015_snapshot.json b/api/drizzle/meta/0015_snapshot.json new file mode 100644 index 000000000..2374bec70 --- /dev/null +++ b/api/drizzle/meta/0015_snapshot.json @@ -0,0 +1,2334 @@ +{ + "id": "2643e129-d280-4491-84cf-21ff9233198a", + "prevId": "482e391e-af9e-4580-9e18-34df14d57e6a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "at_uri": { + "name": "at_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "bookmarked_at": { + "name": "bookmarked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bookmarks_user_at_uri_idx": { + "name": "bookmarks_user_at_uri_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "at_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_object_uri_idx": { + "name": "bookmarks_user_object_uri_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_at_uri_unique_idx": { + "name": "bookmarks_user_at_uri_unique_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "at_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"bookmarks\".\"at_uri\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_object_uri_unique_idx": { + "name": "bookmarks_user_object_uri_unique_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "object_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"bookmarks\".\"object_uri\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bookmarks_user_id_users_id_fk": { + "name": "bookmarks_user_id_users_id_fk", + "tableFrom": "bookmarks", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversation_members": { + "name": "conversation_members", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "conversation_members_conversation_id_conversations_id_fk": { + "name": "conversation_members_conversation_id_conversations_id_fk", + "tableFrom": "conversation_members", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "conversation_members_user_id_users_id_fk": { + "name": "conversation_members_user_id_users_id_fk", + "tableFrom": "conversation_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversations": { + "name": "conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "conversations_user_id_users_id_fk": { + "name": "conversations_user_id_users_id_fk", + "tableFrom": "conversations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sender_id": { + "name": "sender_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "messages_conversation_id_conversations_id_fk": { + "name": "messages_conversation_id_conversations_id_fk", + "tableFrom": "messages", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_sender_id_users_id_fk": { + "name": "messages_sender_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "sender_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "delivery_hash": { + "name": "delivery_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "activity_type": { + "name": "activity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_uri": { + "name": "actor_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_uri": { + "name": "target_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "published_at": { + "name": "published_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()" + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "notifications_delivery_hash_unique": { + "name": "notifications_delivery_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "delivery_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts": { + "name": "posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "post_type": { + "name": "post_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'note'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "posts_author_id_users_id_fk": { + "name": "posts_author_id_users_id_fk", + "tableFrom": "posts", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "web_id": { + "name": "web_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_endpoint": { + "name": "provider_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "atproto_did": { + "name": "atproto_did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "atproto_handle": { + "name": "atproto_handle", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "pod_token": { + "name": "pod_token", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_web_id_unique": { + "name": "users_web_id_unique", + "nullsNotDistinct": false, + "columns": [ + "web_id" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.viewer_relationship_cache": { + "name": "viewer_relationship_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "viewer_user_id": { + "name": "viewer_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "subject_uri": { + "name": "subject_uri", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "subject_protocol": { + "name": "subject_protocol", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'activitypods'" + }, + "is_following": { + "name": "is_following", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_followed_by": { + "name": "is_followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_muted": { + "name": "is_muted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_blocked": { + "name": "is_blocked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_blocked_by": { + "name": "is_blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "refreshed_at": { + "name": "refreshed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "viewer_rel_cache_viewer_subject_idx": { + "name": "viewer_rel_cache_viewer_subject_idx", + "columns": [ + { + "expression": "viewer_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "viewer_relationship_cache_viewer_user_id_users_id_fk": { + "name": "viewer_relationship_cache_viewer_user_id_users_id_fk", + "tableFrom": "viewer_relationship_cache", + "tableTo": "users", + "columnsFrom": [ + "viewer_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ap_actor_cache": { + "name": "ap_actor_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "actor_uri": { + "name": "actor_uri", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "preferred_username": { + "name": "preferred_username", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(640)", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "banner_url": { + "name": "banner_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domain": { + "name": "domain", + "type": "varchar(253)", + "primaryKey": false, + "notNull": false + }, + "followers_count": { + "name": "followers_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "following_count": { + "name": "following_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_count": { + "name": "posts_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cached_at": { + "name": "cached_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ap_actor_cache_actor_uri_unique": { + "name": "ap_actor_cache_actor_uri_unique", + "nullsNotDistinct": false, + "columns": [ + "actor_uri" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ap_remote_posts": { + "name": "ap_remote_posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "object_uri": { + "name": "object_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": true + }, + "author_web_id": { + "name": "author_web_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "author_name": { + "name": "author_name", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "author_domain": { + "name": "author_domain", + "type": "varchar(253)", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "post_type": { + "name": "post_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'note'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "ARRAY[]::text[]" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ingested_at": { + "name": "ingested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "source_relay": { + "name": "source_relay", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "quoted_post_uri": { + "name": "quoted_post_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ap_remote_posts_object_uri_unique": { + "name": "ap_remote_posts_object_uri_unique", + "nullsNotDistinct": false, + "columns": [ + "object_uri" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.at_firehose_cursors": { + "name": "at_firehose_cursors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'relay'" + }, + "committed_seq": { + "name": "committed_seq", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hot_seq": { + "name": "hot_seq", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_connected": { + "name": "is_connected", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_event_at": { + "name": "last_event_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_commit_at": { + "name": "last_commit_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "at_firehose_cursors_source_id_unique": { + "name": "at_firehose_cursors_source_id_unique", + "nullsNotDistinct": false, + "columns": [ + "source_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.at_identities": { + "name": "at_identities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "did": { + "name": "did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "handle": { + "name": "handle", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(640)", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "banner_url": { + "name": "banner_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "followers_count": { + "name": "followers_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "follows_count": { + "name": "follows_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_count": { + "name": "posts_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "did_document": { + "name": "did_document", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "local_user_id": { + "name": "local_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "at_identities_local_user_id_users_id_fk": { + "name": "at_identities_local_user_id_users_id_fk", + "tableFrom": "at_identities", + "tableTo": "users", + "columnsFrom": [ + "local_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "at_identities_did_unique": { + "name": "at_identities_did_unique", + "nullsNotDistinct": false, + "columns": [ + "did" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.at_posts": { + "name": "at_posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "author_did": { + "name": "author_did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "rkey": { + "name": "rkey", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "at_uri": { + "name": "at_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": true + }, + "cid": { + "name": "cid", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "post_type": { + "name": "post_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'note'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "facets": { + "name": "facets", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "embeds": { + "name": "embeds", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ingested_at": { + "name": "ingested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "source_relay": { + "name": "source_relay", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "firehose_seq": { + "name": "firehose_seq", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "at_posts_at_uri_unique": { + "name": "at_posts_at_uri_unique", + "nullsNotDistinct": false, + "columns": [ + "at_uri" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.at_records": { + "name": "at_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "author_did": { + "name": "author_did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "collection": { + "name": "collection", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "rkey": { + "name": "rkey", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "at_uri": { + "name": "at_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": true + }, + "cid": { + "name": "cid", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'create'" + }, + "record": { + "name": "record", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ingested_at": { + "name": "ingested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "source_relay": { + "name": "source_relay", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "firehose_seq": { + "name": "firehose_seq", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "at_records_at_uri_unique": { + "name": "at_records_at_uri_unique", + "nullsNotDistinct": false, + "columns": [ + "at_uri" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_edges": { + "name": "thread_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "item_source": { + "name": "item_source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "item_local_post_id": { + "name": "item_local_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "item_at_post_id": { + "name": "item_at_post_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "item_uri": { + "name": "item_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": true + }, + "reply_author_id": { + "name": "reply_author_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "parent_uri": { + "name": "parent_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "parent_author_id": { + "name": "parent_author_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "root_uri": { + "name": "root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "root_author_id": { + "name": "root_author_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_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": { + "thread_edges_item_uri_unique": { + "name": "thread_edges_item_uri_unique", + "nullsNotDistinct": false, + "columns": [ + "item_uri" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_participants": { + "name": "thread_participants", + "schema": "", + "columns": { + "root_uri": { + "name": "root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": true + }, + "participant_actor_id": { + "name": "participant_actor_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_reply_at": { + "name": "first_reply_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reply_at": { + "name": "last_reply_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_stats": { + "name": "thread_stats", + "schema": "", + "columns": { + "root_uri": { + "name": "root_uri", + "type": "varchar(3072)", + "primaryKey": true, + "notNull": true + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participant_count": { + "name": "participant_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_activity_at": { + "name": "last_activity_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": {}, + "isRLSEnabled": false + }, + "public.chat_convos": { + "name": "chat_convos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(40)", + "primaryKey": true, + "notNull": true + }, + "convo_type": { + "name": "convo_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'direct'" + }, + "name": { + "name": "name", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "rev": { + "name": "rev", + "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": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_members": { + "name": "chat_members", + "schema": "", + "columns": { + "convo_id": { + "name": "convo_id", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "user_did": { + "name": "user_did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_read_rev": { + "name": "last_read_rev", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "chat_members_did_idx": { + "name": "chat_members_did_idx", + "columns": [ + { + "expression": "user_did", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "chat_members_convo_id_user_did_pk": { + "name": "chat_members_convo_id_user_did_pk", + "columns": [ + "convo_id", + "user_did" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_messages": { + "name": "chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(36)", + "primaryKey": true, + "notNull": true + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "convo_id": { + "name": "convo_id", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "sender_did": { + "name": "sender_did", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mentions": { + "name": "mentions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "attachments": { + "name": "attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "in_reply_to_message_id": { + "name": "in_reply_to_message_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": false + }, + "quote_message_id": { + "name": "quote_message_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "rev": { + "name": "rev", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chat_messages_convo_sent_idx": { + "name": "chat_messages_convo_sent_idx", + "columns": [ + { + "expression": "convo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sent_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_convo_id_idx": { + "name": "chat_messages_convo_id_idx", + "columns": [ + { + "expression": "convo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_in_reply_to_idx": { + "name": "chat_messages_in_reply_to_idx", + "columns": [ + { + "expression": "convo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "in_reply_to_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_quote_idx": { + "name": "chat_messages_quote_idx", + "columns": [ + { + "expression": "convo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "quote_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_object_uri_unique_idx": { + "name": "chat_messages_object_uri_unique_idx", + "columns": [ + { + "expression": "object_uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_messages\".\"object_uri\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.posts_view": { + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "post_type": { + "name": "post_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_web_id": { + "name": "author_web_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_provider_endpoint": { + "name": "author_provider_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "definition": "SELECT\n posts.*,\n users.name as author_name,\n users.web_id as author_web_id,\n users.provider_endpoint as author_provider_endpoint\n FROM posts\n INNER JOIN users on posts.author_id = users.id\n WHERE posts.is_public = true", + "name": "posts_view", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.unified_feed_candidates_view": { + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_type": { + "name": "post_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_web_id": { + "name": "author_web_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_provider_endpoint": { + "name": "author_provider_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_avatar": { + "name": "author_avatar", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "at_uri": { + "name": "at_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "candidate_uri": { + "name": "candidate_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "thread_parent_author_id": { + "name": "thread_parent_author_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "thread_root_author_id": { + "name": "thread_root_author_id", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "thread_reply_count": { + "name": "thread_reply_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "thread_participant_count": { + "name": "thread_participant_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "thread_last_activity_at": { + "name": "thread_last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n SELECT\n ufv.*,\n COALESCE(ufv.at_uri, ufv.object_uri) AS candidate_uri,\n te.parent_author_id AS thread_parent_author_id,\n te.root_author_id AS thread_root_author_id,\n ts.reply_count AS thread_reply_count,\n ts.participant_count AS thread_participant_count,\n ts.last_activity_at AS thread_last_activity_at\n FROM unified_feed_view ufv\n LEFT JOIN thread_edges te ON te.item_uri = COALESCE(ufv.at_uri, ufv.object_uri)\n LEFT JOIN thread_stats ts ON ts.root_uri = COALESCE(te.root_uri, COALESCE(ufv.at_uri, ufv.object_uri))\n", + "name": "unified_feed_candidates_view", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.unified_feed_view": { + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hashtags": { + "name": "hashtags", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_type": { + "name": "post_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_url": { + "name": "canonical_url", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_web_id": { + "name": "author_web_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_provider_endpoint": { + "name": "author_provider_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_avatar": { + "name": "author_avatar", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "at_uri": { + "name": "at_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "object_uri": { + "name": "object_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_parent_uri": { + "name": "reply_parent_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + }, + "reply_root_uri": { + "name": "reply_root_uri", + "type": "varchar(3072)", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n SELECT\n posts.id,\n posts.content,\n posts.hashtags,\n posts.post_type,\n posts.name as title,\n posts.summary,\n COALESCE(posts.canonical_url, posts.object_uri) as canonical_url,\n posts.created_at,\n posts.is_public,\n posts.author_id,\n users.name as author_name,\n users.web_id as author_web_id,\n users.provider_endpoint as author_provider_endpoint,\n NULL::varchar as author_avatar,\n 'activitypods' as source,\n NULL::varchar as at_uri,\n posts.object_uri,\n posts.reply_parent_uri,\n posts.reply_root_uri\n FROM posts\n INNER JOIN users ON posts.author_id = users.id\n WHERE posts.is_public = true\n\n UNION ALL\n\n SELECT\n at_posts.id,\n at_posts.content,\n COALESCE(\n ARRAY(\n SELECT DISTINCT '#' || lower(trim(feature->>'tag'))\n FROM jsonb_array_elements(COALESCE(at_posts.facets, '[]'::jsonb)) facet,\n jsonb_array_elements(COALESCE(facet->'features', '[]'::jsonb)) feature\n WHERE feature ? 'tag' AND length(trim(feature->>'tag')) > 0\n ),\n ARRAY[]::text[]\n ) as hashtags,\n at_posts.post_type,\n at_posts.title,\n at_posts.summary,\n at_posts.canonical_url,\n at_posts.created_at,\n at_posts.is_public,\n NULL::integer as author_id,\n COALESCE(at_identities.handle, at_posts.author_did) as author_name,\n at_posts.author_did as author_web_id,\n '' as author_provider_endpoint,\n at_identities.avatar_url as author_avatar,\n 'atproto' as source,\n at_posts.at_uri,\n NULL::text as object_uri,\n at_posts.reply_parent_uri,\n at_posts.reply_root_uri\n FROM at_posts\n LEFT JOIN at_identities ON at_posts.author_did = at_identities.did\n WHERE at_posts.is_public = true\n\n UNION ALL\n\n SELECT\n ap_remote_posts.id,\n ap_remote_posts.content,\n COALESCE(ap_remote_posts.hashtags, ARRAY[]::text[]) as hashtags,\n ap_remote_posts.post_type,\n ap_remote_posts.title,\n ap_remote_posts.summary,\n COALESCE(ap_remote_posts.canonical_url, ap_remote_posts.object_uri) as canonical_url,\n ap_remote_posts.created_at,\n ap_remote_posts.is_public,\n NULL::integer as author_id,\n ap_remote_posts.author_name as author_name,\n ap_remote_posts.author_web_id as author_web_id,\n COALESCE(ap_remote_posts.author_domain, '') as author_provider_endpoint,\n ap_actor_cache.avatar_url as author_avatar,\n 'activitypods' as source,\n NULL::varchar as at_uri,\n ap_remote_posts.object_uri,\n ap_remote_posts.reply_parent_uri,\n ap_remote_posts.reply_root_uri\n FROM ap_remote_posts\n LEFT JOIN ap_actor_cache ON ap_remote_posts.author_web_id = ap_actor_cache.actor_uri\n WHERE ap_remote_posts.is_public = true\n", + "name": "unified_feed_view", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/api/drizzle/meta/_journal.json b/api/drizzle/meta/_journal.json index b32859eea..7fa40d3ea 100644 --- a/api/drizzle/meta/_journal.json +++ b/api/drizzle/meta/_journal.json @@ -106,6 +106,13 @@ "when": 1777896060000, "tag": "0014_chat_pod_commit_projection", "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1778086085032, + "tag": "0015_lethal_master_mold", + "breakpoints": true } ] -} +} \ No newline at end of file From 6a5a147f2aaaf855962707d38fa814f930d33c26 Mon Sep 17 00:00:00 2001 From: Sherif Lawal Date: Thu, 7 May 2026 12:06:32 +0100 Subject: [PATCH 2/4] fix: unified feed query and missing DB views - Rewrite queryFeedCandidates to use plain SQL strings instead of Drizzle sql template tags. Drizzle's parser misinterprets PostgreSQL JSONB path literals like '{subject,uri}' as template interpolation targets, corrupting parameter numbering and generating invalid LIMIT $1,$2 syntax. - Fix jsonb_object_length() (does not exist in PostgreSQL) with (SELECT COUNT(*) > 0 FROM jsonb_object_keys(...)). - Add migration 0016 to recreate unified_feed_view with full column set and create the missing unified_feed_candidates_view. - Uncomment activitypods-frontend service in docker-compose-dev.yml (required for Solid OIDC login UI at localhost:5000). - Add SEMAPPS_FRONTEND_URL to activitypods-backend environment. --- api/drizzle/0016_fix_unified_feed_views.sql | 104 ++++++++++++++++++++ api/src/routes/atBridge.ts | 69 +++++++------ docker-compose-dev.yml | 24 ++--- frontend/bun.lockb | Bin 251733 -> 285440 bytes 4 files changed, 154 insertions(+), 43 deletions(-) create mode 100644 api/drizzle/0016_fix_unified_feed_views.sql diff --git a/api/drizzle/0016_fix_unified_feed_views.sql b/api/drizzle/0016_fix_unified_feed_views.sql new file mode 100644 index 000000000..2e74f3d9c --- /dev/null +++ b/api/drizzle/0016_fix_unified_feed_views.sql @@ -0,0 +1,104 @@ +-- Recreate unified_feed_view with full column set (hashtags, post_type, +-- reply_parent_uri, reply_root_uri, author_avatar, canonical_url, etc.) +-- and create the missing unified_feed_candidates_view. + +DROP VIEW IF EXISTS unified_feed_candidates_view; +DROP VIEW IF EXISTS unified_feed_view; + +CREATE VIEW unified_feed_view AS + SELECT + posts.id, + posts.content, + posts.hashtags, + posts.post_type, + posts.name as title, + posts.summary, + COALESCE(posts.canonical_url, posts.object_uri) as canonical_url, + posts.created_at, + posts.is_public, + posts.author_id, + users.name as author_name, + users.web_id as author_web_id, + users.provider_endpoint as author_provider_endpoint, + NULL::varchar as author_avatar, + 'activitypods' as source, + NULL::varchar as at_uri, + posts.object_uri, + posts.reply_parent_uri, + posts.reply_root_uri + FROM posts + INNER JOIN users ON posts.author_id = users.id + WHERE posts.is_public = true + + UNION ALL + + SELECT + at_posts.id, + at_posts.content, + COALESCE( + ARRAY( + SELECT DISTINCT '#' || lower(trim(feature->>'tag')) + FROM jsonb_array_elements(COALESCE(at_posts.facets, '[]'::jsonb)) facet, + jsonb_array_elements(COALESCE(facet->'features', '[]'::jsonb)) feature + WHERE feature ? 'tag' AND length(trim(feature->>'tag')) > 0 + ), + ARRAY[]::text[] + ) as hashtags, + at_posts.post_type, + at_posts.title, + at_posts.summary, + at_posts.canonical_url, + at_posts.created_at, + at_posts.is_public, + NULL::integer as author_id, + COALESCE(at_identities.handle, at_posts.author_did) as author_name, + at_posts.author_did as author_web_id, + '' as author_provider_endpoint, + at_identities.avatar_url as author_avatar, + 'atproto' as source, + at_posts.at_uri, + NULL::text as object_uri, + at_posts.reply_parent_uri, + at_posts.reply_root_uri + FROM at_posts + LEFT JOIN at_identities ON at_posts.author_did = at_identities.did + WHERE at_posts.is_public = true + + UNION ALL + + SELECT + ap_remote_posts.id, + ap_remote_posts.content, + COALESCE(ap_remote_posts.hashtags, ARRAY[]::text[]) as hashtags, + ap_remote_posts.post_type, + ap_remote_posts.title, + ap_remote_posts.summary, + COALESCE(ap_remote_posts.canonical_url, ap_remote_posts.object_uri) as canonical_url, + ap_remote_posts.created_at, + ap_remote_posts.is_public, + NULL::integer as author_id, + ap_remote_posts.author_name as author_name, + ap_remote_posts.author_web_id as author_web_id, + COALESCE(ap_remote_posts.author_domain, '') as author_provider_endpoint, + ap_actor_cache.avatar_url as author_avatar, + 'activitypods' as source, + NULL::varchar as at_uri, + ap_remote_posts.object_uri, + ap_remote_posts.reply_parent_uri, + ap_remote_posts.reply_root_uri + FROM ap_remote_posts + LEFT JOIN ap_actor_cache ON ap_remote_posts.author_web_id = ap_actor_cache.actor_uri + WHERE ap_remote_posts.is_public = true; + +CREATE VIEW unified_feed_candidates_view AS + SELECT + ufv.*, + COALESCE(ufv.at_uri, ufv.object_uri) AS candidate_uri, + te.parent_author_id AS thread_parent_author_id, + te.root_author_id AS thread_root_author_id, + ts.reply_count AS thread_reply_count, + ts.participant_count AS thread_participant_count, + ts.last_activity_at AS thread_last_activity_at + FROM unified_feed_view ufv + LEFT JOIN thread_edges te ON te.item_uri = COALESCE(ufv.at_uri, ufv.object_uri) + LEFT JOIN thread_stats ts ON ts.root_uri = COALESCE(te.root_uri, COALESCE(ufv.at_uri, ufv.object_uri)); diff --git a/api/src/routes/atBridge.ts b/api/src/routes/atBridge.ts index c37567579..4cd39c50c 100644 --- a/api/src/routes/atBridge.ts +++ b/api/src/routes/atBridge.ts @@ -750,33 +750,40 @@ async function queryFeedCandidates(params: { const includeAp = !source || source === 'all' || source === 'activitypods' const normalizedTag = hashtag ? normalizeHashtag(hashtag) : null - const atFilters: SQL[] = [sql`ap.is_public = true`] - const apFilters: SQL[] = [sql`p.is_public = true`] + // Build query as a plain string with inline params to avoid Drizzle template + // parser misinterpreting PostgreSQL array path literals like '{subject,uri}' + const lim = String(fetchLimit) // safe integer, inline directly + const esc = (v: string) => `'${v.replace(/'/g, "''")}'` // SQL string escape + + // Per-source WHERE clauses + const atWhereParts: string[] = ['ap.is_public = true'] + const apWhereParts: string[] = ['p.is_public = true'] if (normalizedTag) { const tagPattern = `%${normalizedTag}%` - atFilters.push(sql`(ap.content ILIKE ${tagPattern} OR ap.hashtags @> ARRAY[${normalizedTag}]::text[])`) - apFilters.push(sql`(p.content ILIKE ${tagPattern} OR p.hashtags @> ARRAY[${normalizedTag}]::text[])`) + atWhereParts.push(`(ap.content ILIKE ${esc(tagPattern)} OR ap.hashtags @> ARRAY[${esc(normalizedTag)}]::text[])`) + apWhereParts.push(`(p.content ILIKE ${esc(tagPattern)} OR p.hashtags @> ARRAY[${esc(normalizedTag)}]::text[])`) } if (sinceDate) { - atFilters.push(sql`ap.created_at > ${sinceDate}`) - apFilters.push(sql`p.created_at > ${sinceDate}`) + const isoDate = sinceDate.toISOString() + atWhereParts.push(`ap.created_at > ${esc(isoDate)}::timestamptz`) + apWhereParts.push(`p.created_at > ${esc(isoDate)}::timestamptz`) } - const atWhere = sql.join(atFilters, sql` AND `) - const apWhere = sql.join(apFilters, sql` AND `) + const atWhere = atWhereParts.join(' AND ') + const apWhere = apWhereParts.join(' AND ') - const cteFragments: SQL[] = [] - const unionArms: SQL[] = [] + const cteFragments: string[] = [] + const unionArms: string[] = [] if (includeAt) { - cteFragments.push(sql`at_limited AS MATERIALIZED ( + cteFragments.push(`at_limited AS MATERIALIZED ( SELECT ap.id, ap.content, COALESCE(ap.hashtags, ARRAY[]::text[]) AS hashtags, ap.post_type, ap.title, ap.summary, ap.canonical_url, (CASE WHEN jsonb_typeof(ap.embeds) = 'array' THEN jsonb_array_length(ap.embeds) > 0 - WHEN jsonb_typeof(ap.embeds) = 'object' THEN jsonb_object_length(ap.embeds) > 0 + WHEN jsonb_typeof(ap.embeds) = 'object' THEN (SELECT COUNT(*) > 0 FROM jsonb_object_keys(ap.embeds)) ELSE false END) AS has_media, ap.created_at, ap.is_public, @@ -792,13 +799,13 @@ async function queryFeedCandidates(params: { LEFT JOIN at_identities ai ON ap.author_did = ai.did WHERE ${atWhere} ORDER BY LEAST(ap.created_at, NOW()) DESC - LIMIT ${fetchLimit} - `) - unionArms.push(sql`SELECT * FROM at_limited`) + LIMIT ${lim} + )`) + unionArms.push('SELECT * FROM at_limited') } if (includeAp) { - cteFragments.push(sql`ap_limited AS MATERIALIZED ( + cteFragments.push(`ap_limited AS MATERIALIZED ( SELECT p.id, p.content, p.hashtags, p.post_type, p.name AS title, p.summary, @@ -815,11 +822,11 @@ async function queryFeedCandidates(params: { JOIN users u ON p.author_id = u.id WHERE ${apWhere} ORDER BY p.created_at DESC - LIMIT ${fetchLimit} + LIMIT ${lim} )`) - unionArms.push(sql`SELECT * FROM ap_limited`) + unionArms.push('SELECT * FROM ap_limited') - cteFragments.push(sql`ap_remote_limited AS MATERIALIZED ( + cteFragments.push(`ap_remote_limited AS MATERIALIZED ( SELECT apr.id, apr.content, COALESCE(apr.hashtags, ARRAY[]::text[]) AS hashtags, @@ -837,22 +844,22 @@ async function queryFeedCandidates(params: { FROM ap_remote_posts apr WHERE apr.is_public = true ORDER BY apr.created_at DESC - LIMIT ${fetchLimit} + LIMIT ${lim} )`) - unionArms.push(sql`SELECT * FROM ap_remote_limited`) + unionArms.push('SELECT * FROM ap_remote_limited') } - cteFragments.push(sql`combined AS MATERIALIZED ( - ${sql.join(unionArms, sql` UNION ALL `)} + cteFragments.push(`combined AS MATERIALIZED ( + ${unionArms.join(' UNION ALL ')} )`) - cteFragments.push(sql`candidate_uris AS MATERIALIZED ( + cteFragments.push(`candidate_uris AS MATERIALIZED ( SELECT DISTINCT c.candidate_uri FROM combined c WHERE c.candidate_uri IS NOT NULL )`) - cteFragments.push(sql`like_counts AS MATERIALIZED ( + cteFragments.push(`like_counts AS MATERIALIZED ( SELECT ar.record #>> '{subject,uri}' AS subject_uri, COUNT(*)::integer AS like_count @@ -863,10 +870,9 @@ async function queryFeedCandidates(params: { GROUP BY 1 )`) - cteFragments.push(sql`quote_counts AS MATERIALIZED ( + cteFragments.push(`quote_counts AS MATERIALIZED ( SELECT subject_uri, SUM(quote_count)::integer AS quote_count FROM ( - -- AT Protocol quote-posts (embed paths) SELECT COALESCE( ap.embeds #>> '{record,uri}', @@ -891,7 +897,6 @@ async function queryFeedCandidates(params: { ) IN (SELECT candidate_uri FROM candidate_uris) GROUP BY 1 UNION ALL - -- ActivityPub native quote-posts (quoteUrl / FEP-e232 / Misskey fields) SELECT apr.quoted_post_uri AS subject_uri, COUNT(*)::integer AS quote_count @@ -903,8 +908,8 @@ async function queryFeedCandidates(params: { GROUP BY subject_uri )`) - const fullQuery = sql` - WITH ${sql.join(cteFragments, sql`, `)} + const fullQueryText = ` + WITH ${cteFragments.join(', ')} SELECT c.*, te.parent_author_id AS thread_parent_author_id, @@ -920,10 +925,10 @@ async function queryFeedCandidates(params: { LEFT JOIN like_counts lc ON lc.subject_uri = c.candidate_uri LEFT JOIN quote_counts qc ON qc.subject_uri = c.candidate_uri ORDER BY LEAST(c.created_at, NOW()) DESC - LIMIT ${fetchLimit} + LIMIT ${lim} ` - const result = await db.execute(fullQuery) + const result = await db.execute(sql.raw(fullQueryText)) const rows = (result as unknown as { rows: Record[] }).rows const postRows = rows.map(mapDbRowToFeedRow) const repostRows = await loadRepostCandidateRows({ diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 7ce6d738f..cec8d4685 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -26,21 +26,23 @@ services: SEMAPPS_JENA_PASSWORD: admin SEMAPPS_APP_LANG: en SEMAPPS_MAPBOX_ACCESS_TOKEN: ${MAPBOX_ACCESS_TOKEN:?You must set it in the .env.local file} + SEMAPPS_FRONTEND_URL: 'http://localhost:5001' # Allow the Pod backend to access the app backend as localhost:3001 # See https://docs.docker.com/network/drivers/host/ network_mode: 'host' - #activitypods-frontend: - # image: activitypods/frontend - # depends_on: - # - activitypods-backend - # restart: always - # ports: - # - '5000:5000' - # expose: - # - '5000' - # environment: - # REACT_APP_CONFIG_URL: 'http://localhost:3000/.well-known/config.js' + activitypods-frontend: + image: activitypods/frontend + depends_on: + - activitypods-backend + restart: always + ports: + - '5000:5000' + expose: + - '5000' + environment: + REACT_APP_CONFIG_URL: 'http://localhost:3000/.well-known/config.js' + command: serve -s build -l 5000 #mailcatcher: # image: dockage/mailcatcher:0.7.1 # ports: diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 7277e757068270e812a6ad1ffed5373a0f15ffaa..421fd6a6328492fe1f40467bb3621a6fbf8683ef 100755 GIT binary patch delta 72611 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 delta 52073 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 Date: Thu, 7 May 2026 13:01:21 +0100 Subject: [PATCH 3/4] fix: preserve pod-native token on OIDC login OIDC flow was overwriting the pod-native JWT (from /auth/login) with the OIDC access_token. ActivityPods outbox writes require the native JWT - the OIDC token returns INVALID_TOKEN (401). Fix: only store the OIDC token if pod_token is missing. If a pod-native token already exists in the DB, leave it untouched. New users get the OIDC token as a fallback until they also sign in via the legacy /signin flow. --- api/src/routes/oidcAuth.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/api/src/routes/oidcAuth.ts b/api/src/routes/oidcAuth.ts index a4f4b2cb2..b843b5235 100644 --- a/api/src/routes/oidcAuth.ts +++ b/api/src/routes/oidcAuth.ts @@ -8,6 +8,7 @@ import { getTokenObject } from '../services/jwt' import { syncAtprotoIdentity } from '../services/WebIdProfileService' import { localeFromHeaders, translate } from '../i18n' import { toPublicUser } from '../services/PodTokenService' +import { encryptToken } from '../services/TokenVault' /** * How long (in ms) to wait for the ATProto identity link before issuing the @@ -126,9 +127,27 @@ const oidcAuthPlugin = new Elysia({ prefix: '/oidc-auth' }) name: getNameFromWebId(webId), email: `${Buffer.from(webId).toString('base64url')}@memory.local`, webId, - providerEndpoint + providerEndpoint, + // Store the OIDC access_token as a fallback pod token. + // If the pod accepts Solid OIDC tokens for outbox writes this will + // work; otherwise the user must also sign in via /signin to store + // the pod-native JWT. + podToken: encryptToken(tokens.access_token) }) .returning() + } else { + // Only update the pod token if the existing one is missing/empty. + // The pod-native JWT (stored by /signin) takes priority over the OIDC + // access_token because ActivityPods outbox writes require the native JWT. + const existingToken = dbUserRows[0].podToken + if (!existingToken) { + dbUserRows = await db + .update(users) + .set({ podToken: encryptToken(tokens.access_token) }) + .where(eq(users.webId, webId)) + .returning() + } + // If a pod-native token already exists, leave it untouched. } let dbUser = dbUserRows[0] From b0043963db34572bed8de92aa45b10a8c3b30965 Mon Sep 17 00:00:00 2001 From: Sherif Lawal Date: Thu, 7 May 2026 14:27:57 +0100 Subject: [PATCH 4/4] feat: add sign out button to settings ActivityPods uses a proprietary server-signed JWT for pod writes, separate from the OIDC access token. Users who sign in via OIDC need a way to sign out and back in via the legacy /signin flow to refresh the pod-native token stored in users.pod_token. Adds a red Sign out button at the bottom of the Settings page. --- frontend/src/views/SettingsView.vue | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index c538b9dc0..0b6a129fd 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -1,9 +1,11 @@