@meith/db 0.31.0 → 0.32.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/migrations/0060_webhook_payload_format.sql +1 -0
- package/migrations/0061_task_schedule.sql +1 -0
- package/migrations/0062_pretty_zombie.sql +12 -0
- package/migrations/0063_board_digest.sql +3 -0
- package/migrations/0064_redundant_lady_bullseye.sql +2 -0
- package/migrations/0065_serious_puff_adder.sql +1 -0
- package/migrations/meta/0061_snapshot.json +9515 -0
- package/migrations/meta/0062_snapshot.json +9617 -0
- package/migrations/meta/0063_snapshot.json +9653 -0
- package/migrations/meta/0064_snapshot.json +9667 -0
- package/migrations/meta/0065_snapshot.json +9674 -0
- package/migrations/meta/_journal.json +42 -0
- package/package.json +31 -30
- package/src/account-repos.ts +32 -15
- package/src/api-repo.ts +192 -21
- package/src/attachment-repo.ts +26 -0
- package/src/board-digest-repo.ts +45 -0
- package/src/content-counters.ts +14 -0
- package/src/discovery-repo.ts +22 -0
- package/src/draft-repo.ts +40 -1
- package/src/feed-token-repo.ts +103 -0
- package/src/forum-admin-repo.ts +43 -18
- package/src/index.ts +10 -0
- package/src/member-settings-repo.ts +25 -1
- package/src/moderation-queue.ts +61 -21
- package/src/plugin-data.ts +19 -0
- package/src/plugin-grants.ts +83 -0
- package/src/plugin-role.ts +79 -0
- package/src/post-repo.ts +26 -0
- package/src/post-writes.ts +32 -2
- package/src/presence-repo.ts +29 -1
- package/src/read-state-repo.ts +21 -1
- package/src/report-repo.ts +151 -7
- package/src/schema/content.ts +5 -0
- package/src/schema/identity.ts +33 -1
- package/src/schema/platform.ts +2 -0
- package/src/search-repo.ts +48 -17
- package/src/task-repo.ts +19 -7
- package/src/thread-writes.ts +7 -5
- package/src/upgrade-repo.ts +15 -7
- package/src/user-merge-map.ts +1 -0
package/src/task-repo.ts
CHANGED
|
@@ -12,7 +12,12 @@ export class PostgresTaskRepository implements TaskRepository {
|
|
|
12
12
|
constructor(private readonly db: Database) {}
|
|
13
13
|
|
|
14
14
|
async ensureRegistered(
|
|
15
|
-
definitions: readonly {
|
|
15
|
+
definitions: readonly {
|
|
16
|
+
id: string
|
|
17
|
+
intervalSeconds: number
|
|
18
|
+
schedule?: string
|
|
19
|
+
firstRunAt?: Date
|
|
20
|
+
}[],
|
|
16
21
|
): Promise<void> {
|
|
17
22
|
if (definitions.length === 0) return
|
|
18
23
|
|
|
@@ -22,15 +27,19 @@ export class PostgresTaskRepository implements TaskRepository {
|
|
|
22
27
|
definitions.map((d) => ({
|
|
23
28
|
key: d.id,
|
|
24
29
|
intervalSeconds: d.intervalSeconds,
|
|
25
|
-
|
|
30
|
+
schedule: d.schedule ?? null,
|
|
31
|
+
nextRunAt: d.firstRunAt ?? NEVER_RUN,
|
|
26
32
|
})),
|
|
27
33
|
)
|
|
28
34
|
.onConflictDoUpdate({
|
|
29
35
|
target: tasks.key,
|
|
30
36
|
set: {
|
|
31
37
|
intervalSeconds: sql`excluded.interval_seconds`,
|
|
38
|
+
schedule: sql`excluded.schedule`,
|
|
32
39
|
nextRunAt: sql`
|
|
33
|
-
case when
|
|
40
|
+
case when excluded.schedule is not null
|
|
41
|
+
then ${tasks.nextRunAt}
|
|
42
|
+
when ${tasks.intervalSeconds} is distinct from excluded.interval_seconds
|
|
34
43
|
then coalesce(${tasks.lastRunAt}, now())
|
|
35
44
|
+ make_interval(secs => excluded.interval_seconds)
|
|
36
45
|
else ${tasks.nextRunAt}
|
|
@@ -79,15 +88,18 @@ export class PostgresTaskRepository implements TaskRepository {
|
|
|
79
88
|
success: boolean
|
|
80
89
|
detail?: Record<string, unknown>
|
|
81
90
|
error?: string
|
|
91
|
+
nextRunAt?: Date
|
|
82
92
|
}): Promise<void> {
|
|
93
|
+
const nextRunExpr =
|
|
94
|
+
input.nextRunAt === undefined
|
|
95
|
+
? sql`${input.finishedAt}::timestamptz + make_interval(secs => interval_seconds)`
|
|
96
|
+
: sql`${input.nextRunAt}::timestamptz`
|
|
97
|
+
|
|
83
98
|
await this.db.transaction(async (tx) => {
|
|
84
99
|
await tx.execute(sql`
|
|
85
100
|
update tasks
|
|
86
101
|
set locked_until = null,
|
|
87
|
-
|
|
88
|
-
-- unable to resolve timestamp-plus-interval at parse time.
|
|
89
|
-
next_run_at = ${input.finishedAt}::timestamptz
|
|
90
|
-
+ make_interval(secs => interval_seconds),
|
|
102
|
+
next_run_at = ${nextRunExpr},
|
|
91
103
|
consecutive_failures = ${input.success ? sql`0` : sql`consecutive_failures + 1`}
|
|
92
104
|
where key = ${input.taskId}
|
|
93
105
|
`)
|
package/src/thread-writes.ts
CHANGED
|
@@ -22,7 +22,7 @@ import type {
|
|
|
22
22
|
import type { Database } from './client'
|
|
23
23
|
import { applyCreatedContentCounters } from './content-counters'
|
|
24
24
|
import { resultRows } from './result-rows'
|
|
25
|
-
import { SEARCH_DOCUMENT_VERSION, searchVectorSql } from './search-repo'
|
|
25
|
+
import { readSearchConfig, SEARCH_DOCUMENT_VERSION, searchVectorSql } from './search-repo'
|
|
26
26
|
import { readBoardVocabulary } from './vocabulary-repo'
|
|
27
27
|
|
|
28
28
|
export class PostgresThreadWriteRepository implements ThreadWriteRepository, ReplyWriteRepository {
|
|
@@ -79,6 +79,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
79
79
|
{ source: 'post', viewer: authorRef(record.authorUserId) },
|
|
80
80
|
vocabularyOptions(vocabulary),
|
|
81
81
|
)
|
|
82
|
+
const searchConfig = await readSearchConfig(this.db)
|
|
82
83
|
|
|
83
84
|
return this.db.transaction(async (tx) => {
|
|
84
85
|
const threadRows = resultRows(
|
|
@@ -116,7 +117,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
116
117
|
* is_first_post true), which search-repo.test.ts pins so the
|
|
117
118
|
* writer and the backfill cannot drift apart.
|
|
118
119
|
*/
|
|
119
|
-
${searchVectorSql(sql`${record.title}`, sql`${record.message}`)},
|
|
120
|
+
${searchVectorSql(searchConfig, sql`${record.title}`, sql`${record.message}`)},
|
|
120
121
|
${SEARCH_DOCUMENT_VERSION})
|
|
121
122
|
returning id
|
|
122
123
|
`),
|
|
@@ -145,7 +146,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
145
146
|
if (record.subscribe) {
|
|
146
147
|
await tx.execute(sql`
|
|
147
148
|
insert into thread_subscriptions (user_id, thread_id, mode, last_notified_post_id)
|
|
148
|
-
values (${record.authorUserId}, ${threadId}, 'instant', ${postId})
|
|
149
|
+
values (${record.authorUserId}, ${threadId}, ${record.subscribeMode ?? 'instant'}, ${postId})
|
|
149
150
|
on conflict (user_id, thread_id) do nothing
|
|
150
151
|
`)
|
|
151
152
|
}
|
|
@@ -230,6 +231,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
230
231
|
{ source: 'post', viewer: authorRef(record.authorUserId) },
|
|
231
232
|
vocabularyOptions(vocabulary),
|
|
232
233
|
)
|
|
234
|
+
const searchConfig = await readSearchConfig(this.db)
|
|
233
235
|
|
|
234
236
|
return this.db.transaction(async (tx) => {
|
|
235
237
|
const postRows = resultRows(
|
|
@@ -251,7 +253,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
251
253
|
* post in the thread — forty hits that are all the same thread,
|
|
252
254
|
* where the opening post already stands for it.
|
|
253
255
|
*/
|
|
254
|
-
${searchVectorSql(sql`${null}`, sql`${record.message}`)},
|
|
256
|
+
${searchVectorSql(searchConfig, sql`${null}`, sql`${record.message}`)},
|
|
255
257
|
${SEARCH_DOCUMENT_VERSION})
|
|
256
258
|
returning id
|
|
257
259
|
`),
|
|
@@ -274,7 +276,7 @@ export class PostgresThreadWriteRepository implements ThreadWriteRepository, Rep
|
|
|
274
276
|
if (record.subscribe) {
|
|
275
277
|
await tx.execute(sql`
|
|
276
278
|
insert into thread_subscriptions (user_id, thread_id, mode, last_notified_post_id)
|
|
277
|
-
values (${record.authorUserId}, ${record.threadId}, 'instant', ${postId})
|
|
279
|
+
values (${record.authorUserId}, ${record.threadId}, ${record.subscribeMode ?? 'instant'}, ${postId})
|
|
278
280
|
on conflict (user_id, thread_id) do nothing
|
|
279
281
|
`)
|
|
280
282
|
}
|
package/src/upgrade-repo.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm'
|
|
2
2
|
|
|
3
3
|
import type { Database } from './client'
|
|
4
|
+
import { ensurePluginDataRole } from './plugin-role'
|
|
5
|
+
import { resultRows } from './result-rows'
|
|
4
6
|
|
|
5
7
|
export async function readVersion(db: Database, component: string): Promise<string | null> {
|
|
6
8
|
const rows = (await db.execute(sql`
|
|
@@ -49,18 +51,24 @@ export async function applyPluginMigration(
|
|
|
49
51
|
statements: readonly string[],
|
|
50
52
|
): Promise<boolean> {
|
|
51
53
|
return db.transaction(async (tx) => {
|
|
52
|
-
const claimed = (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const claimed = resultRows(
|
|
55
|
+
await tx.execute(sql`
|
|
56
|
+
insert into plugin_migrations (plugin_key, migration_id)
|
|
57
|
+
values (${pluginKey}, ${migrationId})
|
|
58
|
+
on conflict (plugin_key, migration_id) do nothing
|
|
59
|
+
returning migration_id
|
|
60
|
+
`),
|
|
61
|
+
)
|
|
58
62
|
|
|
59
|
-
if (claimed.length === 0)
|
|
63
|
+
if (claimed.length === 0) {
|
|
64
|
+
await ensurePluginDataRole(tx, pluginKey)
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
60
67
|
|
|
61
68
|
for (const statement of statements) {
|
|
62
69
|
await tx.execute(sql.raw(statement))
|
|
63
70
|
}
|
|
71
|
+
await ensurePluginDataRole(tx, pluginKey)
|
|
64
72
|
return true
|
|
65
73
|
})
|
|
66
74
|
}
|
package/src/user-merge-map.ts
CHANGED
|
@@ -74,6 +74,7 @@ export const ACCOUNT_CLOSURE_DISCARD: readonly DiscardColumn[] = [
|
|
|
74
74
|
{ table: 'recovery_codes', column: 'user_id' },
|
|
75
75
|
{ table: 'api_tokens', column: 'user_id' },
|
|
76
76
|
{ table: 'credential_tokens', column: 'user_id' },
|
|
77
|
+
{ table: 'feed_tokens', column: 'user_id' },
|
|
77
78
|
{ table: 'remember_tokens', column: 'user_id' },
|
|
78
79
|
{ table: 'sessions', column: 'user_id' },
|
|
79
80
|
{ table: 'user_identities', column: 'user_id' },
|