@atproto/ozone 0.1.126 → 0.1.128

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.
Files changed (157) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +10 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/emitEvent.d.ts.map +1 -1
  6. package/dist/api/moderation/emitEvent.js +2 -0
  7. package/dist/api/moderation/emitEvent.js.map +1 -1
  8. package/dist/api/moderation/queryEvents.d.ts.map +1 -1
  9. package/dist/api/moderation/queryEvents.js +2 -1
  10. package/dist/api/moderation/queryEvents.js.map +1 -1
  11. package/dist/api/report/createReport.d.ts.map +1 -1
  12. package/dist/api/report/createReport.js +2 -1
  13. package/dist/api/report/createReport.js.map +1 -1
  14. package/dist/api/safelink/addRule.d.ts +4 -0
  15. package/dist/api/safelink/addRule.d.ts.map +1 -0
  16. package/dist/api/safelink/addRule.js +37 -0
  17. package/dist/api/safelink/addRule.js.map +1 -0
  18. package/dist/api/safelink/queryEvents.d.ts +4 -0
  19. package/dist/api/safelink/queryEvents.d.ts.map +1 -0
  20. package/dist/api/safelink/queryEvents.js +29 -0
  21. package/dist/api/safelink/queryEvents.js.map +1 -0
  22. package/dist/api/safelink/queryRules.d.ts +4 -0
  23. package/dist/api/safelink/queryRules.d.ts.map +1 -0
  24. package/dist/api/safelink/queryRules.js +43 -0
  25. package/dist/api/safelink/queryRules.js.map +1 -0
  26. package/dist/api/safelink/removeRule.d.ts +4 -0
  27. package/dist/api/safelink/removeRule.d.ts.map +1 -0
  28. package/dist/api/safelink/removeRule.js +35 -0
  29. package/dist/api/safelink/removeRule.js.map +1 -0
  30. package/dist/api/safelink/updateRule.d.ts +4 -0
  31. package/dist/api/safelink/updateRule.d.ts.map +1 -0
  32. package/dist/api/safelink/updateRule.js +37 -0
  33. package/dist/api/safelink/updateRule.js.map +1 -0
  34. package/dist/api/util.d.ts +8 -0
  35. package/dist/api/util.d.ts.map +1 -1
  36. package/dist/api/util.js +33 -1
  37. package/dist/api/util.js.map +1 -1
  38. package/dist/context.d.ts +3 -0
  39. package/dist/context.d.ts.map +1 -1
  40. package/dist/context.js +12 -6
  41. package/dist/context.js.map +1 -1
  42. package/dist/db/migrations/20250609T110704000Z-safelink.d.ts +4 -0
  43. package/dist/db/migrations/20250609T110704000Z-safelink.d.ts.map +1 -0
  44. package/dist/db/migrations/20250609T110704000Z-safelink.js +51 -0
  45. package/dist/db/migrations/20250609T110704000Z-safelink.js.map +1 -0
  46. package/dist/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.d.ts +4 -0
  47. package/dist/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.d.ts.map +1 -0
  48. package/dist/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.js +20 -0
  49. package/dist/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.js.map +1 -0
  50. package/dist/db/migrations/index.d.ts +2 -0
  51. package/dist/db/migrations/index.d.ts.map +1 -1
  52. package/dist/db/migrations/index.js +3 -1
  53. package/dist/db/migrations/index.js.map +1 -1
  54. package/dist/db/schema/index.d.ts +2 -1
  55. package/dist/db/schema/index.d.ts.map +1 -1
  56. package/dist/db/schema/moderation_event.d.ts +6 -0
  57. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  58. package/dist/db/schema/safelink.d.ts +31 -0
  59. package/dist/db/schema/safelink.d.ts.map +1 -0
  60. package/dist/db/schema/safelink.js +6 -0
  61. package/dist/db/schema/safelink.js.map +1 -0
  62. package/dist/lexicon/index.d.ts +15 -0
  63. package/dist/lexicon/index.d.ts.map +1 -1
  64. package/dist/lexicon/index.js +40 -1
  65. package/dist/lexicon/index.js.map +1 -1
  66. package/dist/lexicon/lexicons.d.ts +3427 -2489
  67. package/dist/lexicon/lexicons.d.ts.map +1 -1
  68. package/dist/lexicon/lexicons.js +499 -0
  69. package/dist/lexicon/lexicons.js.map +1 -1
  70. package/dist/lexicon/types/com/atproto/moderation/createReport.d.ts +14 -0
  71. package/dist/lexicon/types/com/atproto/moderation/createReport.d.ts.map +1 -1
  72. package/dist/lexicon/types/com/atproto/moderation/createReport.js +9 -0
  73. package/dist/lexicon/types/com/atproto/moderation/createReport.js.map +1 -1
  74. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +14 -0
  75. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  76. package/dist/lexicon/types/tools/ozone/moderation/defs.js +9 -0
  77. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  78. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -0
  79. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  80. package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts +2 -0
  81. package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts.map +1 -1
  82. package/dist/lexicon/types/tools/ozone/safelink/addRule.d.ts +47 -0
  83. package/dist/lexicon/types/tools/ozone/safelink/addRule.d.ts.map +1 -0
  84. package/dist/lexicon/types/tools/ozone/safelink/addRule.js +7 -0
  85. package/dist/lexicon/types/tools/ozone/safelink/addRule.js.map +1 -0
  86. package/dist/lexicon/types/tools/ozone/safelink/defs.d.ts +47 -0
  87. package/dist/lexicon/types/tools/ozone/safelink/defs.d.ts.map +1 -0
  88. package/dist/lexicon/types/tools/ozone/safelink/defs.js +25 -0
  89. package/dist/lexicon/types/tools/ozone/safelink/defs.js.map +1 -0
  90. package/dist/lexicon/types/tools/ozone/safelink/queryEvents.d.ts +51 -0
  91. package/dist/lexicon/types/tools/ozone/safelink/queryEvents.d.ts.map +1 -0
  92. package/dist/lexicon/types/tools/ozone/safelink/queryEvents.js +7 -0
  93. package/dist/lexicon/types/tools/ozone/safelink/queryEvents.js.map +1 -0
  94. package/dist/lexicon/types/tools/ozone/safelink/queryRules.d.ts +57 -0
  95. package/dist/lexicon/types/tools/ozone/safelink/queryRules.d.ts.map +1 -0
  96. package/dist/lexicon/types/tools/ozone/safelink/queryRules.js +7 -0
  97. package/dist/lexicon/types/tools/ozone/safelink/queryRules.js.map +1 -0
  98. package/dist/lexicon/types/tools/ozone/safelink/removeRule.d.ts +45 -0
  99. package/dist/lexicon/types/tools/ozone/safelink/removeRule.d.ts.map +1 -0
  100. package/dist/lexicon/types/tools/ozone/safelink/removeRule.js +7 -0
  101. package/dist/lexicon/types/tools/ozone/safelink/removeRule.js.map +1 -0
  102. package/dist/lexicon/types/tools/ozone/safelink/updateRule.d.ts +47 -0
  103. package/dist/lexicon/types/tools/ozone/safelink/updateRule.d.ts.map +1 -0
  104. package/dist/lexicon/types/tools/ozone/safelink/updateRule.js +7 -0
  105. package/dist/lexicon/types/tools/ozone/safelink/updateRule.js.map +1 -0
  106. package/dist/mod-service/index.d.ts +13 -1
  107. package/dist/mod-service/index.d.ts.map +1 -1
  108. package/dist/mod-service/index.js +10 -3
  109. package/dist/mod-service/index.js.map +1 -1
  110. package/dist/mod-service/status.d.ts +6 -0
  111. package/dist/mod-service/status.d.ts.map +1 -1
  112. package/dist/mod-service/views.d.ts.map +1 -1
  113. package/dist/mod-service/views.js +6 -0
  114. package/dist/mod-service/views.js.map +1 -1
  115. package/dist/safelink/service.d.ts +69 -0
  116. package/dist/safelink/service.d.ts.map +1 -0
  117. package/dist/safelink/service.js +179 -0
  118. package/dist/safelink/service.js.map +1 -0
  119. package/package.json +5 -5
  120. package/src/api/index.ts +10 -0
  121. package/src/api/moderation/emitEvent.ts +2 -0
  122. package/src/api/moderation/queryEvents.ts +2 -0
  123. package/src/api/report/createReport.ts +2 -1
  124. package/src/api/safelink/addRule.ts +48 -0
  125. package/src/api/safelink/queryEvents.ts +32 -0
  126. package/src/api/safelink/queryRules.ts +58 -0
  127. package/src/api/safelink/removeRule.ts +42 -0
  128. package/src/api/safelink/updateRule.ts +48 -0
  129. package/src/api/util.ts +38 -0
  130. package/src/context.ts +11 -0
  131. package/src/db/migrations/20250609T110704000Z-safelink.ts +53 -0
  132. package/src/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.ts +18 -0
  133. package/src/db/migrations/index.ts +2 -0
  134. package/src/db/schema/index.ts +3 -1
  135. package/src/db/schema/moderation_event.ts +1 -0
  136. package/src/db/schema/safelink.ts +39 -0
  137. package/src/lexicon/index.ts +70 -0
  138. package/src/lexicon/lexicons.ts +509 -0
  139. package/src/lexicon/types/com/atproto/moderation/createReport.ts +20 -0
  140. package/src/lexicon/types/tools/ozone/moderation/defs.ts +21 -0
  141. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +1 -0
  142. package/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +2 -0
  143. package/src/lexicon/types/tools/ozone/safelink/addRule.ts +64 -0
  144. package/src/lexicon/types/tools/ozone/safelink/defs.ts +76 -0
  145. package/src/lexicon/types/tools/ozone/safelink/queryEvents.ts +68 -0
  146. package/src/lexicon/types/tools/ozone/safelink/queryRules.ts +74 -0
  147. package/src/lexicon/types/tools/ozone/safelink/removeRule.ts +62 -0
  148. package/src/lexicon/types/tools/ozone/safelink/updateRule.ts +64 -0
  149. package/src/mod-service/index.ts +17 -2
  150. package/src/mod-service/views.ts +6 -0
  151. package/src/safelink/service.ts +304 -0
  152. package/tests/__snapshots__/safelink.test.ts.snap +179 -0
  153. package/tests/communication-templates.test.ts +7 -7
  154. package/tests/mod-tool.test.ts +128 -0
  155. package/tests/safelink.test.ts +534 -0
  156. package/tsconfig.build.tsbuildinfo +1 -1
  157. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -0,0 +1,69 @@
