@atproto/ozone 0.1.87 → 0.1.88
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/team/addMember.d.ts.map +1 -1
- package/dist/api/team/addMember.js +5 -1
- package/dist/api/team/addMember.js.map +1 -1
- package/dist/api/team/listMembers.js +1 -1
- package/dist/api/team/listMembers.js.map +1 -1
- package/dist/api/team/updateMember.js +1 -1
- package/dist/api/team/updateMember.js.map +1 -1
- package/dist/config/config.d.ts +1 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +1 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +1 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +1 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/daemon/context.d.ts +3 -0
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js +11 -0
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/team-profile-synchronizer.d.ts +6 -0
- package/dist/daemon/team-profile-synchronizer.d.ts.map +1 -0
- package/dist/daemon/team-profile-synchronizer.js +14 -0
- package/dist/daemon/team-profile-synchronizer.js.map +1 -0
- package/dist/db/migrations/20250221T132135150Z-member-details.d.ts +4 -0
- package/dist/db/migrations/20250221T132135150Z-member-details.d.ts.map +1 -0
- package/dist/db/migrations/20250221T132135150Z-member-details.js +16 -0
- package/dist/db/migrations/20250221T132135150Z-member-details.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/member.d.ts +2 -0
- package/dist/db/schema/member.d.ts.map +1 -1
- package/dist/lexicon/lexicons.d.ts +6 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +3 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/team/listMembers.d.ts +1 -0
- package/dist/lexicon/types/tools/ozone/team/listMembers.d.ts.map +1 -1
- package/dist/team/index.d.ts +12 -6
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +58 -9
- package/dist/team/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/team/addMember.ts +6 -1
- package/src/api/team/listMembers.ts +1 -1
- package/src/api/team/updateMember.ts +1 -1
- package/src/config/config.ts +2 -0
- package/src/config/env.ts +4 -0
- package/src/context.ts +5 -1
- package/src/daemon/context.ts +21 -0
- package/src/daemon/team-profile-synchronizer.ts +15 -0
- package/src/db/migrations/20250221T132135150Z-member-details.ts +14 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/member.ts +2 -0
- package/src/lexicon/lexicons.ts +3 -0
- package/src/lexicon/types/tools/ozone/team/listMembers.ts +1 -0
- package/src/team/index.ts +66 -18
- package/tests/team.test.ts +23 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listMembers.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/tools/ozone/team/listMembers.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,OAAO,MAAM,SAAS,CAAA;AAK7B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,KAAK,kBAAkB,MAAM,WAAW,CAAA;AAMpD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,CAAA;AAEnC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,kBAAkB,CAAA;AAC9E,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI;IAC1D,IAAI,EAAE,EAAE,CAAA;IACR,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,YAAY,CAAA;IACnB,GAAG,EAAE,OAAO,CAAC,OAAO,CAAA;IACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAA;IACrB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C,CAAA;AACD,MAAM,MAAM,OAAO,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI,CACpD,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,KACnB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"listMembers.d.ts","sourceRoot":"","sources":["../../../../../../src/lexicon/types/tools/ozone/team/listMembers.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,OAAO,MAAM,SAAS,CAAA;AAK7B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,KAAK,kBAAkB,MAAM,WAAW,CAAA;AAMpD,MAAM,WAAW,WAAW;IAC1B,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,CAAA;AAEnC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,kBAAkB,CAAA;AAC9E,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI;IAC1D,IAAI,EAAE,EAAE,CAAA;IACR,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,YAAY,CAAA;IACnB,GAAG,EAAE,OAAO,CAAC,OAAO,CAAA;IACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAA;IACrB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C,CAAA;AACD,MAAM,MAAM,OAAO,CAAC,EAAE,SAAS,WAAW,GAAG,KAAK,IAAI,CACpD,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,KACnB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAA"}
|
package/dist/team/index.d.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { Selectable } from 'kysely';
|
|
2
|
-
import
|
|
2
|
+
import AtpAgent from '@atproto/api';
|
|
3
3
|
import { Database } from '../db';
|
|
4
4
|
import { Member } from '../db/schema/member';
|
|
5
5
|
import { ProfileViewDetailed } from '../lexicon/types/app/bsky/actor/defs';
|
|
6
6
|
import { Member as TeamMember } from '../lexicon/types/tools/ozone/team/defs';
|
|
7
|
+
import { AuthHeaders } from '../mod-service/views';
|
|
7
8
|
export type TeamServiceCreator = (db: Database) => TeamService;
|
|
8
9
|
export declare class TeamService {
|
|
9
10
|
db: Database;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
private appviewAgent;
|
|
12
|
+
private appviewDid;
|
|
13
|
+
private createAuthHeaders;
|
|
14
|
+
constructor(db: Database, appviewAgent: AtpAgent, appviewDid: string, createAuthHeaders: (aud: string, method: string) => Promise<AuthHeaders>);
|
|
15
|
+
static creator(appviewAgent: AtpAgent, appviewDid: string, createAuthHeaders: (aud: string, method: string) => Promise<AuthHeaders>): (db: Database) => TeamService;
|
|
16
|
+
list({ cursor, limit, roles, disabled, q, }: {
|
|
17
|
+
q?: string;
|
|
13
18
|
cursor?: string;
|
|
14
19
|
limit?: number;
|
|
15
20
|
disabled?: boolean;
|
|
@@ -33,7 +38,8 @@ export declare class TeamService {
|
|
|
33
38
|
isAdmin: boolean;
|
|
34
39
|
isTriage: boolean;
|
|
35
40
|
};
|
|
36
|
-
getProfiles(dids: string[]
|
|
37
|
-
|
|
41
|
+
getProfiles(dids: string[]): Promise<Map<string, ProfileViewDetailed>>;
|
|
42
|
+
syncMemberProfiles(): Promise<void>;
|
|
43
|
+
view(members: Selectable<Member>[]): Promise<TeamMember[]>;
|
|
38
44
|
}
|
|
39
45
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/team/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/team/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/team/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,QAAQ,MAAM,cAAc,CAAA;AAGnC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAC1E,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,wCAAwC,CAAA;AAE7E,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,MAAM,MAAM,kBAAkB,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,WAAW,CAAA;AAE9D,qBAAa,WAAW;IAEb,EAAE,EAAE,QAAQ;IACnB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,iBAAiB;gBAHlB,EAAE,EAAE,QAAQ,EACX,YAAY,EAAE,QAAQ,EACtB,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,CACzB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,WAAW,CAAC;IAG3B,MAAM,CAAC,OAAO,CACZ,YAAY,EAAE,QAAQ,EACtB,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,QAE5D,QAAQ;IAIhB,IAAI,CAAC,EACT,MAAM,EACN,KAAU,EACV,KAAK,EACL,QAAQ,EACR,CAAC,GACF,EAAE;QACD,CAAC,CAAC,EAAE,MAAM,CAAA;QACV,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KACjB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAqCzD,MAAM,CAAC,EACX,IAAI,EACJ,GAAG,EACH,QAAQ,EACR,SAAS,EACT,SAAS,EACT,aAAa,GACd,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC,GAAG;QACvD,SAAS,CAAC,EAAE,IAAI,CAAA;QAChB,SAAS,CAAC,EAAE,IAAI,CAAA;KACjB,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAkBzB,MAAM,CAAC,EACX,IAAI,EACJ,GAAG,EACH,aAAa,GACd,EAAE,IAAI,CACL,UAAU,CAAC,MAAM,CAAC,EAClB,MAAM,GAAG,KAAK,GAAG,eAAe,CACjC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBX,MAAM,CACV,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CACd,IAAI,CACF,UAAU,CAAC,MAAM,CAAC,EAClB,MAAM,GAAG,UAAU,GAAG,eAAe,GAAG,WAAW,CACpD,CACF,GACA,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAiBxB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU9C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAUrE,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC;;;;;IAenC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IA6BtE,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BnC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CAcjE"}
|
package/dist/team/index.js
CHANGED
|
@@ -6,18 +6,36 @@ const xrpc_server_1 = require("@atproto/xrpc-server");
|
|
|
6
6
|
const lexicons_1 = require("../lexicon/lexicons");
|
|
7
7
|
const logger_1 = require("../logger");
|
|
8
8
|
class TeamService {
|
|
9
|
-
constructor(db) {
|
|
9
|
+
constructor(db, appviewAgent, appviewDid, createAuthHeaders) {
|
|
10
10
|
Object.defineProperty(this, "db", {
|
|
11
11
|
enumerable: true,
|
|
12
12
|
configurable: true,
|
|
13
13
|
writable: true,
|
|
14
14
|
value: db
|
|
15
15
|
});
|
|
16
|
+
Object.defineProperty(this, "appviewAgent", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: appviewAgent
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "appviewDid", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: appviewDid
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "createAuthHeaders", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: createAuthHeaders
|
|
33
|
+
});
|
|
16
34
|
}
|
|
17
|
-
static creator() {
|
|
18
|
-
return (db) => new TeamService(db);
|
|
35
|
+
static creator(appviewAgent, appviewDid, createAuthHeaders) {
|
|
36
|
+
return (db) => new TeamService(db, appviewAgent, appviewDid, createAuthHeaders);
|
|
19
37
|
}
|
|
20
|
-
async list({ cursor, limit = 25, roles, disabled, }) {
|
|
38
|
+
async list({ cursor, limit = 25, roles, disabled, q, }) {
|
|
21
39
|
let builder = this.db.db.selectFrom('member').selectAll();
|
|
22
40
|
if (cursor) {
|
|
23
41
|
builder = builder.where('createdAt', '>', new Date(cursor));
|
|
@@ -34,6 +52,11 @@ class TeamService {
|
|
|
34
52
|
if (disabled !== undefined) {
|
|
35
53
|
builder = builder.where('disabled', disabled ? 'is' : 'is not', true);
|
|
36
54
|
}
|
|
55
|
+
if (q) {
|
|
56
|
+
builder = builder.where((qb) => qb
|
|
57
|
+
.orWhere('handle', 'ilike', `%${q}%`)
|
|
58
|
+
.orWhere('displayName', 'ilike', `%${q}%`));
|
|
59
|
+
}
|
|
37
60
|
const members = await builder
|
|
38
61
|
.limit(limit)
|
|
39
62
|
.orderBy('createdAt', 'asc')
|
|
@@ -122,12 +145,12 @@ class TeamService {
|
|
|
122
145
|
};
|
|
123
146
|
}
|
|
124
147
|
// getProfiles() only allows 25 DIDs at a time so we need to query in chunks
|
|
125
|
-
async getProfiles(dids
|
|
148
|
+
async getProfiles(dids) {
|
|
126
149
|
const profiles = new Map();
|
|
127
150
|
try {
|
|
128
|
-
const headers = await
|
|
151
|
+
const headers = await this.createAuthHeaders(this.appviewDid, lexicons_1.ids.AppBskyActorGetProfiles);
|
|
129
152
|
for (const actors of (0, common_1.chunkArray)(dids, 25)) {
|
|
130
|
-
const { data } = await
|
|
153
|
+
const { data } = await this.appviewAgent.getProfiles({ actors }, headers);
|
|
131
154
|
data.profiles.forEach((profile) => {
|
|
132
155
|
profiles.set(profile.did, profile);
|
|
133
156
|
});
|
|
@@ -138,8 +161,34 @@ class TeamService {
|
|
|
138
161
|
}
|
|
139
162
|
return profiles;
|
|
140
163
|
}
|
|
141
|
-
async
|
|
142
|
-
|
|
164
|
+
async syncMemberProfiles() {
|
|
165
|
+
let lastDid = '';
|
|
166
|
+
// Max 25 profiles can be fetched at a time so let's pull 25 members at a time from the db and update their profile details
|
|
167
|
+
do {
|
|
168
|
+
const members = await this.db.db
|
|
169
|
+
.selectFrom('member')
|
|
170
|
+
.select(['did'])
|
|
171
|
+
.limit(25)
|
|
172
|
+
.if(!!lastDid, (q) => q.where('did', '>', lastDid))
|
|
173
|
+
.orderBy('did', 'asc')
|
|
174
|
+
.execute();
|
|
175
|
+
const dids = members.map((member) => member.did);
|
|
176
|
+
const profiles = await this.getProfiles(dids);
|
|
177
|
+
for (const profile of profiles.values()) {
|
|
178
|
+
await this.db.db
|
|
179
|
+
.updateTable('member')
|
|
180
|
+
.where('did', '=', profile.did)
|
|
181
|
+
.set({
|
|
182
|
+
handle: profile.handle,
|
|
183
|
+
displayName: profile.displayName || null,
|
|
184
|
+
})
|
|
185
|
+
.execute();
|
|
186
|
+
}
|
|
187
|
+
lastDid = dids.at(-1) || '';
|
|
188
|
+
} while (lastDid);
|
|
189
|
+
}
|
|
190
|
+
async view(members) {
|
|
191
|
+
const profiles = await this.getProfiles(members.map(({ did }) => did));
|
|
143
192
|
return members.map((member) => {
|
|
144
193
|
return {
|
|
145
194
|
did: member.did,
|
package/dist/team/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/team/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/team/index.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.
|
|
3
|
+
"version": "0.1.88",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typed-emitter": "^2.1.0",
|
|
36
36
|
"uint8arrays": "3.0.0",
|
|
37
37
|
"undici": "^6.14.1",
|
|
38
|
-
"@atproto/api": "^0.14.
|
|
38
|
+
"@atproto/api": "^0.14.9",
|
|
39
39
|
"@atproto/common": "^0.4.8",
|
|
40
40
|
"@atproto/crypto": "^0.4.4",
|
|
41
41
|
"@atproto/identity": "^0.4.6",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"ts-node": "^10.8.2",
|
|
56
56
|
"typescript": "^5.6.3",
|
|
57
57
|
"@atproto/lex-cli": "^0.6.2",
|
|
58
|
-
"@atproto/pds": "^0.4.
|
|
58
|
+
"@atproto/pds": "^0.4.106"
|
|
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/*/*",
|
|
@@ -26,14 +26,19 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
26
26
|
)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
const profiles = await teamService.getProfiles([did])
|
|
30
|
+
const profile = profiles.get(did)
|
|
31
|
+
|
|
29
32
|
const member = await teamService.create({
|
|
30
33
|
did,
|
|
34
|
+
handle: profile?.handle || null,
|
|
35
|
+
displayName: profile?.displayName || null,
|
|
31
36
|
disabled: false,
|
|
32
37
|
role: getMemberRole(role),
|
|
33
38
|
lastUpdatedBy:
|
|
34
39
|
access.type === 'admin_token' ? 'admin_token' : access.iss,
|
|
35
40
|
})
|
|
36
|
-
const memberView = await teamService.view([member]
|
|
41
|
+
const memberView = await teamService.view([member])
|
|
37
42
|
return memberView[0]
|
|
38
43
|
})
|
|
39
44
|
|
|
@@ -34,7 +34,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
34
34
|
lastUpdatedBy:
|
|
35
35
|
access.type === 'admin_token' ? 'admin_token' : access.iss,
|
|
36
36
|
})
|
|
37
|
-
const memberView = await teamService.view([updated]
|
|
37
|
+
const memberView = await teamService.view([updated])
|
|
38
38
|
return memberView[0]
|
|
39
39
|
})
|
|
40
40
|
|
package/src/config/config.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const envToCfg = (env: OzoneEnvironment): OzoneConfig => {
|
|
|
25
25
|
poolMaxUses: env.dbPoolMaxUses,
|
|
26
26
|
poolIdleTimeoutMs: env.dbPoolIdleTimeoutMs,
|
|
27
27
|
materializedViewRefreshIntervalMs: env.dbMaterializedViewRefreshIntervalMs,
|
|
28
|
+
teamProfileRefreshIntervalMs: env.dbTeamProfileRefreshIntervalMs,
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
assert(env.appviewUrl, 'appviewUrl is required')
|
|
@@ -124,6 +125,7 @@ export type DatabaseConfig = {
|
|
|
124
125
|
poolMaxUses?: number
|
|
125
126
|
poolIdleTimeoutMs?: number
|
|
126
127
|
materializedViewRefreshIntervalMs?: number
|
|
128
|
+
teamProfileRefreshIntervalMs?: number
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
export type AppviewConfig = {
|
package/src/config/env.ts
CHANGED
|
@@ -23,6 +23,9 @@ export const readEnv = (): OzoneEnvironment => {
|
|
|
23
23
|
dbMaterializedViewRefreshIntervalMs: envInt(
|
|
24
24
|
'OZONE_DB_MATERIALIZED_VIEW_REFRESH_INTERVAL_MS',
|
|
25
25
|
),
|
|
26
|
+
dbTeamProfileRefreshIntervalMs: envInt(
|
|
27
|
+
'OZONE_DB_TEAM_PROFILE_REFRESH_INTERVAL_MS',
|
|
28
|
+
),
|
|
26
29
|
didPlcUrl: envStr('OZONE_DID_PLC_URL'),
|
|
27
30
|
didCacheStaleTTL: envInt('OZONE_DID_CACHE_STALE_TTL'),
|
|
28
31
|
didCacheMaxTTL: envInt('OZONE_DID_CACHE_MAX_TTL'),
|
|
@@ -57,6 +60,7 @@ export type OzoneEnvironment = {
|
|
|
57
60
|
dbPoolMaxUses?: number
|
|
58
61
|
dbPoolIdleTimeoutMs?: number
|
|
59
62
|
dbMaterializedViewRefreshIntervalMs?: number
|
|
63
|
+
dbTeamProfileRefreshIntervalMs?: number
|
|
60
64
|
didPlcUrl?: string
|
|
61
65
|
didCacheStaleTTL?: number
|
|
62
66
|
didCacheMaxTTL?: number
|
package/src/context.ts
CHANGED
|
@@ -120,7 +120,11 @@ export class AppContext {
|
|
|
120
120
|
)
|
|
121
121
|
|
|
122
122
|
const communicationTemplateService = CommunicationTemplateService.creator()
|
|
123
|
-
const teamService = TeamService.creator(
|
|
123
|
+
const teamService = TeamService.creator(
|
|
124
|
+
appviewAgent,
|
|
125
|
+
cfg.appview.did,
|
|
126
|
+
createAuthHeaders,
|
|
127
|
+
)
|
|
124
128
|
const setService = SetService.creator()
|
|
125
129
|
const settingService = SettingService.creator()
|
|
126
130
|
|
package/src/daemon/context.ts
CHANGED
|
@@ -7,10 +7,12 @@ import { BackgroundQueue } from '../background'
|
|
|
7
7
|
import { OzoneConfig, OzoneSecrets } from '../config'
|
|
8
8
|
import { Database } from '../db'
|
|
9
9
|
import { ModerationService } from '../mod-service'
|
|
10
|
+
import { TeamService } from '../team'
|
|
10
11
|
import { getSigningKeyId } from '../util'
|
|
11
12
|
import { EventPusher } from './event-pusher'
|
|
12
13
|
import { EventReverser } from './event-reverser'
|
|
13
14
|
import { MaterializedViewRefresher } from './materialized-view-refresher'
|
|
15
|
+
import { TeamProfileSynchronizer } from './team-profile-synchronizer'
|
|
14
16
|
|
|
15
17
|
export type DaemonContextOptions = {
|
|
16
18
|
db: Database
|
|
@@ -20,6 +22,7 @@ export type DaemonContextOptions = {
|
|
|
20
22
|
eventPusher: EventPusher
|
|
21
23
|
eventReverser: EventReverser
|
|
22
24
|
materializedViewRefresher: MaterializedViewRefresher
|
|
25
|
+
teamProfileSynchronizer: TeamProfileSynchronizer
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export class DaemonContext {
|
|
@@ -67,6 +70,16 @@ export class DaemonContext {
|
|
|
67
70
|
appviewAgent,
|
|
68
71
|
createAuthHeaders,
|
|
69
72
|
)
|
|
73
|
+
const teamService = TeamService.creator(
|
|
74
|
+
appviewAgent,
|
|
75
|
+
cfg.appview.did,
|
|
76
|
+
createAuthHeaders,
|
|
77
|
+
)
|
|
78
|
+
const teamProfileSynchronizer = new TeamProfileSynchronizer(
|
|
79
|
+
backgroundQueue,
|
|
80
|
+
teamService(db),
|
|
81
|
+
cfg.db.teamProfileRefreshIntervalMs,
|
|
82
|
+
)
|
|
70
83
|
|
|
71
84
|
const eventReverser = new EventReverser(db, modService)
|
|
72
85
|
|
|
@@ -83,6 +96,7 @@ export class DaemonContext {
|
|
|
83
96
|
eventPusher,
|
|
84
97
|
eventReverser,
|
|
85
98
|
materializedViewRefresher,
|
|
99
|
+
teamProfileSynchronizer,
|
|
86
100
|
...(overrides ?? {}),
|
|
87
101
|
})
|
|
88
102
|
}
|
|
@@ -111,16 +125,22 @@ export class DaemonContext {
|
|
|
111
125
|
return this.opts.materializedViewRefresher
|
|
112
126
|
}
|
|
113
127
|
|
|
128
|
+
get teamProfileSynchronizer(): TeamProfileSynchronizer {
|
|
129
|
+
return this.opts.teamProfileSynchronizer
|
|
130
|
+
}
|
|
131
|
+
|
|
114
132
|
async start() {
|
|
115
133
|
this.eventPusher.start()
|
|
116
134
|
this.eventReverser.start()
|
|
117
135
|
this.materializedViewRefresher.start()
|
|
136
|
+
this.teamProfileSynchronizer.start()
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
async processAll() {
|
|
121
140
|
// Sequential because the materialized view values depend on the events.
|
|
122
141
|
await this.eventPusher.processAll()
|
|
123
142
|
await this.materializedViewRefresher.run()
|
|
143
|
+
await this.teamProfileSynchronizer.run()
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
async destroy() {
|
|
@@ -129,6 +149,7 @@ export class DaemonContext {
|
|
|
129
149
|
this.eventReverser.destroy(),
|
|
130
150
|
this.eventPusher.destroy(),
|
|
131
151
|
this.materializedViewRefresher.destroy(),
|
|
152
|
+
this.teamProfileSynchronizer.destroy(),
|
|
132
153
|
])
|
|
133
154
|
} finally {
|
|
134
155
|
await this.backgroundQueue.destroy()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HOUR } from '@atproto/common'
|
|
2
|
+
import { BackgroundQueue, PeriodicBackgroundTask } from '../background'
|
|
3
|
+
import { TeamService } from '../team'
|
|
4
|
+
|
|
5
|
+
export class TeamProfileSynchronizer extends PeriodicBackgroundTask {
|
|
6
|
+
constructor(
|
|
7
|
+
backgroundQueue: BackgroundQueue,
|
|
8
|
+
teamService: TeamService,
|
|
9
|
+
interval = 24 * HOUR,
|
|
10
|
+
) {
|
|
11
|
+
super(backgroundQueue, interval, async () => {
|
|
12
|
+
await teamService.syncMemberProfiles()
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema.alterTable('member').addColumn('handle', 'text').execute()
|
|
5
|
+
await db.schema
|
|
6
|
+
.alterTable('member')
|
|
7
|
+
.addColumn('displayName', 'text')
|
|
8
|
+
.execute()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
12
|
+
await db.schema.alterTable('member').dropColumn('handle').execute()
|
|
13
|
+
await db.schema.alterTable('member').dropColumn('displayName').execute()
|
|
14
|
+
}
|
|
@@ -21,3 +21,4 @@ export * as _20241220T144630860Z from './20241220T144630860Z-stats-materialized-
|
|
|
21
21
|
export * as _20250204T003647759Z from './20250204T003647759Z-add-subject-priority-score'
|
|
22
22
|
export * as _20250211T003647759Z from './20250211T003647759Z-add-reporter-stats-index'
|
|
23
23
|
export * as _20250211T132135150Z from './20250211T132135150Z-moderation-event-message-partial-idx'
|
|
24
|
+
export * as _20250221T132135150Z from './20250221T132135150Z-member-details'
|
package/src/db/schema/member.ts
CHANGED
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -14365,6 +14365,9 @@ export const schemaDict = {
|
|
|
14365
14365
|
parameters: {
|
|
14366
14366
|
type: 'params',
|
|
14367
14367
|
properties: {
|
|
14368
|
+
q: {
|
|
14369
|
+
type: 'string',
|
|
14370
|
+
},
|
|
14368
14371
|
disabled: {
|
|
14369
14372
|
type: 'boolean',
|
|
14370
14373
|
},
|
package/src/team/index.ts
CHANGED
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { Selectable } from 'kysely'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
2
3
|
import { chunkArray } from '@atproto/common'
|
|
3
4
|
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
4
|
-
import { AppContext } from '../context'
|
|
5
5
|
import { Database } from '../db'
|
|
6
6
|
import { Member } from '../db/schema/member'
|
|
7
7
|
import { ids } from '../lexicon/lexicons'
|
|
8
8
|
import { ProfileViewDetailed } from '../lexicon/types/app/bsky/actor/defs'
|
|
9
9
|
import { Member as TeamMember } from '../lexicon/types/tools/ozone/team/defs'
|
|
10
10
|
import { httpLogger } from '../logger'
|
|
11
|
+
import { AuthHeaders } from '../mod-service/views'
|
|
11
12
|
|
|
12
13
|
export type TeamServiceCreator = (db: Database) => TeamService
|
|
13
14
|
|
|
14
15
|
export class TeamService {
|
|
15
|
-
constructor(
|
|
16
|
+
constructor(
|
|
17
|
+
public db: Database,
|
|
18
|
+
private appviewAgent: AtpAgent,
|
|
19
|
+
private appviewDid: string,
|
|
20
|
+
private createAuthHeaders: (
|
|
21
|
+
aud: string,
|
|
22
|
+
method: string,
|
|
23
|
+
) => Promise<AuthHeaders>,
|
|
24
|
+
) {}
|
|
16
25
|
|
|
17
|
-
static creator(
|
|
18
|
-
|
|
26
|
+
static creator(
|
|
27
|
+
appviewAgent: AtpAgent,
|
|
28
|
+
appviewDid: string,
|
|
29
|
+
createAuthHeaders: (aud: string, method: string) => Promise<AuthHeaders>,
|
|
30
|
+
) {
|
|
31
|
+
return (db: Database) =>
|
|
32
|
+
new TeamService(db, appviewAgent, appviewDid, createAuthHeaders)
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
async list({
|
|
@@ -23,7 +37,9 @@ export class TeamService {
|
|
|
23
37
|
limit = 25,
|
|
24
38
|
roles,
|
|
25
39
|
disabled,
|
|
40
|
+
q,
|
|
26
41
|
}: {
|
|
42
|
+
q?: string
|
|
27
43
|
cursor?: string
|
|
28
44
|
limit?: number
|
|
29
45
|
disabled?: boolean
|
|
@@ -49,6 +65,14 @@ export class TeamService {
|
|
|
49
65
|
if (disabled !== undefined) {
|
|
50
66
|
builder = builder.where('disabled', disabled ? 'is' : 'is not', true)
|
|
51
67
|
}
|
|
68
|
+
if (q) {
|
|
69
|
+
builder = builder.where((qb) =>
|
|
70
|
+
qb
|
|
71
|
+
.orWhere('handle', 'ilike', `%${q}%`)
|
|
72
|
+
.orWhere('displayName', 'ilike', `%${q}%`),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
52
76
|
const members = await builder
|
|
53
77
|
.limit(limit)
|
|
54
78
|
.orderBy('createdAt', 'asc')
|
|
@@ -182,17 +206,17 @@ export class TeamService {
|
|
|
182
206
|
}
|
|
183
207
|
|
|
184
208
|
// getProfiles() only allows 25 DIDs at a time so we need to query in chunks
|
|
185
|
-
async getProfiles(
|
|
186
|
-
dids: string[],
|
|
187
|
-
ctx: AppContext,
|
|
188
|
-
): Promise<Map<string, ProfileViewDetailed>> {
|
|
209
|
+
async getProfiles(dids: string[]): Promise<Map<string, ProfileViewDetailed>> {
|
|
189
210
|
const profiles = new Map<string, ProfileViewDetailed>()
|
|
190
211
|
|
|
191
212
|
try {
|
|
192
|
-
const headers = await
|
|
213
|
+
const headers = await this.createAuthHeaders(
|
|
214
|
+
this.appviewDid,
|
|
215
|
+
ids.AppBskyActorGetProfiles,
|
|
216
|
+
)
|
|
193
217
|
|
|
194
218
|
for (const actors of chunkArray(dids, 25)) {
|
|
195
|
-
const { data } = await
|
|
219
|
+
const { data } = await this.appviewAgent.getProfiles(
|
|
196
220
|
{ actors },
|
|
197
221
|
headers,
|
|
198
222
|
)
|
|
@@ -211,14 +235,38 @@ export class TeamService {
|
|
|
211
235
|
return profiles
|
|
212
236
|
}
|
|
213
237
|
|
|
214
|
-
async
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
238
|
+
async syncMemberProfiles(): Promise<void> {
|
|
239
|
+
let lastDid = ''
|
|
240
|
+
// Max 25 profiles can be fetched at a time so let's pull 25 members at a time from the db and update their profile details
|
|
241
|
+
do {
|
|
242
|
+
const members = await this.db.db
|
|
243
|
+
.selectFrom('member')
|
|
244
|
+
.select(['did'])
|
|
245
|
+
.limit(25)
|
|
246
|
+
.if(!!lastDid, (q) => q.where('did', '>', lastDid))
|
|
247
|
+
.orderBy('did', 'asc')
|
|
248
|
+
.execute()
|
|
249
|
+
|
|
250
|
+
const dids = members.map((member) => member.did)
|
|
251
|
+
const profiles = await this.getProfiles(dids)
|
|
252
|
+
|
|
253
|
+
for (const profile of profiles.values()) {
|
|
254
|
+
await this.db.db
|
|
255
|
+
.updateTable('member')
|
|
256
|
+
.where('did', '=', profile.did)
|
|
257
|
+
.set({
|
|
258
|
+
handle: profile.handle,
|
|
259
|
+
displayName: profile.displayName || null,
|
|
260
|
+
})
|
|
261
|
+
.execute()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
lastDid = dids.at(-1) || ''
|
|
265
|
+
} while (lastDid)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async view(members: Selectable<Member>[]): Promise<TeamMember[]> {
|
|
269
|
+
const profiles = await this.getProfiles(members.map(({ did }) => did))
|
|
222
270
|
return members.map((member) => {
|
|
223
271
|
return {
|
|
224
272
|
did: member.did,
|
package/tests/team.test.ts
CHANGED
|
@@ -11,6 +11,9 @@ describe('team management', () => {
|
|
|
11
11
|
beforeAll(async () => {
|
|
12
12
|
network = await TestNetwork.create({
|
|
13
13
|
dbPostgresSchema: 'ozone_team_test',
|
|
14
|
+
ozone: {
|
|
15
|
+
dbTeamProfileRefreshIntervalMs: 100,
|
|
16
|
+
},
|
|
14
17
|
})
|
|
15
18
|
adminAgent = network.pds.getClient()
|
|
16
19
|
sc = network.getSeedClient()
|
|
@@ -29,6 +32,7 @@ describe('team management', () => {
|
|
|
29
32
|
identifier: sc.accounts[sc.dids.carol].handle,
|
|
30
33
|
password: sc.accounts[sc.dids.carol].password,
|
|
31
34
|
})
|
|
35
|
+
await network.processAll()
|
|
32
36
|
})
|
|
33
37
|
|
|
34
38
|
afterAll(async () => {
|
|
@@ -41,6 +45,7 @@ describe('team management', () => {
|
|
|
41
45
|
adminAgent.tools.ozone.team.listMembers({}),
|
|
42
46
|
triageAgent.tools.ozone.team.listMembers({}),
|
|
43
47
|
])
|
|
48
|
+
|
|
44
49
|
expect(forSnapshot(forAdmin.members)).toMatchSnapshot()
|
|
45
50
|
expect(forSnapshot(forTriage.members)).toMatchSnapshot()
|
|
46
51
|
// Validate that the list looks the same to both admin and triage members
|
|
@@ -89,6 +94,24 @@ describe('team management', () => {
|
|
|
89
94
|
onlyEnabled.members.find(({ disabled }) => disabled),
|
|
90
95
|
).toBeUndefined()
|
|
91
96
|
})
|
|
97
|
+
it('allows filtering members by handle/display name', async () => {
|
|
98
|
+
const [{ data: matchingHandle }, { data: matchingName }] =
|
|
99
|
+
await Promise.all([
|
|
100
|
+
adminAgent.tools.ozone.team.listMembers({
|
|
101
|
+
q: 'bob',
|
|
102
|
+
}),
|
|
103
|
+
adminAgent.tools.ozone.team.listMembers({
|
|
104
|
+
q: 'dev',
|
|
105
|
+
}),
|
|
106
|
+
])
|
|
107
|
+
|
|
108
|
+
expect(matchingHandle.members.length).toEqual(1)
|
|
109
|
+
expect(matchingHandle.members[0]?.profile?.handle).toEqual('bob.test')
|
|
110
|
+
expect(matchingName.members.length).toEqual(1)
|
|
111
|
+
expect(matchingName.members[0]?.profile?.handle).toEqual(
|
|
112
|
+
'mod-authority.test',
|
|
113
|
+
)
|
|
114
|
+
})
|
|
92
115
|
})
|
|
93
116
|
|
|
94
117
|
describe('addMember', () => {
|