@takosjp/yurucommu-core 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +82 -0
- package/migrations/0001_init.sql +495 -0
- package/migrations/0002_social_remote_actor_edges.sql +92 -0
- package/migrations/0003_activity_remote_object_edges.sql +68 -0
- package/migrations/0004_blocklist.sql +26 -0
- package/migrations/0005_story_community_scope.sql +13 -0
- package/migrations/0006_dm_community_read_status.sql +19 -0
- package/migrations/0007_moderation_reports.sql +22 -0
- package/migrations/0008_actor_fields_aka.sql +18 -0
- package/migrations/0009_object_tags.sql +13 -0
- package/migrations/0010_object_recipients_drop_actor_fk.sql +34 -0
- package/migrations/0011_drop_remote_actor_fks.sql +205 -0
- package/migrations/0012_objects_content_fts.sql +39 -0
- package/migrations/0013_efficiency_indexes.sql +13 -0
- package/migrations/0014_inbox_actor_created_idx.sql +15 -0
- package/migrations/0015_community_bans.sql +16 -0
- package/migrations/0016_namespace_takos_oidc_subject.sql +19 -0
- package/migrations/0017_mobile_push_registrations.sql +22 -0
- package/migrations/README.md +122 -0
- package/package.json +75 -0
- package/packages/api/LICENSE +16 -0
- package/packages/api/package.json +30 -0
- package/packages/api/src/index.ts +4 -0
- package/packages/api/src/lib/api/account.ts +20 -0
- package/packages/api/src/lib/api/actors.ts +149 -0
- package/packages/api/src/lib/api/auth.ts +46 -0
- package/packages/api/src/lib/api/communities.ts +329 -0
- package/packages/api/src/lib/api/dm.test.ts +67 -0
- package/packages/api/src/lib/api/dm.ts +236 -0
- package/packages/api/src/lib/api/fetch.ts +111 -0
- package/packages/api/src/lib/api/follow.ts +30 -0
- package/packages/api/src/lib/api/media.ts +100 -0
- package/packages/api/src/lib/api/moderation.ts +98 -0
- package/packages/api/src/lib/api/normalize.ts +71 -0
- package/packages/api/src/lib/api/notifications.test.ts +63 -0
- package/packages/api/src/lib/api/notifications.ts +61 -0
- package/packages/api/src/lib/api/posts.test.ts +110 -0
- package/packages/api/src/lib/api/posts.ts +181 -0
- package/packages/api/src/lib/api/recommendations.ts +22 -0
- package/packages/api/src/lib/api/search.ts +88 -0
- package/packages/api/src/lib/api/stories.ts +80 -0
- package/packages/api/src/lib/api.ts +15 -0
- package/packages/api/src/lib/fetch-with-timeout.ts +42 -0
- package/packages/api/src/lib/transport.ts +40 -0
- package/packages/api/src/social-server.ts +47 -0
- package/packages/api/src/types/index.ts +185 -0
- package/scripts/apply-takosumi-migrations.ts +621 -0
- package/src/backend/federation-helpers.ts +36 -0
- package/src/backend/index.ts +872 -0
- package/src/backend/lib/account-migration.ts +106 -0
- package/src/backend/lib/activitypub-actor-cache.ts +238 -0
- package/src/backend/lib/activitypub-helpers.ts +131 -0
- package/src/backend/lib/activitypub-validators.ts +323 -0
- package/src/backend/lib/ap-context.ts +16 -0
- package/src/backend/lib/ap-ids.ts +101 -0
- package/src/backend/lib/ap-response.ts +30 -0
- package/src/backend/lib/ap-signing.ts +87 -0
- package/src/backend/lib/ap-verify.ts +670 -0
- package/src/backend/lib/auth-lockout.ts +230 -0
- package/src/backend/lib/backend-paths.ts +34 -0
- package/src/backend/lib/base64.ts +30 -0
- package/src/backend/lib/blocklist-purge.ts +109 -0
- package/src/backend/lib/blocklist.ts +279 -0
- package/src/backend/lib/chunk.ts +33 -0
- package/src/backend/lib/client-ip.ts +169 -0
- package/src/backend/lib/community-visibility.ts +230 -0
- package/src/backend/lib/crypto.ts +424 -0
- package/src/backend/lib/delivery/circuit.ts +265 -0
- package/src/backend/lib/delivery/metrics.ts +30 -0
- package/src/backend/lib/delivery/planner.ts +190 -0
- package/src/backend/lib/delivery/queue-batching.ts +626 -0
- package/src/backend/lib/delivery/queue-delivery.ts +641 -0
- package/src/backend/lib/delivery/queue.ts +576 -0
- package/src/backend/lib/delivery/transformers.ts +56 -0
- package/src/backend/lib/delivery/types.ts +139 -0
- package/src/backend/lib/errors.ts +114 -0
- package/src/backend/lib/federation-fetch.ts +296 -0
- package/src/backend/lib/feed-cursor.ts +57 -0
- package/src/backend/lib/feed-exclude.ts +48 -0
- package/src/backend/lib/hex.ts +8 -0
- package/src/backend/lib/log-mask.ts +213 -0
- package/src/backend/lib/logger.ts +285 -0
- package/src/backend/lib/mobile-contract.ts +137 -0
- package/src/backend/lib/oauth-providers.ts +324 -0
- package/src/backend/lib/oauth-utils.ts +148 -0
- package/src/backend/lib/oidc-id-token.ts +151 -0
- package/src/backend/lib/parse-helpers.ts +31 -0
- package/src/backend/lib/post-visibility.ts +190 -0
- package/src/backend/lib/session-actor.ts +61 -0
- package/src/backend/lib/ssrf.ts +428 -0
- package/src/backend/lib/strip-image-metadata.ts +191 -0
- package/src/backend/middleware/bearer-auth.ts +70 -0
- package/src/backend/middleware/body-limit.ts +212 -0
- package/src/backend/middleware/cache.ts +429 -0
- package/src/backend/middleware/csrf.ts +130 -0
- package/src/backend/middleware/error-handler.ts +77 -0
- package/src/backend/middleware/rate-limit.ts +308 -0
- package/src/backend/public.ts +21 -0
- package/src/backend/routes/account-teardown.ts +430 -0
- package/src/backend/routes/activitypub/handlers/actor-inbox-handlers.ts +354 -0
- package/src/backend/routes/activitypub/handlers/inbound-timestamp.ts +29 -0
- package/src/backend/routes/activitypub/handlers/inbox-content-handlers.ts +1634 -0
- package/src/backend/routes/activitypub/handlers/inbox-follow-handlers.ts +547 -0
- package/src/backend/routes/activitypub/handlers/inbox-interaction-handlers.ts +497 -0
- package/src/backend/routes/activitypub/handlers/inbox-shared-helpers.ts +262 -0
- package/src/backend/routes/activitypub/handlers/user-inbox-handlers.ts +35 -0
- package/src/backend/routes/activitypub/inbox-types.ts +74 -0
- package/src/backend/routes/activitypub/inbox.ts +1191 -0
- package/src/backend/routes/activitypub/outbox.ts +0 -0
- package/src/backend/routes/activitypub/query-helpers.ts +227 -0
- package/src/backend/routes/activitypub.ts +616 -0
- package/src/backend/routes/actors-helpers.ts +487 -0
- package/src/backend/routes/actors.ts +1311 -0
- package/src/backend/routes/apps.ts +313 -0
- package/src/backend/routes/auth-helpers.ts +566 -0
- package/src/backend/routes/auth.ts +615 -0
- package/src/backend/routes/communities/membership-invites.ts +208 -0
- package/src/backend/routes/communities/membership-join.ts +335 -0
- package/src/backend/routes/communities/membership-members.ts +539 -0
- package/src/backend/routes/communities/membership-requests.ts +296 -0
- package/src/backend/routes/communities/membership-shared.ts +364 -0
- package/src/backend/routes/communities/messages.ts +479 -0
- package/src/backend/routes/communities/routes.ts +624 -0
- package/src/backend/routes/communities.ts +21 -0
- package/src/backend/routes/dm/contacts.ts +525 -0
- package/src/backend/routes/dm/conversations-helpers.ts +197 -0
- package/src/backend/routes/dm/conversations.ts +25 -0
- package/src/backend/routes/dm/messages.ts +658 -0
- package/src/backend/routes/dm/query-helpers.ts +85 -0
- package/src/backend/routes/dm/read-archive.ts +228 -0
- package/src/backend/routes/dm/requests.ts +222 -0
- package/src/backend/routes/dm/typing.ts +81 -0
- package/src/backend/routes/dm.ts +15 -0
- package/src/backend/routes/follow-helpers.ts +370 -0
- package/src/backend/routes/follow.ts +588 -0
- package/src/backend/routes/media.ts +692 -0
- package/src/backend/routes/mobile.ts +159 -0
- package/src/backend/routes/moderation.ts +373 -0
- package/src/backend/routes/notifications.ts +757 -0
- package/src/backend/routes/posts/delete-cascade.ts +330 -0
- package/src/backend/routes/posts/interactions.ts +795 -0
- package/src/backend/routes/posts/post-helpers.ts +847 -0
- package/src/backend/routes/posts/queries.ts +537 -0
- package/src/backend/routes/posts/routes.ts +865 -0
- package/src/backend/routes/posts/transformers.ts +161 -0
- package/src/backend/routes/posts.ts +17 -0
- package/src/backend/routes/recommendations.ts +88 -0
- package/src/backend/routes/search.ts +730 -0
- package/src/backend/routes/stories/interactions.ts +576 -0
- package/src/backend/routes/stories/query-helpers.ts +482 -0
- package/src/backend/routes/stories/routes.ts +906 -0
- package/src/backend/routes/stories.ts +13 -0
- package/src/backend/routes/takos-tools/dm.ts +249 -0
- package/src/backend/routes/takos-tools/follows.ts +225 -0
- package/src/backend/routes/takos-tools/posts.ts +292 -0
- package/src/backend/routes/takos-tools/search.ts +228 -0
- package/src/backend/routes/takos-tools/timeline.ts +132 -0
- package/src/backend/routes/takos-tools/types.ts +10 -0
- package/src/backend/routes/takos-tools-response.ts +178 -0
- package/src/backend/routes/takos-tools.ts +153 -0
- package/src/backend/routes/timeline.ts +755 -0
- package/src/backend/runtime/bun.ts +620 -0
- package/src/backend/runtime/cloudflare.ts +202 -0
- package/src/backend/runtime/compat-bun/types.ts +44 -0
- package/src/backend/runtime/memory-kv.ts +104 -0
- package/src/backend/runtime/shared.ts +142 -0
- package/src/backend/runtime/types.ts +205 -0
- package/src/backend/server.ts +636 -0
- package/src/backend/types.ts +143 -0
- package/src/db/index.ts +97 -0
- package/src/db/schema/actors.ts +129 -0
- package/src/db/schema/communities.ts +133 -0
- package/src/db/schema/date-utils.ts +17 -0
- package/src/db/schema/index.ts +17 -0
- package/src/db/schema/messaging.ts +241 -0
- package/src/db/schema/mobile.ts +37 -0
- package/src/db/schema/posts.ts +150 -0
- package/src/db/schema/relations.ts +266 -0
- package/src/db/schema/reports.ts +33 -0
- package/src/db/schema/social.ts +106 -0
- package/src/db/schema/stories.ts +70 -0
- package/src/db/schema.ts +15 -0
- package/src/plugin/public.ts +7 -0
- package/src/runtime/site-worker.ts +10 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
-- Activity records mirror ActivityPub payloads. actor_ap_id and object_ap_id
|
|
2
|
+
-- can point at remote actors/objects, so they must remain plain AP-ID strings
|
|
3
|
+
-- rather than local-table foreign keys.
|
|
4
|
+
|
|
5
|
+
PRAGMA foreign_keys = OFF;
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS activities (
|
|
8
|
+
ap_id TEXT PRIMARY KEY,
|
|
9
|
+
type TEXT NOT NULL,
|
|
10
|
+
actor_ap_id TEXT NOT NULL,
|
|
11
|
+
object_ap_id TEXT,
|
|
12
|
+
object_json TEXT,
|
|
13
|
+
target_ap_id TEXT,
|
|
14
|
+
raw_json TEXT NOT NULL,
|
|
15
|
+
direction TEXT,
|
|
16
|
+
processed INTEGER DEFAULT 0,
|
|
17
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS activities_new (
|
|
21
|
+
ap_id TEXT PRIMARY KEY,
|
|
22
|
+
type TEXT NOT NULL,
|
|
23
|
+
actor_ap_id TEXT NOT NULL,
|
|
24
|
+
object_ap_id TEXT,
|
|
25
|
+
object_json TEXT,
|
|
26
|
+
target_ap_id TEXT,
|
|
27
|
+
raw_json TEXT NOT NULL,
|
|
28
|
+
direction TEXT,
|
|
29
|
+
processed INTEGER NOT NULL DEFAULT 0,
|
|
30
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
INSERT OR IGNORE INTO activities_new (
|
|
34
|
+
ap_id,
|
|
35
|
+
type,
|
|
36
|
+
actor_ap_id,
|
|
37
|
+
object_ap_id,
|
|
38
|
+
object_json,
|
|
39
|
+
target_ap_id,
|
|
40
|
+
raw_json,
|
|
41
|
+
direction,
|
|
42
|
+
processed,
|
|
43
|
+
created_at
|
|
44
|
+
)
|
|
45
|
+
SELECT
|
|
46
|
+
ap_id,
|
|
47
|
+
type,
|
|
48
|
+
actor_ap_id,
|
|
49
|
+
object_ap_id,
|
|
50
|
+
object_json,
|
|
51
|
+
target_ap_id,
|
|
52
|
+
raw_json,
|
|
53
|
+
direction,
|
|
54
|
+
COALESCE(processed, 0),
|
|
55
|
+
COALESCE(created_at, datetime('now'))
|
|
56
|
+
FROM activities;
|
|
57
|
+
|
|
58
|
+
DROP TABLE activities;
|
|
59
|
+
ALTER TABLE activities_new RENAME TO activities;
|
|
60
|
+
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_activities_actor ON activities(actor_ap_id);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_activities_object ON activities(object_ap_id);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_activities_type ON activities(type);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_activities_type_created ON activities(type, created_at DESC);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_activities_direction_processed ON activities(direction, processed);
|
|
66
|
+
CREATE INDEX IF NOT EXISTS idx_activities_direction_processed_created ON activities(direction, processed, created_at);
|
|
67
|
+
|
|
68
|
+
PRAGMA foreign_keys = ON;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- Blocklist tables for federation moderation.
|
|
2
|
+
--
|
|
3
|
+
-- `blocked_domains` blocks every actor whose AP-ID hostname matches.
|
|
4
|
+
-- `blocked_actors` blocks an individual remote actor (and is the authoritative
|
|
5
|
+
-- record consulted by inbox content handlers).
|
|
6
|
+
--
|
|
7
|
+
-- Both tables are read on every inbound activity, so each lookup column has
|
|
8
|
+
-- its own index (PRIMARY KEY for the natural identifier covers the lookup).
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS blocked_domains (
|
|
11
|
+
domain TEXT PRIMARY KEY,
|
|
12
|
+
reason TEXT,
|
|
13
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_blocked_domains_created
|
|
17
|
+
ON blocked_domains(created_at);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS blocked_actors (
|
|
20
|
+
actor_ap_id TEXT PRIMARY KEY,
|
|
21
|
+
reason TEXT,
|
|
22
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_blocked_actors_created
|
|
26
|
+
ON blocked_actors(created_at);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- B0.3 — give Stories a community scope dimension.
|
|
2
|
+
--
|
|
3
|
+
-- Stories are stored in the shared `objects` table (type='Story'). That table
|
|
4
|
+
-- already carries `community_ap_id` (added in 0001 for community-scoped posts),
|
|
5
|
+
-- so no column change is required: a community story simply sets it, a personal
|
|
6
|
+
-- story leaves it NULL.
|
|
7
|
+
--
|
|
8
|
+
-- The new read path filters active community stories by
|
|
9
|
+
-- (type='Story' AND community_ap_id=? AND end_time>?)
|
|
10
|
+
-- so add a covering index for it. Idempotent: re-applying is a no-op.
|
|
11
|
+
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_objects_type_community_end
|
|
13
|
+
ON objects(type, community_ap_id, end_time);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Track per-viewer read position for community (group) chats so the DM contact
|
|
2
|
+
-- list can show an unread badge for communities, mirroring `dm_read_status`
|
|
3
|
+
-- for one-to-one DMs.
|
|
4
|
+
--
|
|
5
|
+
-- A row records the most recent moment `actor_ap_id` read messages in
|
|
6
|
+
-- `community_ap_id`. Community unread = messages in the community published
|
|
7
|
+
-- after `last_read_at` that the viewer did not author. Absence of a row means
|
|
8
|
+
-- the viewer has never opened that community chat (treated as "read from epoch"
|
|
9
|
+
-- on the read path so a brand-new join does not surface a stale badge — the
|
|
10
|
+
-- contacts handler clamps the baseline to the join time). Idempotent.
|
|
11
|
+
CREATE TABLE IF NOT EXISTS dm_community_read_status (
|
|
12
|
+
actor_ap_id TEXT NOT NULL,
|
|
13
|
+
community_ap_id TEXT NOT NULL,
|
|
14
|
+
last_read_at TEXT NOT NULL,
|
|
15
|
+
PRIMARY KEY (actor_ap_id, community_ap_id)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS dm_community_read_status_actor_idx
|
|
19
|
+
ON dm_community_read_status(actor_ap_id);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Moderation reports for federation abuse handling.
|
|
2
|
+
--
|
|
3
|
+
-- Inbound `Flag` activities are persisted here so operators can review them
|
|
4
|
+
-- via the moderation API instead of the report being lost to a log line.
|
|
5
|
+
-- Rows are append-only at ingest; an operator marks a report handled by
|
|
6
|
+
-- stamping `resolved_at`.
|
|
7
|
+
|
|
8
|
+
CREATE TABLE IF NOT EXISTS reports (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
reporter_ap_id TEXT NOT NULL,
|
|
11
|
+
target_ap_id TEXT,
|
|
12
|
+
content TEXT,
|
|
13
|
+
instance TEXT,
|
|
14
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15
|
+
resolved_at TEXT
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS reports_created_idx
|
|
19
|
+
ON reports(created_at);
|
|
20
|
+
|
|
21
|
+
CREATE INDEX IF NOT EXISTS reports_resolved_idx
|
|
22
|
+
ON reports(resolved_at);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Mastodon-parity actor profile + account-migration fields.
|
|
2
|
+
--
|
|
3
|
+
-- `fields_json` holds the structured profile metadata rows (PropertyValue
|
|
4
|
+
-- attachments) shown on a profile, stored as a JSON array of {name,value}.
|
|
5
|
+
-- `also_known_as_json` holds the actor's declared aliases (other AP IDs the
|
|
6
|
+
-- account claims), the prerequisite for an incoming Move that points here.
|
|
7
|
+
-- `moved_to` records the target AP ID once this actor has migrated away; when
|
|
8
|
+
-- set it is emitted as `movedTo` on the served actor document so remote
|
|
9
|
+
-- servers can follow the Move.
|
|
10
|
+
--
|
|
11
|
+
-- All three default to portable, federation-safe values: empty arrays and a
|
|
12
|
+
-- NULL (not yet moved) target.
|
|
13
|
+
|
|
14
|
+
ALTER TABLE actors ADD COLUMN fields_json TEXT NOT NULL DEFAULT '[]';
|
|
15
|
+
|
|
16
|
+
ALTER TABLE actors ADD COLUMN also_known_as_json TEXT NOT NULL DEFAULT '[]';
|
|
17
|
+
|
|
18
|
+
ALTER TABLE actors ADD COLUMN moved_to TEXT;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Object-level ActivityPub `tag` array (Mentions, etc.).
|
|
2
|
+
--
|
|
3
|
+
-- Wave 1 made the outbound `Create` carry a `Mention` tag, but the served
|
|
4
|
+
-- object at `GET /ap/objects/:id` could not emit `tag` because the `objects`
|
|
5
|
+
-- table had no column for it (mentions lived only in the outbound Create's
|
|
6
|
+
-- `activities.rawJson`). Persist the computed Mention tag array onto the
|
|
7
|
+
-- object row so the object can be served faithfully (`tag` round-trips).
|
|
8
|
+
--
|
|
9
|
+
-- Stored as the JSON-encoded array of `{ type, href, name }` tag entries that
|
|
10
|
+
-- also goes on the Create; defaults to an empty array for posts with no tags
|
|
11
|
+
-- and for legacy rows created before this column existed.
|
|
12
|
+
|
|
13
|
+
ALTER TABLE objects ADD COLUMN tags_json TEXT NOT NULL DEFAULT '[]';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- 0010: object_recipients.recipient_ap_id must NOT reference actors(ap_id).
|
|
2
|
+
--
|
|
3
|
+
-- object_recipients addresses an object to a recipient that is EITHER an actor
|
|
4
|
+
-- (direct messages) OR a community (group-chat audience). The group-chat reader
|
|
5
|
+
-- GET /api/communities/:id/messages selects rows where
|
|
6
|
+
-- recipient_ap_id = community.apId AND type = 'audience'
|
|
7
|
+
-- so a community apId is a legitimate recipient value. But a community is not an
|
|
8
|
+
-- actor, so the original `REFERENCES actors(ap_id)` foreign key made every
|
|
9
|
+
-- community group-chat send fail with a foreign-key violation (HTTP 500) on
|
|
10
|
+
-- Cloudflare D1, where foreign keys are enforced. (The send handler tried to
|
|
11
|
+
-- "bypass" the FK with raw SQL, which does not actually bypass enforcement.)
|
|
12
|
+
--
|
|
13
|
+
-- Rebuild the table keeping the objects FK (a recipient row always references a
|
|
14
|
+
-- real object) + primary key + recipient index, dropping only the spurious
|
|
15
|
+
-- actors FK. This matches the Drizzle schema, which already declares no FK on
|
|
16
|
+
-- recipient_ap_id (the FK was schema drift inherited from 0001_init.sql).
|
|
17
|
+
|
|
18
|
+
CREATE TABLE object_recipients_new (
|
|
19
|
+
object_ap_id TEXT NOT NULL REFERENCES objects(ap_id) ON DELETE CASCADE,
|
|
20
|
+
recipient_ap_id TEXT NOT NULL,
|
|
21
|
+
type TEXT NOT NULL,
|
|
22
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
23
|
+
PRIMARY KEY (object_ap_id, recipient_ap_id)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
INSERT INTO object_recipients_new (object_ap_id, recipient_ap_id, type, created_at)
|
|
27
|
+
SELECT object_ap_id, recipient_ap_id, type, created_at FROM object_recipients;
|
|
28
|
+
|
|
29
|
+
DROP TABLE object_recipients;
|
|
30
|
+
|
|
31
|
+
ALTER TABLE object_recipients_new RENAME TO object_recipients;
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_object_recipients_recipient
|
|
34
|
+
ON object_recipients(recipient_ap_id, created_at DESC);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
-- 0011: drop the stale `REFERENCES actors(ap_id)` foreign keys that block
|
|
2
|
+
-- INBOUND federation, WITHOUT cascade-deleting child rows.
|
|
3
|
+
--
|
|
4
|
+
-- Remote actors live ONLY in `actor_cache`, never in `actors`. The inbound
|
|
5
|
+
-- handlers store a remote actor's Note in `objects` (attributed_to = remote),
|
|
6
|
+
-- their Like in `likes`, their Announce in `announces`, story interactions in
|
|
7
|
+
-- `story_*`, and the raw activity in `inbox` — all keyed by a remote actor that
|
|
8
|
+
-- is absent from `actors`. 0001_init.sql declared these columns with
|
|
9
|
+
-- `REFERENCES actors(ap_id)`; 0002/0003 stripped the equivalent FKs from
|
|
10
|
+
-- follows/blocks/mutes/activities but missed these tables. Cloudflare D1
|
|
11
|
+
-- ENFORCES foreign keys (the "D1 ignores FK" assumption is wrong), so every
|
|
12
|
+
-- inbound remote post/like/boost/story insert fails with a foreign-key
|
|
13
|
+
-- violation. The Drizzle schema declares NO foreign keys at all.
|
|
14
|
+
--
|
|
15
|
+
-- CRITICAL ordering: `objects` is referenced by likes / announces / bookmarks /
|
|
16
|
+
-- object_recipients / story_views / story_votes / story_shares with
|
|
17
|
+
-- `ON DELETE CASCADE`. On D1 (which will not honour `PRAGMA foreign_keys=OFF`)
|
|
18
|
+
-- a DROP of `objects` performs an implicit DELETE that FIRES those cascades and
|
|
19
|
+
-- wipes the child rows. So every child is rebuilt FIRST to drop its FK to
|
|
20
|
+
-- `objects` (and its actor FK), and only then is `objects` itself rebuilt — by
|
|
21
|
+
-- which point no surviving table has a cascade edge into it. All row data is
|
|
22
|
+
-- copied through every rebuild. This matches the Drizzle schema (zero FKs);
|
|
23
|
+
-- application code already manages cascades (routes/posts/delete-cascade.ts).
|
|
24
|
+
|
|
25
|
+
PRAGMA defer_foreign_keys = TRUE;
|
|
26
|
+
|
|
27
|
+
-- ---- children of objects: rebuilt first, dropping ALL their FKs ----
|
|
28
|
+
|
|
29
|
+
-- likes
|
|
30
|
+
CREATE TABLE likes_new (
|
|
31
|
+
actor_ap_id TEXT NOT NULL,
|
|
32
|
+
object_ap_id TEXT NOT NULL,
|
|
33
|
+
activity_ap_id TEXT,
|
|
34
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
35
|
+
PRIMARY KEY (actor_ap_id, object_ap_id)
|
|
36
|
+
);
|
|
37
|
+
INSERT INTO likes_new (actor_ap_id, object_ap_id, activity_ap_id, created_at)
|
|
38
|
+
SELECT actor_ap_id, object_ap_id, activity_ap_id, created_at FROM likes;
|
|
39
|
+
DROP TABLE likes;
|
|
40
|
+
ALTER TABLE likes_new RENAME TO likes;
|
|
41
|
+
CREATE INDEX idx_likes_object ON likes(object_ap_id);
|
|
42
|
+
CREATE INDEX idx_likes_actor ON likes(actor_ap_id);
|
|
43
|
+
CREATE INDEX idx_likes_actor_object ON likes(actor_ap_id, object_ap_id);
|
|
44
|
+
|
|
45
|
+
-- announces
|
|
46
|
+
CREATE TABLE announces_new (
|
|
47
|
+
actor_ap_id TEXT NOT NULL,
|
|
48
|
+
object_ap_id TEXT NOT NULL,
|
|
49
|
+
activity_ap_id TEXT,
|
|
50
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
51
|
+
PRIMARY KEY (actor_ap_id, object_ap_id)
|
|
52
|
+
);
|
|
53
|
+
INSERT INTO announces_new (actor_ap_id, object_ap_id, activity_ap_id, created_at)
|
|
54
|
+
SELECT actor_ap_id, object_ap_id, activity_ap_id, created_at FROM announces;
|
|
55
|
+
DROP TABLE announces;
|
|
56
|
+
ALTER TABLE announces_new RENAME TO announces;
|
|
57
|
+
CREATE INDEX idx_announces_object ON announces(object_ap_id);
|
|
58
|
+
CREATE INDEX idx_announces_actor ON announces(actor_ap_id);
|
|
59
|
+
CREATE INDEX idx_announces_actor_object ON announces(actor_ap_id, object_ap_id);
|
|
60
|
+
|
|
61
|
+
-- bookmarks
|
|
62
|
+
CREATE TABLE bookmarks_new (
|
|
63
|
+
actor_ap_id TEXT NOT NULL,
|
|
64
|
+
object_ap_id TEXT NOT NULL,
|
|
65
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
66
|
+
PRIMARY KEY (actor_ap_id, object_ap_id)
|
|
67
|
+
);
|
|
68
|
+
INSERT INTO bookmarks_new (actor_ap_id, object_ap_id, created_at)
|
|
69
|
+
SELECT actor_ap_id, object_ap_id, created_at FROM bookmarks;
|
|
70
|
+
DROP TABLE bookmarks;
|
|
71
|
+
ALTER TABLE bookmarks_new RENAME TO bookmarks;
|
|
72
|
+
CREATE INDEX idx_bookmarks_actor ON bookmarks(actor_ap_id);
|
|
73
|
+
|
|
74
|
+
-- story_views
|
|
75
|
+
CREATE TABLE story_views_new (
|
|
76
|
+
actor_ap_id TEXT NOT NULL,
|
|
77
|
+
story_ap_id TEXT NOT NULL,
|
|
78
|
+
viewed_at TEXT DEFAULT (datetime('now')),
|
|
79
|
+
PRIMARY KEY (actor_ap_id, story_ap_id)
|
|
80
|
+
);
|
|
81
|
+
INSERT INTO story_views_new (actor_ap_id, story_ap_id, viewed_at)
|
|
82
|
+
SELECT actor_ap_id, story_ap_id, viewed_at FROM story_views;
|
|
83
|
+
DROP TABLE story_views;
|
|
84
|
+
ALTER TABLE story_views_new RENAME TO story_views;
|
|
85
|
+
CREATE INDEX idx_story_views_actor ON story_views(actor_ap_id);
|
|
86
|
+
CREATE INDEX idx_story_views_story ON story_views(story_ap_id);
|
|
87
|
+
|
|
88
|
+
-- story_votes
|
|
89
|
+
CREATE TABLE story_votes_new (
|
|
90
|
+
id TEXT PRIMARY KEY,
|
|
91
|
+
story_ap_id TEXT NOT NULL,
|
|
92
|
+
actor_ap_id TEXT NOT NULL,
|
|
93
|
+
option_index INTEGER NOT NULL,
|
|
94
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
95
|
+
UNIQUE(story_ap_id, actor_ap_id)
|
|
96
|
+
);
|
|
97
|
+
INSERT INTO story_votes_new (id, story_ap_id, actor_ap_id, option_index, created_at)
|
|
98
|
+
SELECT id, story_ap_id, actor_ap_id, option_index, created_at FROM story_votes;
|
|
99
|
+
DROP TABLE story_votes;
|
|
100
|
+
ALTER TABLE story_votes_new RENAME TO story_votes;
|
|
101
|
+
CREATE INDEX idx_story_votes_story ON story_votes(story_ap_id);
|
|
102
|
+
CREATE INDEX idx_story_votes_actor ON story_votes(actor_ap_id);
|
|
103
|
+
|
|
104
|
+
-- story_shares
|
|
105
|
+
CREATE TABLE story_shares_new (
|
|
106
|
+
id TEXT PRIMARY KEY,
|
|
107
|
+
story_ap_id TEXT NOT NULL,
|
|
108
|
+
actor_ap_id TEXT NOT NULL,
|
|
109
|
+
shared_at TEXT DEFAULT (datetime('now')),
|
|
110
|
+
UNIQUE(story_ap_id, actor_ap_id)
|
|
111
|
+
);
|
|
112
|
+
INSERT INTO story_shares_new (id, story_ap_id, actor_ap_id, shared_at)
|
|
113
|
+
SELECT id, story_ap_id, actor_ap_id, shared_at FROM story_shares;
|
|
114
|
+
DROP TABLE story_shares;
|
|
115
|
+
ALTER TABLE story_shares_new RENAME TO story_shares;
|
|
116
|
+
CREATE INDEX idx_story_shares_story ON story_shares(story_ap_id);
|
|
117
|
+
CREATE INDEX idx_story_shares_actor ON story_shares(actor_ap_id);
|
|
118
|
+
|
|
119
|
+
-- object_recipients (0010 already dropped its actors FK; drop the objects FK too
|
|
120
|
+
-- so it is not a cascade edge into objects)
|
|
121
|
+
CREATE TABLE object_recipients_new (
|
|
122
|
+
object_ap_id TEXT NOT NULL,
|
|
123
|
+
recipient_ap_id TEXT NOT NULL,
|
|
124
|
+
type TEXT NOT NULL,
|
|
125
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
126
|
+
PRIMARY KEY (object_ap_id, recipient_ap_id)
|
|
127
|
+
);
|
|
128
|
+
INSERT INTO object_recipients_new (object_ap_id, recipient_ap_id, type, created_at)
|
|
129
|
+
SELECT object_ap_id, recipient_ap_id, type, created_at FROM object_recipients;
|
|
130
|
+
DROP TABLE object_recipients;
|
|
131
|
+
ALTER TABLE object_recipients_new RENAME TO object_recipients;
|
|
132
|
+
CREATE INDEX idx_object_recipients_recipient
|
|
133
|
+
ON object_recipients(recipient_ap_id, created_at DESC);
|
|
134
|
+
|
|
135
|
+
-- ---- objects: rebuilt last, now that no surviving table cascades into it ----
|
|
136
|
+
-- drop attributed_to -> actors; keep community_ap_id -> communities.
|
|
137
|
+
CREATE TABLE objects_new (
|
|
138
|
+
ap_id TEXT PRIMARY KEY,
|
|
139
|
+
type TEXT NOT NULL DEFAULT 'Note',
|
|
140
|
+
attributed_to TEXT NOT NULL,
|
|
141
|
+
content TEXT NOT NULL DEFAULT '',
|
|
142
|
+
summary TEXT,
|
|
143
|
+
attachments_json TEXT DEFAULT '[]',
|
|
144
|
+
in_reply_to TEXT,
|
|
145
|
+
conversation TEXT,
|
|
146
|
+
visibility TEXT DEFAULT 'public',
|
|
147
|
+
to_json TEXT DEFAULT '[]',
|
|
148
|
+
cc_json TEXT DEFAULT '[]',
|
|
149
|
+
audience_json TEXT DEFAULT '[]',
|
|
150
|
+
community_ap_id TEXT REFERENCES communities(ap_id) ON DELETE SET NULL,
|
|
151
|
+
end_time TEXT,
|
|
152
|
+
like_count INTEGER DEFAULT 0,
|
|
153
|
+
reply_count INTEGER DEFAULT 0,
|
|
154
|
+
announce_count INTEGER DEFAULT 0,
|
|
155
|
+
share_count INTEGER DEFAULT 0,
|
|
156
|
+
published TEXT DEFAULT (datetime('now')),
|
|
157
|
+
updated TEXT,
|
|
158
|
+
is_local INTEGER DEFAULT 1,
|
|
159
|
+
raw_json TEXT,
|
|
160
|
+
deleted_at TEXT,
|
|
161
|
+
tags_json TEXT NOT NULL DEFAULT '[]'
|
|
162
|
+
);
|
|
163
|
+
INSERT INTO objects_new (
|
|
164
|
+
ap_id, type, attributed_to, content, summary, attachments_json, in_reply_to,
|
|
165
|
+
conversation, visibility, to_json, cc_json, audience_json, community_ap_id,
|
|
166
|
+
end_time, like_count, reply_count, announce_count, share_count, published,
|
|
167
|
+
updated, is_local, raw_json, deleted_at, tags_json
|
|
168
|
+
)
|
|
169
|
+
SELECT
|
|
170
|
+
ap_id, type, attributed_to, content, summary, attachments_json, in_reply_to,
|
|
171
|
+
conversation, visibility, to_json, cc_json, audience_json, community_ap_id,
|
|
172
|
+
end_time, like_count, reply_count, announce_count, share_count, published,
|
|
173
|
+
updated, is_local, raw_json, deleted_at, tags_json
|
|
174
|
+
FROM objects;
|
|
175
|
+
DROP TABLE objects;
|
|
176
|
+
ALTER TABLE objects_new RENAME TO objects;
|
|
177
|
+
CREATE INDEX idx_objects_attributed_to ON objects(attributed_to);
|
|
178
|
+
CREATE INDEX idx_objects_in_reply_to ON objects(in_reply_to);
|
|
179
|
+
CREATE INDEX idx_objects_community_ap_id ON objects(community_ap_id);
|
|
180
|
+
CREATE INDEX idx_objects_published ON objects(published DESC);
|
|
181
|
+
CREATE INDEX idx_objects_visibility ON objects(visibility);
|
|
182
|
+
CREATE INDEX idx_objects_end_time ON objects(end_time);
|
|
183
|
+
CREATE INDEX idx_objects_deleted_at ON objects(deleted_at);
|
|
184
|
+
CREATE INDEX idx_objects_attributed_to_published ON objects(attributed_to, published DESC);
|
|
185
|
+
CREATE INDEX idx_objects_visibility_published ON objects(visibility, published DESC);
|
|
186
|
+
CREATE INDEX idx_objects_type_visibility_published ON objects(type, visibility, published DESC);
|
|
187
|
+
CREATE INDEX idx_objects_conversation ON objects(conversation);
|
|
188
|
+
CREATE INDEX idx_objects_community_published ON objects(community_ap_id, published DESC);
|
|
189
|
+
CREATE INDEX idx_objects_is_local ON objects(is_local);
|
|
190
|
+
CREATE INDEX idx_objects_type_community_end ON objects(type, community_ap_id, end_time);
|
|
191
|
+
|
|
192
|
+
-- inbox: references activities (not objects); drop only the actors FK.
|
|
193
|
+
CREATE TABLE inbox_new (
|
|
194
|
+
actor_ap_id TEXT NOT NULL,
|
|
195
|
+
activity_ap_id TEXT NOT NULL REFERENCES activities(ap_id) ON DELETE CASCADE,
|
|
196
|
+
read INTEGER DEFAULT 0,
|
|
197
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
198
|
+
PRIMARY KEY (actor_ap_id, activity_ap_id)
|
|
199
|
+
);
|
|
200
|
+
INSERT INTO inbox_new (actor_ap_id, activity_ap_id, read, created_at)
|
|
201
|
+
SELECT actor_ap_id, activity_ap_id, read, created_at FROM inbox;
|
|
202
|
+
DROP TABLE inbox;
|
|
203
|
+
ALTER TABLE inbox_new RENAME TO inbox;
|
|
204
|
+
CREATE INDEX idx_inbox_actor_read ON inbox(actor_ap_id, read, created_at DESC);
|
|
205
|
+
CREATE INDEX idx_inbox_activity ON inbox(activity_ap_id);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- Full-text index for post-content search (GET /api/search/posts).
|
|
2
|
+
--
|
|
3
|
+
-- The content search used `content LIKE '%query%'`, a leading-wildcard scan of
|
|
4
|
+
-- every object on each query. This adds an FTS5 index over objects.content using
|
|
5
|
+
-- the `trigram` tokenizer, which (unlike the default unicode61) matches arbitrary
|
|
6
|
+
-- substrings of spaceless CJK text — confirmed on D1 and the in-memory test
|
|
7
|
+
-- engine. The route uses MATCH for queries >= 3 chars (trigram's minimum) and
|
|
8
|
+
-- keeps LIKE only as a fallback for 1-2 char queries.
|
|
9
|
+
--
|
|
10
|
+
-- External-content table: the FTS index stores only the inverted index and reads
|
|
11
|
+
-- the text back from `objects` by rowid, kept in sync by the trigger trio below
|
|
12
|
+
-- (the canonical SQLite external-content pattern; DELETE/UPDATE use the special
|
|
13
|
+
-- 'delete' command with the OLD content so the right terms are removed). All
|
|
14
|
+
-- statements are idempotent so a direct re-apply is safe.
|
|
15
|
+
|
|
16
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS objects_fts USING fts5(
|
|
17
|
+
content,
|
|
18
|
+
content='objects',
|
|
19
|
+
content_rowid='rowid',
|
|
20
|
+
tokenize='trigram'
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TRIGGER IF NOT EXISTS objects_fts_ai AFTER INSERT ON objects BEGIN
|
|
24
|
+
INSERT INTO objects_fts(rowid, content) VALUES (new.rowid, new.content);
|
|
25
|
+
END;
|
|
26
|
+
|
|
27
|
+
CREATE TRIGGER IF NOT EXISTS objects_fts_ad AFTER DELETE ON objects BEGIN
|
|
28
|
+
INSERT INTO objects_fts(objects_fts, rowid, content)
|
|
29
|
+
VALUES ('delete', old.rowid, old.content);
|
|
30
|
+
END;
|
|
31
|
+
|
|
32
|
+
CREATE TRIGGER IF NOT EXISTS objects_fts_au AFTER UPDATE OF content ON objects BEGIN
|
|
33
|
+
INSERT INTO objects_fts(objects_fts, rowid, content)
|
|
34
|
+
VALUES ('delete', old.rowid, old.content);
|
|
35
|
+
INSERT INTO objects_fts(rowid, content) VALUES (new.rowid, new.content);
|
|
36
|
+
END;
|
|
37
|
+
|
|
38
|
+
-- Backfill the index from existing rows (no-op on a fresh database).
|
|
39
|
+
INSERT INTO objects_fts(objects_fts) VALUES ('rebuild');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Efficiency indexes (2026-06-22).
|
|
2
|
+
|
|
3
|
+
-- Composite (conversation, published) so the ~4s-polled DM message reader
|
|
4
|
+
-- (WHERE conversation = ? ORDER BY published DESC) reads in index order instead
|
|
5
|
+
-- of filesorting the conversation's rows on every poll.
|
|
6
|
+
CREATE INDEX IF NOT EXISTS objects_conversation_published_idx
|
|
7
|
+
ON objects (conversation, published);
|
|
8
|
+
|
|
9
|
+
-- Mirror of follows_following_created_idx for the FOLLOWING-list direction
|
|
10
|
+
-- (WHERE follower_ap_id = ? ORDER BY created_at) — previously only the followers
|
|
11
|
+
-- direction (following_ap_id, created_at) had a created-at covering index.
|
|
12
|
+
CREATE INDEX IF NOT EXISTS follows_follower_created_idx
|
|
13
|
+
ON follows (follower_ap_id, created_at);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Notification-list index (2026-06-23).
|
|
2
|
+
--
|
|
3
|
+
-- GET /api/notifications (the most-polled endpoint — list + badge) drives from
|
|
4
|
+
-- `inbox` with WHERE actor_ap_id = ? (no constraint on `read`) ORDER BY
|
|
5
|
+
-- created_at DESC, activity_ap_id DESC. The only inbox index
|
|
6
|
+
-- (actor_ap_id, read, created_at) cannot serve that ORDER BY: the unconstrained
|
|
7
|
+
-- `read` column sits BETWEEN the equality prefix and the sort key, so SQLite can
|
|
8
|
+
-- only use the actor_ap_id = prefix and must then FILESORT the matched rows
|
|
9
|
+
-- (a near-full inbox scan + temp B-tree on every poll). The unread COUNT query
|
|
10
|
+
-- (which DOES constrain read = 0) keeps using the existing index and is unaffected.
|
|
11
|
+
--
|
|
12
|
+
-- This composite covers the equality + both sort columns so the list query seeks
|
|
13
|
+
-- by actor and reads in (created_at, activity_ap_id) order with no filesort.
|
|
14
|
+
CREATE INDEX IF NOT EXISTS inbox_actor_created_idx
|
|
15
|
+
ON inbox (actor_ap_id, created_at, activity_ap_id);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Durable per-community bans. Removing a member only deleted their membership
|
|
2
|
+
-- state, with no record — so a kicked user could immediately re-join an OPEN
|
|
3
|
+
-- community (local re-POST /join, or a remote re-Follow that auto-Accepts). A
|
|
4
|
+
-- ban row makes a removal stick: the local join route and the inbound Group
|
|
5
|
+
-- Follow handler consult it, and explicit re-admission (approve / invite / add)
|
|
6
|
+
-- clears it. No FK to actors (a banned actor may be REMOTE, with no local
|
|
7
|
+
-- actors row — same rationale as the other remote-actor-referencing tables).
|
|
8
|
+
CREATE TABLE IF NOT EXISTS community_bans (
|
|
9
|
+
community_ap_id TEXT NOT NULL,
|
|
10
|
+
banned_ap_id TEXT NOT NULL,
|
|
11
|
+
created_at TEXT NOT NULL,
|
|
12
|
+
PRIMARY KEY (community_ap_id, banned_ap_id)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE INDEX IF NOT EXISTS community_bans_banned_idx
|
|
16
|
+
ON community_bans (banned_ap_id);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- DEEP round-2 #11: the `takos` OIDC subject was stored verbatim in
|
|
2
|
+
-- actors.takos_user_id while every OTHER identity source is namespaced
|
|
3
|
+
-- (password:owner, local:<name>, google:<id>, x:<id>). A trusted-but-
|
|
4
|
+
-- misconfigured/compromised issuer that emitted a subject equal to a reserved
|
|
5
|
+
-- key (e.g. "password:owner" / "local:tako") would resolve the OIDC
|
|
6
|
+
-- get-or-create to that pre-existing higher-privileged account instead of
|
|
7
|
+
-- provisioning a fresh actor. findOrCreateOAuthActor now stores the takos
|
|
8
|
+
-- subject as "takos:<sub>"; prefix existing un-namespaced takos rows to match.
|
|
9
|
+
--
|
|
10
|
+
-- A takos row is any non-null takos_user_id that does NOT already carry a known
|
|
11
|
+
-- namespace prefix. The "takos:" guard keeps this idempotent.
|
|
12
|
+
UPDATE actors
|
|
13
|
+
SET takos_user_id = 'takos:' || takos_user_id
|
|
14
|
+
WHERE takos_user_id IS NOT NULL
|
|
15
|
+
AND takos_user_id NOT LIKE 'takos:%'
|
|
16
|
+
AND takos_user_id NOT LIKE 'password:%'
|
|
17
|
+
AND takos_user_id NOT LIKE 'local:%'
|
|
18
|
+
AND takos_user_id NOT LIKE 'google:%'
|
|
19
|
+
AND takos_user_id NOT LIKE 'x:%';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS mobile_push_registrations (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
actor_ap_id TEXT NOT NULL,
|
|
4
|
+
product TEXT NOT NULL,
|
|
5
|
+
token TEXT NOT NULL,
|
|
6
|
+
token_hash TEXT NOT NULL,
|
|
7
|
+
environment TEXT NOT NULL DEFAULT 'production',
|
|
8
|
+
host_url TEXT,
|
|
9
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
10
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
11
|
+
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
12
|
+
FOREIGN KEY (actor_ap_id) REFERENCES actors(ap_id) ON DELETE CASCADE
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE UNIQUE INDEX IF NOT EXISTS mobile_push_registrations_actor_product_token_idx
|
|
16
|
+
ON mobile_push_registrations(actor_ap_id, product, token_hash);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS mobile_push_registrations_actor_idx
|
|
19
|
+
ON mobile_push_registrations(actor_ap_id);
|
|
20
|
+
|
|
21
|
+
CREATE INDEX IF NOT EXISTS mobile_push_registrations_last_seen_idx
|
|
22
|
+
ON mobile_push_registrations(last_seen_at);
|