1
+ import { Selectable } from 'kysely';
2
+ import { ToolsOzoneSafelinkDefs } from '@atproto/api';
3
+ import { SafelinkActionType, SafelinkPatternType, SafelinkReasonType } from '../api/util';
4
+ import { Database } from '../db';
5
+ import { SafelinkEvent, SafelinkRule } from '../db/schema/safelink';
6
+ export type SafelinkRuleServiceCreator = (db: Database) => SafelinkRuleService;
7
+ export declare class SafelinkRuleService {
8
+ db: Database;
9
+ constructor(db: Database);
10
+ static creator(): (db: Database) => SafelinkRuleService;
11
+ formatEvent(event: Selectable<SafelinkEvent>): ToolsOzoneSafelinkDefs.Event;
12
+ addRule({ url, pattern, action, reason, createdBy, comment, }: {
13
+ url: string;
14
+ pattern: SafelinkPatternType;
15
+ action: SafelinkActionType;
16
+ reason: SafelinkReasonType;
17
+ createdBy: string;
18
+ comment?: string;
19
+ }): Promise<Selectable<SafelinkEvent>>;
20
+ updateRule({ url, pattern, action, reason, createdBy, comment, }: {
21
+ url: string;
22
+ pattern: SafelinkPatternType;
23
+ action: SafelinkActionType;
24
+ reason: SafelinkReasonType;
25
+ createdBy: string;
26
+ comment?: string;
27
+ }): Promise<Selectable<SafelinkEvent>>;
28
+ removeRule({ url, pattern, createdBy, comment, }: {
29
+ url: string;
30
+ pattern: SafelinkPatternType;
31
+ createdBy: string;
32
+ comment?: string;
33
+ }): Promise<Selectable<SafelinkEvent>>;
34
+ getActiveRule(url: string, pattern: SafelinkPatternType): Promise<{
35
+ id: number;
36
+ action: SafelinkActionType;
37
+ comment: string | null;
38
+ createdAt: string;
39
+ createdBy: string;
40
+ updatedAt: string;
41
+ reason: SafelinkReasonType;
42
+ url: string;
43
+ pattern: SafelinkPatternType;
44
+ } | null>;
45
+ getActiveRules({ cursor, limit, urls, patternType, actions, reason, createdBy, direction, }?: {
46
+ cursor?: string;
47
+ limit?: number;
48
+ urls?: string[];
49
+ patternType?: SafelinkPatternType;
50
+ actions?: SafelinkActionType[];
51
+ reason?: SafelinkReasonType;
52
+ createdBy?: string;
53
+ direction?: 'asc' | 'desc';
54
+ }): Promise<{
55
+ rules: Selectable<SafelinkRule>[];
56
+ cursor?: string;
57
+ }>;
58
+ queryEvents({ cursor, limit, urls, patternType, direction, }?: {
59
+ cursor?: string;
60
+ limit?: number;
61
+ urls?: string[];
62
+ patternType?: SafelinkPatternType;
63
+ direction?: 'asc' | 'desc';
64
+ }): Promise<{
65
+ events: Selectable<SafelinkEvent>[];
66
+ cursor?: string;
67
+ }>;
68
+ }
69
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SafelinkRuleService = void 0;
4
+ const xrpc_server_1 = require("@atproto/xrpc-server");
5
+ class SafelinkRuleService {
6
+ constructor(db) {
7
+ Object.defineProperty(this, "db", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: db
12
+ });
13
+ }
14
+ static creator() {
15
+ return (db) => new SafelinkRuleService(db);
16
+ }
17
+ formatEvent(event) {
18
+ return {
19
+ id: event.id,
20
+ eventType: event.eventType,
21
+ url: event.url,
22
+ pattern: event.pattern,
23
+ action: event.action,
24
+ reason: event.reason,
25
+ createdBy: event.createdBy,
26
+ createdAt: new Date(event.createdAt).toISOString(),
27
+ comment: event.comment || undefined,
28
+ };
29
+ }
30
+ async addRule({ url, pattern, action, reason, createdBy, comment, }) {
31
+ const existingRule = await this.getActiveRule(url, pattern);
32
+ if (existingRule) {
33
+ throw new xrpc_server_1.InvalidRequestError('A rule for this URL/domain already exists', 'RuleAlreadyExists');
34
+ }
35
+ const now = new Date().toISOString();
36
+ const rule = {
37
+ url,
38
+ pattern,
39
+ action,
40
+ reason,
41
+ createdBy,
42
+ comment: comment || null,
43
+ createdAt: now,
44
+ };
45
+ return await this.db.transaction(async (txn) => {
46
+ const event = await txn.db
47
+ .insertInto('safelink_event')
48
+ .values({
49
+ eventType: 'addRule',
50
+ ...rule,
51
+ })
52
+ .returningAll()
53
+ .executeTakeFirstOrThrow();
54
+ await txn.db
55
+ .insertInto('safelink_rule')
56
+ .values({ ...rule, updatedAt: now })
57
+ .execute();
58
+ return event;
59
+ });
60
+ }
61
+ async updateRule({ url, pattern, action, reason, createdBy, comment, }) {
62
+ const existingRule = await this.getActiveRule(url, pattern);
63
+ if (!existingRule) {
64
+ throw new xrpc_server_1.InvalidRequestError('No active rule found for this URL/domain', 'RuleNotFound');
65
+ }
66
+ const now = new Date().toISOString();
67
+ const rule = {
68
+ action,
69
+ reason,
70
+ createdBy,
71
+ comment: comment || null,
72
+ };
73
+ return await this.db.transaction(async (txn) => {
74
+ const event = await txn.db
75
+ .insertInto('safelink_event')
76
+ .values({
77
+ createdAt: now,
78
+ url: existingRule.url,
79
+ pattern: existingRule.pattern,
80
+ eventType: 'updateRule',
81
+ ...rule,
82
+ })
83
+ .returningAll()
84
+ .executeTakeFirstOrThrow();
85
+ await txn.db
86
+ .updateTable('safelink_rule')
87
+ .set(rule)
88
+ .where('url', '=', existingRule.url)
89
+ .where('pattern', '=', existingRule.pattern)
90
+ .execute();
91
+ return event;
92
+ });
93
+ }
94
+ async removeRule({ url, pattern, createdBy, comment, }) {
95
+ const existingRule = await this.getActiveRule(url, pattern);
96
+ if (!existingRule) {
97
+ throw new xrpc_server_1.InvalidRequestError('No active rule found for this URL/domain', 'RuleNotFound');
98
+ }
99
+ return await this.db.transaction(async (txn) => {
100
+ const event = await txn.db
101
+ .insertInto('safelink_event')
102
+ .values({
103
+ eventType: 'removeRule',
104
+ url,
105
+ pattern,
106
+ action: existingRule.action,
107
+ reason: existingRule.reason,
108
+ createdBy,
109
+ comment: comment || null,
110
+ createdAt: new Date().toISOString(),
111
+ })
112
+ .returningAll()
113
+ .executeTakeFirstOrThrow();
114
+ await txn.db
115
+ .deleteFrom('safelink_rule')
116
+ .where('url', '=', url)
117
+ .where('pattern', '=', pattern)
118
+ .execute();
119
+ return event;
120
+ });
121
+ }
122
+ async getActiveRule(url, pattern) {
123
+ const rule = await this.db.db
124
+ .selectFrom('safelink_rule')
125
+ .selectAll()
126
+ .where('url', '=', url)
127
+ .where('pattern', '=', pattern)
128
+ .executeTakeFirst();
129
+ if (!rule) {
130
+ return null;
131
+ }
132
+ return rule;
133
+ }
134
+ async getActiveRules({ cursor, limit = 50, urls, patternType, actions, reason, createdBy, direction = 'desc', } = {}) {
135
+ let query = this.db.db.selectFrom('safelink_rule').selectAll();
136
+ if (urls && urls.length > 0) {
137
+ query = query.where('url', 'in', urls);
138
+ }
139
+ if (patternType) {
140
+ query = query.where('pattern', '=', patternType);
141
+ }
142
+ if (actions && actions.length > 0) {
143
+ query = query.where('action', 'in', actions);
144
+ }
145
+ if (reason) {
146
+ query = query.where('reason', '=', reason);
147
+ }
148
+ if (createdBy) {
149
+ query = query.where('createdBy', '=', createdBy);
150
+ }
151
+ if (cursor) {
152
+ query = query.where('id', direction === 'asc' ? '>' : '<', parseInt(cursor, 10));
153
+ }
154
+ const rules = await query.orderBy('id', direction).limit(limit).execute();
155
+ return {
156
+ rules,
157
+ cursor: rules.at(-1)?.id?.toString(),
158
+ };
159
+ }
160
+ async queryEvents({ cursor, limit = 50, urls, patternType, direction = 'desc', } = {}) {
161
+ let query = this.db.db.selectFrom('safelink_event').selectAll();
162
+ if (urls && urls.length > 0) {
163
+ query = query.where('url', 'in', urls);
164
+ }
165
+ if (patternType) {
166
+ query = query.where('pattern', '=', patternType);
167
+ }
168
+ if (cursor) {
169
+ query = query.where('id', direction === 'asc' ? '>' : '<', parseInt(cursor, 10));
170
+ }
171
+ const events = await query.orderBy('id', direction).limit(limit).execute();
172
+ return {
173
+ events,
174
+ cursor: events.at(-1)?.id?.toString(),
175
+ };
176
+ }
177
+ }
178
+ exports.SafelinkRuleService = SafelinkRuleService;
179
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/ozone",
3
- "version": "0.1.126",
3
+ "version": "0.1.128",
4
4
  "license": "MIT",
