@atproto/ozone 0.1.154 → 0.1.156
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 +21 -0
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +22 -8
- package/dist/api/moderation/emitEvent.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-pusher.js +17 -5
- package/dist/daemon/event-pusher.js.map +1 -1
- package/dist/daemon/materialized-view-refresher.d.ts.map +1 -1
- package/dist/daemon/materialized-view-refresher.js +0 -1
- package/dist/daemon/materialized-view-refresher.js.map +1 -1
- package/dist/daemon/scheduled-action-processor.d.ts +5 -1
- package/dist/daemon/scheduled-action-processor.d.ts.map +1 -1
- package/dist/daemon/scheduled-action-processor.js +44 -2
- package/dist/daemon/scheduled-action-processor.js.map +1 -1
- package/dist/jetstream/service.d.ts +1 -1
- package/dist/jetstream/service.d.ts.map +1 -1
- package/dist/jetstream/service.js +2 -2
- package/dist/jetstream/service.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +66 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +33 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +4 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.d.ts +10 -0
- package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.js.map +1 -1
- package/dist/mod-service/index.d.ts +2 -2
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +10 -4
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +7 -0
- package/dist/mod-service/views.js.map +1 -1
- package/dist/setting/validators.d.ts.map +1 -1
- package/dist/setting/validators.js +10 -0
- package/dist/setting/validators.js.map +1 -1
- package/package.json +5 -4
- package/src/api/moderation/emitEvent.ts +37 -10
- package/src/daemon/event-pusher.ts +22 -5
- package/src/daemon/materialized-view-refresher.ts +0 -1
- package/src/daemon/scheduled-action-processor.ts +59 -1
- package/src/jetstream/service.ts +1 -1
- package/src/lexicon/lexicons.ts +39 -0
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +4 -0
- package/src/lexicon/types/tools/ozone/moderation/scheduleAction.ts +10 -0
- package/src/mod-service/index.ts +21 -7
- package/src/mod-service/views.ts +10 -0
- package/src/setting/validators.ts +15 -0
- package/tests/query-labels.test.ts +2 -1
- package/tests/scheduled-action-processor.test.ts +30 -6
- package/tests/takedown.test.ts +43 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @atproto/ozone
|
|
2
2
|
|
|
3
|
+
## 0.1.156
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4340](https://github.com/bluesky-social/atproto/pull/4340) [`032abf6b5`](https://github.com/bluesky-social/atproto/commit/032abf6b500fd36f3c0fc1af83bf62caae44fa6e) Thanks [@foysalit](https://github.com/foysalit)! - Add optional email data to scheduled action api in ozone
|
|
8
|
+
|
|
9
|
+
- [#4344](https://github.com/bluesky-social/atproto/pull/4344) [`9115325c7`](https://github.com/bluesky-social/atproto/commit/9115325c7b36f0293f87f79bb8edb49f72fec2bc) Thanks [@foysalit](https://github.com/foysalit)! - Add targetServices param to takedown events allowing mods to specify which service to apply takedown on
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`032abf6b5`](https://github.com/bluesky-social/atproto/commit/032abf6b500fd36f3c0fc1af83bf62caae44fa6e), [`9115325c7`](https://github.com/bluesky-social/atproto/commit/9115325c7b36f0293f87f79bb8edb49f72fec2bc), [`1dd20d3a8`](https://github.com/bluesky-social/atproto/commit/1dd20d3a81cda29392d8d63d13082254ec5f68a8)]:
|
|
12
|
+
- @atproto/api@0.18.1
|
|
13
|
+
- @atproto/xrpc-server@0.9.6
|
|
14
|
+
- @atproto/ws-client@0.0.2
|
|
15
|
+
|
|
16
|
+
## 0.1.155
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#4330](https://github.com/bluesky-social/atproto/pull/4330) [`3628cebfb`](https://github.com/bluesky-social/atproto/commit/3628cebfbb04ba49f326bbf411a2d15de2900302) Thanks [@mistydemeo](https://github.com/mistydemeo)! - adjust explicit-slurs regex
|
|
21
|
+
|
|
22
|
+
- [#4335](https://github.com/bluesky-social/atproto/pull/4335) [`82e75bf6c`](https://github.com/bluesky-social/atproto/commit/82e75bf6c1b31daa834386edce35c8aa4c787229) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove non-existing `reporter_stats` from materliaziled view to refresh
|
|
23
|
+
|
|
3
24
|
## 0.1.154
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitEvent.d.ts","sourceRoot":"","sources":["../../../src/api/moderation/emitEvent.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"emitEvent.d.ts","sourceRoot":"","sources":["../../../src/api/moderation/emitEvent.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAqTtC,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqCvD"}
|
|
@@ -5,6 +5,7 @@ const defs_1 = require("@atproto/api/dist/client/types/tools/ozone/moderation/de
|
|
|
5
5
|
const xrpc_server_1 = require("@atproto/xrpc-server");
|
|
6
6
|
const lexicons_1 = require("../../lexicon/lexicons");
|
|
7
7
|
const defs_2 = require("../../lexicon/types/tools/ozone/moderation/defs");
|
|
8
|
+
const logger_1 = require("../../logger");
|
|
8
9
|
const subject_1 = require("../../mod-service/subject");
|
|
9
10
|
const tag_service_1 = require("../../tag-service");
|
|
10
11
|
const util_1 = require("../../tag-service/util");
|
|
@@ -69,7 +70,8 @@ const handleModerationEvent = async ({ ctx, input, auth, }) => {
|
|
|
69
70
|
...(event.negateLabelVals ?? []),
|
|
70
71
|
]);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
const isTakedownOrReverseTakedownEvent = isTakedownEvent || isReverseTakedownEvent;
|
|
74
|
+
if (isTakedownOrReverseTakedownEvent || isLabelEvent) {
|
|
73
75
|
const status = await moderationService.getStatus(subject);
|
|
74
76
|
if (status?.takendown && isTakedownEvent) {
|
|
75
77
|
throw new xrpc_server_1.InvalidRequestError(`Subject is already taken down`);
|
|
@@ -102,11 +104,19 @@ const handleModerationEvent = async ({ ctx, input, auth, }) => {
|
|
|
102
104
|
throw new xrpc_server_1.InvalidRequestError('Email can only be sent to a repo subject');
|
|
103
105
|
}
|
|
104
106
|
const { content, subjectLine } = event;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
// on error, don't fail the whole event. instead, log the event data with isDelivered false
|
|
108
|
+
try {
|
|
109
|
+
await (0, util_2.retryHttp)(() => ctx.modService(db).sendEmail({
|
|
110
|
+
subject: subjectLine,
|
|
111
|
+
content,
|
|
112
|
+
recipientDid: subject.did,
|
|
113
|
+
}));
|
|
114
|
+
event.isDelivered = true;
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
event.isDelivered = false;
|
|
118
|
+
logger_1.httpLogger.error({ err, event }, 'failed to send mod event email');
|
|
119
|
+
}
|
|
110
120
|
}
|
|
111
121
|
if ((0, defs_1.isModEventDivert)(event) && subject.isRecord()) {
|
|
112
122
|
if (!ctx.blobDiverter) {
|
|
@@ -147,7 +157,9 @@ const handleModerationEvent = async ({ ctx, input, auth, }) => {
|
|
|
147
157
|
if (subject.isRepo()) {
|
|
148
158
|
if (isTakedownEvent) {
|
|
149
159
|
const isSuspend = !!result.event.durationInHours;
|
|
150
|
-
await moderationTxn.takedownRepo(subject, result.event.id,
|
|
160
|
+
await moderationTxn.takedownRepo(subject, result.event.id, new Set(result.event.meta?.targetServices
|
|
161
|
+
? `${result.event.meta.targetServices}`.split(',')
|
|
162
|
+
: undefined), isSuspend);
|
|
151
163
|
}
|
|
152
164
|
else if (isReverseTakedownEvent) {
|
|
153
165
|
await moderationTxn.reverseTakedownRepo(subject);
|
|
@@ -155,7 +167,9 @@ const handleModerationEvent = async ({ ctx, input, auth, }) => {
|
|
|
155
167
|
}
|
|
156
168
|
if (subject.isRecord()) {
|
|
157
169
|
if (isTakedownEvent) {
|
|
158
|
-
await moderationTxn.takedownRecord(subject, result.event.id
|
|
170
|
+
await moderationTxn.takedownRecord(subject, result.event.id, new Set(result.event.meta?.targetServices
|
|
171
|
+
? `${result.event.meta.targetServices}`.split(',')
|
|
172
|
+
: undefined));
|
|
159
173
|
}
|
|
160
174
|
else if (isReverseTakedownEvent) {
|
|
161
175
|
await moderationTxn.reverseTakedownRecord(subject);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitEvent.js","sourceRoot":"","sources":["../../../src/api/moderation/emitEvent.ts"],"names":[],"mappings":"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","sourcesContent":["import { isModEventDivert } from '@atproto/api/dist/client/types/tools/ozone/moderation/defs'\nimport { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'\nimport { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier'\nimport { AppContext } from '../../context'\nimport { Server } from '../../lexicon'\nimport { ids } from '../../lexicon/lexicons'\nimport {\n ModEventTag,\n isAgeAssuranceEvent,\n isAgeAssuranceOverrideEvent,\n isModEventAcknowledge,\n isModEventEmail,\n isModEventLabel,\n isModEventMuteReporter,\n isModEventReport,\n isModEventReverseTakedown,\n isModEventTag,\n isModEventTakedown,\n isModEventUnmuteReporter,\n isRevokeAccountCredentialsEvent,\n} from '../../lexicon/types/tools/ozone/moderation/defs'\nimport { HandlerInput } from '../../lexicon/types/tools/ozone/moderation/emitEvent'\nimport { subjectFromInput } from '../../mod-service/subject'\nimport { SettingService } from '../../setting/service'\nimport { TagService } from '../../tag-service'\nimport { getTagForReport } from '../../tag-service/util'\nimport { retryHttp } from '../../util'\nimport { getEventType } from '../util'\nimport { assertProtectedTagAction, getProtectedTags } from './util'\n\nconst handleModerationEvent = async ({\n ctx,\n input,\n auth,\n}: {\n ctx: AppContext\n input: HandlerInput\n auth: ModeratorOutput | AdminTokenOutput\n}) => {\n const access = auth.credentials\n const createdBy =\n auth.credentials.type === 'moderator'\n ? auth.credentials.iss\n : input.body.createdBy\n const db = ctx.db\n const moderationService = ctx.modService(db)\n const settingService = ctx.settingService(db)\n const { event, externalId } = input.body\n const isAcknowledgeEvent = isModEventAcknowledge(event)\n const isTakedownEvent = isModEventTakedown(event)\n const isReverseTakedownEvent = isModEventReverseTakedown(event)\n const isLabelEvent = isModEventLabel(event)\n const subject = subjectFromInput(\n input.body.subject,\n input.body.subjectBlobCids,\n )\n\n if (isAgeAssuranceEvent(event) && !subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n\n if (isAgeAssuranceOverrideEvent(event)) {\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n if (!auth.credentials.isModerator) {\n throw new AuthRequiredError(\n 'Must be a full moderator to override age assurance',\n )\n }\n }\n\n if (isRevokeAccountCredentialsEvent(event)) {\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n\n if (!auth.credentials.isAdmin) {\n throw new AuthRequiredError(\n 'Must be an admin to revoke account credentials',\n )\n }\n\n if (!ctx.pdsAgent) {\n throw new InvalidRequestError('PDS not configured')\n }\n\n await ctx.pdsAgent.com.atproto.temp.revokeAccountCredentials(\n { account: subject.did },\n await ctx.pdsAuth(ids.ComAtprotoTempRevokeAccountCredentials),\n )\n }\n\n // if less than moderator access then can only take ack and escalation actions\n if (isTakedownEvent || isReverseTakedownEvent) {\n if (!access.isModerator) {\n throw new AuthRequiredError(\n 'Must be a full moderator to take this type of action',\n )\n }\n\n // Non admins should not be able to take down feed generators\n if (\n !access.isAdmin &&\n subject.recordPath?.includes('app.bsky.feed.generator/')\n ) {\n throw new AuthRequiredError(\n 'Must be a full admin to take this type of action on feed generators',\n )\n }\n }\n // if less than moderator access then can not apply labels\n if (!access.isModerator && isLabelEvent) {\n throw new AuthRequiredError('Must be a full moderator to label content')\n }\n\n if (isLabelEvent) {\n validateLabels([\n ...(event.createLabelVals ?? []),\n ...(event.negateLabelVals ?? []),\n ])\n }\n\n if (isTakedownEvent || isReverseTakedownEvent) {\n const status = await moderationService.getStatus(subject)\n\n if (status?.takendown && isTakedownEvent) {\n throw new InvalidRequestError(`Subject is already taken down`)\n }\n\n if (!status?.takendown && isReverseTakedownEvent) {\n throw new InvalidRequestError(`Subject is not taken down`)\n }\n\n if (status?.tags?.length) {\n const protectedTags = await getProtectedTags(\n settingService,\n ctx.cfg.service.did,\n )\n\n if (protectedTags) {\n assertProtectedTagAction({\n protectedTags,\n subjectTags: status.tags,\n actionAuthor: createdBy,\n isAdmin: auth.credentials.isAdmin,\n isModerator: auth.credentials.isModerator,\n isTriage: auth.credentials.isTriage,\n })\n }\n }\n\n if (status?.takendown && isReverseTakedownEvent && subject.isRecord()) {\n // due to the way blob status is modeled, we should reverse takedown on all\n // blobs for the record being restored, which aren't taken down on another record.\n subject.blobCids = status.blobCids ?? []\n }\n }\n\n if (isModEventEmail(event) && event.content) {\n // sending email prior to logging the event to avoid a long transaction below\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Email can only be sent to a repo subject')\n }\n const { content, subjectLine } = event\n await retryHttp(() =>\n ctx.modService(db).sendEmail({\n subject: subjectLine,\n content,\n recipientDid: subject.did,\n }),\n )\n }\n\n if (isModEventDivert(event) && subject.isRecord()) {\n if (!ctx.blobDiverter) {\n throw new InvalidRequestError(\n 'BlobDiverter not configured for this service',\n )\n }\n await ctx.blobDiverter.uploadBlobOnService(subject.info())\n }\n\n if (\n (isModEventMuteReporter(event) || isModEventUnmuteReporter(event)) &&\n !subject.isRepo()\n ) {\n throw new InvalidRequestError('Subject must be a repo when muting reporter')\n }\n\n if (isModEventTag(event)) {\n await assertTagAuth(settingService, ctx.cfg.service.did, event, auth)\n }\n\n if (isModEventReport(event)) {\n await ctx.moderationServiceProfile().validateReasonType(event.reportType)\n }\n\n const moderationEvent = await db.transaction(async (dbTxn) => {\n const moderationTxn = ctx.modService(dbTxn)\n\n if (externalId) {\n const existingEvent = await moderationTxn.getEventByExternalId(\n getEventType(event.$type),\n externalId,\n subject,\n )\n\n if (existingEvent) {\n throw new InvalidRequestError(\n `An event with the same external ID already exists for the subject.`,\n 'DuplicateExternalId',\n )\n }\n }\n\n const result = await moderationTxn.logEvent({\n event,\n subject,\n createdBy,\n modTool: input.body.modTool,\n externalId,\n })\n\n const tagService = new TagService(\n subject,\n result.subjectStatus,\n ctx.cfg.service.did,\n moderationTxn,\n )\n\n const initialTags = isModEventReport(event)\n ? [getTagForReport(event.reportType)]\n : undefined\n await tagService.evaluateForSubject(initialTags)\n\n if (subject.isRepo()) {\n if (isTakedownEvent) {\n const isSuspend = !!result.event.durationInHours\n await moderationTxn.takedownRepo(subject, result.event.id, isSuspend)\n } else if (isReverseTakedownEvent) {\n await moderationTxn.reverseTakedownRepo(subject)\n }\n }\n\n if (subject.isRecord()) {\n if (isTakedownEvent) {\n await moderationTxn.takedownRecord(subject, result.event.id)\n } else if (isReverseTakedownEvent) {\n await moderationTxn.reverseTakedownRecord(subject)\n }\n }\n\n if (\n (isTakedownEvent || isAcknowledgeEvent) &&\n result.event.meta?.acknowledgeAccountSubjects\n ) {\n await moderationTxn.resolveSubjectsForAccount(\n subject.did,\n createdBy,\n result.event,\n )\n }\n\n if (isLabelEvent) {\n await moderationTxn.formatAndCreateLabels(\n result.event.subjectUri ?? result.event.subjectDid,\n result.event.subjectCid,\n {\n create: result.event.createLabelVals?.length\n ? result.event.createLabelVals.split(' ')\n : undefined,\n negate: result.event.negateLabelVals?.length\n ? result.event.negateLabelVals.split(' ')\n : undefined,\n },\n result.event.durationInHours ?? undefined,\n )\n }\n\n return result.event\n })\n\n return moderationService.views.formatEvent(moderationEvent)\n}\n\nexport default function (server: Server, ctx: AppContext) {\n server.tools.ozone.moderation.emitEvent({\n auth: ctx.authVerifier.modOrAdminToken,\n handler: async ({ input, auth }) => {\n const moderationEvent = await handleModerationEvent({\n input,\n auth,\n ctx,\n })\n\n // On divert events, we need to automatically take down the blobs\n if (isModEventDivert(input.body.event)) {\n await handleModerationEvent({\n auth,\n ctx,\n input: {\n ...input,\n body: {\n ...input.body,\n event: {\n ...input.body.event,\n $type: 'tools.ozone.moderation.defs#modEventTakedown',\n comment:\n '[DIVERT_SIDE_EFFECT]: Automatically taking down after divert event',\n },\n modTool: input.body.modTool,\n },\n },\n })\n }\n\n return {\n encoding: 'application/json',\n body: moderationEvent,\n }\n },\n })\n}\n\nconst assertTagAuth = async (\n settingService: SettingService,\n serviceDid: string,\n event: ModEventTag,\n auth: ModeratorOutput | AdminTokenOutput,\n) => {\n // admins can add/remove any tag\n if (auth.credentials.isAdmin) return\n\n const protectedTags = await getProtectedTags(settingService, serviceDid)\n\n if (!protectedTags) {\n return\n }\n\n for (const tag of Object.keys(protectedTags)) {\n if (event.add.includes(tag) || event.remove.includes(tag)) {\n // if specific moderators are configured to manage this tag but the current user\n // is not one of them, then throw an error\n const configuredModerators = protectedTags[tag]?.['moderators']\n if (\n configuredModerators &&\n !configuredModerators.includes(auth.credentials.iss)\n ) {\n throw new InvalidRequestError(`Not allowed to manage tag: ${tag}`)\n }\n\n const configuredRoles = protectedTags[tag]?.['roles']\n if (configuredRoles) {\n // admins can already do everything so we only check for moderator and triage role config\n if (\n auth.credentials.isModerator &&\n !configuredRoles.includes('tools.ozone.team.defs#roleModerator')\n ) {\n throw new InvalidRequestError(\n `Can not manage tag ${tag} with moderator role`,\n )\n } else if (\n auth.credentials.isTriage &&\n !configuredRoles.includes('tools.ozone.team.defs#roleTriage')\n ) {\n throw new InvalidRequestError(\n `Can not manage tag ${tag} with triage role`,\n )\n }\n }\n }\n }\n}\n\nconst validateLabels = (labels: string[]) => {\n for (const label of labels) {\n for (const char of badChars) {\n if (label.includes(char)) {\n throw new InvalidRequestError(`Invalid label: ${label}`)\n }\n }\n }\n}\n\nconst badChars = [' ', ',', ';', `'`, `\"`]\n"]}
|
|
1
|
+
{"version":3,"file":"emitEvent.js","sourceRoot":"","sources":["../../../src/api/moderation/emitEvent.ts"],"names":[],"mappings":"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","sourcesContent":["import { isModEventDivert } from '@atproto/api/dist/client/types/tools/ozone/moderation/defs'\nimport { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'\nimport { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier'\nimport { AppContext } from '../../context'\nimport { Server } from '../../lexicon'\nimport { ids } from '../../lexicon/lexicons'\nimport {\n ModEventTag,\n isAgeAssuranceEvent,\n isAgeAssuranceOverrideEvent,\n isModEventAcknowledge,\n isModEventEmail,\n isModEventLabel,\n isModEventMuteReporter,\n isModEventReport,\n isModEventReverseTakedown,\n isModEventTag,\n isModEventTakedown,\n isModEventUnmuteReporter,\n isRevokeAccountCredentialsEvent,\n} from '../../lexicon/types/tools/ozone/moderation/defs'\nimport { HandlerInput } from '../../lexicon/types/tools/ozone/moderation/emitEvent'\nimport { httpLogger } from '../../logger'\nimport { subjectFromInput } from '../../mod-service/subject'\nimport { SettingService } from '../../setting/service'\nimport { TagService } from '../../tag-service'\nimport { getTagForReport } from '../../tag-service/util'\nimport { retryHttp } from '../../util'\nimport { getEventType } from '../util'\nimport { assertProtectedTagAction, getProtectedTags } from './util'\n\nconst handleModerationEvent = async ({\n ctx,\n input,\n auth,\n}: {\n ctx: AppContext\n input: HandlerInput\n auth: ModeratorOutput | AdminTokenOutput\n}) => {\n const access = auth.credentials\n const createdBy =\n auth.credentials.type === 'moderator'\n ? auth.credentials.iss\n : input.body.createdBy\n const db = ctx.db\n const moderationService = ctx.modService(db)\n const settingService = ctx.settingService(db)\n const { event, externalId } = input.body\n const isAcknowledgeEvent = isModEventAcknowledge(event)\n const isTakedownEvent = isModEventTakedown(event)\n const isReverseTakedownEvent = isModEventReverseTakedown(event)\n const isLabelEvent = isModEventLabel(event)\n const subject = subjectFromInput(\n input.body.subject,\n input.body.subjectBlobCids,\n )\n\n if (isAgeAssuranceEvent(event) && !subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n\n if (isAgeAssuranceOverrideEvent(event)) {\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n if (!auth.credentials.isModerator) {\n throw new AuthRequiredError(\n 'Must be a full moderator to override age assurance',\n )\n }\n }\n\n if (isRevokeAccountCredentialsEvent(event)) {\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Invalid subject type')\n }\n\n if (!auth.credentials.isAdmin) {\n throw new AuthRequiredError(\n 'Must be an admin to revoke account credentials',\n )\n }\n\n if (!ctx.pdsAgent) {\n throw new InvalidRequestError('PDS not configured')\n }\n\n await ctx.pdsAgent.com.atproto.temp.revokeAccountCredentials(\n { account: subject.did },\n await ctx.pdsAuth(ids.ComAtprotoTempRevokeAccountCredentials),\n )\n }\n\n // if less than moderator access then can only take ack and escalation actions\n if (isTakedownEvent || isReverseTakedownEvent) {\n if (!access.isModerator) {\n throw new AuthRequiredError(\n 'Must be a full moderator to take this type of action',\n )\n }\n\n // Non admins should not be able to take down feed generators\n if (\n !access.isAdmin &&\n subject.recordPath?.includes('app.bsky.feed.generator/')\n ) {\n throw new AuthRequiredError(\n 'Must be a full admin to take this type of action on feed generators',\n )\n }\n }\n // if less than moderator access then can not apply labels\n if (!access.isModerator && isLabelEvent) {\n throw new AuthRequiredError('Must be a full moderator to label content')\n }\n\n if (isLabelEvent) {\n validateLabels([\n ...(event.createLabelVals ?? []),\n ...(event.negateLabelVals ?? []),\n ])\n }\n\n const isTakedownOrReverseTakedownEvent =\n isTakedownEvent || isReverseTakedownEvent\n if (isTakedownOrReverseTakedownEvent || isLabelEvent) {\n const status = await moderationService.getStatus(subject)\n\n if (status?.takendown && isTakedownEvent) {\n throw new InvalidRequestError(`Subject is already taken down`)\n }\n\n if (!status?.takendown && isReverseTakedownEvent) {\n throw new InvalidRequestError(`Subject is not taken down`)\n }\n\n if (status?.tags?.length) {\n const protectedTags = await getProtectedTags(\n settingService,\n ctx.cfg.service.did,\n )\n\n if (protectedTags) {\n assertProtectedTagAction({\n protectedTags,\n subjectTags: status.tags,\n actionAuthor: createdBy,\n isAdmin: auth.credentials.isAdmin,\n isModerator: auth.credentials.isModerator,\n isTriage: auth.credentials.isTriage,\n })\n }\n }\n\n if (status?.takendown && isReverseTakedownEvent && subject.isRecord()) {\n // due to the way blob status is modeled, we should reverse takedown on all\n // blobs for the record being restored, which aren't taken down on another record.\n subject.blobCids = status.blobCids ?? []\n }\n }\n\n if (isModEventEmail(event) && event.content) {\n // sending email prior to logging the event to avoid a long transaction below\n if (!subject.isRepo()) {\n throw new InvalidRequestError('Email can only be sent to a repo subject')\n }\n const { content, subjectLine } = event\n // on error, don't fail the whole event. instead, log the event data with isDelivered false\n try {\n await retryHttp(() =>\n ctx.modService(db).sendEmail({\n subject: subjectLine,\n content,\n recipientDid: subject.did,\n }),\n )\n event.isDelivered = true\n } catch (err) {\n event.isDelivered = false\n httpLogger.error({ err, event }, 'failed to send mod event email')\n }\n }\n\n if (isModEventDivert(event) && subject.isRecord()) {\n if (!ctx.blobDiverter) {\n throw new InvalidRequestError(\n 'BlobDiverter not configured for this service',\n )\n }\n await ctx.blobDiverter.uploadBlobOnService(subject.info())\n }\n\n if (\n (isModEventMuteReporter(event) || isModEventUnmuteReporter(event)) &&\n !subject.isRepo()\n ) {\n throw new InvalidRequestError('Subject must be a repo when muting reporter')\n }\n\n if (isModEventTag(event)) {\n await assertTagAuth(settingService, ctx.cfg.service.did, event, auth)\n }\n\n if (isModEventReport(event)) {\n await ctx.moderationServiceProfile().validateReasonType(event.reportType)\n }\n\n const moderationEvent = await db.transaction(async (dbTxn) => {\n const moderationTxn = ctx.modService(dbTxn)\n\n if (externalId) {\n const existingEvent = await moderationTxn.getEventByExternalId(\n getEventType(event.$type),\n externalId,\n subject,\n )\n\n if (existingEvent) {\n throw new InvalidRequestError(\n `An event with the same external ID already exists for the subject.`,\n 'DuplicateExternalId',\n )\n }\n }\n\n const result = await moderationTxn.logEvent({\n event,\n subject,\n createdBy,\n modTool: input.body.modTool,\n externalId,\n })\n\n const tagService = new TagService(\n subject,\n result.subjectStatus,\n ctx.cfg.service.did,\n moderationTxn,\n )\n\n const initialTags = isModEventReport(event)\n ? [getTagForReport(event.reportType)]\n : undefined\n await tagService.evaluateForSubject(initialTags)\n\n if (subject.isRepo()) {\n if (isTakedownEvent) {\n const isSuspend = !!result.event.durationInHours\n await moderationTxn.takedownRepo(\n subject,\n result.event.id,\n new Set(\n result.event.meta?.targetServices\n ? `${result.event.meta.targetServices}`.split(',')\n : undefined,\n ),\n isSuspend,\n )\n } else if (isReverseTakedownEvent) {\n await moderationTxn.reverseTakedownRepo(subject)\n }\n }\n\n if (subject.isRecord()) {\n if (isTakedownEvent) {\n await moderationTxn.takedownRecord(\n subject,\n result.event.id,\n new Set(\n result.event.meta?.targetServices\n ? `${result.event.meta.targetServices}`.split(',')\n : undefined,\n ),\n )\n } else if (isReverseTakedownEvent) {\n await moderationTxn.reverseTakedownRecord(subject)\n }\n }\n\n if (\n (isTakedownEvent || isAcknowledgeEvent) &&\n result.event.meta?.acknowledgeAccountSubjects\n ) {\n await moderationTxn.resolveSubjectsForAccount(\n subject.did,\n createdBy,\n result.event,\n )\n }\n\n if (isLabelEvent) {\n await moderationTxn.formatAndCreateLabels(\n result.event.subjectUri ?? result.event.subjectDid,\n result.event.subjectCid,\n {\n create: result.event.createLabelVals?.length\n ? result.event.createLabelVals.split(' ')\n : undefined,\n negate: result.event.negateLabelVals?.length\n ? result.event.negateLabelVals.split(' ')\n : undefined,\n },\n result.event.durationInHours ?? undefined,\n )\n }\n\n return result.event\n })\n\n return moderationService.views.formatEvent(moderationEvent)\n}\n\nexport default function (server: Server, ctx: AppContext) {\n server.tools.ozone.moderation.emitEvent({\n auth: ctx.authVerifier.modOrAdminToken,\n handler: async ({ input, auth }) => {\n const moderationEvent = await handleModerationEvent({\n input,\n auth,\n ctx,\n })\n\n // On divert events, we need to automatically take down the blobs\n if (isModEventDivert(input.body.event)) {\n await handleModerationEvent({\n auth,\n ctx,\n input: {\n ...input,\n body: {\n ...input.body,\n event: {\n ...input.body.event,\n $type: 'tools.ozone.moderation.defs#modEventTakedown',\n comment:\n '[DIVERT_SIDE_EFFECT]: Automatically taking down after divert event',\n },\n modTool: input.body.modTool,\n },\n },\n })\n }\n\n return {\n encoding: 'application/json',\n body: moderationEvent,\n }\n },\n })\n}\n\nconst assertTagAuth = async (\n settingService: SettingService,\n serviceDid: string,\n event: ModEventTag,\n auth: ModeratorOutput | AdminTokenOutput,\n) => {\n // admins can add/remove any tag\n if (auth.credentials.isAdmin) return\n\n const protectedTags = await getProtectedTags(settingService, serviceDid)\n\n if (!protectedTags) {\n return\n }\n\n for (const tag of Object.keys(protectedTags)) {\n if (event.add.includes(tag) || event.remove.includes(tag)) {\n // if specific moderators are configured to manage this tag but the current user\n // is not one of them, then throw an error\n const configuredModerators = protectedTags[tag]?.['moderators']\n if (\n configuredModerators &&\n !configuredModerators.includes(auth.credentials.iss)\n ) {\n throw new InvalidRequestError(`Not allowed to manage tag: ${tag}`)\n }\n\n const configuredRoles = protectedTags[tag]?.['roles']\n if (configuredRoles) {\n // admins can already do everything so we only check for moderator and triage role config\n if (\n auth.credentials.isModerator &&\n !configuredRoles.includes('tools.ozone.team.defs#roleModerator')\n ) {\n throw new InvalidRequestError(\n `Can not manage tag ${tag} with moderator role`,\n )\n } else if (\n auth.credentials.isTriage &&\n !configuredRoles.includes('tools.ozone.team.defs#roleTriage')\n ) {\n throw new InvalidRequestError(\n `Can not manage tag ${tag} with triage role`,\n )\n }\n }\n }\n }\n}\n\nconst validateLabels = (labels: string[]) => {\n for (const label of labels) {\n for (const char of badChars) {\n if (label.includes(char)) {\n throw new InvalidRequestError(`Invalid label: ${label}`)\n }\n }\n }\n}\n\nconst badChars = [' ', ',', ';', `'`, `\"`]\n"]}
|
|
@@ -36,7 +36,7 @@ export declare class EventPusher {
|
|
|
36
36
|
};
|
|
37
37
|
});
|
|
38
38
|
start(): void;
|
|
39
|
-
|
|
39
|
+
getTakedownServices(targetServices: Set<string>): RepoPushEventType[];
|
|
40
40
|
poll(state: PollState, fn: () => Promise<void>): void;
|
|
41
41
|
processAll(): Promise<void>;
|
|
42
42
|
destroy(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-pusher.d.ts","sourceRoot":"","sources":["../../src/daemon/event-pusher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAQhE,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;IACtB,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAA;KACtB,CAAA;CACF,CAAA;AAED,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,QAAQ,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,qBAAa,WAAW;IAiBb,EAAE,EAAE,QAAQ;IACZ,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,WAAW,CAAC;IApB3B,SAAS,UAAQ;IAEjB,aAAa,EAAE,SAAS,CAEvB;IACD,eAAe,EAAE,SAAS,CAEzB;IACD,aAAa,EAAE,SAAS,CAEvB;IAED,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,GAAG,EAAE,OAAO,GAAG,SAAS,CAAA;gBAGf,EAAE,EAAE,QAAQ,EACZ,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,WAAW,CAAC,EACzB,QAAQ,EAAE;QACR,OAAO,CAAC,EAAE;YACR,GAAG,EAAE,MAAM,CAAA;YACX,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;QACD,GAAG,CAAC,EAAE;YACJ,GAAG,EAAE,MAAM,CAAA;YACX,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;KACF;IAgBH,KAAK;
|
|
1
|
+
{"version":3,"file":"event-pusher.d.ts","sourceRoot":"","sources":["../../src/daemon/event-pusher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAQhE,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;IACtB,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAA;KACtB,CAAA;CACF,CAAA;AAED,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,QAAQ,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,qBAAa,WAAW;IAiBb,EAAE,EAAE,QAAQ;IACZ,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,WAAW,CAAC;IApB3B,SAAS,UAAQ;IAEjB,aAAa,EAAE,SAAS,CAEvB;IACD,eAAe,EAAE,SAAS,CAEzB;IACD,aAAa,EAAE,SAAS,CAEvB;IAED,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,GAAG,EAAE,OAAO,GAAG,SAAS,CAAA;gBAGf,EAAE,EAAE,QAAQ,EACZ,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,WAAW,CAAC,EACzB,QAAQ,EAAE;QACR,OAAO,CAAC,EAAE;YACR,GAAG,EAAE,MAAM,CAAA;YACX,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;QACD,GAAG,CAAC,EAAE;YACJ,GAAG,EAAE,MAAM,CAAA;YACX,GAAG,EAAE,MAAM,CAAA;SACZ,CAAA;KACF;IAgBH,KAAK;IASL,mBAAmB,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,iBAAiB,EAAE;IAqBrE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAWxC,UAAU;IAWV,OAAO;IAeP,cAAc;IAYd,gBAAgB;IAYhB,cAAc;YAYN,sBAAsB;IAgC9B,gBAAgB,CAAC,EAAE,EAAE,MAAM;IAsC3B,kBAAkB,CAAC,EAAE,EAAE,MAAM;IAuC7B,gBAAgB,CAAC,EAAE,EAAE,MAAM;IA4B3B,oBAAoB,CACxB,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,EAChC,SAAS,EAAE,OAAO;IAkBd,gBAAgB,CACpB,UAAU,EAAE,UAAU,CAAC,aAAa,CAAC,EAAE,EACvC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;CAsB9B"}
|
|
@@ -84,13 +84,25 @@ class EventPusher {
|
|
|
84
84
|
this.poll(this.recordPollState, () => this.pushRecordEvents());
|
|
85
85
|
this.poll(this.blobPollState, () => this.pushBlobEvents());
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// event pusher may be configured with both appview and pds
|
|
88
|
+
// but the takedown may particularly want only one of them
|
|
89
|
+
// unless the target services are specified, we will push to all configured services
|
|
90
|
+
getTakedownServices(targetServices) {
|
|
91
|
+
let configured = [];
|
|
89
92
|
if (this.pds)
|
|
90
|
-
|
|
93
|
+
configured.push('pds_takedown');
|
|
91
94
|
if (this.appview)
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
configured.push('appview_takedown');
|
|
96
|
+
if (!targetServices.size) {
|
|
97
|
+
return configured;
|
|
98
|
+
}
|
|
99
|
+
if (!targetServices.has('appview')) {
|
|
100
|
+
configured = configured.filter((service) => service !== 'appview_takedown');
|
|
101
|
+
}
|
|
102
|
+
if (!targetServices.has('pds')) {
|
|
103
|
+
configured = configured.filter((service) => service !== 'pds_takedown');
|
|
104
|
+
}
|
|
105
|
+
return configured;
|
|
94
106
|
}
|
|
95
107
|
poll(state, fn) {
|
|
96
108
|
if (this.destroyed)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-pusher.js","sourceRoot":"","sources":["../../src/daemon/event-pusher.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport { Insertable, Selectable } from 'kysely'\nimport { AtpAgent } from '@atproto/api'\nimport { SECOND } from '@atproto/common'\nimport { Database } from '../db'\nimport { BlobPushEvent } from '../db/schema/blob_push_event'\nimport { RepoPushEventType } from '../db/schema/repo_push_event'\nimport { ids } from '../lexicon/lexicons'\nimport { InputSchema } from '../lexicon/types/com/atproto/admin/updateSubjectStatus'\nimport { dbLogger } from '../logger'\nimport { retryHttp } from '../util'\n\ntype EventSubject = InputSchema['subject']\n\ntype PollState = {\n timer?: NodeJS.Timeout\n promise: Promise<void>\n}\n\ntype AuthHeaders = {\n headers: {\n authorization: string\n }\n}\n\ntype Service = {\n agent: AtpAgent\n did: string\n}\n\nexport class EventPusher {\n destroyed = false\n\n repoPollState: PollState = {\n promise: Promise.resolve(),\n }\n recordPollState: PollState = {\n promise: Promise.resolve(),\n }\n blobPollState: PollState = {\n promise: Promise.resolve(),\n }\n\n appview: Service | undefined\n pds: Service | undefined\n\n constructor(\n public db: Database,\n public createAuthHeaders: (\n aud: string,\n method: string,\n ) => Promise<AuthHeaders>,\n services: {\n appview?: {\n url: string\n did: string\n }\n pds?: {\n url: string\n did: string\n }\n },\n ) {\n if (services.appview) {\n this.appview = {\n agent: new AtpAgent({ service: services.appview.url }),\n did: services.appview.did,\n }\n }\n if (services.pds) {\n this.pds = {\n agent: new AtpAgent({ service: services.pds.url }),\n did: services.pds.did,\n }\n }\n }\n\n start() {\n this.poll(this.repoPollState, () => this.pushRepoEvents())\n this.poll(this.recordPollState, () => this.pushRecordEvents())\n this.poll(this.blobPollState, () => this.pushBlobEvents())\n }\n\n get takedowns(): RepoPushEventType[] {\n const takedowns: RepoPushEventType[] = []\n if (this.pds) takedowns.push('pds_takedown')\n if (this.appview) takedowns.push('appview_takedown')\n return takedowns\n }\n\n poll(state: PollState, fn: () => Promise<void>) {\n if (this.destroyed) return\n state.promise = fn()\n .catch((err) => {\n dbLogger.error({ err }, 'event push failed')\n })\n .finally(() => {\n state.timer = setTimeout(() => this.poll(state, fn), 30 * SECOND)\n })\n }\n\n async processAll() {\n await Promise.all([\n this.pushRepoEvents(),\n this.pushRecordEvents(),\n this.pushBlobEvents(),\n this.repoPollState.promise,\n this.recordPollState.promise,\n this.blobPollState.promise,\n ])\n }\n\n async destroy() {\n this.destroyed = true\n const destroyState = (state: PollState) => {\n if (state.timer) {\n clearTimeout(state.timer)\n }\n return state.promise\n }\n await Promise.all([\n destroyState(this.repoPollState),\n destroyState(this.recordPollState),\n destroyState(this.blobPollState),\n ])\n }\n\n async pushRepoEvents() {\n const toPush = await this.db.db\n .selectFrom('repo_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptRepoEvent(evt.id)))\n }\n\n async pushRecordEvents() {\n const toPush = await this.db.db\n .selectFrom('record_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptRecordEvent(evt.id)))\n }\n\n async pushBlobEvents() {\n const toPush = await this.db.db\n .selectFrom('blob_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptBlobEvent(evt.id)))\n }\n\n private async updateSubjectOnService(\n service: Service,\n subject: EventSubject,\n takedownRef: string | null,\n ): Promise<boolean> {\n const auth = await this.createAuthHeaders(\n service.did,\n ids.ComAtprotoAdminUpdateSubjectStatus,\n )\n try {\n await retryHttp(() =>\n service.agent.com.atproto.admin.updateSubjectStatus(\n {\n subject,\n takedown: {\n applied: !!takedownRef,\n ref: takedownRef ?? undefined,\n },\n },\n {\n ...auth,\n encoding: 'application/json',\n },\n ),\n )\n return true\n } catch (err) {\n dbLogger.error({ err, subject, takedownRef }, 'failed to push out event')\n return false\n }\n }\n\n async attemptRepoEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('repo_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.admin.defs#repoRef',\n did: evt.subjectDid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await dbTxn.db\n .updateTable('repo_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (evt.attempts ?? 0) + 1,\n },\n )\n .where('subjectDid', '=', evt.subjectDid)\n .where('eventType', '=', evt.eventType)\n .execute()\n })\n }\n\n async attemptRecordEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('record_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.repo.strongRef',\n uri: evt.subjectUri,\n cid: evt.subjectCid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await dbTxn.db\n .updateTable('record_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (evt.attempts ?? 0) + 1,\n },\n )\n .where('subjectUri', '=', evt.subjectUri)\n .where('eventType', '=', evt.eventType)\n .execute()\n })\n }\n\n async attemptBlobEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('blob_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.admin.defs#repoBlobRef',\n did: evt.subjectDid,\n cid: evt.subjectBlobCid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await this.markBlobEventAttempt(dbTxn, evt, succeeded)\n })\n }\n\n async markBlobEventAttempt(\n dbTxn: Database,\n event: Selectable<BlobPushEvent>,\n succeeded: boolean,\n ) {\n await dbTxn.db\n .updateTable('blob_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (event.attempts ?? 0) + 1,\n },\n )\n .where('subjectDid', '=', event.subjectDid)\n .where('subjectBlobCid', '=', event.subjectBlobCid)\n .where('eventType', '=', event.eventType)\n .execute()\n }\n\n async logBlobPushEvent(\n blobValues: Insertable<BlobPushEvent>[],\n takedownRef?: string | null,\n ) {\n return this.db.db\n .insertInto('blob_push_event')\n .values(blobValues)\n .onConflict((oc) =>\n oc.columns(['subjectDid', 'subjectBlobCid', 'eventType']).doUpdateSet({\n takedownRef,\n confirmedAt: null,\n attempts: 0,\n lastAttempted: null,\n }),\n )\n .returning([\n 'id',\n 'subjectDid',\n 'subjectUri',\n 'subjectBlobCid',\n 'eventType',\n ])\n .execute()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"event-pusher.js","sourceRoot":"","sources":["../../src/daemon/event-pusher.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAEhC,sCAAuC;AACvC,4CAAwC;AAIxC,kDAAyC;AAEzC,sCAAoC;AACpC,kCAAmC;AAoBnC,MAAa,WAAW;IAgBtB,YACS,EAAY,EACZ,iBAGkB,EACzB,QASC;QAdD;;;;mBAAO,EAAE;WAAU;QACnB;;;;mBAAO,iBAAiB;WAGC;QApB3B;;;;mBAAY,KAAK;WAAA;QAEjB;;;;mBAA2B;gBACzB,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;aAC3B;WAAA;QACD;;;;mBAA6B;gBAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;aAC3B;WAAA;QACD;;;;mBAA2B;gBACzB,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;aAC3B;WAAA;QAED;;;;;WAA4B;QAC5B;;;;;WAAwB;QAmBtB,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,GAAG;gBACb,KAAK,EAAE,IAAI,cAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACtD,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG;aAC1B,CAAA;QACH,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,GAAG;gBACT,KAAK,EAAE,IAAI,cAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;gBAClD,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG;aACtB,CAAA;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;QAC1D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAA;QAC9D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,2DAA2D;IAC3D,0DAA0D;IAC1D,oFAAoF;IACpF,mBAAmB,CAAC,cAA2B;QAC7C,IAAI,UAAU,GAAwB,EAAE,CAAA;QACxC,IAAI,IAAI,CAAC,GAAG;YAAE,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC7C,IAAI,IAAI,CAAC,OAAO;YAAE,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;QAErD,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YACzB,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,UAAU,GAAG,UAAU,CAAC,MAAM,CAC5B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,kBAAkB,CAC5C,CAAA;QACH,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,cAAc,CAAC,CAAA;QACzE,CAAC;QAED,OAAO,UAAU,CAAA;IACnB,CAAC;IAED,IAAI,CAAC,KAAgB,EAAE,EAAuB;QAC5C,IAAI,IAAI,CAAC,SAAS;YAAE,OAAM;QAC1B,KAAK,CAAC,OAAO,GAAG,EAAE,EAAE;aACjB,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,iBAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAA;QAC9C,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,eAAM,CAAC,CAAA;QACnE,CAAC,CAAC,CAAA;IACN,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,IAAI,CAAC,cAAc,EAAE;YACrB,IAAI,CAAC,gBAAgB,EAAE;YACvB,IAAI,CAAC,cAAc,EAAE;YACrB,IAAI,CAAC,aAAa,CAAC,OAAO;YAC1B,IAAI,CAAC,eAAe,CAAC,OAAO;YAC5B,IAAI,CAAC,aAAa,CAAC,OAAO;SAC3B,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,MAAM,YAAY,GAAG,CAAC,KAAgB,EAAE,EAAE;YACxC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC3B,CAAC;YACD,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,CAAC,CAAA;QACD,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC;YAChC,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC;YAClC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC;SACjC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC5B,UAAU,CAAC,iBAAiB,CAAC;aAC7B,MAAM,CAAC,IAAI,CAAC;aACZ,SAAS,EAAE;aACX,UAAU,EAAE;aACZ,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;aAChC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC;aAC1B,OAAO,EAAE,CAAA;QACZ,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACvE,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC5B,UAAU,CAAC,mBAAmB,CAAC;aAC/B,MAAM,CAAC,IAAI,CAAC;aACZ,SAAS,EAAE;aACX,UAAU,EAAE;aACZ,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;aAChC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC;aAC1B,OAAO,EAAE,CAAA;QACZ,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC5B,UAAU,CAAC,iBAAiB,CAAC;aAC7B,MAAM,CAAC,IAAI,CAAC;aACZ,SAAS,EAAE;aACX,UAAU,EAAE;aACZ,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;aAChC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC;aAC1B,OAAO,EAAE,CAAA;QACZ,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACvE,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAClC,OAAgB,EAChB,OAAqB,EACrB,WAA0B;QAE1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACvC,OAAO,CAAC,GAAG,EACX,cAAG,CAAC,kCAAkC,CACvC,CAAA;QACD,IAAI,CAAC;YACH,MAAM,IAAA,gBAAS,EAAC,GAAG,EAAE,CACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CACjD;gBACE,OAAO;gBACP,QAAQ,EAAE;oBACR,OAAO,EAAE,CAAC,CAAC,WAAW;oBACtB,GAAG,EAAE,WAAW,IAAI,SAAS;iBAC9B;aACF,EACD;gBACE,GAAG,IAAI;gBACP,QAAQ,EAAE,kBAAkB;aAC7B,CACF,CACF,CAAA;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iBAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,0BAA0B,CAAC,CAAA;YACzE,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU;QAC/B,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,EAAE;iBACvB,UAAU,CAAC,iBAAiB,CAAC;iBAC7B,SAAS,EAAE;iBACX,SAAS,EAAE;iBACX,UAAU,EAAE;iBACZ,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;iBACpB,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;iBAChC,gBAAgB,EAAE,CAAA;YACrB,IAAI,CAAC,GAAG;gBAAE,OAAM;YAChB,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;YAC1E,IAAA,qBAAM,EAAC,OAAO,CAAC,CAAA;YACf,MAAM,OAAO,GAAG;gBACd,KAAK,EAAE,gCAAgC;gBACvC,GAAG,EAAE,GAAG,CAAC,UAAU;aACpB,CAAA;YACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,sBAAsB,CACjD,OAAO,EACP,OAAO,EACP,GAAG,CAAC,WAAW,CAChB,CAAA;YACD,MAAM,KAAK,CAAC,EAAE;iBACX,WAAW,CAAC,iBAAiB,CAAC;iBAC9B,GAAG,CACF,SAAS;gBACP,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE;gBAC7B,CAAC,CAAC;oBACE,aAAa,EAAE,IAAI,IAAI,EAAE;oBACzB,QAAQ,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;iBAClC,CACN;iBACA,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC;iBACxC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC;iBACtC,OAAO,EAAE,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,EAAU;QACjC,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,EAAE;iBACvB,UAAU,CAAC,mBAAmB,CAAC;iBAC/B,SAAS,EAAE;iBACX,SAAS,EAAE;iBACX,UAAU,EAAE;iBACZ,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;iBACpB,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;iBAChC,gBAAgB,EAAE,CAAA;YACrB,IAAI,CAAC,GAAG;gBAAE,OAAM;YAChB,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;YAC1E,IAAA,qBAAM,EAAC,OAAO,CAAC,CAAA;YACf,MAAM,OAAO,GAAG;gBACd,KAAK,EAAE,4BAA4B;gBACnC,GAAG,EAAE,GAAG,CAAC,UAAU;gBACnB,GAAG,EAAE,GAAG,CAAC,UAAU;aACpB,CAAA;YACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,sBAAsB,CACjD,OAAO,EACP,OAAO,EACP,GAAG,CAAC,WAAW,CAChB,CAAA;YACD,MAAM,KAAK,CAAC,EAAE;iBACX,WAAW,CAAC,mBAAmB,CAAC;iBAChC,GAAG,CACF,SAAS;gBACP,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE;gBAC7B,CAAC,CAAC;oBACE,aAAa,EAAE,IAAI,IAAI,EAAE;oBACzB,QAAQ,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;iBAClC,CACN;iBACA,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC;iBACxC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC;iBACtC,OAAO,EAAE,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU;QAC/B,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,EAAE;iBACvB,UAAU,CAAC,iBAAiB,CAAC;iBAC7B,SAAS,EAAE;iBACX,SAAS,EAAE;iBACX,UAAU,EAAE;iBACZ,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;iBACpB,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;iBAChC,gBAAgB,EAAE,CAAA;YACrB,IAAI,CAAC,GAAG;gBAAE,OAAM;YAEhB,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;YAC1E,IAAA,qBAAM,EAAC,OAAO,CAAC,CAAA;YACf,MAAM,OAAO,GAAG;gBACd,KAAK,EAAE,oCAAoC;gBAC3C,GAAG,EAAE,GAAG,CAAC,UAAU;gBACnB,GAAG,EAAE,GAAG,CAAC,cAAc;aACxB,CAAA;YACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,sBAAsB,CACjD,OAAO,EACP,OAAO,EACP,GAAG,CAAC,WAAW,CAChB,CAAA;YACD,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,CAAA;QACxD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,KAAe,EACf,KAAgC,EAChC,SAAkB;QAElB,MAAM,KAAK,CAAC,EAAE;aACX,WAAW,CAAC,iBAAiB,CAAC;aAC9B,GAAG,CACF,SAAS;YACP,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE;YAC7B,CAAC,CAAC;gBACE,aAAa,EAAE,IAAI,IAAI,EAAE;gBACzB,QAAQ,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;aACpC,CACN;aACA,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC;aAC1C,KAAK,CAAC,gBAAgB,EAAE,GAAG,EAAE,KAAK,CAAC,cAAc,CAAC;aAClD,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC;aACxC,OAAO,EAAE,CAAA;IACd,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,UAAuC,EACvC,WAA2B;QAE3B,OAAO,IAAI,CAAC,EAAE,CAAC,EAAE;aACd,UAAU,CAAC,iBAAiB,CAAC;aAC7B,MAAM,CAAC,UAAU,CAAC;aAClB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CACjB,EAAE,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;YACpE,WAAW;YACX,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,CAAC;YACX,aAAa,EAAE,IAAI;SACpB,CAAC,CACH;aACA,SAAS,CAAC;YACT,IAAI;YACJ,YAAY;YACZ,YAAY;YACZ,gBAAgB;YAChB,WAAW;SACZ,CAAC;aACD,OAAO,EAAE,CAAA;IACd,CAAC;CACF;AA5UD,kCA4UC","sourcesContent":["import assert from 'node:assert'\nimport { Insertable, Selectable } from 'kysely'\nimport { AtpAgent } from '@atproto/api'\nimport { SECOND } from '@atproto/common'\nimport { Database } from '../db'\nimport { BlobPushEvent } from '../db/schema/blob_push_event'\nimport { RepoPushEventType } from '../db/schema/repo_push_event'\nimport { ids } from '../lexicon/lexicons'\nimport { InputSchema } from '../lexicon/types/com/atproto/admin/updateSubjectStatus'\nimport { dbLogger } from '../logger'\nimport { retryHttp } from '../util'\n\ntype EventSubject = InputSchema['subject']\n\ntype PollState = {\n timer?: NodeJS.Timeout\n promise: Promise<void>\n}\n\ntype AuthHeaders = {\n headers: {\n authorization: string\n }\n}\n\ntype Service = {\n agent: AtpAgent\n did: string\n}\n\nexport class EventPusher {\n destroyed = false\n\n repoPollState: PollState = {\n promise: Promise.resolve(),\n }\n recordPollState: PollState = {\n promise: Promise.resolve(),\n }\n blobPollState: PollState = {\n promise: Promise.resolve(),\n }\n\n appview: Service | undefined\n pds: Service | undefined\n\n constructor(\n public db: Database,\n public createAuthHeaders: (\n aud: string,\n method: string,\n ) => Promise<AuthHeaders>,\n services: {\n appview?: {\n url: string\n did: string\n }\n pds?: {\n url: string\n did: string\n }\n },\n ) {\n if (services.appview) {\n this.appview = {\n agent: new AtpAgent({ service: services.appview.url }),\n did: services.appview.did,\n }\n }\n if (services.pds) {\n this.pds = {\n agent: new AtpAgent({ service: services.pds.url }),\n did: services.pds.did,\n }\n }\n }\n\n start() {\n this.poll(this.repoPollState, () => this.pushRepoEvents())\n this.poll(this.recordPollState, () => this.pushRecordEvents())\n this.poll(this.blobPollState, () => this.pushBlobEvents())\n }\n\n // event pusher may be configured with both appview and pds\n // but the takedown may particularly want only one of them\n // unless the target services are specified, we will push to all configured services\n getTakedownServices(targetServices: Set<string>): RepoPushEventType[] {\n let configured: RepoPushEventType[] = []\n if (this.pds) configured.push('pds_takedown')\n if (this.appview) configured.push('appview_takedown')\n\n if (!targetServices.size) {\n return configured\n }\n\n if (!targetServices.has('appview')) {\n configured = configured.filter(\n (service) => service !== 'appview_takedown',\n )\n }\n if (!targetServices.has('pds')) {\n configured = configured.filter((service) => service !== 'pds_takedown')\n }\n\n return configured\n }\n\n poll(state: PollState, fn: () => Promise<void>) {\n if (this.destroyed) return\n state.promise = fn()\n .catch((err) => {\n dbLogger.error({ err }, 'event push failed')\n })\n .finally(() => {\n state.timer = setTimeout(() => this.poll(state, fn), 30 * SECOND)\n })\n }\n\n async processAll() {\n await Promise.all([\n this.pushRepoEvents(),\n this.pushRecordEvents(),\n this.pushBlobEvents(),\n this.repoPollState.promise,\n this.recordPollState.promise,\n this.blobPollState.promise,\n ])\n }\n\n async destroy() {\n this.destroyed = true\n const destroyState = (state: PollState) => {\n if (state.timer) {\n clearTimeout(state.timer)\n }\n return state.promise\n }\n await Promise.all([\n destroyState(this.repoPollState),\n destroyState(this.recordPollState),\n destroyState(this.blobPollState),\n ])\n }\n\n async pushRepoEvents() {\n const toPush = await this.db.db\n .selectFrom('repo_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptRepoEvent(evt.id)))\n }\n\n async pushRecordEvents() {\n const toPush = await this.db.db\n .selectFrom('record_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptRecordEvent(evt.id)))\n }\n\n async pushBlobEvents() {\n const toPush = await this.db.db\n .selectFrom('blob_push_event')\n .select('id')\n .forUpdate()\n .skipLocked()\n .where('confirmedAt', 'is', null)\n .where('attempts', '<', 10)\n .execute()\n await Promise.all(toPush.map((evt) => this.attemptBlobEvent(evt.id)))\n }\n\n private async updateSubjectOnService(\n service: Service,\n subject: EventSubject,\n takedownRef: string | null,\n ): Promise<boolean> {\n const auth = await this.createAuthHeaders(\n service.did,\n ids.ComAtprotoAdminUpdateSubjectStatus,\n )\n try {\n await retryHttp(() =>\n service.agent.com.atproto.admin.updateSubjectStatus(\n {\n subject,\n takedown: {\n applied: !!takedownRef,\n ref: takedownRef ?? undefined,\n },\n },\n {\n ...auth,\n encoding: 'application/json',\n },\n ),\n )\n return true\n } catch (err) {\n dbLogger.error({ err, subject, takedownRef }, 'failed to push out event')\n return false\n }\n }\n\n async attemptRepoEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('repo_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.admin.defs#repoRef',\n did: evt.subjectDid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await dbTxn.db\n .updateTable('repo_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (evt.attempts ?? 0) + 1,\n },\n )\n .where('subjectDid', '=', evt.subjectDid)\n .where('eventType', '=', evt.eventType)\n .execute()\n })\n }\n\n async attemptRecordEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('record_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.repo.strongRef',\n uri: evt.subjectUri,\n cid: evt.subjectCid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await dbTxn.db\n .updateTable('record_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (evt.attempts ?? 0) + 1,\n },\n )\n .where('subjectUri', '=', evt.subjectUri)\n .where('eventType', '=', evt.eventType)\n .execute()\n })\n }\n\n async attemptBlobEvent(id: number) {\n await this.db.transaction(async (dbTxn) => {\n const evt = await dbTxn.db\n .selectFrom('blob_push_event')\n .selectAll()\n .forUpdate()\n .skipLocked()\n .where('id', '=', id)\n .where('confirmedAt', 'is', null)\n .executeTakeFirst()\n if (!evt) return\n\n const service = evt.eventType === 'pds_takedown' ? this.pds : this.appview\n assert(service)\n const subject = {\n $type: 'com.atproto.admin.defs#repoBlobRef',\n did: evt.subjectDid,\n cid: evt.subjectBlobCid,\n }\n const succeeded = await this.updateSubjectOnService(\n service,\n subject,\n evt.takedownRef,\n )\n await this.markBlobEventAttempt(dbTxn, evt, succeeded)\n })\n }\n\n async markBlobEventAttempt(\n dbTxn: Database,\n event: Selectable<BlobPushEvent>,\n succeeded: boolean,\n ) {\n await dbTxn.db\n .updateTable('blob_push_event')\n .set(\n succeeded\n ? { confirmedAt: new Date() }\n : {\n lastAttempted: new Date(),\n attempts: (event.attempts ?? 0) + 1,\n },\n )\n .where('subjectDid', '=', event.subjectDid)\n .where('subjectBlobCid', '=', event.subjectBlobCid)\n .where('eventType', '=', event.eventType)\n .execute()\n }\n\n async logBlobPushEvent(\n blobValues: Insertable<BlobPushEvent>[],\n takedownRef?: string | null,\n ) {\n return this.db.db\n .insertInto('blob_push_event')\n .values(blobValues)\n .onConflict((oc) =>\n oc.columns(['subjectDid', 'subjectBlobCid', 'eventType']).doUpdateSet({\n takedownRef,\n confirmedAt: null,\n attempts: 0,\n lastAttempted: null,\n }),\n )\n .returning([\n 'id',\n 'subjectDid',\n 'subjectUri',\n 'subjectBlobCid',\n 'eventType',\n ])\n .execute()\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialized-view-refresher.d.ts","sourceRoot":"","sources":["../../src/daemon/materialized-view-refresher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAEvE,qBAAa,yBAA0B,SAAQ,sBAAsB;gBACvD,eAAe,EAAE,eAAe,EAAE,QAAQ,SAAc;
|
|
1
|
+
{"version":3,"file":"materialized-view-refresher.d.ts","sourceRoot":"","sources":["../../src/daemon/materialized-view-refresher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAEvE,qBAAa,yBAA0B,SAAQ,sBAAsB;gBACvD,eAAe,EAAE,eAAe,EAAE,QAAQ,SAAc;CAqBrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialized-view-refresher.js","sourceRoot":"","sources":["../../src/daemon/materialized-view-refresher.ts"],"names":[],"mappings":";;;AAAA,mCAA4B;AAC5B,4CAAwC;AACxC,8CAAuE;AAEvE,MAAa,yBAA0B,SAAQ,mCAAsB;IACnE,YAAY,eAAgC,EAAE,QAAQ,GAAG,EAAE,GAAG,eAAM;QAClE,KAAK,CAAC,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;YACxD,KAAK,MAAM,IAAI,IAAI;gBACjB,sBAAsB;gBACtB,qBAAqB;gBACrB,6BAA6B;gBAC7B,6BAA6B;
|
|
1
|
+
{"version":3,"file":"materialized-view-refresher.js","sourceRoot":"","sources":["../../src/daemon/materialized-view-refresher.ts"],"names":[],"mappings":";;;AAAA,mCAA4B;AAC5B,4CAAwC;AACxC,8CAAuE;AAEvE,MAAa,yBAA0B,SAAQ,mCAAsB;IACnE,YAAY,eAAgC,EAAE,QAAQ,GAAG,EAAE,GAAG,eAAM;QAClE,KAAK,CAAC,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;YACxD,KAAK,MAAM,IAAI,IAAI;gBACjB,sBAAsB;gBACtB,qBAAqB;gBACrB,6BAA6B;gBAC7B,6BAA6B;aAC9B,EAAE,CAAC;gBACF,IAAI,MAAM,CAAC,OAAO;oBAAE,MAAK;gBAEzB,sEAAsE;gBACtE,uEAAuE;gBACvE,qEAAqE;gBACrE,qEAAqE;gBACrE,kBAAkB;gBAClB,MAAM,IAAA,YAAG,EAAA,0CAA0C,YAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CACvE,EAAE,CACH,CAAA;YACH,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAtBD,8DAsBC","sourcesContent":["import { sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { BackgroundQueue, PeriodicBackgroundTask } from '../background'\n\nexport class MaterializedViewRefresher extends PeriodicBackgroundTask {\n constructor(backgroundQueue: BackgroundQueue, interval = 30 * MINUTE) {\n super(backgroundQueue, interval, async ({ db }, signal) => {\n for (const view of [\n 'account_events_stats',\n 'record_events_stats',\n 'account_record_events_stats',\n 'account_record_status_stats',\n ]) {\n if (signal.aborted) break\n\n // Kysely does not provide a way to cancel a running query. Because of\n // this, killing the process during a refresh will cause the process to\n // wait for the current refresh to finish before exiting. This is not\n // ideal, but it is the best we can do until Kysely provides a way to\n // cancel a query.\n await sql`REFRESH MATERIALIZED VIEW CONCURRENTLY ${sql.id(view)}`.execute(\n db,\n )\n }\n })\n }\n}\n"]}
|
|
@@ -20,7 +20,11 @@ export declare class ScheduledActionProcessor {
|
|
|
20
20
|
poll(): void;
|
|
21
21
|
destroy(): Promise<void>;
|
|
22
22
|
executeScheduledAction(actionId: number): Promise<void>;
|
|
23
|
-
performTakedown({ action, event, modTool, moderationTxn, settingService, }: {
|
|
23
|
+
performTakedown({ email, action, event, modTool, moderationTxn, settingService, }: {
|
|
24
|
+
email: {
|
|
25
|
+
subject: string;
|
|
26
|
+
content: string;
|
|
27
|
+
};
|
|
24
28
|
action: Selectable<ScheduledAction>;
|
|
25
29
|
event: ModEventType;
|
|
26
30
|
modTool: ModTool | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled-action-processor.d.ts","sourceRoot":"","sources":["../../src/daemon/scheduled-action-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAMnC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAEL,OAAO,EACR,MAAM,8CAA8C,CAAA;AAErD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAE5E,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"scheduled-action-processor.d.ts","sourceRoot":"","sources":["../../src/daemon/scheduled-action-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAMnC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAEL,OAAO,EACR,MAAM,8CAA8C,CAAA;AAErD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAE5E,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAG1E,qBAAa,wBAAwB;IAMjC,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,sBAAsB;IAThC,SAAS,UAAQ;IACjB,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAoB;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGZ,EAAE,EAAE,QAAQ,EACZ,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,qBAAqB,EACrC,UAAU,EAAE,wBAAwB,EACpC,sBAAsB,EAAE,6BAA6B;IAG/D,KAAK;IAIL,IAAI;IAWE,OAAO;IASP,sBAAsB,CAAC,QAAQ,EAAE,MAAM;IAqGvC,eAAe,CAAC,EACpB,KAAK,EACL,MAAM,EACN,KAAK,EACL,OAAO,EACP,aAAa,EACb,cAAc,GACf,EAAE;QACD,KAAK,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;QAC3C,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,CAAA;QACnC,KAAK,EAAE,YAAY,CAAA;QACnB,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;QAE5B,aAAa,EAAE,iBAAiB,CAAA;QAChC,cAAc,EAAE,cAAc,CAAA;KAC/B;;;;IAkFK,8BAA8B;CAqCrC"}
|
|
@@ -5,6 +5,7 @@ const common_1 = require("@atproto/common");
|
|
|
5
5
|
const util_1 = require("../api/moderation/util");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
7
|
const subject_1 = require("../mod-service/subject");
|
|
8
|
+
const util_2 = require("../util");
|
|
8
9
|
class ScheduledActionProcessor {
|
|
9
10
|
constructor(db, serviceDid, settingService, modService, scheduledActionService) {
|
|
10
11
|
Object.defineProperty(this, "db", {
|
|
@@ -94,6 +95,10 @@ class ScheduledActionProcessor {
|
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
96
97
|
let event;
|
|
98
|
+
const email = {
|
|
99
|
+
subject: '',
|
|
100
|
+
content: '',
|
|
101
|
+
};
|
|
97
102
|
let modTool;
|
|
98
103
|
// Create the appropriate moderation action based on the scheduled action type
|
|
99
104
|
switch (action.action) {
|
|
@@ -107,7 +112,13 @@ class ScheduledActionProcessor {
|
|
|
107
112
|
durationInHours: eventData.durationInHours,
|
|
108
113
|
acknowledgeAccountSubjects: eventData.acknowledgeAccountSubjects,
|
|
109
114
|
policies: eventData.policies,
|
|
115
|
+
severityLevel: eventData.severityLevel,
|
|
116
|
+
strikeCount: eventData.strikeCount,
|
|
110
117
|
};
|
|
118
|
+
if (eventData.emailSubject && eventData.emailContent) {
|
|
119
|
+
email.subject = eventData.emailSubject;
|
|
120
|
+
email.content = eventData.emailContent;
|
|
121
|
+
}
|
|
111
122
|
}
|
|
112
123
|
break;
|
|
113
124
|
default:
|
|
@@ -119,6 +130,7 @@ class ScheduledActionProcessor {
|
|
|
119
130
|
modTool,
|
|
120
131
|
moderationTxn,
|
|
121
132
|
settingService,
|
|
133
|
+
email,
|
|
122
134
|
});
|
|
123
135
|
// Mark the scheduled action as executed
|
|
124
136
|
await scheduledActionTxn.markActionAsExecuted(actionId, moderationEvent.event.id);
|
|
@@ -139,7 +151,7 @@ class ScheduledActionProcessor {
|
|
|
139
151
|
}
|
|
140
152
|
});
|
|
141
153
|
}
|
|
142
|
-
async performTakedown({ action, event, modTool, moderationTxn, settingService, }) {
|
|
154
|
+
async performTakedown({ email, action, event, modTool, moderationTxn, settingService, }) {
|
|
143
155
|
const subject = new subject_1.RepoSubject(action.did);
|
|
144
156
|
const status = await moderationTxn.getStatus(subject);
|
|
145
157
|
if (status?.takendown) {
|
|
@@ -166,7 +178,37 @@ class ScheduledActionProcessor {
|
|
|
166
178
|
createdBy: action.createdBy,
|
|
167
179
|
});
|
|
168
180
|
// register the takedown in event pusher
|
|
169
|
-
await moderationTxn.takedownRepo(subject, moderationEvent.event.id
|
|
181
|
+
await moderationTxn.takedownRepo(subject, moderationEvent.event.id, new Set(moderationEvent.event.meta?.targetServices
|
|
182
|
+
? `${moderationEvent.event.meta.targetServices}`.split(',')
|
|
183
|
+
: undefined));
|
|
184
|
+
if (email.content && email.subject) {
|
|
185
|
+
let isDelivered = false;
|
|
186
|
+
try {
|
|
187
|
+
await (0, util_2.retryHttp)(() => moderationTxn.sendEmail({
|
|
188
|
+
...email,
|
|
189
|
+
recipientDid: action.did,
|
|
190
|
+
}));
|
|
191
|
+
isDelivered = true;
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
logger_1.dbLogger.error({ err, did: action.did }, 'failed to send takedown email');
|
|
195
|
+
}
|
|
196
|
+
await moderationTxn.logEvent({
|
|
197
|
+
event: {
|
|
198
|
+
content: email.content,
|
|
199
|
+
subjectLine: email.subject,
|
|
200
|
+
$type: 'tools.ozone.moderation.defs#modEventEmail',
|
|
201
|
+
comment: [
|
|
202
|
+
'Communication attached to scheduled action',
|
|
203
|
+
isDelivered ? '' : 'Email delivery failed',
|
|
204
|
+
].join('.'),
|
|
205
|
+
isDelivered,
|
|
206
|
+
},
|
|
207
|
+
subject,
|
|
208
|
+
modTool,
|
|
209
|
+
createdBy: action.createdBy,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
170
212
|
return moderationEvent;
|
|
171
213
|
}
|
|
172
214
|
async findAndExecuteScheduledActions() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled-action-processor.js","sourceRoot":"","sources":["../../src/daemon/scheduled-action-processor.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { MINUTE, SECOND } from '@atproto/common'\nimport {\n assertProtectedTagAction,\n getProtectedTags,\n} from '../api/moderation/util'\nimport { Database } from '../db'\nimport { ScheduledAction } from '../db/schema/scheduled-action'\nimport {\n ModEventTakedown,\n ModTool,\n} from '../lexicon/types/tools/ozone/moderation/defs'\nimport { dbLogger } from '../logger'\nimport { ModerationService, ModerationServiceCreator } from '../mod-service'\nimport { RepoSubject } from '../mod-service/subject'\nimport { ModEventType } from '../mod-service/types'\nimport { ScheduledActionServiceCreator } from '../scheduled-action/service'\nimport { SettingService, SettingServiceCreator } from '../setting/service'\n\nexport class ScheduledActionProcessor {\n destroyed = false\n processingPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private serviceDid: string,\n private settingService: SettingServiceCreator,\n private modService: ModerationServiceCreator,\n private scheduledActionService: ScheduledActionServiceCreator,\n ) {}\n\n start() {\n this.poll()\n }\n\n poll() {\n if (this.destroyed) return\n this.processingPromise = this.findAndExecuteScheduledActions()\n .catch((err) =>\n dbLogger.error({ err }, 'scheduled action processing errored'),\n )\n .finally(() => {\n this.timer = setTimeout(() => this.poll(), getInterval())\n })\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.processingPromise\n }\n\n async executeScheduledAction(actionId: number) {\n await this.db.transaction(async (dbTxn) => {\n const settingService = this.settingService(dbTxn)\n const moderationTxn = this.modService(dbTxn)\n const scheduledActionTxn = this.scheduledActionService(dbTxn)\n\n try {\n // maybe overfetching here to get the action again within the transaction to ensure it's still pending\n const action = await dbTxn.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('id', '=', actionId)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (!action) {\n // already processed or cancelled\n return\n }\n\n let event: ModEventType\n let modTool: ModTool | undefined\n\n // Create the appropriate moderation action based on the scheduled action type\n switch (action.action) {\n case 'takedown':\n {\n const eventData = action.eventData as ModEventTakedown & {\n modTool?: ModTool\n }\n modTool = eventData.modTool\n event = {\n $type: 'tools.ozone.moderation.defs#modEventTakedown',\n comment: `[SCHEDULED_ACTION] ${eventData.comment || 'Scheduled takedown executed'}`,\n durationInHours: eventData.durationInHours,\n acknowledgeAccountSubjects:\n eventData.acknowledgeAccountSubjects,\n policies: eventData.policies,\n }\n }\n break\n default:\n throw new Error(\n `Unsupported scheduled action type: ${action.action}`,\n )\n }\n\n const moderationEvent = await this.performTakedown({\n action,\n event,\n modTool,\n moderationTxn,\n settingService,\n })\n\n // Mark the scheduled action as executed\n await scheduledActionTxn.markActionAsExecuted(\n actionId,\n moderationEvent.event.id,\n )\n\n dbLogger.info(\n {\n did: action.did,\n scheduledActionId: actionId,\n moderationEventId: moderationEvent.event.id,\n },\n 'executed scheduled action',\n )\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error'\n\n // mark as failed\n await scheduledActionTxn.markActionAsFailed(actionId, errorMessage)\n\n dbLogger.error(\n {\n scheduledActionId: actionId,\n error: errorMessage,\n },\n 'failed to execute scheduled action',\n )\n }\n })\n }\n\n async performTakedown({\n action,\n event,\n modTool,\n moderationTxn,\n settingService,\n }: {\n action: Selectable<ScheduledAction>\n event: ModEventType\n modTool: ModTool | undefined\n\n moderationTxn: ModerationService\n settingService: SettingService\n }) {\n const subject = new RepoSubject(action.did)\n\n const status = await moderationTxn.getStatus(subject)\n\n if (status?.takendown) {\n throw new Error(`Account is already taken down`)\n }\n\n if (status?.tags?.length) {\n const protectedTags = await getProtectedTags(\n settingService,\n this.serviceDid,\n )\n\n if (protectedTags) {\n assertProtectedTagAction({\n protectedTags,\n subjectTags: status.tags,\n actionAuthor: action.createdBy,\n isAdmin: true,\n isModerator: false,\n isTriage: false,\n })\n }\n }\n\n // log the event which also applies the necessary state changes to moderation subject\n const moderationEvent = await moderationTxn.logEvent({\n event,\n subject,\n modTool,\n createdBy: action.createdBy,\n })\n\n // register the takedown in event pusher\n await moderationTxn.takedownRepo(subject, moderationEvent.event.id)\n\n return moderationEvent\n }\n\n async findAndExecuteScheduledActions() {\n const scheduledActionService = this.scheduledActionService(this.db)\n const now = new Date()\n\n const actionsToExecute =\n await scheduledActionService.getPendingActionsToExecute(now)\n\n for (const action of actionsToExecute) {\n // For randomized execution, check if we should execute now or wait\n if (action.randomizeExecution && action.executeAfter) {\n const executeAfter = new Date(action.executeAfter)\n // Default to a 30 second window for execution\n const executeUntil = action.executeUntil\n ? new Date(action.executeUntil)\n : new Date(executeAfter.getTime() + 30 * SECOND)\n\n // Only execute if we're past the earliest time\n if (now < executeAfter) {\n continue\n }\n\n // For randomized scheduling, randomly decide whether to execute now\n // The probability increases as we get closer to the deadline\n const timeRange = executeUntil.getTime() - executeAfter.getTime()\n const timeElapsed = now.getTime() - executeAfter.getTime()\n const executeProb = Math.min(timeElapsed / timeRange, 1)\n\n // Execute with increasing probability as we approach the deadline\n // Always execute if we're at or past the deadline\n if (now < executeUntil && Math.random() > executeProb * 0.1) {\n continue\n }\n }\n\n await this.executeScheduledAction(action.id)\n }\n }\n}\n\nconst getInterval = (): number => {\n // Process scheduled actions every minute\n const now = Date.now()\n const intervalMs = MINUTE\n const nextIteration = Math.ceil(now / intervalMs)\n return nextIteration * intervalMs - now\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scheduled-action-processor.js","sourceRoot":"","sources":["../../src/daemon/scheduled-action-processor.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { MINUTE, SECOND } from '@atproto/common'\nimport {\n assertProtectedTagAction,\n getProtectedTags,\n} from '../api/moderation/util'\nimport { Database } from '../db'\nimport { ScheduledAction } from '../db/schema/scheduled-action'\nimport {\n ModEventTakedown,\n ModTool,\n} from '../lexicon/types/tools/ozone/moderation/defs'\nimport { dbLogger } from '../logger'\nimport { ModerationService, ModerationServiceCreator } from '../mod-service'\nimport { RepoSubject } from '../mod-service/subject'\nimport { ModEventType } from '../mod-service/types'\nimport { ScheduledActionServiceCreator } from '../scheduled-action/service'\nimport { SettingService, SettingServiceCreator } from '../setting/service'\nimport { retryHttp } from '../util'\n\nexport class ScheduledActionProcessor {\n destroyed = false\n processingPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private serviceDid: string,\n private settingService: SettingServiceCreator,\n private modService: ModerationServiceCreator,\n private scheduledActionService: ScheduledActionServiceCreator,\n ) {}\n\n start() {\n this.poll()\n }\n\n poll() {\n if (this.destroyed) return\n this.processingPromise = this.findAndExecuteScheduledActions()\n .catch((err) =>\n dbLogger.error({ err }, 'scheduled action processing errored'),\n )\n .finally(() => {\n this.timer = setTimeout(() => this.poll(), getInterval())\n })\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.processingPromise\n }\n\n async executeScheduledAction(actionId: number) {\n await this.db.transaction(async (dbTxn) => {\n const settingService = this.settingService(dbTxn)\n const moderationTxn = this.modService(dbTxn)\n const scheduledActionTxn = this.scheduledActionService(dbTxn)\n\n try {\n // maybe overfetching here to get the action again within the transaction to ensure it's still pending\n const action = await dbTxn.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('id', '=', actionId)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (!action) {\n // already processed or cancelled\n return\n }\n\n let event: ModEventType\n const email = {\n subject: '',\n content: '',\n }\n let modTool: ModTool | undefined\n\n // Create the appropriate moderation action based on the scheduled action type\n switch (action.action) {\n case 'takedown':\n {\n const eventData = action.eventData as ModEventTakedown & {\n modTool?: ModTool\n emailSubject?: string\n emailContent?: string\n }\n modTool = eventData.modTool\n event = {\n $type: 'tools.ozone.moderation.defs#modEventTakedown',\n comment: `[SCHEDULED_ACTION] ${eventData.comment || 'Scheduled takedown executed'}`,\n durationInHours: eventData.durationInHours,\n acknowledgeAccountSubjects:\n eventData.acknowledgeAccountSubjects,\n policies: eventData.policies,\n severityLevel: eventData.severityLevel,\n strikeCount: eventData.strikeCount,\n }\n\n if (eventData.emailSubject && eventData.emailContent) {\n email.subject = eventData.emailSubject\n email.content = eventData.emailContent\n }\n }\n break\n default:\n throw new Error(\n `Unsupported scheduled action type: ${action.action}`,\n )\n }\n\n const moderationEvent = await this.performTakedown({\n action,\n event,\n modTool,\n moderationTxn,\n settingService,\n email,\n })\n\n // Mark the scheduled action as executed\n await scheduledActionTxn.markActionAsExecuted(\n actionId,\n moderationEvent.event.id,\n )\n\n dbLogger.info(\n {\n did: action.did,\n scheduledActionId: actionId,\n moderationEventId: moderationEvent.event.id,\n },\n 'executed scheduled action',\n )\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error'\n\n // mark as failed\n await scheduledActionTxn.markActionAsFailed(actionId, errorMessage)\n\n dbLogger.error(\n {\n scheduledActionId: actionId,\n error: errorMessage,\n },\n 'failed to execute scheduled action',\n )\n }\n })\n }\n\n async performTakedown({\n email,\n action,\n event,\n modTool,\n moderationTxn,\n settingService,\n }: {\n email: { subject: string; content: string }\n action: Selectable<ScheduledAction>\n event: ModEventType\n modTool: ModTool | undefined\n\n moderationTxn: ModerationService\n settingService: SettingService\n }) {\n const subject = new RepoSubject(action.did)\n\n const status = await moderationTxn.getStatus(subject)\n\n if (status?.takendown) {\n throw new Error(`Account is already taken down`)\n }\n\n if (status?.tags?.length) {\n const protectedTags = await getProtectedTags(\n settingService,\n this.serviceDid,\n )\n\n if (protectedTags) {\n assertProtectedTagAction({\n protectedTags,\n subjectTags: status.tags,\n actionAuthor: action.createdBy,\n isAdmin: true,\n isModerator: false,\n isTriage: false,\n })\n }\n }\n\n // log the event which also applies the necessary state changes to moderation subject\n const moderationEvent = await moderationTxn.logEvent({\n event,\n subject,\n modTool,\n createdBy: action.createdBy,\n })\n\n // register the takedown in event pusher\n await moderationTxn.takedownRepo(\n subject,\n moderationEvent.event.id,\n new Set(\n moderationEvent.event.meta?.targetServices\n ? `${moderationEvent.event.meta.targetServices}`.split(',')\n : undefined,\n ),\n )\n\n if (email.content && email.subject) {\n let isDelivered = false\n try {\n await retryHttp(() =>\n moderationTxn.sendEmail({\n ...email,\n recipientDid: action.did,\n }),\n )\n isDelivered = true\n } catch (err) {\n dbLogger.error(\n { err, did: action.did },\n 'failed to send takedown email',\n )\n }\n await moderationTxn.logEvent({\n event: {\n content: email.content,\n subjectLine: email.subject,\n $type: 'tools.ozone.moderation.defs#modEventEmail',\n comment: [\n 'Communication attached to scheduled action',\n isDelivered ? '' : 'Email delivery failed',\n ].join('.'),\n isDelivered,\n },\n subject,\n modTool,\n createdBy: action.createdBy,\n })\n }\n\n return moderationEvent\n }\n\n async findAndExecuteScheduledActions() {\n const scheduledActionService = this.scheduledActionService(this.db)\n const now = new Date()\n\n const actionsToExecute =\n await scheduledActionService.getPendingActionsToExecute(now)\n\n for (const action of actionsToExecute) {\n // For randomized execution, check if we should execute now or wait\n if (action.randomizeExecution && action.executeAfter) {\n const executeAfter = new Date(action.executeAfter)\n // Default to a 30 second window for execution\n const executeUntil = action.executeUntil\n ? new Date(action.executeUntil)\n : new Date(executeAfter.getTime() + 30 * SECOND)\n\n // Only execute if we're past the earliest time\n if (now < executeAfter) {\n continue\n }\n\n // For randomized scheduling, randomly decide whether to execute now\n // The probability increases as we get closer to the deadline\n const timeRange = executeUntil.getTime() - executeAfter.getTime()\n const timeElapsed = now.getTime() - executeAfter.getTime()\n const executeProb = Math.min(timeElapsed / timeRange, 1)\n\n // Execute with increasing probability as we approach the deadline\n // Always execute if we're at or past the deadline\n if (now < executeUntil && Math.random() > executeProb * 0.1) {\n continue\n }\n }\n\n await this.executeScheduledAction(action.id)\n }\n }\n}\n\nconst getInterval = (): number => {\n // Process scheduled actions every minute\n const now = Date.now()\n const intervalMs = MINUTE\n const nextIteration = Math.ceil(now / intervalMs)\n return nextIteration * intervalMs - now\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebSocketKeepAlive } from '@atproto/
|
|
1
|
+
import { WebSocketKeepAlive } from '@atproto/ws-client';
|
|
2
2
|
type JetstreamRecord = Record<string, unknown>;
|
|
3
3
|
type OnCreateCallback<T extends JetstreamRecord> = (e: CommitCreateEvent<T>) => Promise<void>;
|
|
4
4
|
export type JetstreamOptions = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/jetstream/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/jetstream/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAEvD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAC9C,KAAK,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI,CACjD,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,KACpB,OAAO,CAAC,IAAI,CAAC,CAAA;AAElB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IAErB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AACD,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IAEf,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AACD,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AACD,MAAM,WAAW,iBAAiB,CAAC,UAAU,SAAS,eAAe,CACnE,SAAQ,SAAS;IACjB,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE;QACN,SAAS,EAAE,QAAQ,CAAA;QACnB,MAAM,EAAE,UAAU,CAAA;KACnB,GAAG,UAAU,CAAA;CACf;AAED,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE;QACN,SAAS,EAAE,QAAQ,CAAA;KACpB,GAAG,UAAU,CAAA;CACf;AAED,qBAAa,SAAS;IACb,EAAE,CAAC,EAAE,kBAAkB,CAAA;IACvB,GAAG,EAAE,GAAG,CAAA;IACf,0BAA0B;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEV,IAAI,EAAE,gBAAgB;IAW5B,KAAK,CAAC,OAAO,EAAE;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAA;QAChD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;KACnE;IAyBD;;OAEG;IACH,KAAK;CAGN"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Jetstream = void 0;
|
|
4
|
-
const
|
|
4
|
+
const ws_client_1 = require("@atproto/ws-client");
|
|
5
5
|
class Jetstream {
|
|
6
6
|
constructor(opts) {
|
|
7
7
|
Object.defineProperty(this, "ws", {
|
|
@@ -34,7 +34,7 @@ class Jetstream {
|
|
|
34
34
|
this.cursor = opts.cursor;
|
|
35
35
|
}
|
|
36
36
|
async start(options) {
|
|
37
|
-
this.ws = new
|
|
37
|
+
this.ws = new ws_client_1.WebSocketKeepAlive({
|
|
38
38
|
getUrl: async () => {
|
|
39
39
|
if (this.cursor)
|
|
40
40
|
this.url.searchParams.set('cursor', this.cursor.toString());
|