@atproto/ozone 0.1.126 → 0.1.127
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 +9 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +10 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/safelink/addRule.d.ts +4 -0
- package/dist/api/safelink/addRule.d.ts.map +1 -0
- package/dist/api/safelink/addRule.js +37 -0
- package/dist/api/safelink/addRule.js.map +1 -0
- package/dist/api/safelink/queryEvents.d.ts +4 -0
- package/dist/api/safelink/queryEvents.d.ts.map +1 -0
- package/dist/api/safelink/queryEvents.js +29 -0
- package/dist/api/safelink/queryEvents.js.map +1 -0
- package/dist/api/safelink/queryRules.d.ts +4 -0
- package/dist/api/safelink/queryRules.d.ts.map +1 -0
- package/dist/api/safelink/queryRules.js +43 -0
- package/dist/api/safelink/queryRules.js.map +1 -0
- package/dist/api/safelink/removeRule.d.ts +4 -0
- package/dist/api/safelink/removeRule.d.ts.map +1 -0
- package/dist/api/safelink/removeRule.js +35 -0
- package/dist/api/safelink/removeRule.js.map +1 -0
- package/dist/api/safelink/updateRule.d.ts +4 -0
- package/dist/api/safelink/updateRule.d.ts.map +1 -0
- package/dist/api/safelink/updateRule.js +37 -0
- package/dist/api/safelink/updateRule.js.map +1 -0
- package/dist/api/util.d.ts +8 -0
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +33 -1
- package/dist/api/util.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +12 -6
- package/dist/context.js.map +1 -1
- package/dist/db/migrations/20250609T110704000Z-safelink.d.ts +4 -0
- package/dist/db/migrations/20250609T110704000Z-safelink.d.ts.map +1 -0
- package/dist/db/migrations/20250609T110704000Z-safelink.js +51 -0
- package/dist/db/migrations/20250609T110704000Z-safelink.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/schema/index.d.ts +2 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/safelink.d.ts +31 -0
- package/dist/db/schema/safelink.d.ts.map +1 -0
- package/dist/db/schema/safelink.js +6 -0
- package/dist/db/schema/safelink.js.map +1 -0
- package/dist/lexicon/index.d.ts +15 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +40 -1
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +10972 -10140
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +446 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/safelink/addRule.d.ts +47 -0
- package/dist/lexicon/types/tools/ozone/safelink/addRule.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/addRule.js +7 -0
- package/dist/lexicon/types/tools/ozone/safelink/addRule.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/defs.d.ts +47 -0
- package/dist/lexicon/types/tools/ozone/safelink/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/defs.js +25 -0
- package/dist/lexicon/types/tools/ozone/safelink/defs.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryEvents.d.ts +51 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryEvents.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryEvents.js +7 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryEvents.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryRules.d.ts +57 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryRules.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryRules.js +7 -0
- package/dist/lexicon/types/tools/ozone/safelink/queryRules.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/removeRule.d.ts +45 -0
- package/dist/lexicon/types/tools/ozone/safelink/removeRule.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/removeRule.js +7 -0
- package/dist/lexicon/types/tools/ozone/safelink/removeRule.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/updateRule.d.ts +47 -0
- package/dist/lexicon/types/tools/ozone/safelink/updateRule.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/safelink/updateRule.js +7 -0
- package/dist/lexicon/types/tools/ozone/safelink/updateRule.js.map +1 -0
- package/dist/mod-service/status.d.ts +6 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/safelink/service.d.ts +69 -0
- package/dist/safelink/service.d.ts.map +1 -0
- package/dist/safelink/service.js +179 -0
- package/dist/safelink/service.js.map +1 -0
- package/package.json +4 -4
- package/src/api/index.ts +10 -0
- package/src/api/safelink/addRule.ts +48 -0
- package/src/api/safelink/queryEvents.ts +32 -0
- package/src/api/safelink/queryRules.ts +58 -0
- package/src/api/safelink/removeRule.ts +42 -0
- package/src/api/safelink/updateRule.ts +48 -0
- package/src/api/util.ts +38 -0
- package/src/context.ts +11 -0
- package/src/db/migrations/20250609T110704000Z-safelink.ts +53 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/index.ts +3 -1
- package/src/db/schema/safelink.ts +39 -0
- package/src/lexicon/index.ts +70 -0
- package/src/lexicon/lexicons.ts +451 -0
- package/src/lexicon/types/tools/ozone/safelink/addRule.ts +64 -0
- package/src/lexicon/types/tools/ozone/safelink/defs.ts +76 -0
- package/src/lexicon/types/tools/ozone/safelink/queryEvents.ts +68 -0
- package/src/lexicon/types/tools/ozone/safelink/queryRules.ts +74 -0
- package/src/lexicon/types/tools/ozone/safelink/removeRule.ts +62 -0
- package/src/lexicon/types/tools/ozone/safelink/updateRule.ts +64 -0
- package/src/safelink/service.ts +304 -0
- package/tests/__snapshots__/safelink.test.ts.snap +179 -0
- package/tests/communication-templates.test.ts +7 -7
- package/tests/safelink.test.ts +534 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -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":";;;AAEA,sDAA0D;AAW1D,MAAa,mBAAmB;IAC9B,YAAmB,EAAY;QAAnB;;;;mBAAO,EAAE;WAAU;IAAG,CAAC;IAEnC,MAAM,CAAC,OAAO;QACZ,OAAO,CAAC,EAAY,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,WAAW,CAAC,KAAgC;QAC1C,OAAO;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;YAClD,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,SAAS;SACpC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EACZ,GAAG,EACH,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,OAAO,GAQR;QACC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC3D,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,IAAI,iCAAmB,CAC3B,2CAA2C,EAC3C,mBAAmB,CACpB,CAAA;QACH,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG;YACX,GAAG;YACH,OAAO;YACP,MAAM;YACN,MAAM;YACN,SAAS;YACT,OAAO,EAAE,OAAO,IAAI,IAAI;YACxB,SAAS,EAAE,GAAG;SACf,CAAA;QAED,OAAO,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE;iBACvB,UAAU,CAAC,gBAAgB,CAAC;iBAC5B,MAAM,CAAC;gBACN,SAAS,EAAE,SAAS;gBACpB,GAAG,IAAI;aACR,CAAC;iBACD,YAAY,EAAE;iBACd,uBAAuB,EAAE,CAAA;YAE5B,MAAM,GAAG,CAAC,EAAE;iBACT,UAAU,CAAC,eAAe,CAAC;iBAC3B,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;iBACnC,OAAO,EAAE,CAAA;YAEZ,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EACf,GAAG,EACH,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,OAAO,GAQR;QACC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,iCAAmB,CAC3B,0CAA0C,EAC1C,cAAc,CACf,CAAA;QACH,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG;YACX,MAAM;YACN,MAAM;YACN,SAAS;YACT,OAAO,EAAE,OAAO,IAAI,IAAI;SACzB,CAAA;QAED,OAAO,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE;iBACvB,UAAU,CAAC,gBAAgB,CAAC;iBAC5B,MAAM,CAAC;gBACN,SAAS,EAAE,GAAG;gBACd,GAAG,EAAE,YAAY,CAAC,GAAG;gBACrB,OAAO,EAAE,YAAY,CAAC,OAAO;gBAC7B,SAAS,EAAE,YAAY;gBACvB,GAAG,IAAI;aACR,CAAC;iBACD,YAAY,EAAE;iBACd,uBAAuB,EAAE,CAAA;YAE5B,MAAM,GAAG,CAAC,EAAE;iBACT,WAAW,CAAC,eAAe,CAAC;iBAC5B,GAAG,CAAC,IAAI,CAAC;iBACT,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC;iBACnC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC;iBAC3C,OAAO,EAAE,CAAA;YAEZ,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EACf,GAAG,EACH,OAAO,EACP,SAAS,EACT,OAAO,GAMR;QACC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,iCAAmB,CAC3B,0CAA0C,EAC1C,cAAc,CACf,CAAA;QACH,CAAC;QAED,OAAO,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE;iBACvB,UAAU,CAAC,gBAAgB,CAAC;iBAC5B,MAAM,CAAC;gBACN,SAAS,EAAE,YAAY;gBACvB,GAAG;gBACH,OAAO;gBACP,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,SAAS;gBACT,OAAO,EAAE,OAAO,IAAI,IAAI;gBACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC;iBACD,YAAY,EAAE;iBACd,uBAAuB,EAAE,CAAA;YAE5B,MAAM,GAAG,CAAC,EAAE;iBACT,UAAU,CAAC,eAAe,CAAC;iBAC3B,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC;iBACtB,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC;iBAC9B,OAAO,EAAE,CAAA;YAEZ,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAW,EAAE,OAA4B;QAC3D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC1B,UAAU,CAAC,eAAe,CAAC;aAC3B,SAAS,EAAE;aACX,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC;aACtB,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC;aAC9B,gBAAgB,EAAE,CAAA;QAErB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,EACnB,MAAM,EACN,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,WAAW,EACX,OAAO,EACP,MAAM,EACN,SAAS,EACT,SAAS,GAAG,MAAM,MAUhB,EAAE;QAIJ,IAAI,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,SAAS,EAAE,CAAA;QAE9D,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QACxC,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;QAClD,CAAC;QAED,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QAC9C,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,SAAS,CAAC,CAAA;QAClD,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,GAAG,KAAK,CAAC,KAAK,CACjB,IAAI,EACJ,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAC/B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CACrB,CAAA;QACH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;QAEzE,OAAO;YACL,KAAK;YACL,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE;SACrC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAChB,MAAM,EACN,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,WAAW,EACX,SAAS,GAAG,MAAM,MAOhB,EAAE;QAIJ,IAAI,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,SAAS,EAAE,CAAA;QAE/D,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QACxC,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;QAClD,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,GAAG,KAAK,CAAC,KAAK,CACjB,IAAI,EACJ,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAC/B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CACrB,CAAA;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;QAE1E,OAAO;YACL,MAAM;YACN,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE;SACtC,CAAA;IACH,CAAC;CACF;AAlSD,kDAkSC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/ozone",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.127",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"uint8arrays": "3.0.0",
|
|
36
36
|
"undici": "^6.14.1",
|
|
37
37
|
"ws": "^8.12.0",
|
|
38
|
-
"@atproto/api": "^0.15.
|
|
38
|
+
"@atproto/api": "^0.15.22",
|
|
39
39
|
"@atproto/common": "^0.4.11",
|
|
40
40
|
"@atproto/crypto": "^0.4.4",
|
|
41
|
+
"@atproto/identity": "^0.4.8",
|
|
41
42
|
"@atproto/lexicon": "^0.4.11",
|
|
42
43
|
"@atproto/syntax": "^0.4.0",
|
|
43
|
-
"@atproto/identity": "^0.4.8",
|
|
44
44
|
"@atproto/xrpc": "^0.7.0",
|
|
45
45
|
"@atproto/xrpc-server": "^0.8.0"
|
|
46
46
|
},
|
|
@@ -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.
|
|
58
|
+
"@atproto/pds": "^0.4.157"
|
|
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
|
}
|
|
@@ -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'])
|
package/src/context.ts
CHANGED
|
@@ -17,6 +17,10 @@ import { BlobDiverter } from './daemon/blob-diverter'
|
|
|
17
17
|
import { Database } from './db'
|
|
18
18
|
import { ImageInvalidator } from './image-invalidator'
|
|
19
19
|
import { ModerationService, ModerationServiceCreator } from './mod-service'
|
|
20
|
+
import {
|
|
21
|
+
SafelinkRuleService,
|
|
22
|
+
SafelinkRuleServiceCreator,
|
|
23
|
+
} from './safelink/service'
|
|
20
24
|
import { Sequencer } from './sequencer/sequencer'
|
|
21
25
|
import { SetService, SetServiceCreator } from './set/service'
|
|
22
26
|
import { SettingService, SettingServiceCreator } from './setting/service'
|
|
@@ -42,6 +46,7 @@ export type AppContextOptions = {
|
|
|
42
46
|
cfg: OzoneConfig
|
|
43
47
|
modService: ModerationServiceCreator
|
|
44
48
|
communicationTemplateService: CommunicationTemplateServiceCreator
|
|
49
|
+
safelinkRuleService: SafelinkRuleServiceCreator
|
|
45
50
|
setService: SetServiceCreator
|
|
46
51
|
settingService: SettingServiceCreator
|
|
47
52
|
teamService: TeamServiceCreator
|
|
@@ -130,6 +135,7 @@ export class AppContext {
|
|
|
130
135
|
)
|
|
131
136
|
|
|
132
137
|
const communicationTemplateService = CommunicationTemplateService.creator()
|
|
138
|
+
const safelinkRuleService = SafelinkRuleService.creator()
|
|
133
139
|
const teamService = TeamService.creator(
|
|
134
140
|
appviewAgent,
|
|
135
141
|
cfg.appview.did,
|
|
@@ -154,6 +160,7 @@ export class AppContext {
|
|
|
154
160
|
cfg,
|
|
155
161
|
modService,
|
|
156
162
|
communicationTemplateService,
|
|
163
|
+
safelinkRuleService,
|
|
157
164
|
teamService,
|
|
158
165
|
setService,
|
|
159
166
|
settingService,
|
|
@@ -204,6 +211,10 @@ export class AppContext {
|
|
|
204
211
|
return this.opts.communicationTemplateService
|
|
205
212
|
}
|
|
206
213
|
|
|
214
|
+
get safelinkRuleService(): SafelinkRuleServiceCreator {
|
|
215
|
+
return this.opts.safelinkRuleService
|
|
216
|
+
}
|
|
217
|
+
|
|
207
218
|
get teamService(): TeamServiceCreator {
|
|
208
219
|
return this.opts.teamService
|
|
209
220
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable('safelink_event')
|
|
6
|
+
.addColumn('id', 'bigserial', (col) => col.primaryKey())
|
|
7
|
+
.addColumn('eventType', 'varchar', (col) => col.notNull())
|
|
8
|
+
.addColumn('url', 'varchar', (col) => col.notNull())
|
|
9
|
+
.addColumn('pattern', 'varchar', (col) => col.notNull())
|
|
10
|
+
.addColumn('action', 'varchar', (col) => col.notNull())
|
|
11
|
+
.addColumn('reason', 'varchar', (col) => col.notNull())
|
|
12
|
+
.addColumn('createdBy', 'varchar', (col) => col.notNull())
|
|
13
|
+
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
|
14
|
+
.addColumn('comment', 'text')
|
|
15
|
+
.execute()
|
|
16
|
+
|
|
17
|
+
await db.schema
|
|
18
|
+
.createTable('safelink_rule')
|
|
19
|
+
.addColumn('id', 'bigserial', (col) => col.primaryKey())
|
|
20
|
+
.addColumn('url', 'varchar', (col) => col.notNull())
|
|
21
|
+
.addColumn('pattern', 'varchar', (col) => col.notNull())
|
|
22
|
+
.addColumn('action', 'varchar', (col) => col.notNull())
|
|
23
|
+
.addColumn('reason', 'varchar', (col) => col.notNull())
|
|
24
|
+
.addColumn('createdBy', 'varchar', (col) => col.notNull())
|
|
25
|
+
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
|
26
|
+
.addColumn('updatedAt', 'varchar', (col) => col.notNull())
|
|
27
|
+
.addColumn('comment', 'text')
|
|
28
|
+
.addUniqueConstraint('safelink_rule_url_pattern_key', ['url', 'pattern'])
|
|
29
|
+
.execute()
|
|
30
|
+
|
|
31
|
+
await db.schema
|
|
32
|
+
.createIndex('safelink_event_url_pattern_idx')
|
|
33
|
+
.on('safelink_event')
|
|
34
|
+
.columns(['url', 'pattern'])
|
|
35
|
+
.execute()
|
|
36
|
+
|
|
37
|
+
await db.schema
|
|
38
|
+
.createIndex('safelink_rule_action_idx')
|
|
39
|
+
.on('safelink_rule')
|
|
40
|
+
.column('action')
|
|
41
|
+
.execute()
|
|
42
|
+
|
|
43
|
+
await db.schema
|
|
44
|
+
.createIndex('safelink_rule_reason_idx')
|
|
45
|
+
.on('safelink_rule')
|
|
46
|
+
.column('reason')
|
|
47
|
+
.execute()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
51
|
+
await db.schema.dropTable('safelink_rule').execute()
|
|
52
|
+
await db.schema.dropTable('safelink_event').execute()
|
|
53
|
+
}
|
|
@@ -25,3 +25,4 @@ export * as _20250221T132135150Z from './20250221T132135150Z-member-details'
|
|
|
25
25
|
export * as _20250404T201720309Z from './20250404T201720309Z-subject-status-sort-idxs'
|
|
26
26
|
export * as _20250415T201720309Z from './20250415T201720309Z-verification'
|
|
27
27
|
export * as _20250417T201720309Z from './20250417T201720309Z-firehose-cursor'
|
|
28
|
+
export * as _20250609T110704000Z from './20250609T110704000Z-safelink'
|
package/src/db/schema/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import * as set from './ozone_set'
|
|
|
13
13
|
import * as recordEventsStats from './record_events_stats'
|
|
14
14
|
import * as recordPushEvent from './record_push_event'
|
|
15
15
|
import * as repoPushEvent from './repo_push_event'
|
|
16
|
+
import * as safelink from './safelink'
|
|
16
17
|
import * as setting from './setting'
|
|
17
18
|
import * as signingKey from './signing_key'
|
|
18
19
|
import * as verification from './verification'
|
|
@@ -33,7 +34,8 @@ export type DatabaseSchemaType = modEvent.PartialDB &
|
|
|
33
34
|
accountRecordEventsStats.PartialDB &
|
|
34
35
|
accountRecordStatusStats.PartialDB &
|
|
35
36
|
verification.PartialDB &
|
|
36
|
-
firehoseCursor.PartialDB
|
|
37
|
+
firehoseCursor.PartialDB &
|
|
38
|
+
safelink.PartialDB
|
|
37
39
|
|
|
38
40
|
export type DatabaseSchema = Kysely<DatabaseSchemaType>
|
|
39
41
|
|