5
5
  "description": "Backend service for moderating the Bluesky network.",
6
6
  "keywords": [
@@ -35,14 +35,14 @@
35
35
  "uint8arrays": "3.0.0",
36
36
  "undici": "^6.14.1",
37
37
  "ws": "^8.12.0",
38
- "@atproto/api": "^0.15.21",
38
+ "@atproto/api": "^0.15.23",
39
39
  "@atproto/common": "^0.4.11",
40
40
  "@atproto/crypto": "^0.4.4",
41
41
  "@atproto/lexicon": "^0.4.11",
42
42
  "@atproto/syntax": "^0.4.0",
43
- "@atproto/identity": "^0.4.8",
44
43
  "@atproto/xrpc": "^0.7.0",
45
- "@atproto/xrpc-server": "^0.8.0"
44
+ "@atproto/xrpc-server": "^0.8.0",
45
+ "@atproto/identity": "^0.4.8"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@did-plc/server": "^0.0.1",
@@ -55,7 +55,7 @@
55
55
  "ts-node": "^10.8.2",
56
56
  "typescript": "^5.6.3",
57
57
  "@atproto/lex-cli": "^0.8.3",
58
- "@atproto/pds": "^0.4.156"
58
+ "@atproto/pds": "^0.4.158"
59
59
  },
