Skip to content

Commit

Permalink
Split out moderation backend (#1970)
Browse files Browse the repository at this point in the history
* mv appview

* copy

* finalize copy

* package names

* big WIP

* first pass at mod servce

* some tidy

* tidy & fix compiler errors

* rename to ozone, db migrations, add to dev-env & pds cfg

* getRecord & getRepo mostly working

* fix open handle

* get record tests all working

* moderation events working

* statuses working

* tidy test suite

* search repos

* server & db tests

* moderation tests

* wip daemon + push events

* pds fanout working

* fix db test

* fanning takedowns out to appview

* rm try/catch

* bsky moderation test

* introduce mod subject wrappers

* more tidy

* refactor event reversal

* tidy some db stuff

* tidy

* rename service to mod-service

* fix test

* tidy config

* refactor auth in bsky

* wip patching up auto-mod

* add label ingester in appview

* fix a couple build issues

* fix some timing bugs

* tidy polling logic

* fix up tests

* fix some pds tests

* eslint ignore

* fix ozone tests

* move seeds to dev-env

* move images around

* fix db schemas

* use service auth admin reqs

* fix remaining tests

* auth tests bsky

* another test

* random tidy

* fix up search

* clean up bsky mod service

* more tidy

* default attempts to 0

* tidy old test

* random tidy

* tidy package.json

* tidy logger

* takedownId -> takedownRef

* misc pr feedback

* split daemon out from ozone application

* fix blob takedown mgiration

* refactor ozone config

* do push event fanout on write instead of on read

* make suspend error work again

* add attempts check & add supporting index

* fix takedown test ref

* get tests working

* rm old test

* fix timing bug in event pusher tests

* attempt another fix for timing bug

* await req

* service files

* remove labelerDid cfg

* update snaps for labeler did + some cfg changes

* fix more snaps

* pnpm i

* build ozone images

* build

* make label provider optional

* fix build issues

* fix build

* fix build

* build pds

* build on ghcr

* fix syntax in entry

* another fix

* use correct import

* export logger

* remove event reverser

* adjust push event fanout

* push out multiple

* remove builds
  • Loading branch information
dholms authored Jan 5, 2024
1 parent 65254ab commit de2dbc2
Show file tree
Hide file tree
Showing 468 changed files with 26,610 additions and 4,857 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
packages/api/src/client
packages/bsky/src/lexicon
packages/pds/src/lexicon
packages/ozone/src/lexicon
1 change: 0 additions & 1 deletion .github/workflows/build-and-push-bsky-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ on:
push:
branches:
- main
- appeal-report
env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/build-and-push-ozone-aws.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: build-and-push-ozone-aws
on:
push:
branches:
- main
env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
PASSWORD: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_PASSWORD }}
IMAGE_NAME: ozone

jobs:
ozone-container-aws:
if: github.repository == 'bluesky-social/atproto'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.USERNAME}}
password: ${{ env.PASSWORD }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,enable=true,priority=100,prefix=,suffix=,format=long
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
file: ./services/ozone/Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
56 changes: 56 additions & 0 deletions .github/workflows/build-and-push-ozone-ghcr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: build-and-push-ozone-ghcr
on:
push:
branches:
- main
env:
REGISTRY: ghcr.io
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}

# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}

