@atproto/ozone 0.0.15 → 0.0.16

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 (45) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/api/proxied.d.ts +3 -0
  3. package/dist/auth-verifier.d.ts +72 -0
  4. package/dist/config/config.d.ts +9 -2
  5. package/dist/config/env.d.ts +4 -0
  6. package/dist/context.d.ts +3 -90
  7. package/dist/db/index.js.map +2 -2
  8. package/dist/index.js +29669 -29467
  9. package/dist/index.js.map +3 -3
  10. package/dist/lexicon/lexicons.d.ts +5 -0
  11. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +1 -1
  12. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +1 -0
  13. package/dist/mod-service/index.d.ts +16 -7
  14. package/dist/mod-service/views.d.ts +3 -3
  15. package/package.json +5 -5
  16. package/src/api/admin/createCommunicationTemplate.ts +2 -2
  17. package/src/api/admin/deleteCommunicationTemplate.ts +2 -2
  18. package/src/api/admin/emitModerationEvent.ts +23 -4
  19. package/src/api/admin/getModerationEvent.ts +1 -1
  20. package/src/api/admin/getRecord.ts +2 -2
  21. package/src/api/admin/getRepo.ts +2 -2
  22. package/src/api/admin/listCommunicationTemplates.ts +2 -2
  23. package/src/api/admin/queryModerationEvents.ts +1 -1
  24. package/src/api/admin/queryModerationStatuses.ts +1 -1
  25. package/src/api/admin/searchRepos.ts +1 -1
  26. package/src/api/admin/updateCommunicationTemplate.ts +2 -2
  27. package/src/api/index.ts +2 -0
  28. package/src/api/moderation/createReport.ts +2 -2
  29. package/src/api/proxied.ts +116 -0
  30. package/src/api/temp/fetchLabels.ts +2 -2
  31. package/src/api/well-known.ts +3 -3
  32. package/src/auth-verifier.ts +218 -0
  33. package/src/config/config.ts +18 -2
  34. package/src/config/env.ts +9 -1
  35. package/src/context.ts +24 -38
  36. package/src/daemon/context.ts +10 -4
  37. package/src/daemon/event-pusher.ts +3 -3
  38. package/src/lexicon/lexicons.ts +5 -0
  39. package/src/lexicon/types/app/bsky/actor/defs.ts +1 -1
  40. package/src/lexicon/types/com/atproto/admin/defs.ts +2 -0
  41. package/src/mod-service/index.ts +64 -7
  42. package/src/mod-service/views.ts +22 -18
  43. package/tests/moderation-events.test.ts +49 -0
  44. package/dist/auth.d.ts +0 -81
  45. package/src/auth.ts +0 -147
@@ -779,6 +779,10 @@ export declare const schemaDict: {
779
779
  type: string;
780
780
  description: string;
781
781
  };