60
60
  "scripts": {
61
61
  "codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/tools/ozone/*/*",
package/src/api/index.ts CHANGED
@@ -21,6 +21,11 @@ import queryStatuses from './moderation/queryStatuses'
21
21
  import searchRepos from './moderation/searchRepos'
22
22
  import proxied from './proxied'
23
23
  import createReport from './report/createReport'
24
+ import addSafelinkRule from './safelink/addRule'
25
+ import querySafelinkEvents from './safelink/queryEvents'
26
+ import querySafelinkRules from './safelink/queryRules'
27
+ import removeSafelinkRule from './safelink/removeRule'
28
+ import updateSafelinkRule from './safelink/updateRule'
24
29
  import getConfig from './server/getConfig'
25
30
  import setAddValues from './set/addValues'
26
31
  import deleteSet from './set/deleteSet'
@@ -82,5 +87,10 @@ export default function (server: Server, ctx: AppContext) {
82
87
  grantVerifications(server, ctx)
83
88
  revokeVerifications(server, ctx)
84
89
  listVerifications(server, ctx)
90
+ addSafelinkRule(server, ctx)
91
+ updateSafelinkRule(server, ctx)
92
+ removeSafelinkRule(server, ctx)
93
+ querySafelinkEvents(server, ctx)
94
+ querySafelinkRules(server, ctx)
85
95
  return server
86
96
  }
@@ -154,6 +154,7 @@ const handleModerationEvent = async ({
154
154
  event,
155
155
  subject,
156
156
  createdBy,
157
+ modTool: input.body.modTool,
157
158
  })
158
159
 
159
160
  const tagService = new TagService(
@@ -243,6 +244,7 @@ export default function (server: Server, ctx: AppContext) {
243
244
  comment:
244
245
  '[DIVERT_SIDE_EFFECT]: Automatically taking down after divert event',
245
246
  },
247
+ modTool: input.body.modTool,
246
248
  },
247
249
  },