jobs:
ozone-container-ghcr:
if: github.repository == 'bluesky-social/atproto'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.USERNAME }}
password: ${{ env.PASSWORD }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,enable=true,priority=100,prefix=ozone:,suffix=,format=long
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
file: ./services/ozone/Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ codegen: ## Re-generate packages from lexicon/ files
cd packages/api; pnpm run codegen
cd packages/pds; pnpm run codegen
cd packages/bsky; pnpm run codegen
cd packages/ozone; pnpm run codegen
# clean up codegen output
pnpm format

Expand Down
1 change: 1 addition & 0 deletions lexicons/com/atproto/admin/defs.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"did": { "type": "string", "format": "did" },
"handle": { "type": "string", "format": "handle" },
"email": { "type": "string" },
"relatedRecords": { "type": "array", "items": { "type": "unknown" } },
"indexedAt": { "type": "string", "format": "datetime" },
"invitedBy": {
"type": "ref",
Expand Down
36 changes: 36 additions & 0 deletions lexicons/com/atproto/admin/getAccountInfos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lexicon": 1,
"id": "com.atproto.admin.getAccountInfos",
"defs": {
"main": {
"type": "query",
"description": "Get details about some accounts.",
"parameters": {
"type": "params",
"required": ["dids"],
"properties": {
"dids": {
"type": "array",
"items": { "type": "string", "format": "did" }
}
}
},
"output": {
"encoding": "application/json",
"schema": {
"type": "object",
"required": ["infos"],
"properties": {
"infos": {
"type": "array",
"items": {
"type": "ref",
"ref": "com.atproto.admin.defs#accountView"
}
}
}
}
}
}
}
}
13 changes: 13 additions & 0 deletions packages/api/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as ComAtprotoAdminDisableInviteCodes from './types/com/atproto/admin/di
import * as ComAtprotoAdminEmitModerationEvent from './types/com/atproto/admin/emitModerationEvent'
import * as ComAtprotoAdminEnableAccountInvites from './types/com/atproto/admin/enableAccountInvites'
import * as ComAtprotoAdminGetAccountInfo from './types/com/atproto/admin/getAccountInfo'
import * as ComAtprotoAdminGetAccountInfos from './types/com/atproto/admin/getAccountInfos'
import * as ComAtprotoAdminGetInviteCodes from './types/com/atproto/admin/getInviteCodes'
import * as ComAtprotoAdminGetModerationEvent from './types/com/atproto/admin/getModerationEvent'
import * as ComAtprotoAdminGetRecord from './types/com/atproto/admin/getRecord'
Expand Down Expand Up @@ -153,6 +154,7 @@ export * as ComAtprotoAdminDisableInviteCodes from './types/com/atproto/admin/di
export * as ComAtprotoAdminEmitModerationEvent from './types/com/atproto/admin/emitModerationEvent'
export * as ComAtprotoAdminEnableAccountInvites from './types/com/atproto/admin/enableAccountInvites'
export * as ComAtprotoAdminGetAccountInfo from './types/com/atproto/admin/getAccountInfo'
export * as ComAtprotoAdminGetAccountInfos from './types/com/atproto/admin/getAccountInfos'
export * as ComAtprotoAdminGetInviteCodes from './types/com/atproto/admin/getInviteCodes'
export * as ComAtprotoAdminGetModerationEvent from './types/com/atproto/admin/getModerationEvent'
export * as ComAtprotoAdminGetRecord from './types/com/atproto/admin/getRecord'
Expand Down Expand Up @@ -441,6 +443,17 @@ export class AdminNS {
})
}

getAccountInfos(
params?: ComAtprotoAdminGetAccountInfos.QueryParams,
opts?: ComAtprotoAdminGetAccountInfos.CallOptions,
): Promise<ComAtprotoAdminGetAccountInfos.Response> {
return this._service.xrpc
.call('com.atproto.admin.getAccountInfos', params, undefined, opts)
.catch((e) => {
throw ComAtprotoAdminGetAccountInfos.toKnownErr(e)
})
}