782
+ content: {
783
+ type: string;
784
+ description: string;
785
+ };
782
786
  comment: {
783
787
  type: string;
784
788
  description: string;
@@ -4731,6 +4735,7 @@ export declare const schemaDict: {
4731
4735
  hideRepliesByUnfollowed: {
4732
4736
  type: string;
4733
4737
  description: string;
4738
+ default: boolean;
4734
4739
  };
4735
4740
  hideRepliesByLikeCount: {
4736
4741
  type: string;
@@ -88,7 +88,7 @@ export declare function validatePersonalDetailsPref(v: unknown): ValidationResul
88
88
  export interface FeedViewPref {
89
89
  feed: string;
90
90
  hideReplies?: boolean;
91
- hideRepliesByUnfollowed?: boolean;
91
+ hideRepliesByUnfollowed: boolean;
92
92
  hideRepliesByLikeCount?: number;
93
93
  hideReposts?: boolean;
94
94
  hideQuotePosts?: boolean;
@@ -313,6 +313,7 @@ export declare function isModEventUnmute(v: unknown): v is ModEventUnmute;
313
313
  export declare function validateModEventUnmute(v: unknown): ValidationResult;
314
314
  export interface ModEventEmail {
315
315
  subjectLine: string;
316
+ content?: string;
316
317
  comment?: string;
317
318
  [k: string]: unknown;
318
319
  }
@@ -1,29 +1,33 @@
1
1
  import { CID } from 'multiformats/cid';
2
2
  import { AtUri } from '@atproto/syntax';
3
+ import { IdResolver } from '@atproto/identity';
4
+ import AtpAgent from '@atproto/api';
3
5
  import { Database } from '../db';
4
- import { AppviewAuth, ModerationViews } from './views';
6
+ import { AuthHeaders, ModerationViews } from './views';
5
7
  import { Main as StrongRef } from '../lexicon/types/com/atproto/repo/strongRef';
6
8
  import { RepoRef, RepoBlobRef } from '../lexicon/types/com/atproto/admin/defs';
7
9
  import { ModEventType, ModerationEventRow, ModerationSubjectStatusRow, ReversibleModerationEvent } from './types';
8
10
  import { ModerationEvent } from '../db/schema/moderation_event';
9
- import AtpAgent from '@atproto/api';
10
11
  import { Label } from '../lexicon/types/com/atproto/label/defs';
11
12
  import { ModSubject, RecordSubject, RepoSubject } from './subject';
12
13
  import { BackgroundQueue } from '../background';
13
14
  import { EventPusher } from '../daemon';
14
15
  import { ImageInvalidator } from '../image-invalidator';
16
+ import { OzoneConfig } from '../config';
15
17
  export type ModerationServiceCreator = (db: Database) => ModerationService;
16
18
  export declare class ModerationService {
17
19
  db: Database;
20
+ cfg: OzoneConfig;
18
21
  backgroundQueue: BackgroundQueue;
22
+ idResolver: IdResolver;
19
23
  eventPusher: EventPusher;
20
24
  appviewAgent: AtpAgent;
21
- private appviewAuth;
25
+ private createAuthHeaders;
22
26
  serverDid: string;
23
27
  imgInvalidator?: ImageInvalidator | undefined;
24
28
  cdnPaths?: string[] | undefined;
25
- constructor(db: Database, backgroundQueue: BackgroundQueue, eventPusher: EventPusher, appviewAgent: AtpAgent, appviewAuth: AppviewAuth, serverDid: string, imgInvalidator?: ImageInvalidator | undefined, cdnPaths?: string[] | undefined);
26
- static creator(backgroundQueue: BackgroundQueue, eventPusher: EventPusher, appviewAgent: AtpAgent, appviewAuth: AppviewAuth, serverDid: string, imgInvalidator?: ImageInvalidator, cdnPaths?: string[]): (db: Database) => ModerationService;
29
+ constructor(db: Database, cfg: OzoneConfig, backgroundQueue: BackgroundQueue, idResolver: IdResolver, eventPusher: EventPusher, appviewAgent: AtpAgent, createAuthHeaders: (aud: string) => Promise<AuthHeaders>, serverDid: string, imgInvalidator?: ImageInvalidator | undefined, cdnPaths?: string[] | undefined);
30
+ static creator(cfg: OzoneConfig, backgroundQueue: BackgroundQueue, idResolver: IdResolver, eventPusher: EventPusher, appviewAgent: AtpAgent, createAuthHeaders: (aud: string) => Promise<AuthHeaders>, serverDid: string, imgInvalidator?: ImageInvalidator, cdnPaths?: string[]): (db: Database) => ModerationService;
27
31
  views: ModerationViews;
28
32
  getEvent(id: number): Promise<ModerationEventRow | undefined>;
29
33
  getEventOrThrow(id: number): Promise<ModerationEventRow>;
@@ -56,8 +60,8 @@ export declare class ModerationService {
56
60
  } | {
57
61
  cids: CID[];
58
62
  }): Promise<{
59
- did: string;
60
63
  id: number;
64
+ did: string;
61
65
  createdAt: string;
62
66
  updatedAt: string;
63
67
  reviewState: "com.atproto.admin.defs#reviewOpen" | "com.atproto.admin.defs#reviewEscalated" | "com.atproto.admin.defs#reviewClosed";
@@ -142,8 +146,8 @@ export declare class ModerationService {
142
146
  }): Promise<{
143
147
  statuses: {
144
148
  handle: string;
145
- did: string;
146
149
  id: number;
150
+ did: string;
147
151
  createdAt: string;
148
152
  updatedAt: string;
149
153
  reviewState: "com.atproto.admin.defs#reviewOpen" | "com.atproto.admin.defs#reviewEscalated" | "com.atproto.admin.defs#reviewClosed";
@@ -169,6 +173,11 @@ export declare class ModerationService {
169
173
  negate?: string[];
170
174
  }): Promise<Label[]>;
171
175
  createLabels(labels: Label[]): Promise<void>;
176
+ sendEmail(opts: {
177
+ content: string;
178
+ recipientDid: string;
179
+ subject: string;
180
+ }): Promise<void>;
172
181
  }
173
182
  export type TakedownSubjects = {
174
183
  did: string;
@@ -5,16 +5,16 @@ import { ModEventView, RepoView, RepoViewDetail, RecordView, RecordViewDetail, R
5
5
  import { OutputSchema as ReportOutput } from '../lexicon/types/com/atproto/moderation/createReport';
6
6
  import { Label } from '../lexicon/types/com/atproto/label/defs';
7
7
  import { ModerationEventRowWithHandle, ModerationSubjectStatusRowWithHandle } from './types';
8
- export type AppviewAuth = () => Promise<{
8
+ export type AuthHeaders = {
9
9
  headers: {
10
10
  authorization: string;
11
11
  };
12
- } | undefined>;
12
+ };
13
13
  export declare class ModerationViews {
14
14
  private db;
15
15
  private appviewAgent;
16
16
  private appviewAuth;
17
- constructor(db: Database, appviewAgent: AtpAgent, appviewAuth: AppviewAuth);
17
+ constructor(db: Database, appviewAgent: AtpAgent, appviewAuth: () => Promise<AuthHeaders>);
18
18
  getAccoutInfosByDid(dids: string[]): Promise<Map<string, AccountView>>;
19
19
  repos(dids: string[]): Promise<Map<string, RepoView>>;
20
20
  formatEvent(event: ModerationEventRowWithHandle): ModEventView;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/ozone",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "license": "MIT",
5
5
  "description": "Backend service for moderating the Bluesky network.",
6
6
  "keywords": [
@@ -30,7 +30,7 @@
30
30
  "pino-http": "^8.2.1",
31
31
  "typed-emitter": "^2.1.0",
32
32
  "uint8arrays": "3.0.0",
33
- "@atproto/api": "^0.10.4",
33
+ "@atproto/api": "^0.10.5",
34
34
  "@atproto/common": "^0.3.3",
35
35
  "@atproto/crypto": "^0.3.0",
36
36
  "@atproto/identity": "^0.3.2",
@@ -46,10 +46,10 @@
46
46
  "@types/pg": "^8.6.6",
47
47
  "@types/qs": "^6.9.7",
48
48
  "axios": "^0.27.2",
49
- "@atproto/api": "^0.10.4",
50
- "@atproto/dev-env": "^0.2.36",
49
+ "@atproto/api": "^0.10.5",
50
+ "@atproto/dev-env": "^0.2.37",
51
51
  "@atproto/lex-cli": "^0.3.1",
52
- "@atproto/pds": "^0.4.4",
52
+ "@atproto/pds": "^0.4.5",
53
53
  "@atproto/xrpc": "^0.4.2"
54
54
  },
55
55
  "scripts": {
@@ -4,13 +4,13 @@ import AppContext from '../../context'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.createCommunicationTemplate({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ input, auth }) => {
9
9
  const access = auth.credentials
10
10
  const db = ctx.db
11
11
  const { createdBy, ...template } = input.body
12
12
 
13
- if (!access.admin) {
13
+ if (!access.isAdmin) {
14
14
  throw new AuthRequiredError(
15
15
  'Must be an admin to create a communication template',
16
16
  )
@@ -4,13 +4,13 @@ import AppContext from '../../context'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.deleteCommunicationTemplate({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ input, auth }) => {
9
9
  const access = auth.credentials
10
10
  const db = ctx.db
11
11
  const { id } = input.body
12
12
 
13
- if (!access.admin) {
13
+ if (!access.isAdmin) {
14
14
  throw new AuthRequiredError(
15
15
  'Must be an admin to delete a communication template',
16
16
  )
@@ -2,16 +2,18 @@ import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
2
2
  import { Server } from '../../lexicon'
3
3
  import AppContext from '../../context'
4
4
  import {
5
+ isModEventEmail,
5
6
  isModEventLabel,
6
7
  isModEventReverseTakedown,
7
8
  isModEventTakedown,
8
9
  } from '../../lexicon/types/com/atproto/admin/defs'
9
10
  import { subjectFromInput } from '../../mod-service/subject'
10
11
  import { ModerationLangService } from '../../mod-service/lang'
12
+ import { retryHttp } from '../../util'
11
13
 
12
14
  export default function (server: Server, ctx: AppContext) {
13
15
  server.com.atproto.admin.emitModerationEvent({
14
- auth: ctx.roleVerifier,
16
+ auth: ctx.authVerifier.modOrRole,
15
17
  handler: async ({ input, auth }) => {
16
18
  const access = auth.credentials
17
19
  const db = ctx.db
@@ -29,7 +31,7 @@ export default function (server: Server, ctx: AppContext) {
29
31
 
30
32
  // if less than moderator access then can only take ack and escalation actions
31
33
  if (isTakedownEvent || isReverseTakedownEvent) {
32
- if (!access.moderator) {
34
+ if (!access.isModerator) {
33
35
  throw new AuthRequiredError(
34
36
  'Must be a full moderator to take this type of action',
35
37
  )
@@ -37,7 +39,7 @@ export default function (server: Server, ctx: AppContext) {
37
39
 
38
40
  // Non admins should not be able to take down feed generators
39
41
  if (
40
- !access.admin &&
42
+ !access.isAdmin &&
41
43
  subject.recordPath?.includes('app.bsky.feed.generator/')
42
44
  ) {
43
45
  throw new AuthRequiredError(
@@ -46,7 +48,7 @@ export default function (server: Server, ctx: AppContext) {
46
48
  }
47
49
  }
48
50
  // if less than moderator access then can not apply labels
49
- if (!access.moderator && isLabelEvent) {
51
+ if (!access.isModerator && isLabelEvent) {
50
52
  throw new AuthRequiredError('Must be a full moderator to label content')
51
53
  }
52
54
 
@@ -75,6 +77,23 @@ export default function (server: Server, ctx: AppContext) {
75
77
  }
76
78
  }
77
79
 
80
+ if (isModEventEmail(event) && event.content) {
81
+ // sending email prior to logging the event to avoid a long transaction below
82
+ if (!subject.isRepo()) {
83
+ throw new InvalidRequestError(
84
+ 'Email can only be sent to a repo subject',
85
+ )
86
+ }
87
+ const { content, subjectLine } = event
88
+ await retryHttp(() =>
89
+ ctx.modService(db).sendEmail({
90
+ subject: subjectLine,
91
+ content,
92
+ recipientDid: subject.did,
93
+ }),
94
+ )
95
+ }
96
+
78
97
  const moderationEvent = await db.transaction(async (dbTxn) => {
79
98
  const moderationTxn = ctx.modService(dbTxn)
80
99
 
@@ -3,7 +3,7 @@ import AppContext from '../../context'
3
3
 
4
4
  export default function (server: Server, ctx: AppContext) {
5
5
  server.com.atproto.admin.getModerationEvent({
6
- auth: ctx.roleVerifier,
6
+ auth: ctx.authVerifier.modOrRole,
7
7
  handler: async ({ params }) => {
8
8
  const { id } = params
9
9
  const db = ctx.db
@@ -6,7 +6,7 @@ import { AtUri } from '@atproto/syntax'
6
6
 
7
7
  export default function (server: Server, ctx: AppContext) {
8
8
  server.com.atproto.admin.getRecord({
9
- auth: ctx.roleVerifier,
9
+ auth: ctx.authVerifier.modOrRole,
10
10
  handler: async ({ params, auth }) => {
11
11
  const db = ctx.db
12
12
 
@@ -22,7 +22,7 @@ export default function (server: Server, ctx: AppContext) {
22
22
  record.repo = addAccountInfoToRepoView(
23
23
  record.repo,
24
24
  accountInfo,
25
- auth.credentials.moderator,
25
+ auth.credentials.isModerator,
26
26
  )
27
27
 
28
28
  return {
@@ -5,7 +5,7 @@ import { addAccountInfoToRepoViewDetail, getPdsAccountInfo } from './util'
5
5
 
6
6
  export default function (server: Server, ctx: AppContext) {
7
7
  server.com.atproto.admin.getRepo({
8
- auth: ctx.roleVerifier,
8
+ auth: ctx.authVerifier.modOrRole,
9
9
  handler: async ({ params, auth }) => {
10
10
  const { did } = params
11
11
  const db = ctx.db
@@ -20,7 +20,7 @@ export default function (server: Server, ctx: AppContext) {
20
20
  const repo = addAccountInfoToRepoViewDetail(
21
21
  partialRepo,
22
22
  accountInfo,
23
- auth.credentials.moderator,
23
+ auth.credentials.isModerator,
24
24
  )
25
25
  return {
26
26
  encoding: 'application/json',
@@ -4,12 +4,12 @@ import AppContext from '../../context'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.listCommunicationTemplates({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ auth }) => {
9
9
  const access = auth.credentials
10
10
  const db = ctx.db
11
11
 
12
- if (!access.moderator) {
12
+ if (!access.isModerator) {
13
13
  throw new AuthRequiredError(
14
14
  'Must be a full moderator to view list of communication template',
15
15
  )
@@ -4,7 +4,7 @@ import { getEventType } from '../moderation/util'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.queryModerationEvents({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ params }) => {
9
9
  const {
10
10
  subject,
@@ -4,7 +4,7 @@ import { getReviewState } from '../moderation/util'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.queryModerationStatuses({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ params }) => {
9
9
  const {
10
10
  subject,
@@ -4,7 +4,7 @@ import { mapDefined } from '@atproto/common'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.searchRepos({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ params }) => {
9
9
  const modService = ctx.modService(ctx.db)
10
10
 
@@ -4,13 +4,13 @@ import AppContext from '../../context'
4
4
 
5
5
  export default function (server: Server, ctx: AppContext) {
6
6
  server.com.atproto.admin.updateCommunicationTemplate({
7
- auth: ctx.roleVerifier,
7
+ auth: ctx.authVerifier.modOrRole,
8
8
  handler: async ({ input, auth }) => {
9
9
  const access = auth.credentials
10
10
  const db = ctx.db
11
11
  const { id, updatedBy, ...template } = input.body
12
12
 
13
- if (!access.admin) {
13
+ if (!access.isAdmin) {
14
14
  throw new AuthRequiredError(
15
15
  'Must be an admin to update a communication template',
16
16
  )
package/src/api/index.ts CHANGED
@@ -15,6 +15,7 @@ import createCommunicationTemplate from './admin/createCommunicationTemplate'
15
15
  import updateCommunicationTemplate from './admin/updateCommunicationTemplate'
16
16
  import deleteCommunicationTemplate from './admin/deleteCommunicationTemplate'
17
17
  import listCommunicationTemplates from './admin/listCommunicationTemplates'
18
+ import proxied from './proxied'
18
19
 
19
20
  export * as health from './health'
20
21
 
@@ -36,5 +37,6 @@ export default function (server: Server, ctx: AppContext) {
36
37
  createCommunicationTemplate(server, ctx)
37
38
  updateCommunicationTemplate(server, ctx)
38
39
  deleteCommunicationTemplate(server, ctx)
40
+ proxied(server, ctx)
39
41
  return server
40
42
  }
@@ -9,10 +9,10 @@ import { ModerationLangService } from '../../mod-service/lang'
9
9
  export default function (server: Server, ctx: AppContext) {
10
10
  server.com.atproto.moderation.createReport({
11
11
  // @TODO anonymous reports w/ optional auth are a temporary measure
12
- auth: ctx.authOptionalAccessOrRoleVerifier,
12
+ auth: ctx.authVerifier.standardOptionalOrRole,
13
13
  handler: async ({ input, auth }) => {
14
14
  const requester =
15
- 'did' in auth.credentials ? auth.credentials.did : ctx.cfg.service.did
15
+ 'iss' in auth.credentials ? auth.credentials.iss : ctx.cfg.service.did
16
16
  const { reasonType, reason } = input.body
17
17
  const subject = subjectFromInput(input.body.subject)
18
18
 
@@ -0,0 +1,116 @@
1
+ import { Server } from '../lexicon'
2
+ import AppContext from '../context'
3
+
4
+ export default function (server: Server, ctx: AppContext) {
5
+ server.app.bsky.actor.getProfile({
6
+ auth: ctx.authVerifier.modOrRole,
7
+ handler: async (request) => {
8
+ const res = await ctx.appviewAgent.api.app.bsky.actor.getProfile(
9
+ request.params,
10
+ await ctx.appviewAuth(),
11
+ )
12
+ return {
13
+ encoding: 'application/json',
14
+ body: res.data,
15
+ }
16
+ },
17
+ })
18
+
19
+ server.app.bsky.actor.getProfiles({
20
+ auth: ctx.authVerifier.modOrRole,
21
+ handler: async (request) => {
22
+ const res = await ctx.appviewAgent.api.app.bsky.actor.getProfiles(
23
+ request.params,
24
+ await ctx.appviewAuth(),
25
+ )
26
+ return {
27
+ encoding: 'application/json',
28
+ body: res.data,
29
+ }
30
+ },
31
+ })
32
+
33
+ server.app.bsky.feed.getAuthorFeed({
34
+ auth: ctx.authVerifier.modOrRole,
35
+ handler: async (request) => {
36
+ const res = await ctx.appviewAgent.api.app.bsky.feed.getAuthorFeed(
37
+ request.params,
38
+ await ctx.appviewAuth(),
39
+ )
40
+ return {
41
+ encoding: 'application/json',
42
+ body: res.data,
43
+ }
44
+ },
45
+ })
46
+
47
+ server.app.bsky.feed.getPostThread({
48
+ auth: ctx.authVerifier.modOrRole,
49
+ handler: async (request) => {
50
+ const res = await ctx.appviewAgent.api.app.bsky.feed.getPostThread(
51
+ request.params,
52
+ await ctx.appviewAuth(),
53
+ )
54
+ return {
55
+ encoding: 'application/json',
56
+ body: res.data,
57
+ }
58
+ },
59
+ })
60
+
61
+ server.app.bsky.feed.getFeedGenerator({
62
+ auth: ctx.authVerifier.modOrRole,
63
+ handler: async (request) => {
64
+ const res = await ctx.appviewAgent.api.app.bsky.feed.getFeedGenerator(
65
+ request.params,
66
+ await ctx.appviewAuth(),
67
+ )
68
+ return {
69
+ encoding: 'application/json',
70
+ body: res.data,
71
+ }
72
+ },
73
+ })
74
+
75
+ server.app.bsky.graph.getFollows({
76
+ auth: ctx.authVerifier.modOrRole,
77
+ handler: async (request) => {
78
+ const res = await ctx.appviewAgent.api.app.bsky.graph.getFollows(
79
+ request.params,
80
+ await ctx.appviewAuth(),
81
+ )
82
+ return {
83
+ encoding: 'application/json',
84
+ body: res.data,
85
+ }
86
+ },
87
+ })
88
+
89
+ server.app.bsky.graph.getFollowers({
90
+ auth: ctx.authVerifier.modOrRole,
91
+ handler: async (request) => {
92
+ const res = await ctx.appviewAgent.api.app.bsky.graph.getFollowers(
93
+ request.params,
94
+ await ctx.appviewAuth(),
95
+ )
96
+ return {
97
+ encoding: 'application/json',
98
+ body: res.data,
99
+ }
100
+ },
101
+ })
102
+
103
+ server.app.bsky.graph.getList({
104
+ auth: ctx.authVerifier.modOrRole,
105
+ handler: async (request) => {
106
+ const res = await ctx.appviewAgent.api.app.bsky.graph.getList(
107
+ request.params,
108
+ await ctx.appviewAuth(),
109
+ )
110
+ return {
111
+ encoding: 'application/json',
112
+ body: res.data,
113
+ }
114
+ },
115
+ })
116
+ }
@@ -8,13 +8,13 @@ import {
8
8
 
9
9
  export default function (server: Server, ctx: AppContext) {
10
10
  server.com.atproto.temp.fetchLabels({
11
- auth: ctx.authOptionalAccessOrRoleVerifier,
11
+ auth: ctx.authVerifier.standardOptionalOrRole,
12
12
  handler: async ({ auth, params }) => {
13
13
  const { limit } = params
14
14
  const since =
15
15
  params.since !== undefined ? new Date(params.since).toISOString() : ''
16
16
  const includeUnspeccedTakedowns =
17
- auth.credentials.type === 'role' && auth.credentials.admin
17
+ auth.credentials.type === 'none' ? false : auth.credentials.isAdmin
18
18
  const labelRes = await ctx.db.db
19
19
  .selectFrom('label')
20
20
  .selectAll()
@@ -15,7 +15,7 @@ export const createRouter = (ctx: AppContext): express.Router => {
15
15
  id: ctx.cfg.service.did,
16
16
  verificationMethod: [
17
17
  {
18
- id: `${ctx.cfg.service.did}#atproto`,
18
+ id: `${ctx.cfg.service.did}#atproto_label`,
19
19
  type: 'Multikey',
20
20
  controller: ctx.cfg.service.did,
21
21
  publicKeyMultibase: ctx.signingKey.did().replace('did:key:', ''),
@@ -23,8 +23,8 @@ export const createRouter = (ctx: AppContext): express.Router => {
23
23
  ],
24
24
  service: [
25
25
  {
26
- id: '#atproto_mod',
27
- type: 'AtprotoModerationService',
26
+ id: '#atproto_labeler',
27
+ type: 'AtprotoLabeler',
28
28
  serviceEndpoint: `https://${hostname}`,
29
29
  },
30
30
  ],