248
250
  })
@@ -26,6 +26,7 @@ export default function (server: Server, ctx: AppContext) {
26
26
  collections = [],
27
27
  subjectType,
28
28
  policies,
29
+ modTool,
29
30
  } = params
30
31
  const db = ctx.db
31
32
  const modService = ctx.modService(db)
@@ -49,6 +50,7 @@ export default function (server: Server, ctx: AppContext) {
49
50
  collections,
50
51
  subjectType,
51
52
  policies,
53
+ modTool,
52
54
  })
53
55
  return {
54
56
  encoding: 'application/json',
@@ -17,7 +17,7 @@ export default function (server: Server, ctx: AppContext) {
17
17
  handler: async ({ input, auth }) => {
18
18
  const requester =
19
19
  'iss' in auth.credentials ? auth.credentials.iss : ctx.cfg.service.did
20
- const { reasonType, reason } = input.body
20
+ const { reasonType, reason, modTool } = input.body
21
21
  const subject = subjectFromInput(input.body.subject)
22
22
 
23
23
  // If the report is an appeal, the requester must be the author of the subject
@@ -37,6 +37,7 @@ export default function (server: Server, ctx: AppContext) {
37
37
  reason,
38
38
  subject,
39
39
  reportedBy: requester || ctx.cfg.service.did,
40
+ modTool,
40
41
  })
41
42
 
42
43
  const tagService = new TagService(
@@ -0,0 +1,48 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { AppContext } from '../../context'
3
+ import { Server } from '../../lexicon'
4
+ import {
5
+ getSafelinkAction,
6
+ getSafelinkPattern,
7
+ getSafelinkReason,
8
+ } from '../util'
9
+
10
+ export default function (server: Server, ctx: AppContext) {
11
+ server.tools.ozone.safelink.addRule({
12
+ auth: ctx.authVerifier.modOrAdminToken,
13
+ handler: async ({ input, auth }) => {
14
+ const access = auth.credentials
15
+ const db = ctx.db
16
+ const { url, pattern, action, reason, comment, createdBy } = input.body
17
+
18
+ if (!access.isModerator) {
19
+ throw new AuthRequiredError('Must be a moderator to add URL rules')
20
+ }
21
+
22
+ if (access.type === 'admin_token' && !createdBy) {
23
+ throw new AuthRequiredError(
24
+ 'Must specify createdBy when using admin auth',
25
+ )
26
+ }
27
+
28
+ const safelinkRuleService = ctx.safelinkRuleService(db)
29
+
30
+ const event = await safelinkRuleService.addRule({
31
+ url,
32
+ pattern: getSafelinkPattern(pattern),
33
+ action: getSafelinkAction(action),
34
+ reason: getSafelinkReason(reason),
35
+ createdBy:
36
+ access.type === 'admin_token'
37
+ ? createdBy || ctx.cfg.service.did
38
+ : access.iss,
39
+ comment,
40
+ })
41
+
42
+ return {
43
+ encoding: 'application/json',
44
+ body: safelinkRuleService.formatEvent(event),
45
+ }
46
+ },
47
+ })
48
+ }
@@ -0,0 +1,32 @@
1
+ import { AppContext } from '../../context'
2
+ import { Server } from '../../lexicon'
3
+ import { getSafelinkPattern } from '../util'
4
+
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.tools.ozone.safelink.queryEvents({
7
+ auth: ctx.authVerifier.modOrAdminToken,
8
+ handler: async ({ input }) => {
9
+ const db = ctx.db
10
+ const { cursor, limit, urls, patternType, sortDirection } = input.body
11
+
12
+ const safelinkRuleService = ctx.safelinkRuleService(db)
13
+ const result = await safelinkRuleService.queryEvents({
14
+ cursor,
15
+ limit,
16
+ urls,
17
+ patternType: patternType ? getSafelinkPattern(patternType) : undefined,
18
+ direction: sortDirection as 'asc' | 'desc' | undefined,
19
+ })
20
+
21
+ return {
22
+ encoding: 'application/json',
23
+ body: {
24
+ cursor: result.cursor,
25
+ events: result.events.map((event) =>
26
+ safelinkRuleService.formatEvent(event),
27
+ ),
28
+ },
29
+ }
30
+ },
31
+ })
32
+ }
@@ -0,0 +1,58 @@
1
+ import { AppContext } from '../../context'
2
+ import { Server } from '../../lexicon'
3
+ import {
4
+ getSafelinkAction,
5
+ getSafelinkPattern,
6
+ getSafelinkReason,
7
+ } from '../util'
8
+
9
+ export default function (server: Server, ctx: AppContext) {
10
+ server.tools.ozone.safelink.queryRules({
11
+ auth: ctx.authVerifier.modOrAdminToken,
12
+ handler: async ({ input }) => {
13
+ const db = ctx.db
14
+ const {
15
+ cursor,
16
+ limit,
17
+ urls,
18
+ patternType,
19
+ actions,
20
+ reason,
21
+ createdBy,
22
+ sortDirection,
23
+ } = input.body
24
+
25
+ const safelinkRuleService = ctx.safelinkRuleService(db)
26
+ const result = await safelinkRuleService.getActiveRules({
27
+ cursor,
28
+ limit,
29
+ urls,
30
+ patternType: patternType ? getSafelinkPattern(patternType) : undefined,
31
+ actions:
32
+ actions && actions.length > 0
33
+ ? actions.map(getSafelinkAction)
34
+ : undefined,
35
+ reason: reason ? getSafelinkReason(reason) : undefined,
36
+ createdBy,
37
+ direction: sortDirection as 'asc' | 'desc' | undefined,
38
+ })
39
+
40
+ return {
41
+ encoding: 'application/json',
42
+ body: {
43
+ cursor: result.cursor,
44
+ rules: result.rules.map((rule) => ({
45
+ url: rule.url,
46
+ pattern: rule.pattern,
47
+ action: rule.action,
48
+ reason: rule.reason,
49
+ createdBy: rule.createdBy,
50
+ createdAt: new Date(rule.createdAt).toISOString(),
51
+ updatedAt: new Date(rule.updatedAt).toISOString(),
52
+ comment: rule.comment || undefined,
53
+ })),
54
+ },
55
+ }
56
+ },
57
+ })
58
+ }
@@ -0,0 +1,42 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { AppContext } from '../../context'
3
+ import { Server } from '../../lexicon'
4
+ import { getSafelinkPattern } from '../util'
5
+
6
+ export default function (server: Server, ctx: AppContext) {
7
+ server.tools.ozone.safelink.removeRule({
8
+ auth: ctx.authVerifier.modOrAdminToken,
9
+ handler: async ({ input, auth }) => {
10
+ const access = auth.credentials
11
+ const db = ctx.db
12
+ const { url, pattern, comment, createdBy } = input.body
13
+
14
+ if (!access.isModerator) {
15
+ throw new AuthRequiredError('Must be a moderator to remove URL rules')
16
+ }
17
+
18
+ if (access.type === 'admin_token' && !createdBy) {
19
+ throw new AuthRequiredError(
20
+ 'Must specify createdBy when using admin auth',
21
+ )
22
+ }
23
+
24
+ const safelinkRuleService = ctx.safelinkRuleService(db)
25
+
26
+ const event = await safelinkRuleService.removeRule({
27
+ url,
28
+ pattern: getSafelinkPattern(pattern),
29
+ createdBy:
30
+ access.type === 'admin_token'
31
+ ? createdBy || ctx.cfg.service.did
32
+ : access.iss,
33
+ comment,
34
+ })
35
+
36
+ return {
37
+ encoding: 'application/json',
38
+ body: safelinkRuleService.formatEvent(event),
39
+ }
40
+ },
41
+ })
42
+ }
@@ -0,0 +1,48 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { AppContext } from '../../context'
3
+ import { Server } from '../../lexicon'
4
+ import {
5
+ getSafelinkAction,
6
+ getSafelinkPattern,
7
+ getSafelinkReason,
8
+ } from '../util'
9
+
10
+ export default function (server: Server, ctx: AppContext) {
11
+ server.tools.ozone.safelink.updateRule({
12
+ auth: ctx.authVerifier.modOrAdminToken,
13
+ handler: async ({ input, auth }) => {
14
+ const access = auth.credentials
15
+ const db = ctx.db
16
+ const { url, pattern, action, reason, comment, createdBy } = input.body
17
+
18
+ if (!access.isModerator) {
19
+ throw new AuthRequiredError('Must be a moderator to update URL rules')
20
+ }
21
+
22
+ if (access.type === 'admin_token' && !createdBy) {
23
+ throw new AuthRequiredError(
24
+ 'Must specify createdBy when using admin auth',
25
+ )
26
+ }
27
+
28
+ const safelinkRuleService = ctx.safelinkRuleService(db)
29
+
30
+ const event = await safelinkRuleService.updateRule({
31
+ url,
32
+ pattern: getSafelinkPattern(pattern),
33
+ action: getSafelinkAction(action),
34
+ reason: getSafelinkReason(reason),
35
+ createdBy:
36
+ access.type === 'admin_token'
37
+ ? createdBy || ctx.cfg.service.did
38
+ : access.iss,
39
+ comment,
40
+ })
41
+
42
+ return {
43
+ encoding: 'application/json',
44
+ body: safelinkRuleService.formatEvent(event),
45
+ }
46
+ },
47
+ })
48
+ }
package/src/api/util.ts CHANGED
@@ -191,3 +191,41 @@ const memberRoles = new Set([
191
191
  ROLETRIAGE,
192
192
  ROLEVERIFIER,
193
193
  ])
194
+
195
+ export const getSafelinkPattern = (pattern: string): SafelinkPatternType => {
196
+ if (safelinkPatterns.has(pattern)) {
197
+ return pattern as SafelinkPatternType
198
+ }
199
+ throw new InvalidRequestError('Invalid safelink pattern type')
200
+ }
201
+
202
+ export const getSafelinkAction = (action: string): SafelinkActionType => {
203
+ if (safelinkActions.has(action)) {
204
+ return action as SafelinkActionType
205
+ }
206
+ throw new InvalidRequestError('Invalid safelink action type')
207
+ }
208
+
209
+ export const getSafelinkReason = (reason: string): SafelinkReasonType => {
210
+ if (safelinkReasons.has(reason)) {
211
+ return reason as SafelinkReasonType
212
+ }
213
+ throw new InvalidRequestError('Invalid safelink reason type')
214
+ }
215
+
216
+ export const getSafelinkEventType = (eventType: string): SafelinkEventType => {
217
+ if (safelinkEventTypes.has(eventType)) {
218
+ return eventType as SafelinkEventType
219
+ }
220
+ throw new InvalidRequestError('Invalid safelink event type')
221
+ }
222
+
223
+ export type SafelinkEventType = 'addRule' | 'updateRule' | 'removeRule'
224
+ export type SafelinkPatternType = 'domain' | 'url'
225
+ export type SafelinkActionType = 'block' | 'warn' | 'whitelist'
226
+ export type SafelinkReasonType = 'csam' | 'spam' | 'phishing' | 'none'
227
+
228
+ const safelinkPatterns = new Set(['domain', 'url'])
229
+ const safelinkActions = new Set(['block', 'warn', 'whitelist'])
230
+ const safelinkReasons = new Set(['csam', 'spam', 'phishing', 'none'])
231
+ const safelinkEventTypes = new Set(['addRule', 'updateRule', 'removeRule'])