getInviteCodes(
params?: ComAtprotoAdminGetInviteCodes.QueryParams,
opts?: ComAtprotoAdminGetInviteCodes.CallOptions,
Expand Down
46 changes: 46 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,12 @@ export const schemaDict = {
email: {
type: 'string',
},
relatedRecords: {
type: 'array',
items: {
type: 'unknown',
},
},
indexedAt: {
type: 'string',
format: 'datetime',
Expand Down Expand Up @@ -1046,6 +1052,45 @@ export const schemaDict = {
},
},
},
ComAtprotoAdminGetAccountInfos: {
lexicon: 1,
id: 'com.atproto.admin.getAccountInfos',
defs: {
main: {
type: 'query',
description: 'Get details about some accounts.',
parameters: {
type: 'params',
required: ['dids'],
properties: {
dids: {
type: 'array',
items: {
type: 'string',
format: 'did',
},
},
},
},
output: {
encoding: 'application/json',
schema: {
type: 'object',
required: ['infos'],
properties: {
infos: {
type: 'array',
items: {
type: 'ref',
ref: 'lex:com.atproto.admin.defs#accountView',
},
},
},
},
},
},
},
},
ComAtprotoAdminGetInviteCodes: {
lexicon: 1,
id: 'com.atproto.admin.getInviteCodes',
Expand Down Expand Up @@ -7875,6 +7920,7 @@ export const ids = {
ComAtprotoAdminEmitModerationEvent: 'com.atproto.admin.emitModerationEvent',
ComAtprotoAdminEnableAccountInvites: 'com.atproto.admin.enableAccountInvites',
ComAtprotoAdminGetAccountInfo: 'com.atproto.admin.getAccountInfo',
ComAtprotoAdminGetAccountInfos: 'com.atproto.admin.getAccountInfos',
ComAtprotoAdminGetInviteCodes: 'com.atproto.admin.getInviteCodes',
ComAtprotoAdminGetModerationEvent: 'com.atproto.admin.getModerationEvent',
ComAtprotoAdminGetRecord: 'com.atproto.admin.getRecord',
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/client/types/com/atproto/admin/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export interface AccountView {
did: string
handle: string
email?: string
relatedRecords?: {}[]
indexedAt: string
invitedBy?: ComAtprotoServerDefs.InviteCode
invites?: ComAtprotoServerDefs.InviteCode[]
Expand Down
36 changes: 36 additions & 0 deletions packages/api/src/client/types/com/atproto/admin/getAccountInfos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* GENERATED CODE - DO NOT MODIFY
*/
import { Headers, XRPCError } from '@atproto/xrpc'
import { ValidationResult, BlobRef } from '@atproto/lexicon'
import { isObj, hasProp } from '../../../../util'
import { lexicons } from '../../../../lexicons'
import { CID } from 'multiformats/cid'
import * as ComAtprotoAdminDefs from './defs'

export interface QueryParams {
dids: string[]
}

export type InputSchema = undefined

export interface OutputSchema {
infos: ComAtprotoAdminDefs.AccountView[]
[k: string]: unknown
}

export interface CallOptions {
headers?: Headers
}

export interface Response {
success: boolean
headers: Headers
data: OutputSchema
}

export function toKnownErr(e: any) {
if (e instanceof XRPCError) {
}
return e
}
23 changes: 10 additions & 13 deletions packages/bsky/src/api/app/bsky/actor/getProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ import { ModerationService } from '../../../../services/moderation'
export default function (server: Server, ctx: AppContext) {
const getProfile = createPipeline(skeleton, hydration, noRules, presentation)
server.app.bsky.actor.getProfile({
auth: ctx.authOptionalAccessOrRoleVerifier,
auth: ctx.authVerifier.optionalStandardOrRole,
handler: async ({ auth, params, res }) => {
const db = ctx.db.getReplica()
const actorService = ctx.services.actor(db)
const modService = ctx.services.moderation(ctx.db.getPrimary())
const viewer = 'did' in auth.credentials ? auth.credentials.did : null
const canViewTakendownProfile =
auth.credentials.type === 'role' && auth.credentials.triage
const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth)

const [result, repoRev] = await Promise.allSettled([
getProfile(
{ ...params, viewer, canViewTakendownProfile },
{ ...params, viewer, canViewTakedowns },
{ db, actorService, modService },
),
actorService.getRepoRev(viewer),
Expand All @@ -52,15 +50,14 @@ const skeleton = async (
params: Params,
ctx: Context,
): Promise<SkeletonState> => {
const { actorService, modService } = ctx
const { canViewTakendownProfile } = params
const { actorService } = ctx
const { canViewTakedowns } = params
const actor = await actorService.getActor(params.actor, true)
if (!actor) {
throw new InvalidRequestError('Profile not found')
}
if (!canViewTakendownProfile && softDeleted(actor)) {
const isSuspended = await modService.isSubjectSuspended(actor.did)
if (isSuspended) {
if (!canViewTakedowns && softDeleted(actor)) {
if (actor.takedownRef?.includes('SUSPEND')) {
throw new InvalidRequestError(
'Account has been temporarily suspended',
'AccountTakedown',
Expand All @@ -78,10 +75,10 @@ const skeleton = async (
const hydration = async (state: SkeletonState, ctx: Context) => {
const { actorService } = ctx
const { params, actor } = state
const { viewer, canViewTakendownProfile } = params
const { viewer, canViewTakedowns } = params
const hydration = await actorService.views.profileDetailHydration(
[actor.did],
{ viewer, includeSoftDeleted: canViewTakendownProfile },
{ viewer, includeSoftDeleted: canViewTakedowns },
)
return { ...state, ...hydration }
}
Expand Down Expand Up @@ -110,7 +107,7 @@ type Context = {

type Params = QueryParams & {
viewer: string | null
canViewTakendownProfile: boolean
canViewTakedowns: boolean
}

type SkeletonState = { params: Params; actor: Actor }
Expand Down
Loading

0 comments on commit de2dbc2

Please sign in to comment.