@atproto/ozone 0.2.3 → 0.2.4
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/CHANGELOG.md +17 -0
- package/dist/api/health.d.ts.map +1 -1
- package/dist/api/moderation/util.d.ts.map +1 -1
- package/dist/api/util.d.ts +2 -2
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/well-known.d.ts.map +1 -1
- package/dist/assignment/index.d.ts.map +1 -1
- package/dist/assignment/index.js.map +1 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js.map +1 -1
- package/dist/communication-service/template.d.ts.map +1 -1
- package/dist/communication-service/template.js.map +1 -1
- package/dist/communication-service/util.d.ts.map +1 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/secrets.d.ts.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/daemon/blob-diverter.d.ts.map +1 -1
- package/dist/daemon/blob-diverter.js.map +1 -1
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/event-pusher.d.ts +1 -1
- package/dist/daemon/event-pusher.d.ts.map +1 -1
- package/dist/daemon/event-reverser.d.ts.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/materialized-view-refresher.d.ts.map +1 -1
- package/dist/daemon/queue-router.d.ts.map +1 -1
- package/dist/daemon/scheduled-action-processor.d.ts.map +1 -1
- package/dist/daemon/stats-computer.d.ts.map +1 -1
- package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
- package/dist/daemon/team-profile-synchronizer.d.ts.map +1 -1
- package/dist/daemon/verification-listener.d.ts.map +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/migrations/provider.d.ts.map +1 -1
- package/dist/db/migrations/provider.js.map +1 -1
- package/dist/db/pagination.d.ts +1 -1
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/types.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/jetstream/service.d.ts.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +17160 -17038
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +68 -1
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +11 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.js +7 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.d.ts +25 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.js +5 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.js.map +1 -1
- package/dist/lexicon/util.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +38 -38
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/profile.d.ts.map +1 -1
- package/dist/mod-service/status.d.ts +80 -80
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/mod-service/strike.d.ts.map +1 -1
- package/dist/mod-service/strike.js.map +1 -1
- package/dist/mod-service/subject.d.ts +4 -4
- package/dist/mod-service/subject.d.ts.map +1 -1
- package/dist/mod-service/util.d.ts +1 -1
- package/dist/mod-service/util.d.ts.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js.map +1 -1
- package/dist/queue/service.d.ts.map +1 -1
- package/dist/queue/service.js.map +1 -1
- package/dist/report/activity.d.ts +5 -5
- package/dist/report/activity.d.ts.map +1 -1
- package/dist/report/handle-report-update.d.ts.map +1 -1
- package/dist/report/handle-report-update.js.map +1 -1
- package/dist/report/stats.d.ts.map +1 -1
- package/dist/report/stats.js.map +1 -1
- package/dist/report/views.d.ts +2 -2
- package/dist/report/views.d.ts.map +1 -1
- package/dist/safelink/service.d.ts +3 -3
- package/dist/safelink/service.d.ts.map +1 -1
- package/dist/safelink/service.js.map +1 -1
- package/dist/scheduled-action/service.d.ts.map +1 -1
- package/dist/scheduled-action/service.js.map +1 -1
- package/dist/sequencer/outbox.d.ts.map +1 -1
- package/dist/sequencer/outbox.js.map +1 -1
- package/dist/sequencer/sequencer.d.ts.map +1 -1
- package/dist/sequencer/sequencer.js.map +1 -1
- package/dist/set/service.d.ts +1 -1
- package/dist/set/service.d.ts.map +1 -1
- package/dist/set/service.js.map +1 -1
- package/dist/setting/service.d.ts.map +1 -1
- package/dist/setting/service.js.map +1 -1
- package/dist/tag-service/content-tagger.d.ts.map +1 -1
- package/dist/tag-service/content-tagger.js.map +1 -1
- package/dist/tag-service/embed-tagger.d.ts.map +1 -1
- package/dist/tag-service/index.d.ts.map +1 -1
- package/dist/tag-service/index.js.map +1 -1
- package/dist/tag-service/language-tagger.d.ts.map +1 -1
- package/dist/tag-service/util.d.ts.map +1 -1
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/verification/issuer.d.ts +1 -1
- package/dist/verification/issuer.d.ts.map +1 -1
- package/dist/verification/service.d.ts +5 -5
- package/dist/verification/service.d.ts.map +1 -1
- package/dist/verification/service.js.map +1 -1
- package/dist/verification/util.d.ts.map +1 -1
- package/package.json +13 -14
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/report/stats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable, sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { Database } from '../db/index.js'\nimport { ComputedAtIdKeyset, paginate } from '../db/pagination.js'\nimport { ReportStat } from '../db/schema/report_stat.js'\nimport { jsonb } from '../db/types.js'\nimport { dbLogger } from '../logger.js'\n\n/**\n * Grouped report types. Stats are computed per group rather than per individual report type.\n */\nexport const REPORT_TYPE_GROUPS: Record<string, string[]> = {\n Legacy: [\n 'com.atproto.moderation.defs#reasonSpam',\n 'com.atproto.moderation.defs#reasonViolation',\n 'com.atproto.moderation.defs#reasonMisleading',\n 'com.atproto.moderation.defs#reasonSexual',\n 'com.atproto.moderation.defs#reasonRude',\n 'com.atproto.moderation.defs#reasonOther',\n 'com.atproto.moderation.defs#reasonAppeal',\n ],\n Appeal: ['tools.ozone.report.defs#reasonAppeal'],\n Violence: [\n 'tools.ozone.report.defs#reasonViolenceAnimalWelfare',\n 'tools.ozone.report.defs#reasonViolenceThreats',\n 'tools.ozone.report.defs#reasonViolenceGraphicContent',\n 'tools.ozone.report.defs#reasonViolenceSelfHarm',\n 'tools.ozone.report.defs#reasonViolenceGlorification',\n 'tools.ozone.report.defs#reasonViolenceExtremistContent',\n 'tools.ozone.report.defs#reasonViolenceTrafficking',\n 'tools.ozone.report.defs#reasonViolenceOther',\n ],\n Sexual: [\n 'tools.ozone.report.defs#reasonSexualAbuseContent',\n 'tools.ozone.report.defs#reasonSexualNCII',\n 'tools.ozone.report.defs#reasonSexualSextortion',\n 'tools.ozone.report.defs#reasonSexualDeepfake',\n 'tools.ozone.report.defs#reasonSexualAnimal',\n 'tools.ozone.report.defs#reasonSexualUnlabeled',\n 'tools.ozone.report.defs#reasonSexualOther',\n ],\n 'Child Safety': [\n 'tools.ozone.report.defs#reasonChildSafetyCSAM',\n 'tools.ozone.report.defs#reasonChildSafetyGroom',\n 'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy',\n 'tools.ozone.report.defs#reasonChildSafetyEndangerment',\n 'tools.ozone.report.defs#reasonChildSafetyHarassment',\n 'tools.ozone.report.defs#reasonChildSafetyPromotion',\n 'tools.ozone.report.defs#reasonChildSafetyOther',\n ],\n Harassment: [\n 'tools.ozone.report.defs#reasonHarassmentTroll',\n 'tools.ozone.report.defs#reasonHarassmentTargeted',\n 'tools.ozone.report.defs#reasonHarassmentHateSpeech',\n 'tools.ozone.report.defs#reasonHarassmentDoxxing',\n 'tools.ozone.report.defs#reasonHarassmentOther',\n ],\n Misleading: [\n 'tools.ozone.report.defs#reasonMisleadingBot',\n 'tools.ozone.report.defs#reasonMisleadingImpersonation',\n 'tools.ozone.report.defs#reasonMisleadingSpam',\n 'tools.ozone.report.defs#reasonMisleadingScam',\n 'tools.ozone.report.defs#reasonMisleadingSyntheticContent',\n 'tools.ozone.report.defs#reasonMisleadingMisinformation',\n 'tools.ozone.report.defs#reasonMisleadingOther',\n ],\n 'Rule Violations': [\n 'tools.ozone.report.defs#reasonRuleSiteSecurity',\n 'tools.ozone.report.defs#reasonRuleStolenContent',\n 'tools.ozone.report.defs#reasonRuleProhibitedSales',\n 'tools.ozone.report.defs#reasonRuleBanEvasion',\n 'tools.ozone.report.defs#reasonRuleOther',\n ],\n Civic: [\n 'tools.ozone.report.defs#reasonCivicElectoralProcess',\n 'tools.ozone.report.defs#reasonCivicDisclosure',\n 'tools.ozone.report.defs#reasonCivicInterference',\n 'tools.ozone.report.defs#reasonCivicMisinformation',\n 'tools.ozone.report.defs#reasonCivicImpersonation',\n ],\n}\n\nconst REPORT_STAT_LIVE_TTL = 15 * MINUTE\n\nexport type ReportStatsServiceCreator = (db: Database) => ReportStatsService\n\nexport type ReportStatGroup = {\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n}\nexport type AggregateStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type QueueStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ModeratorStatistics = {\n inboundCount: number\n actionedCount: number\n avgHandlingTimeSec?: number\n}\nexport type ReportTypeStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ReportStatistics =\n | QueueStatistics\n | ModeratorStatistics\n | AggregateStatistics\n | ReportTypeStatistics\n\n// Batched query result types\ntype QueueCountRow = {\n queueId: number | null\n count: string\n}\ntype QueueWindowRow = {\n queueId: number | null\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype TypeCountRow = {\n reportType: string\n count: string\n}\ntype TypeWindowRow = {\n reportType: string\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype ModeratorWindowRow = {\n did: string\n inboundCount: string\n actionedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype BatchedStats = {\n queuePending: QueueCountRow[]\n queueWindow: QueueWindowRow[]\n typePending: TypeCountRow[]\n typeWindow: TypeWindowRow[]\n moderator: ModeratorWindowRow[]\n}\n\ntype UpsertRow = {\n date: string\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n inboundCount: number | null\n pendingCount: number | null\n actionedCount: number | null\n escalatedCount: number | null\n actionRate: number | null\n avgHandlingTimeSec: number | null\n computedAt: string\n}\n\nexport class ReportStatsService {\n constructor(public db: Database) {}\n\n static creator(): ReportStatsServiceCreator {\n return (db: Database) => new ReportStatsService(db)\n }\n\n /**\n * Compute stats for today and finalize yesterday if needed.\n * Called periodically by the StatsComputer daemon.\n */\n async materializeAll(opts?: { force?: boolean }): Promise<void> {\n try {\n const start = Date.now()\n const today = toDateString(new Date())\n const yesterday = toDateString(new Date(Date.now() - 24 * 60 * 60 * 1000))\n\n // Always compute today's stats\n await this.materializeDate(today, opts)\n\n // Finalize yesterday if its snapshot is missing or stale\n if (!opts?.force) {\n const yesterdayRow = await this.db.db\n .selectFrom('report_stat')\n .select('computedAt')\n .where('date', '=', yesterday)\n .orderBy('computedAt', 'desc')\n .executeTakeFirst()\n const endOfYesterday = new Date(`${yesterday}T23:59:59.999Z`).getTime()\n if (\n !yesterdayRow ||\n new Date(yesterdayRow.computedAt).getTime() < endOfYesterday\n ) {\n await this.materializeDate(yesterday, { force: true })\n }\n } else {\n await this.materializeDate(yesterday, { force: true })\n }\n\n const duration = Date.now() - start\n dbLogger.info({ duration }, 'report stats materialization completed')\n } catch (err) {\n dbLogger.error({ err }, 'report stats materialization errored')\n }\n }\n\n /**\n * Compute stats for a specific date range. Used by the refreshStats endpoint.\n */\n async refreshDateRange(opts: {\n startDate: string\n endDate: string\n queueIds?: number[]\n }): Promise<void> {\n const start = new Date(opts.startDate)\n const end = new Date(opts.endDate)\n\n for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {\n const dateStr = toDateString(d)\n if (opts.queueIds?.length) {\n // Recompute only specific queue groups for this date\n const batched = await this.computeBatchedStats(dateStr)\n const rows: UpsertRow[] = []\n for (const queueId of opts.queueIds) {\n const group: ReportStatGroup = {\n queueId,\n moderatorDid: null,\n reportTypes: null,\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(dateStr, group, stats))\n }\n await this.bulkUpsert(rows)\n } else {\n await this.materializeDate(dateStr, { force: true })\n }\n }\n }\n\n /** Compute and write all groups for a single date. */\n private async materializeDate(\n date: string,\n opts?: { force?: boolean },\n ): Promise<void> {\n const groups = await this.enumerateGroups()\n const batched = await this.computeBatchedStats(date)\n const today = toDateString(new Date())\n const isToday = date === today\n\n // Batch the cache check so we don't issue one SELECT per group.\n const existingByKey = !opts?.force\n ? await this.fetchExistingStatsByKey(date)\n : null\n\n const rows: UpsertRow[] = []\n for (const group of groups) {\n try {\n if (existingByKey) {\n const cached = existingByKey.get(groupKey(group))\n if (cached) {\n // Historical dates: never recompute. Today: recompute if stale.\n if (!isToday) continue\n const age = Date.now() - new Date(cached.computedAt).getTime()\n if (age < REPORT_STAT_LIVE_TTL) continue\n }\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(date, group, stats))\n } catch (err) {\n dbLogger.error(\n { err, group, date },\n 'error preparing report stats group',\n )\n }\n }\n\n await this.bulkUpsert(rows)\n }\n\n /** Fetch all stat rows for a date, keyed by groupKey for O(1) lookup. */\n private async fetchExistingStatsByKey(\n date: string,\n ): Promise<Map<string, Selectable<ReportStat>>> {\n const existing = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n .execute()\n const map = new Map<string, Selectable<ReportStat>>()\n for (const row of existing) {\n map.set(\n groupKey({\n queueId: row.queueId,\n moderatorDid: row.moderatorDid,\n reportTypes: row.reportTypes,\n }),\n row,\n )\n }\n return map\n }\n\n /** List out the groups to compute stats for. */\n private async enumerateGroups(): Promise<ReportStatGroup[]> {\n const groups: ReportStatGroup[] = []\n\n const queues = await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('enabled', '=', true)\n .where('deletedAt', 'is', null)\n .execute()\n const members = await this.db.db\n .selectFrom('member')\n .select('did')\n .where('disabled', '=', false)\n .where('role', 'in', [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n ])\n .execute()\n\n // aggregate\n groups.push({ queueId: null, moderatorDid: null, reportTypes: null })\n // per queue\n for (const queue of queues) {\n groups.push({ queueId: queue.id, moderatorDid: null, reportTypes: null })\n }\n // unqueued\n groups.push({ queueId: -1, moderatorDid: null, reportTypes: null })\n // per moderator\n for (const member of members) {\n groups.push({\n queueId: null,\n moderatorDid: member.did,\n reportTypes: null,\n })\n }\n // per report type group\n for (const groupTypes of Object.values(REPORT_TYPE_GROUPS)) {\n groups.push({\n queueId: null,\n moderatorDid: null,\n reportTypes: groupTypes,\n })\n }\n\n return groups\n }\n\n /**\n * Run batched GROUP BY queries for a calendar date.\n * Returns 5 result sets covering all group types.\n */\n private async computeBatchedStats(date: string): Promise<BatchedStats> {\n const dayStart = `${date}T00:00:00.000Z`\n const dayEnd = `${nextDate(date)}T00:00:00.000Z`\n\n const [queuePending, aggregatePending] = await Promise.all([\n // Pending count is a snapshot of all non-closed reports at time of computation\n this.db.db\n .selectFrom('report')\n .select(['queueId', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute(),\n // Aggregate pending (includes all reports, even un-routed)\n this.db.db\n .selectFrom('report')\n .select(sql<string>`count(*)`.as('count'))\n .where('status', '!=', 'closed')\n .executeTakeFirst(),\n ])\n\n const queueWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'queueId',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute()\n\n // Aggregate windowed (includes all reports)\n const aggregateWindow = await this.db.db\n .selectFrom('report')\n .select([\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .executeTakeFirst()\n\n const typePending = await this.db.db\n .selectFrom('report')\n .select(['reportType', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .groupBy('reportType')\n .execute()\n\n const typeWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'reportType',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .groupBy('reportType')\n .execute()\n\n const moderator = await this.db.db\n .selectFrom('report as r')\n .innerJoin('moderator_assignment as ma', (join) =>\n join.onRef('ma.reportId', '=', 'r.id').on('ma.endAt', 'is', null),\n )\n .select([\n 'ma.did',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where r.\"status\" = 'closed')`.as(\n 'actionedCount',\n ),\n sql<string>`sum(extract(epoch from (r.\"closedAt\"::timestamp - ma.\"startAt\"::timestamp))) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('r.createdAt', '>=', dayStart)\n .where('r.createdAt', '<', dayEnd)\n .groupBy('ma.did')\n .execute()\n\n // Inject aggregate as a synthetic row with queueId=null so resolveQueueStats can find it\n const allQueuePending: QueueCountRow[] = [\n ...queuePending,\n { queueId: null, count: aggregatePending?.count ?? '0' },\n ]\n const allQueueWindow: QueueWindowRow[] = aggregateWindow\n ? [\n ...queueWindow,\n {\n queueId: null,\n inboundCount: aggregateWindow.inboundCount,\n actionedCount: aggregateWindow.actionedCount,\n escalatedCount: aggregateWindow.escalatedCount,\n handlingTimeSum: aggregateWindow.handlingTimeSum,\n handlingTimeCount: aggregateWindow.handlingTimeCount,\n },\n ]\n : queueWindow\n\n return {\n queuePending: allQueuePending,\n queueWindow: allQueueWindow,\n typePending,\n typeWindow,\n moderator,\n }\n }\n\n /** Resolve a single group's stats from batched query results (pure in-memory). */\n private resolveGroupStats(\n group: ReportStatGroup,\n batched: BatchedStats,\n ): ReportStatistics {\n if (group.moderatorDid) {\n return this.resolveModeratorStats(group.moderatorDid, batched.moderator)\n }\n if (group.reportTypes !== null) {\n return this.resolveReportTypeStats(group.reportTypes, batched)\n }\n return this.resolveQueueStats(group.queueId, batched)\n }\n\n private resolveQueueStats(\n queueId: number | null,\n batched: BatchedStats,\n ): AggregateStatistics | QueueStatistics {\n // queueId=null is the synthetic aggregate row\n const pending = batched.queuePending.find((r) => r.queueId === queueId)\n const window = batched.queueWindow.find((r) => r.queueId === queueId)\n\n const pendingCount = num(pending?.count)\n const inboundCount = num(window?.inboundCount)\n const actionedCount = num(window?.actionedCount)\n const escalatedCount = num(window?.escalatedCount)\n const handlingTimeSum = Number(window?.handlingTimeSum ?? 0)\n const handlingTimeCount = num(window?.handlingTimeCount)\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveReportTypeStats(\n reportTypes: string[],\n batched: BatchedStats,\n ): ReportTypeStatistics {\n const types = new Set(reportTypes)\n\n const matchingPending = batched.typePending.filter((r) =>\n types.has(r.reportType),\n )\n const matchingWindow = batched.typeWindow.filter((r) =>\n types.has(r.reportType),\n )\n\n const pendingCount = sumNum(matchingPending, 'count')\n const inboundCount = sumNum(matchingWindow, 'inboundCount')\n const actionedCount = sumNum(matchingWindow, 'actionedCount')\n const escalatedCount = sumNum(matchingWindow, 'escalatedCount')\n const handlingTimeSum = matchingWindow.reduce(\n (sum, r) => sum + Number(r.handlingTimeSum ?? 0),\n 0,\n )\n const handlingTimeCount = sumNum(matchingWindow, 'handlingTimeCount')\n\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveModeratorStats(\n moderatorDid: string,\n rows: ModeratorWindowRow[],\n ): ModeratorStatistics {\n const row = rows.find((r) => r.did === moderatorDid)\n\n const inboundCount = num(row?.inboundCount)\n const actionedCount = num(row?.actionedCount)\n const handlingTimeCount = num(row?.handlingTimeCount)\n const avgHandlingTimeSec =\n handlingTimeCount > 0 && row?.handlingTimeSum\n ? Math.round(Number(row.handlingTimeSum) / handlingTimeCount)\n : undefined\n\n return { inboundCount, actionedCount, avgHandlingTimeSec }\n }\n\n /** Build an upsert row from (date, group, stats). */\n private buildUpsertRow(\n date: string,\n group: ReportStatGroup,\n stats: ReportStatistics,\n ): UpsertRow {\n const pendingCount =\n 'pendingCount' in stats ? stats.pendingCount ?? null : null\n const escalatedCount =\n 'escalatedCount' in stats ? stats.escalatedCount ?? null : null\n const actionRate = 'actionRate' in stats ? stats.actionRate ?? null : null\n\n return {\n date,\n queueId: group.queueId,\n moderatorDid: group.moderatorDid,\n reportTypes: group.reportTypes,\n inboundCount: stats.inboundCount ?? null,\n pendingCount,\n actionedCount: stats.actionedCount ?? null,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec: stats.avgHandlingTimeSec ?? null,\n computedAt: new Date().toISOString(),\n }\n }\n\n /**\n * Wraps a DELETE+INSERT for each row in a single transaction so we pay one\n * commit per cycle instead of one per group. NULL-aware WHERE clauses match\n * the existing PG <15 NULL semantics without needing a unique index.\n */\n private async bulkUpsert(rows: UpsertRow[]): Promise<void> {\n if (!rows.length) return\n\n await this.db.transaction(async (dbTxn) => {\n for (const r of rows) {\n let del = dbTxn.db.deleteFrom('report_stat').where('date', '=', r.date)\n del =\n r.queueId !== null\n ? del.where('queueId', '=', r.queueId)\n : del.where('queueId', 'is', null)\n del =\n r.moderatorDid !== null\n ? del.where('moderatorDid', '=', r.moderatorDid)\n : del.where('moderatorDid', 'is', null)\n del =\n r.reportTypes !== null\n ? del.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(r.reportTypes)}::jsonb`,\n )\n : del.where('reportTypes', 'is', null)\n await del.execute()\n\n await dbTxn.db\n .insertInto('report_stat')\n .values({\n date: r.date,\n queueId: r.queueId,\n moderatorDid: r.moderatorDid,\n reportTypes: r.reportTypes !== null ? jsonb(r.reportTypes) : null,\n inboundCount: r.inboundCount,\n pendingCount: r.pendingCount,\n actionedCount: r.actionedCount,\n escalatedCount: r.escalatedCount,\n actionRate: r.actionRate,\n avgHandlingTimeSec: r.avgHandlingTimeSec,\n computedAt: r.computedAt,\n })\n .execute()\n }\n })\n }\n\n // ─── Read methods ───\n\n /** Get a single stat row for a date + group. */\n private async getStatForDate(\n date: string,\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n let qb = this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n if (group.queueId !== null) {\n qb = qb.where('queueId', '=', group.queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (group.moderatorDid) {\n qb = qb.where('moderatorDid', '=', group.moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (group.reportTypes !== null) {\n qb = qb.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(group.reportTypes)}::jsonb`,\n )\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n return qb.executeTakeFirst()\n }\n\n /** Get today's live stats for a group. */\n async getLiveStats(\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n const today = toDateString(new Date())\n return this.getStatForDate(today, group)\n }\n\n /** Get live stats for multiple queues in a single query. */\n async getLiveStatsForQueues(\n queueIds: number[],\n ): Promise<Map<number, Selectable<ReportStat>>> {\n if (!queueIds.length) return new Map()\n\n const today = toDateString(new Date())\n const rows = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', today)\n .where('queueId', 'in', queueIds)\n .where('moderatorDid', 'is', null)\n .where('reportTypes', 'is', null)\n .execute()\n\n const result = new Map<number, Selectable<ReportStat>>()\n for (const row of rows) {\n if (row.queueId !== null) {\n result.set(row.queueId, row)\n }\n }\n return result\n }\n\n /** Get historical stats for a date range, paginated. */\n async getHistoricalStats(opts: {\n group: ReportStatGroup\n startDate?: string\n endDate?: string\n limit: number\n cursor?: string\n }): Promise<{ stats: Selectable<ReportStat>[]; cursor?: string }> {\n const { group, startDate, endDate, limit } = opts\n const { queueId, moderatorDid, reportTypes } = group\n const { ref } = this.db.db.dynamic\n\n let qb = this.db.db.selectFrom('report_stat').selectAll()\n\n if (queueId !== null) {\n qb = qb.where('queueId', '=', queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (moderatorDid) {\n qb = qb.where('moderatorDid', '=', moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (reportTypes !== null) {\n qb = qb.where(sql`\"reportTypes\"::jsonb = ${jsonb(reportTypes)}::jsonb`)\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n if (startDate) {\n qb = qb.where('date', '>=', toDateString(new Date(startDate)))\n }\n if (endDate) {\n qb = qb.where('date', '<=', toDateString(new Date(endDate)))\n }\n\n const keyset = new ComputedAtIdKeyset(ref('computedAt'), ref('id'))\n const paginatedBuilder = paginate(qb, {\n limit,\n cursor: opts.cursor,\n keyset,\n direction: 'desc',\n tryIndex: true,\n })\n\n const stats = await paginatedBuilder.execute()\n\n return { stats, cursor: keyset.packFromResult(stats) }\n }\n}\n\n// ─── Helpers ───\n\n/** Parse a pg bigint string to number, defaulting to 0. */\nfunction num(val: string | undefined | null): number {\n return val ? Number(val) : 0\n}\n\n/** Sum a numeric string field across rows. */\nfunction sumNum<T>(rows: T[], field: keyof T): number {\n return rows.reduce((sum, r) => sum + Number(r[field] ?? 0), 0)\n}\n\n/**\n * Stable cache-key for a stat group. Used to look up an existing row in the\n * batched cache map without issuing per-group SELECTs. Report types are\n * stringified in stored order, which matches REPORT_TYPE_GROUPS.\n */\nfunction groupKey(g: ReportStatGroup): string {\n return [\n g.queueId ?? 'null',\n g.moderatorDid ?? 'null',\n g.reportTypes ? JSON.stringify(g.reportTypes) : 'null',\n ].join('|')\n}\n\n/** Convert a Date to an ISO date string (YYYY-MM-DD). */\nfunction toDateString(d: Date): string {\n return d.toISOString().slice(0, 10)\n}\n\n/** Get the next calendar date string. */\nfunction nextDate(dateStr: string): string {\n const d = new Date(`${dateStr}T00:00:00.000Z`)\n d.setUTCDate(d.getUTCDate() + 1)\n return toDateString(d)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable, sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { Database } from '../db/index.js'\nimport { ComputedAtIdKeyset, paginate } from '../db/pagination.js'\nimport { ReportStat } from '../db/schema/report_stat.js'\nimport { jsonb } from '../db/types.js'\nimport { dbLogger } from '../logger.js'\n\n/**\n * Grouped report types. Stats are computed per group rather than per individual report type.\n */\nexport const REPORT_TYPE_GROUPS: Record<string, string[]> = {\n Legacy: [\n 'com.atproto.moderation.defs#reasonSpam',\n 'com.atproto.moderation.defs#reasonViolation',\n 'com.atproto.moderation.defs#reasonMisleading',\n 'com.atproto.moderation.defs#reasonSexual',\n 'com.atproto.moderation.defs#reasonRude',\n 'com.atproto.moderation.defs#reasonOther',\n 'com.atproto.moderation.defs#reasonAppeal',\n ],\n Appeal: ['tools.ozone.report.defs#reasonAppeal'],\n Violence: [\n 'tools.ozone.report.defs#reasonViolenceAnimalWelfare',\n 'tools.ozone.report.defs#reasonViolenceThreats',\n 'tools.ozone.report.defs#reasonViolenceGraphicContent',\n 'tools.ozone.report.defs#reasonViolenceSelfHarm',\n 'tools.ozone.report.defs#reasonViolenceGlorification',\n 'tools.ozone.report.defs#reasonViolenceExtremistContent',\n 'tools.ozone.report.defs#reasonViolenceTrafficking',\n 'tools.ozone.report.defs#reasonViolenceOther',\n ],\n Sexual: [\n 'tools.ozone.report.defs#reasonSexualAbuseContent',\n 'tools.ozone.report.defs#reasonSexualNCII',\n 'tools.ozone.report.defs#reasonSexualSextortion',\n 'tools.ozone.report.defs#reasonSexualDeepfake',\n 'tools.ozone.report.defs#reasonSexualAnimal',\n 'tools.ozone.report.defs#reasonSexualUnlabeled',\n 'tools.ozone.report.defs#reasonSexualOther',\n ],\n 'Child Safety': [\n 'tools.ozone.report.defs#reasonChildSafetyCSAM',\n 'tools.ozone.report.defs#reasonChildSafetyGroom',\n 'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy',\n 'tools.ozone.report.defs#reasonChildSafetyEndangerment',\n 'tools.ozone.report.defs#reasonChildSafetyHarassment',\n 'tools.ozone.report.defs#reasonChildSafetyPromotion',\n 'tools.ozone.report.defs#reasonChildSafetyOther',\n ],\n Harassment: [\n 'tools.ozone.report.defs#reasonHarassmentTroll',\n 'tools.ozone.report.defs#reasonHarassmentTargeted',\n 'tools.ozone.report.defs#reasonHarassmentHateSpeech',\n 'tools.ozone.report.defs#reasonHarassmentDoxxing',\n 'tools.ozone.report.defs#reasonHarassmentOther',\n ],\n Misleading: [\n 'tools.ozone.report.defs#reasonMisleadingBot',\n 'tools.ozone.report.defs#reasonMisleadingImpersonation',\n 'tools.ozone.report.defs#reasonMisleadingSpam',\n 'tools.ozone.report.defs#reasonMisleadingScam',\n 'tools.ozone.report.defs#reasonMisleadingSyntheticContent',\n 'tools.ozone.report.defs#reasonMisleadingMisinformation',\n 'tools.ozone.report.defs#reasonMisleadingOther',\n ],\n 'Rule Violations': [\n 'tools.ozone.report.defs#reasonRuleSiteSecurity',\n 'tools.ozone.report.defs#reasonRuleStolenContent',\n 'tools.ozone.report.defs#reasonRuleProhibitedSales',\n 'tools.ozone.report.defs#reasonRuleBanEvasion',\n 'tools.ozone.report.defs#reasonRuleOther',\n ],\n Civic: [\n 'tools.ozone.report.defs#reasonCivicElectoralProcess',\n 'tools.ozone.report.defs#reasonCivicDisclosure',\n 'tools.ozone.report.defs#reasonCivicInterference',\n 'tools.ozone.report.defs#reasonCivicMisinformation',\n 'tools.ozone.report.defs#reasonCivicImpersonation',\n ],\n}\n\nconst REPORT_STAT_LIVE_TTL = 15 * MINUTE\n\nexport type ReportStatsServiceCreator = (db: Database) => ReportStatsService\n\nexport type ReportStatGroup = {\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n}\nexport type AggregateStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type QueueStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ModeratorStatistics = {\n inboundCount: number\n actionedCount: number\n avgHandlingTimeSec?: number\n}\nexport type ReportTypeStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ReportStatistics =\n | QueueStatistics\n | ModeratorStatistics\n | AggregateStatistics\n | ReportTypeStatistics\n\n// Batched query result types\ntype QueueCountRow = {\n queueId: number | null\n count: string\n}\ntype QueueWindowRow = {\n queueId: number | null\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype TypeCountRow = {\n reportType: string\n count: string\n}\ntype TypeWindowRow = {\n reportType: string\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype ModeratorWindowRow = {\n did: string\n inboundCount: string\n actionedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype BatchedStats = {\n queuePending: QueueCountRow[]\n queueWindow: QueueWindowRow[]\n typePending: TypeCountRow[]\n typeWindow: TypeWindowRow[]\n moderator: ModeratorWindowRow[]\n}\n\ntype UpsertRow = {\n date: string\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n inboundCount: number | null\n pendingCount: number | null\n actionedCount: number | null\n escalatedCount: number | null\n actionRate: number | null\n avgHandlingTimeSec: number | null\n computedAt: string\n}\n\nexport class ReportStatsService {\n constructor(public db: Database) {}\n\n static creator(): ReportStatsServiceCreator {\n return (db: Database) => new ReportStatsService(db)\n }\n\n /**\n * Compute stats for today and finalize yesterday if needed.\n * Called periodically by the StatsComputer daemon.\n */\n async materializeAll(opts?: { force?: boolean }): Promise<void> {\n try {\n const start = Date.now()\n const today = toDateString(new Date())\n const yesterday = toDateString(new Date(Date.now() - 24 * 60 * 60 * 1000))\n\n // Always compute today's stats\n await this.materializeDate(today, opts)\n\n // Finalize yesterday if its snapshot is missing or stale\n if (!opts?.force) {\n const yesterdayRow = await this.db.db\n .selectFrom('report_stat')\n .select('computedAt')\n .where('date', '=', yesterday)\n .orderBy('computedAt', 'desc')\n .executeTakeFirst()\n const endOfYesterday = new Date(`${yesterday}T23:59:59.999Z`).getTime()\n if (\n !yesterdayRow ||\n new Date(yesterdayRow.computedAt).getTime() < endOfYesterday\n ) {\n await this.materializeDate(yesterday, { force: true })\n }\n } else {\n await this.materializeDate(yesterday, { force: true })\n }\n\n const duration = Date.now() - start\n dbLogger.info({ duration }, 'report stats materialization completed')\n } catch (err) {\n dbLogger.error({ err }, 'report stats materialization errored')\n }\n }\n\n /**\n * Compute stats for a specific date range. Used by the refreshStats endpoint.\n */\n async refreshDateRange(opts: {\n startDate: string\n endDate: string\n queueIds?: number[]\n }): Promise<void> {\n const start = new Date(opts.startDate)\n const end = new Date(opts.endDate)\n\n for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {\n const dateStr = toDateString(d)\n if (opts.queueIds?.length) {\n // Recompute only specific queue groups for this date\n const batched = await this.computeBatchedStats(dateStr)\n const rows: UpsertRow[] = []\n for (const queueId of opts.queueIds) {\n const group: ReportStatGroup = {\n queueId,\n moderatorDid: null,\n reportTypes: null,\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(dateStr, group, stats))\n }\n await this.bulkUpsert(rows)\n } else {\n await this.materializeDate(dateStr, { force: true })\n }\n }\n }\n\n /** Compute and write all groups for a single date. */\n private async materializeDate(\n date: string,\n opts?: { force?: boolean },\n ): Promise<void> {\n const groups = await this.enumerateGroups()\n const batched = await this.computeBatchedStats(date)\n const today = toDateString(new Date())\n const isToday = date === today\n\n // Batch the cache check so we don't issue one SELECT per group.\n const existingByKey = !opts?.force\n ? await this.fetchExistingStatsByKey(date)\n : null\n\n const rows: UpsertRow[] = []\n for (const group of groups) {\n try {\n if (existingByKey) {\n const cached = existingByKey.get(groupKey(group))\n if (cached) {\n // Historical dates: never recompute. Today: recompute if stale.\n if (!isToday) continue\n const age = Date.now() - new Date(cached.computedAt).getTime()\n if (age < REPORT_STAT_LIVE_TTL) continue\n }\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(date, group, stats))\n } catch (err) {\n dbLogger.error(\n { err, group, date },\n 'error preparing report stats group',\n )\n }\n }\n\n await this.bulkUpsert(rows)\n }\n\n /** Fetch all stat rows for a date, keyed by groupKey for O(1) lookup. */\n private async fetchExistingStatsByKey(\n date: string,\n ): Promise<Map<string, Selectable<ReportStat>>> {\n const existing = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n .execute()\n const map = new Map<string, Selectable<ReportStat>>()\n for (const row of existing) {\n map.set(\n groupKey({\n queueId: row.queueId,\n moderatorDid: row.moderatorDid,\n reportTypes: row.reportTypes,\n }),\n row,\n )\n }\n return map\n }\n\n /** List out the groups to compute stats for. */\n private async enumerateGroups(): Promise<ReportStatGroup[]> {\n const groups: ReportStatGroup[] = []\n\n const queues = await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('enabled', '=', true)\n .where('deletedAt', 'is', null)\n .execute()\n const members = await this.db.db\n .selectFrom('member')\n .select('did')\n .where('disabled', '=', false)\n .where('role', 'in', [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n ])\n .execute()\n\n // aggregate\n groups.push({ queueId: null, moderatorDid: null, reportTypes: null })\n // per queue\n for (const queue of queues) {\n groups.push({ queueId: queue.id, moderatorDid: null, reportTypes: null })\n }\n // unqueued\n groups.push({ queueId: -1, moderatorDid: null, reportTypes: null })\n // per moderator\n for (const member of members) {\n groups.push({\n queueId: null,\n moderatorDid: member.did,\n reportTypes: null,\n })\n }\n // per report type group\n for (const groupTypes of Object.values(REPORT_TYPE_GROUPS)) {\n groups.push({\n queueId: null,\n moderatorDid: null,\n reportTypes: groupTypes,\n })\n }\n\n return groups\n }\n\n /**\n * Run batched GROUP BY queries for a calendar date.\n * Returns 5 result sets covering all group types.\n */\n private async computeBatchedStats(date: string): Promise<BatchedStats> {\n const dayStart = `${date}T00:00:00.000Z`\n const dayEnd = `${nextDate(date)}T00:00:00.000Z`\n\n const [queuePending, aggregatePending] = await Promise.all([\n // Pending count is a snapshot of all non-closed reports at time of computation\n this.db.db\n .selectFrom('report')\n .select(['queueId', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute(),\n // Aggregate pending (includes all reports, even un-routed)\n this.db.db\n .selectFrom('report')\n .select(sql<string>`count(*)`.as('count'))\n .where('status', '!=', 'closed')\n .executeTakeFirst(),\n ])\n\n const queueWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'queueId',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute()\n\n // Aggregate windowed (includes all reports)\n const aggregateWindow = await this.db.db\n .selectFrom('report')\n .select([\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .executeTakeFirst()\n\n const typePending = await this.db.db\n .selectFrom('report')\n .select(['reportType', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .groupBy('reportType')\n .execute()\n\n const typeWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'reportType',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .groupBy('reportType')\n .execute()\n\n const moderator = await this.db.db\n .selectFrom('report as r')\n .innerJoin('moderator_assignment as ma', (join) =>\n join.onRef('ma.reportId', '=', 'r.id').on('ma.endAt', 'is', null),\n )\n .select([\n 'ma.did',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where r.\"status\" = 'closed')`.as(\n 'actionedCount',\n ),\n sql<string>`sum(extract(epoch from (r.\"closedAt\"::timestamp - ma.\"startAt\"::timestamp))) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('r.createdAt', '>=', dayStart)\n .where('r.createdAt', '<', dayEnd)\n .groupBy('ma.did')\n .execute()\n\n // Inject aggregate as a synthetic row with queueId=null so resolveQueueStats can find it\n const allQueuePending: QueueCountRow[] = [\n ...queuePending,\n { queueId: null, count: aggregatePending?.count ?? '0' },\n ]\n const allQueueWindow: QueueWindowRow[] = aggregateWindow\n ? [\n ...queueWindow,\n {\n queueId: null,\n inboundCount: aggregateWindow.inboundCount,\n actionedCount: aggregateWindow.actionedCount,\n escalatedCount: aggregateWindow.escalatedCount,\n handlingTimeSum: aggregateWindow.handlingTimeSum,\n handlingTimeCount: aggregateWindow.handlingTimeCount,\n },\n ]\n : queueWindow\n\n return {\n queuePending: allQueuePending,\n queueWindow: allQueueWindow,\n typePending,\n typeWindow,\n moderator,\n }\n }\n\n /** Resolve a single group's stats from batched query results (pure in-memory). */\n private resolveGroupStats(\n group: ReportStatGroup,\n batched: BatchedStats,\n ): ReportStatistics {\n if (group.moderatorDid) {\n return this.resolveModeratorStats(group.moderatorDid, batched.moderator)\n }\n if (group.reportTypes !== null) {\n return this.resolveReportTypeStats(group.reportTypes, batched)\n }\n return this.resolveQueueStats(group.queueId, batched)\n }\n\n private resolveQueueStats(\n queueId: number | null,\n batched: BatchedStats,\n ): AggregateStatistics | QueueStatistics {\n // queueId=null is the synthetic aggregate row\n const pending = batched.queuePending.find((r) => r.queueId === queueId)\n const window = batched.queueWindow.find((r) => r.queueId === queueId)\n\n const pendingCount = num(pending?.count)\n const inboundCount = num(window?.inboundCount)\n const actionedCount = num(window?.actionedCount)\n const escalatedCount = num(window?.escalatedCount)\n const handlingTimeSum = Number(window?.handlingTimeSum ?? 0)\n const handlingTimeCount = num(window?.handlingTimeCount)\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveReportTypeStats(\n reportTypes: string[],\n batched: BatchedStats,\n ): ReportTypeStatistics {\n const types = new Set(reportTypes)\n\n const matchingPending = batched.typePending.filter((r) =>\n types.has(r.reportType),\n )\n const matchingWindow = batched.typeWindow.filter((r) =>\n types.has(r.reportType),\n )\n\n const pendingCount = sumNum(matchingPending, 'count')\n const inboundCount = sumNum(matchingWindow, 'inboundCount')\n const actionedCount = sumNum(matchingWindow, 'actionedCount')\n const escalatedCount = sumNum(matchingWindow, 'escalatedCount')\n const handlingTimeSum = matchingWindow.reduce(\n (sum, r) => sum + Number(r.handlingTimeSum ?? 0),\n 0,\n )\n const handlingTimeCount = sumNum(matchingWindow, 'handlingTimeCount')\n\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveModeratorStats(\n moderatorDid: string,\n rows: ModeratorWindowRow[],\n ): ModeratorStatistics {\n const row = rows.find((r) => r.did === moderatorDid)\n\n const inboundCount = num(row?.inboundCount)\n const actionedCount = num(row?.actionedCount)\n const handlingTimeCount = num(row?.handlingTimeCount)\n const avgHandlingTimeSec =\n handlingTimeCount > 0 && row?.handlingTimeSum\n ? Math.round(Number(row.handlingTimeSum) / handlingTimeCount)\n : undefined\n\n return { inboundCount, actionedCount, avgHandlingTimeSec }\n }\n\n /** Build an upsert row from (date, group, stats). */\n private buildUpsertRow(\n date: string,\n group: ReportStatGroup,\n stats: ReportStatistics,\n ): UpsertRow {\n const pendingCount =\n 'pendingCount' in stats ? stats.pendingCount ?? null : null\n const escalatedCount =\n 'escalatedCount' in stats ? stats.escalatedCount ?? null : null\n const actionRate = 'actionRate' in stats ? stats.actionRate ?? null : null\n\n return {\n date,\n queueId: group.queueId,\n moderatorDid: group.moderatorDid,\n reportTypes: group.reportTypes,\n inboundCount: stats.inboundCount ?? null,\n pendingCount,\n actionedCount: stats.actionedCount ?? null,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec: stats.avgHandlingTimeSec ?? null,\n computedAt: new Date().toISOString(),\n }\n }\n\n /**\n * Wraps a DELETE+INSERT for each row in a single transaction so we pay one\n * commit per cycle instead of one per group. NULL-aware WHERE clauses match\n * the existing PG <15 NULL semantics without needing a unique index.\n */\n private async bulkUpsert(rows: UpsertRow[]): Promise<void> {\n if (!rows.length) return\n\n await this.db.transaction(async (dbTxn) => {\n for (const r of rows) {\n let del = dbTxn.db.deleteFrom('report_stat').where('date', '=', r.date)\n del =\n r.queueId !== null\n ? del.where('queueId', '=', r.queueId)\n : del.where('queueId', 'is', null)\n del =\n r.moderatorDid !== null\n ? del.where('moderatorDid', '=', r.moderatorDid)\n : del.where('moderatorDid', 'is', null)\n del =\n r.reportTypes !== null\n ? del.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(r.reportTypes)}::jsonb`,\n )\n : del.where('reportTypes', 'is', null)\n await del.execute()\n\n await dbTxn.db\n .insertInto('report_stat')\n .values({\n date: r.date,\n queueId: r.queueId,\n moderatorDid: r.moderatorDid,\n reportTypes: r.reportTypes !== null ? jsonb(r.reportTypes) : null,\n inboundCount: r.inboundCount,\n pendingCount: r.pendingCount,\n actionedCount: r.actionedCount,\n escalatedCount: r.escalatedCount,\n actionRate: r.actionRate,\n avgHandlingTimeSec: r.avgHandlingTimeSec,\n computedAt: r.computedAt,\n })\n .execute()\n }\n })\n }\n\n // ─── Read methods ───\n\n /** Get a single stat row for a date + group. */\n private async getStatForDate(\n date: string,\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n let qb = this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n if (group.queueId !== null) {\n qb = qb.where('queueId', '=', group.queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (group.moderatorDid) {\n qb = qb.where('moderatorDid', '=', group.moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (group.reportTypes !== null) {\n qb = qb.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(group.reportTypes)}::jsonb`,\n )\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n return qb.executeTakeFirst()\n }\n\n /** Get today's live stats for a group. */\n async getLiveStats(\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n const today = toDateString(new Date())\n return this.getStatForDate(today, group)\n }\n\n /** Get live stats for multiple queues in a single query. */\n async getLiveStatsForQueues(\n queueIds: number[],\n ): Promise<Map<number, Selectable<ReportStat>>> {\n if (!queueIds.length) return new Map()\n\n const today = toDateString(new Date())\n const rows = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', today)\n .where('queueId', 'in', queueIds)\n .where('moderatorDid', 'is', null)\n .where('reportTypes', 'is', null)\n .execute()\n\n const result = new Map<number, Selectable<ReportStat>>()\n for (const row of rows) {\n if (row.queueId !== null) {\n result.set(row.queueId, row)\n }\n }\n return result\n }\n\n /** Get historical stats for a date range, paginated. */\n async getHistoricalStats(opts: {\n group: ReportStatGroup\n startDate?: string\n endDate?: string\n limit: number\n cursor?: string\n }): Promise<{ stats: Selectable<ReportStat>[]; cursor?: string }> {\n const { group, startDate, endDate, limit } = opts\n const { queueId, moderatorDid, reportTypes } = group\n const { ref } = this.db.db.dynamic\n\n let qb = this.db.db.selectFrom('report_stat').selectAll()\n\n if (queueId !== null) {\n qb = qb.where('queueId', '=', queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (moderatorDid) {\n qb = qb.where('moderatorDid', '=', moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (reportTypes !== null) {\n qb = qb.where(sql`\"reportTypes\"::jsonb = ${jsonb(reportTypes)}::jsonb`)\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n if (startDate) {\n qb = qb.where('date', '>=', toDateString(new Date(startDate)))\n }\n if (endDate) {\n qb = qb.where('date', '<=', toDateString(new Date(endDate)))\n }\n\n const keyset = new ComputedAtIdKeyset(ref('computedAt'), ref('id'))\n const paginatedBuilder = paginate(qb, {\n limit,\n cursor: opts.cursor,\n keyset,\n direction: 'desc',\n tryIndex: true,\n })\n\n const stats = await paginatedBuilder.execute()\n\n return { stats, cursor: keyset.packFromResult(stats) }\n }\n}\n\n// ─── Helpers ───\n\n/** Parse a pg bigint string to number, defaulting to 0. */\nfunction num(val: string | undefined | null): number {\n return val ? Number(val) : 0\n}\n\n/** Sum a numeric string field across rows. */\nfunction sumNum<T>(rows: T[], field: keyof T): number {\n return rows.reduce((sum, r) => sum + Number(r[field] ?? 0), 0)\n}\n\n/**\n * Stable cache-key for a stat group. Used to look up an existing row in the\n * batched cache map without issuing per-group SELECTs. Report types are\n * stringified in stored order, which matches REPORT_TYPE_GROUPS.\n */\nfunction groupKey(g: ReportStatGroup): string {\n return [\n g.queueId ?? 'null',\n g.moderatorDid ?? 'null',\n g.reportTypes ? JSON.stringify(g.reportTypes) : 'null',\n ].join('|')\n}\n\n/** Convert a Date to an ISO date string (YYYY-MM-DD). */\nfunction toDateString(d: Date): string {\n return d.toISOString().slice(0, 10)\n}\n\n/** Get the next calendar date string. */\nfunction nextDate(dateStr: string): string {\n const d = new Date(`${dateStr}T00:00:00.000Z`)\n d.setUTCDate(d.getUTCDate() + 1)\n return toDateString(d)\n}\n"]}
|
package/dist/report/views.d.ts
CHANGED
|
@@ -35,7 +35,6 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
35
35
|
repo: ToolsOzoneModerationDefs.RepoViewDetail | undefined;
|
|
36
36
|
record: RecordViewDetail | undefined;
|
|
37
37
|
profile: {
|
|
38
|
-
$type: "app.bsky.actor.defs#profileViewDetailed";
|
|
39
38
|
did: string;
|
|
40
39
|
handle: string;
|
|
41
40
|
displayName?: string;
|
|
@@ -57,6 +56,7 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
57
56
|
verification?: AppBskyActorDefs.VerificationState;
|
|
58
57
|
status?: AppBskyActorDefs.StatusView;
|
|
59
58
|
debug?: { [_ in string]: unknown; };
|
|
59
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed';
|
|
60
60
|
} | undefined;
|
|
61
61
|
status: ToolsOzoneModerationDefs.SubjectStatusView | undefined;
|
|
62
62
|
};
|
|
@@ -67,7 +67,6 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
67
67
|
subject: string;
|
|
68
68
|
repo: ToolsOzoneModerationDefs.RepoViewDetail | undefined;
|
|
69
69
|
profile: {
|
|
70
|
-
$type: "app.bsky.actor.defs#profileViewDetailed";
|
|
71
70
|
did: string;
|
|
72
71
|
handle: string;
|
|
73
72
|
displayName?: string;
|
|
@@ -89,6 +88,7 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
89
88
|
verification?: AppBskyActorDefs.VerificationState;
|
|
90
89
|
status?: AppBskyActorDefs.StatusView;
|
|
91
90
|
debug?: { [_ in string]: unknown; };
|
|
91
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed';
|
|
92
92
|
} | undefined;
|
|
93
93
|
status: ToolsOzoneModerationDefs.SubjectStatusView | undefined;
|
|
94
94
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/report/views.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,KAAK,gBAAgB,MAAM,yCAAyC,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAA;AACxE,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACT,MAAM,iDAAiD,CAAA;AACxD,OAAO,KAAK,wBAAwB,MAAM,iDAAiD,CAAA;AAC3F,OAAO,KAAK,mBAAmB,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,oBAAoB,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,KAAK,WAAW,GAAG;IACjB,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjC,aAAa,CACX,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,EAC3B,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;IACzC,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,CAAA;CAC9D,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAA;IAC5C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IAC3D,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;IAClD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACrC,CAAA;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,eAAe,EAAE,EAC1B,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,EAC7E,SAAS,EAAE,CACT,QAAQ,EAAE,MAAM,EAAE,KACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC,EACxD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EACpE,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAuCzB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,wBAAwB,CAAC,YAAY,EAAE
|
|
1
|
+
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/report/views.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,15 +32,15 @@ export declare class SafelinkRuleService {
|
|
|
32
32
|
comment?: string;
|
|
33
33
|
}): Promise<Selectable<SafelinkEvent>>;
|
|
34
34
|
getActiveRule(url: string, pattern: SafelinkPatternType): Promise<{
|
|
35
|
-
id: number;
|
|
36
35
|
action: SafelinkActionType;
|
|
37
36
|
comment: string | null;
|
|
38
37
|
createdAt: string;
|
|
39
38
|
createdBy: string;
|
|
39
|
+
id: number;
|
|
40
|
+
pattern: SafelinkPatternType;
|
|
41
|
+
reason: SafelinkReasonType;
|
|
40
42
|
updatedAt: string;
|
|
41
43
|
url: string;
|
|
42
|
-
reason: SafelinkReasonType;
|
|
43
|
-
pattern: SafelinkPatternType;
|
|
44
44
|
} | null>;
|
|
45
45
|
getActiveRules({ cursor, limit, urls, patternType, actions, reason, createdBy, direction, }?: {
|
|
46
46
|
cursor?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEtE,MAAM,MAAM,0BAA0B,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,mBAAmB,CAAA;AAE9E,qBAAa,mBAAmB;IACX,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { ToolsOzoneSafelinkDefs } from '@atproto/api'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport {\n SafelinkActionType,\n SafelinkPatternType,\n SafelinkReasonType,\n} from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { SafelinkEvent, SafelinkRule } from '../db/schema/safelink.js'\n\nexport type SafelinkRuleServiceCreator = (db: Database) => SafelinkRuleService\n\nexport class SafelinkRuleService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new SafelinkRuleService(db)\n }\n\n formatEvent(event: Selectable<SafelinkEvent>): ToolsOzoneSafelinkDefs.Event {\n return {\n id: event.id,\n eventType: event.eventType,\n url: event.url,\n pattern: event.pattern,\n action: event.action,\n reason: event.reason,\n createdBy: event.createdBy,\n createdAt: new Date(event.createdAt).toISOString(),\n comment: event.comment || undefined,\n }\n }\n\n async addRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (existingRule) {\n throw new InvalidRequestError(\n 'A rule for this URL/domain already exists',\n 'RuleAlreadyExists',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment: comment || null,\n createdAt: now,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'addRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .insertInto('safelink_rule')\n .values({ ...rule, updatedAt: now })\n .execute()\n\n return event\n })\n }\n\n async updateRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n action,\n reason,\n createdBy,\n comment: comment || null,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n createdAt: now,\n url: existingRule.url,\n pattern: existingRule.pattern,\n eventType: 'updateRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .updateTable('safelink_rule')\n .set(rule)\n .where('url', '=', existingRule.url)\n .where('pattern', '=', existingRule.pattern)\n .execute()\n\n return event\n })\n }\n\n async removeRule({\n url,\n pattern,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'removeRule',\n url,\n pattern,\n action: existingRule.action,\n reason: existingRule.reason,\n createdBy,\n comment: comment || null,\n createdAt: new Date().toISOString(),\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .deleteFrom('safelink_rule')\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .execute()\n\n return event\n })\n }\n\n async getActiveRule(url: string, pattern: SafelinkPatternType) {\n const rule = await this.db.db\n .selectFrom('safelink_rule')\n .selectAll()\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .executeTakeFirst()\n\n if (!rule) {\n return null\n }\n\n return rule\n }\n\n async getActiveRules({\n cursor,\n limit = 50,\n urls,\n patternType,\n actions,\n reason,\n createdBy,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n actions?: SafelinkActionType[]\n reason?: SafelinkReasonType\n createdBy?: string\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n rules: Selectable<SafelinkRule>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_rule').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (actions && actions.length > 0) {\n query = query.where('action', 'in', actions)\n }\n\n if (reason) {\n query = query.where('reason', '=', reason)\n }\n\n if (createdBy) {\n query = query.where('createdBy', '=', createdBy)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const rules = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n rules,\n cursor: rules.at(-1)?.id?.toString(),\n }\n }\n\n async queryEvents({\n cursor,\n limit = 50,\n urls,\n patternType,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n events: Selectable<SafelinkEvent>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_event').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const events = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n events,\n cursor: events.at(-1)?.id?.toString(),\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { ToolsOzoneSafelinkDefs } from '@atproto/api'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport {\n SafelinkActionType,\n SafelinkPatternType,\n SafelinkReasonType,\n} from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { SafelinkEvent, SafelinkRule } from '../db/schema/safelink.js'\n\nexport type SafelinkRuleServiceCreator = (db: Database) => SafelinkRuleService\n\nexport class SafelinkRuleService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new SafelinkRuleService(db)\n }\n\n formatEvent(event: Selectable<SafelinkEvent>): ToolsOzoneSafelinkDefs.Event {\n return {\n id: event.id,\n eventType: event.eventType,\n url: event.url,\n pattern: event.pattern,\n action: event.action,\n reason: event.reason,\n createdBy: event.createdBy,\n createdAt: new Date(event.createdAt).toISOString(),\n comment: event.comment || undefined,\n }\n }\n\n async addRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (existingRule) {\n throw new InvalidRequestError(\n 'A rule for this URL/domain already exists',\n 'RuleAlreadyExists',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment: comment || null,\n createdAt: now,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'addRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .insertInto('safelink_rule')\n .values({ ...rule, updatedAt: now })\n .execute()\n\n return event\n })\n }\n\n async updateRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n action,\n reason,\n createdBy,\n comment: comment || null,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n createdAt: now,\n url: existingRule.url,\n pattern: existingRule.pattern,\n eventType: 'updateRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .updateTable('safelink_rule')\n .set(rule)\n .where('url', '=', existingRule.url)\n .where('pattern', '=', existingRule.pattern)\n .execute()\n\n return event\n })\n }\n\n async removeRule({\n url,\n pattern,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'removeRule',\n url,\n pattern,\n action: existingRule.action,\n reason: existingRule.reason,\n createdBy,\n comment: comment || null,\n createdAt: new Date().toISOString(),\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .deleteFrom('safelink_rule')\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .execute()\n\n return event\n })\n }\n\n async getActiveRule(url: string, pattern: SafelinkPatternType) {\n const rule = await this.db.db\n .selectFrom('safelink_rule')\n .selectAll()\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .executeTakeFirst()\n\n if (!rule) {\n return null\n }\n\n return rule\n }\n\n async getActiveRules({\n cursor,\n limit = 50,\n urls,\n patternType,\n actions,\n reason,\n createdBy,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n actions?: SafelinkActionType[]\n reason?: SafelinkReasonType\n createdBy?: string\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n rules: Selectable<SafelinkRule>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_rule').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (actions && actions.length > 0) {\n query = query.where('action', 'in', actions)\n }\n\n if (reason) {\n query = query.where('reason', '=', reason)\n }\n\n if (createdBy) {\n query = query.where('createdBy', '=', createdBy)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const rules = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n rules,\n cursor: rules.at(-1)?.id?.toString(),\n }\n }\n\n async queryEvents({\n cursor,\n limit = 50,\n urls,\n patternType,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n events: Selectable<SafelinkEvent>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_event').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const events = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n events,\n cursor: events.at(-1)?.id?.toString(),\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAA;AAErF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,6BAA6B,GAAG,CAC1C,EAAE,EAAE,QAAQ,KACT,sBAAsB,CAAA;AAE3B,qBAAa,sBAAsB;IACd,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAA;AAErF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,6BAA6B,GAAG,CAC1C,EAAE,EAAE,QAAQ,KACT,sBAAsB,CAAA;AAE3B,qBAAa,sBAAsB;IACd,EAAE,EAAE,QAAQ;IAA/B,YAAmB,EAAE,EAAE,QAAQ,EAAI;IAEnC,MAAM,CAAC,OAAO,SACA,QAAQ,4BACrB;IAED,qBAAqB,CACnB,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,GAClC,mBAAmB,CA0BrB;IAEK,cAAc,CAClB,gBAAgB,EAAE,gBAAgB,GACjC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAsDtC;IAEK,0BAA0B,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,CAU7C;IAEK,oBAAoB,CAAC,EACzB,MAAM,EACN,KAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,EACR,QAAa,EACb,SAAkB,GACnB,EAAE;QACD,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,IAAI,CAAA;QAChB,OAAO,CAAC,EAAE,IAAI,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;QACnB,QAAQ,EAAE,qBAAqB,EAAE,CAAA;QACjC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAC3B,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,UAAU,CAAC,eAAe,CAAC,EAAE,CAAA;QACtC,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC,CA6CD;IAEK,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACxD,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAC7D,CAAC,CAoCD;IAEK,0BAA0B,CAC9B,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,CAWxC;IAEK,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAYf;IAEK,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAYf;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { ScheduledActionStatus, ScheduledActionType } from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { ScheduledAction } from '../db/schema/scheduled-action.js'\nimport { ScheduledActionView } from '../lexicon/types/tools/ozone/moderation/defs.js'\nimport { dbLogger } from '../logger.js'\nimport { SchedulingParams } from './types.js'\n\nexport type ScheduledActionServiceCreator = (\n db: Database,\n) => ScheduledActionService\n\nexport class ScheduledActionService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new ScheduledActionService(db)\n }\n\n formatScheduledAction(\n action: Selectable<ScheduledAction>,\n ): ScheduledActionView {\n return {\n id: action.id,\n action: action.action,\n eventData: action.eventData as { [x: string]: unknown } | undefined,\n did: action.did,\n executeAt: action.executeAt\n ? new Date(action.executeAt).toISOString()\n : undefined,\n executeAfter: action.executeAfter\n ? new Date(action.executeAfter).toISOString()\n : undefined,\n executeUntil: action.executeUntil\n ? new Date(action.executeUntil).toISOString()\n : undefined,\n randomizeExecution: action.randomizeExecution,\n createdBy: action.createdBy,\n createdAt: new Date(action.createdAt).toISOString(),\n updatedAt: new Date(action.updatedAt).toISOString(),\n status: action.status,\n lastExecutedAt: action.lastExecutedAt\n ? new Date(action.lastExecutedAt).toISOString()\n : undefined,\n lastFailureReason: action.lastFailureReason || undefined,\n executionEventId: action.executionEventId || undefined,\n }\n }\n\n async scheduleAction(\n schedulingParams: SchedulingParams,\n ): Promise<Selectable<ScheduledAction>> {\n const { action, eventData, did, createdBy } = schedulingParams\n\n // Only allow one pending action at a time for a given subject and action type\n const existingAction = await this.getPendingActionForSubject(did, action)\n if (existingAction) {\n throw new InvalidRequestError(\n 'A pending scheduled action already exists for this subject',\n 'ActionAlreadyExists',\n )\n }\n\n // When a time-range for action is specified, ensure that the range is valid\n if (\n 'executeAfter' in schedulingParams &&\n schedulingParams.executeAfter &&\n schedulingParams.executeUntil &&\n schedulingParams.executeAfter >= schedulingParams.executeUntil\n ) {\n throw new InvalidRequestError(\n 'executeAfter must be before executeUntil',\n 'InvalidScheduling',\n )\n }\n\n const now = new Date().toISOString()\n const randomizeExecution =\n !('executeAt' in schedulingParams) && 'executeAfter' in schedulingParams\n\n const scheduledAction = await this.db.db\n .insertInto('scheduled_action')\n .values({\n action,\n eventData: JSON.stringify(eventData),\n did,\n executeAt: randomizeExecution\n ? null\n : schedulingParams.executeAt?.toISOString(),\n executeAfter: randomizeExecution\n ? schedulingParams.executeAfter?.toISOString()\n : null,\n executeUntil: randomizeExecution\n ? schedulingParams.executeUntil?.toISOString()\n : null,\n randomizeExecution,\n createdBy,\n createdAt: now,\n updatedAt: now,\n status: 'pending',\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n return scheduledAction\n }\n\n async getPendingActionForSubject(\n did: string,\n action: ScheduledActionType,\n ): Promise<Selectable<ScheduledAction> | null> {\n const scheduledAction = await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('did', '=', did)\n .where('action', '=', action)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n return scheduledAction || null\n }\n\n async listScheduledActions({\n cursor,\n limit = 50,\n startTime,\n endTime,\n subjects,\n statuses = [],\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n startTime?: Date\n endTime?: Date\n subjects?: string[]\n statuses: ScheduledActionStatus[]\n direction?: 'asc' | 'desc'\n }): Promise<{\n actions: Selectable<ScheduledAction>[]\n cursor?: string\n }> {\n let query = this.db.db\n .selectFrom('scheduled_action')\n .where('status', 'in', statuses)\n .selectAll()\n\n if (subjects && subjects.length > 0) {\n query = query.where('did', 'in', subjects)\n }\n\n if (startTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '>=', startTime.toISOString())\n .orWhere('executeAfter', '>=', startTime.toISOString())\n })\n }\n\n if (endTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '<=', endTime.toISOString())\n .orWhere('executeUntil', '<=', endTime.toISOString())\n .orWhere((sqb) => {\n return sqb\n .where('executeUntil', 'is', null)\n .where('executeAfter', '<=', endTime.toISOString())\n })\n })\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const actions = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n actions,\n cursor: actions.at(-1)?.id?.toString(),\n }\n }\n\n async cancelScheduledActions(subjects: string[]): Promise<{\n succeeded: string[]\n failed: { did: string; error: string; errorCode?: string }[]\n }> {\n const succeeded: string[] = []\n const failed: { did: string; error: string; errorCode?: string }[] = []\n\n for (const did of subjects) {\n try {\n const result = await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'cancelled',\n updatedAt: new Date().toISOString(),\n })\n .where('did', '=', did)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (result.numUpdatedRows && result.numUpdatedRows > 0) {\n succeeded.push(did)\n } else {\n failed.push({\n did,\n error: 'No pending scheduled actions found for subject',\n errorCode: 'NoPendingActions',\n })\n }\n } catch (err) {\n dbLogger.error({ err, subjects }, 'Error cancelling scheduled action')\n failed.push({\n did,\n error: 'Unknown error',\n errorCode: 'DatabaseError',\n })\n }\n }\n\n return { succeeded, failed }\n }\n\n async getPendingActionsToExecute(\n now: Date,\n ): Promise<Selectable<ScheduledAction>[]> {\n return await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('status', '=', 'pending')\n .where((qb) => {\n return qb\n .orWhere('executeAfter', '<=', now.toISOString())\n .orWhere('executeAt', '<=', now.toISOString())\n })\n .execute()\n }\n\n async markActionAsExecuted(\n actionId: number,\n executionEventId: number,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'executed',\n lastExecutedAt: now,\n executionEventId,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n\n async markActionAsFailed(\n actionId: number,\n failureReason: string,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'failed',\n lastExecutedAt: now,\n lastFailureReason: failureReason,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { ScheduledActionStatus, ScheduledActionType } from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { ScheduledAction } from '../db/schema/scheduled-action.js'\nimport { ScheduledActionView } from '../lexicon/types/tools/ozone/moderation/defs.js'\nimport { dbLogger } from '../logger.js'\nimport { SchedulingParams } from './types.js'\n\nexport type ScheduledActionServiceCreator = (\n db: Database,\n) => ScheduledActionService\n\nexport class ScheduledActionService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new ScheduledActionService(db)\n }\n\n formatScheduledAction(\n action: Selectable<ScheduledAction>,\n ): ScheduledActionView {\n return {\n id: action.id,\n action: action.action,\n eventData: action.eventData as { [x: string]: unknown } | undefined,\n did: action.did,\n executeAt: action.executeAt\n ? new Date(action.executeAt).toISOString()\n : undefined,\n executeAfter: action.executeAfter\n ? new Date(action.executeAfter).toISOString()\n : undefined,\n executeUntil: action.executeUntil\n ? new Date(action.executeUntil).toISOString()\n : undefined,\n randomizeExecution: action.randomizeExecution,\n createdBy: action.createdBy,\n createdAt: new Date(action.createdAt).toISOString(),\n updatedAt: new Date(action.updatedAt).toISOString(),\n status: action.status,\n lastExecutedAt: action.lastExecutedAt\n ? new Date(action.lastExecutedAt).toISOString()\n : undefined,\n lastFailureReason: action.lastFailureReason || undefined,\n executionEventId: action.executionEventId || undefined,\n }\n }\n\n async scheduleAction(\n schedulingParams: SchedulingParams,\n ): Promise<Selectable<ScheduledAction>> {\n const { action, eventData, did, createdBy } = schedulingParams\n\n // Only allow one pending action at a time for a given subject and action type\n const existingAction = await this.getPendingActionForSubject(did, action)\n if (existingAction) {\n throw new InvalidRequestError(\n 'A pending scheduled action already exists for this subject',\n 'ActionAlreadyExists',\n )\n }\n\n // When a time-range for action is specified, ensure that the range is valid\n if (\n 'executeAfter' in schedulingParams &&\n schedulingParams.executeAfter &&\n schedulingParams.executeUntil &&\n schedulingParams.executeAfter >= schedulingParams.executeUntil\n ) {\n throw new InvalidRequestError(\n 'executeAfter must be before executeUntil',\n 'InvalidScheduling',\n )\n }\n\n const now = new Date().toISOString()\n const randomizeExecution =\n !('executeAt' in schedulingParams) && 'executeAfter' in schedulingParams\n\n const scheduledAction = await this.db.db\n .insertInto('scheduled_action')\n .values({\n action,\n eventData: JSON.stringify(eventData),\n did,\n executeAt: randomizeExecution\n ? null\n : schedulingParams.executeAt?.toISOString(),\n executeAfter: randomizeExecution\n ? schedulingParams.executeAfter?.toISOString()\n : null,\n executeUntil: randomizeExecution\n ? schedulingParams.executeUntil?.toISOString()\n : null,\n randomizeExecution,\n createdBy,\n createdAt: now,\n updatedAt: now,\n status: 'pending',\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n return scheduledAction\n }\n\n async getPendingActionForSubject(\n did: string,\n action: ScheduledActionType,\n ): Promise<Selectable<ScheduledAction> | null> {\n const scheduledAction = await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('did', '=', did)\n .where('action', '=', action)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n return scheduledAction || null\n }\n\n async listScheduledActions({\n cursor,\n limit = 50,\n startTime,\n endTime,\n subjects,\n statuses = [],\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n startTime?: Date\n endTime?: Date\n subjects?: string[]\n statuses: ScheduledActionStatus[]\n direction?: 'asc' | 'desc'\n }): Promise<{\n actions: Selectable<ScheduledAction>[]\n cursor?: string\n }> {\n let query = this.db.db\n .selectFrom('scheduled_action')\n .where('status', 'in', statuses)\n .selectAll()\n\n if (subjects && subjects.length > 0) {\n query = query.where('did', 'in', subjects)\n }\n\n if (startTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '>=', startTime.toISOString())\n .orWhere('executeAfter', '>=', startTime.toISOString())\n })\n }\n\n if (endTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '<=', endTime.toISOString())\n .orWhere('executeUntil', '<=', endTime.toISOString())\n .orWhere((sqb) => {\n return sqb\n .where('executeUntil', 'is', null)\n .where('executeAfter', '<=', endTime.toISOString())\n })\n })\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const actions = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n actions,\n cursor: actions.at(-1)?.id?.toString(),\n }\n }\n\n async cancelScheduledActions(subjects: string[]): Promise<{\n succeeded: string[]\n failed: { did: string; error: string; errorCode?: string }[]\n }> {\n const succeeded: string[] = []\n const failed: { did: string; error: string; errorCode?: string }[] = []\n\n for (const did of subjects) {\n try {\n const result = await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'cancelled',\n updatedAt: new Date().toISOString(),\n })\n .where('did', '=', did)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (result.numUpdatedRows && result.numUpdatedRows > 0) {\n succeeded.push(did)\n } else {\n failed.push({\n did,\n error: 'No pending scheduled actions found for subject',\n errorCode: 'NoPendingActions',\n })\n }\n } catch (err) {\n dbLogger.error({ err, subjects }, 'Error cancelling scheduled action')\n failed.push({\n did,\n error: 'Unknown error',\n errorCode: 'DatabaseError',\n })\n }\n }\n\n return { succeeded, failed }\n }\n\n async getPendingActionsToExecute(\n now: Date,\n ): Promise<Selectable<ScheduledAction>[]> {\n return await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('status', '=', 'pending')\n .where((qb) => {\n return qb\n .orWhere('executeAfter', '<=', now.toISOString())\n .orWhere('executeAt', '<=', now.toISOString())\n })\n .execute()\n }\n\n async markActionAsExecuted(\n actionId: number,\n executionEventId: number,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'executed',\n lastExecutedAt: now,\n executionEventId,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n\n async markActionAsFailed(\n actionId: number,\n failureReason: string,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'failed',\n lastExecutedAt: now,\n lastFailureReason: failureReason,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,MAAM;IAQR,SAAS,EAAE,SAAS;IAP7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,QAAQ,SAAK;IAEb,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,MAAM;IAQR,SAAS,EAAE,SAAS;IAP7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,QAAQ,SAAK;IAEb,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;IAEjC,YACS,SAAS,EAAE,SAAS,EAC3B,IAAI,GAAE,OAAO,CAAC,UAAU,CAAM,EAK/B;IAWM,MAAM,CACX,cAAc,CAAC,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,SAAS,CAAC,CAmE3B;IAGM,WAAW,CAAC,cAAc,EAAE,MAAM,4CAexC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAO1D,MAAM,OAAO,MAAM;IAOjB,YACS,SAAoB,EAC3B,
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncBuffer, AsyncBufferFullError } from '@atproto/common'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { LabelsEvt, Sequencer } from './sequencer.js'\n\nexport type OutboxOpts = {\n maxBufferSize: number\n}\n\nexport class Outbox {\n private caughtUp = false\n lastSeen = -1\n\n cutoverBuffer: LabelsEvt[]\n outBuffer: AsyncBuffer<LabelsEvt>\n\n constructor(\n public sequencer: Sequencer,\n opts: Partial<OutboxOpts> = {},\n ) {\n const { maxBufferSize = 500 } = opts\n this.cutoverBuffer = []\n this.outBuffer = new AsyncBuffer<LabelsEvt>(maxBufferSize)\n }\n\n // event stream occurs in 3 phases\n // 1. backfill events: events that have been added to the DB since the last time a connection was open.\n // The outbox is not yet listening for new events from the sequencer\n // 2. cutover: the outbox has caught up with where the sequencer purports to be,\n // but the sequencer might already be halfway through sending out a round of updates.\n // Therefore, we start accepting the sequencer's events in a buffer, while making our own request to the\n // database to ensure we're caught up. We then dedupe the query & the buffer & stream the events in order\n // 3. streaming: we're all caught up on historic state, so the sequencer outputs events and we\n // immediately yield them\n async *events(\n backfillCursor?: number,\n signal?: AbortSignal,\n ): AsyncGenerator<LabelsEvt> {\n // catch up as much as we can\n if (backfillCursor !== undefined) {\n for await (const evt of this.getBackfill(backfillCursor)) {\n if (signal?.aborted) return\n this.lastSeen = evt.seq\n yield evt\n }\n } else {\n // if not backfill, we don't need to cutover, just start streaming\n this.caughtUp = true\n }\n\n // streams updates from sequencer, but buffers them for cutover as it makes a last request\n\n const addToBuffer = (evts) => {\n if (this.caughtUp) {\n this.outBuffer.pushMany(evts)\n } else {\n this.cutoverBuffer = [...this.cutoverBuffer, ...evts]\n }\n }\n\n if (!signal?.aborted) {\n this.sequencer.on('events', addToBuffer)\n }\n signal?.addEventListener('abort', () =>\n this.sequencer.off('events', addToBuffer),\n )\n\n const cutover = async () => {\n // only need to perform cutover if we've been backfilling\n if (backfillCursor !== undefined) {\n const cutoverEvts = await this.sequencer.requestLabelRange({\n earliestId: this.lastSeen > -1 ? this.lastSeen : backfillCursor,\n })\n this.outBuffer.pushMany(cutoverEvts)\n // dont worry about dupes, we ensure order on yield\n this.outBuffer.pushMany(this.cutoverBuffer)\n this.caughtUp = true\n this.cutoverBuffer = []\n } else {\n this.caughtUp = true\n }\n }\n cutover()\n\n while (true) {\n try {\n for await (const evt of this.outBuffer.events()) {\n if (signal?.aborted) return\n if (evt.seq > this.lastSeen) {\n this.lastSeen = evt.seq\n yield evt\n }\n }\n } catch (err) {\n if (err instanceof AsyncBufferFullError) {\n throw new InvalidRequestError(\n 'Stream consumer too slow',\n 'ConsumerTooSlow',\n )\n } else {\n throw err\n }\n }\n }\n }\n\n // yields only historical events\n async *getBackfill(backfillCursor: number) {\n const PAGE_SIZE = 500\n while (true) {\n const evts = await this.sequencer.requestLabelRange({\n earliestId: this.lastSeen > -1 ? this.lastSeen : backfillCursor,\n limit: PAGE_SIZE,\n })\n for (const evt of evts) {\n yield evt\n }\n // if we're within half a pagesize of the sequencer, we call it good & switch to cutover\n const seqCursor = this.sequencer.lastSeen ?? -1\n if (seqCursor - this.lastSeen < PAGE_SIZE / 2) break\n if (evts.length < 1) break\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,KAAK,UAAU,GAAG,EAAE,CAAC,UAAU,CAAA;AAC/B,OAAO,KAAK,YAAY,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,KAAK,IAAI,UAAU,EAAgB,MAAM,uBAAuB,CAAA;AACzE,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,uDAAuD,CAAA;AAE3F,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAE3D,YAAY,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,uDAAuD,CAAA;AAChG,KAAK,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"sequencer.d.ts","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,KAAK,UAAU,GAAG,EAAE,CAAC,UAAU,CAAA;AAC/B,OAAO,KAAK,YAAY,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,KAAK,IAAI,UAAU,EAAgB,MAAM,uBAAuB,CAAA;AACzE,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,uDAAuD,CAAA;AAE3F,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAE3D,YAAY,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,uDAAuD,CAAA;AAChG,KAAK,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAA;wCAEoB,gBAAgB;AAA1E,qBAAa,SAAU,SAAQ,cAA4C;IAQhE,OAAO,EAAE,iBAAiB;IAC1B,QAAQ;IARjB,EAAE,EAAE,QAAQ,CAAA;IACZ,SAAS,UAAQ;IACjB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACtC,MAAM,UAAQ;IACd,IAAI,EAAE,UAAU,GAAG,SAAS,CAAA;IAE5B,YACS,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,SAAI,EAMpB;IAEK,KAAK,kBAWV;IAEK,OAAO,kBAWZ;IAEK,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnC;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CASnD;IAEK,iBAAiB,CAAC,IAAI,EAAE;QAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAwBvB;IAED,OAAO,CAAC,IAAI;CA+Bb;AAED,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACnC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AAOtC,OAAO,EAAuB,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzE,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,cAAc,CAAA;AAM/C,MAAM,OAAO,SAAU,SAAS,YAA2C;IAOzE,YACS,OAA0B,EAC1B,
|
|
1
|
+
{"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'node:events'\nimport { Selectable } from 'kysely'\n// eslint-disable-next-line import/default\nimport pg from 'pg'\ntype PoolClient = pg.PoolClient\nimport type TypedEmitter from 'typed-emitter'\nimport { Database } from '../db/index.js'\nimport { Label as LabelTable, LabelChannel } from '../db/schema/label.js'\nimport { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels.js'\nimport { seqLogger as log } from '../logger.js'\nimport { ModerationService } from '../mod-service/index.js'\n\nexport type { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels.js'\ntype LabelRow = Selectable<LabelTable>\n\nexport class Sequencer extends (EventEmitter as new () => SequencerEmitter) {\n db: Database\n destroyed = false\n pollPromise: Promise<void> | undefined\n queued = false\n conn: PoolClient | undefined\n\n constructor(\n public modSrvc: ModerationService,\n public lastSeen = 0,\n ) {\n super()\n this.db = modSrvc.db\n // note: this does not err when surpassed, just prints a warning to stderr\n this.setMaxListeners(100)\n }\n\n async start() {\n const curr = await this.curr()\n this.lastSeen = curr ?? 0\n this.poll()\n this.conn = await this.db.pool.connect()\n this.conn.query(`listen ${LabelChannel}`) // if this errors, unhandled rejection should cause process to exit\n this.conn.on('notification', (notif) => {\n if (notif.channel === LabelChannel) {\n this.poll()\n }\n })\n }\n\n async destroy() {\n if (this.destroyed) return\n this.destroyed = true\n if (this.conn) {\n this.conn.release()\n this.conn = undefined\n }\n if (this.pollPromise) {\n await this.pollPromise\n }\n this.emit('close')\n }\n\n async curr(): Promise<number | null> {\n const got = await this.db.db\n .selectFrom('label')\n .selectAll()\n .orderBy('id', 'desc')\n .limit(1)\n .executeTakeFirst()\n return got?.id ?? null\n }\n\n async next(cursor: number): Promise<LabelRow | null> {\n const got = await this.db.db\n .selectFrom('label')\n .selectAll()\n .where('id', '>', cursor)\n .limit(1)\n .orderBy('id', 'asc')\n .executeTakeFirst()\n return got || null\n }\n\n async requestLabelRange(opts: {\n earliestId?: number\n limit?: number\n }): Promise<LabelsEvt[]> {\n const { earliestId, limit } = opts\n\n let seqQb = this.db.db.selectFrom('label').selectAll().orderBy('id', 'asc')\n if (earliestId !== undefined) {\n seqQb = seqQb.where('id', '>', earliestId)\n }\n if (limit !== undefined) {\n seqQb = seqQb.limit(limit)\n }\n\n const rows = await seqQb.execute()\n if (rows.length < 1) {\n return []\n }\n\n const evts: LabelsEvt[] = await Promise.all(\n rows.map(async (row) => {\n const formatted = await this.modSrvc.views.formatLabelAndEnsureSig(row)\n return { seq: row.id, labels: [formatted] }\n }),\n )\n\n return evts\n }\n\n private poll() {\n if (this.destroyed) return\n if (this.pollPromise) {\n this.queued = true\n return\n }\n this.queued = false\n this.pollPromise = this.requestLabelRange({\n earliestId: this.lastSeen,\n limit: 500,\n })\n .then((evts) => {\n this.emit('events', evts)\n this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen\n if (evts.length > 0) {\n this.queued = true\n }\n })\n .catch((err) => {\n log.error(\n { err, lastSeen: this.lastSeen },\n 'sequencer failed to poll db',\n )\n })\n .finally(() => {\n this.pollPromise = undefined\n if (this.queued) {\n this.poll()\n }\n })\n }\n}\n\ntype SequencerEvents = {\n events: (evts: LabelsEvt[]) => void\n close: () => void\n}\n\nexport type SequencerEmitter = TypedEmitter.default<SequencerEvents>\n"]}
|