Admin labeling (#801)
* adding to moderation flows * some bugfixing in labels * hardcode profile nsid * make labels off moderation action * db updates * wip * report labels in admin views & reverse moderation actions * Test admin get record and repo w/ labels * update db * fix * exclude negs from labels * exclude neg on moderation views as well * Check-in missing lex * Check-in missing lex * In-progress admin label tests * Test label creation/reversal via actions * Admin label test snapshots (#808) * new snapshots for label on user * fix get moderation action snap * fix dev-env --------- Co-authored-by: Devin Ivy <devinivy@gmail.com>
This commit is contained in:
parent
6592fcd6eb
commit
aa46ad1e1c
lexicons/com/atproto/admin
packages
api/src/client
bsky/src/lexicon
dev-env/src/mock
pds
src
api/com/atproto/admin
app-view/services/label
db
labeler
lexicon
services/moderation
tests
__snapshots__
event-stream/__snapshots__
labeler
moderation.test.tsseeds
views
__snapshots__
author-feed.test.ts.snaplikes.test.ts.snapnotifications.test.ts.snapprofile.test.ts.snapreposts.test.ts.snaptimeline.test.ts.snap
admin
__snapshots__
get-moderation-action.test.ts.snapget-moderation-actions.test.ts.snapget-moderation-report.test.ts.snapget-moderation-reports.test.ts.snapget-record.test.ts.snapget-repo.test.ts.snap
get-moderation-action.test.tsget-moderation-actions.test.tsget-record.test.tsget-repo.test.ts@ -146,6 +146,10 @@
|
||||
"relatedRecords": {"type": "array", "items": {"type": "unknown"}},
|
||||
"indexedAt": {"type": "string", "format": "datetime"},
|
||||
"moderation": {"type": "ref", "ref": "#moderationDetail"},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {"type": "ref", "ref": "com.atproto.label.defs#label"}
|
||||
},
|
||||
"invitedBy": {"type": "ref", "ref": "com.atproto.server.defs#inviteCode"},
|
||||
"invites": {
|
||||
"type": "array",
|
||||
@ -181,6 +185,10 @@
|
||||
"cid": {"type": "string", "format": "cid"},
|
||||
"value": {"type": "unknown"},
|
||||
"blobs": {"type": "array", "items": {"type": "ref", "ref": "#blobView"}},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {"type": "ref", "ref": "com.atproto.label.defs#label"}
|
||||
},
|
||||
"indexedAt": {"type": "string", "format": "datetime"},
|
||||
"moderation": {"type": "ref", "ref": "#moderationDetail"},
|
||||
"repo": {"type": "ref", "ref": "#repoView"}
|
||||
|
@ -364,6 +364,13 @@ export const schemaDict = {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.admin.defs#moderationDetail',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
invitedBy: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.server.defs#inviteCode',
|
||||
@ -461,6 +468,13 @@ export const schemaDict = {
|
||||
ref: 'lex:com.atproto.admin.defs#blobView',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
indexedAt: {
|
||||
type: 'string',
|
||||
format: 'datetime',
|
||||
@ -1055,7 +1069,7 @@ export const schemaDict = {
|
||||
defs: {
|
||||
main: {
|
||||
type: 'procedure',
|
||||
description: 'Administrative action to update an accounts handle',
|
||||
description: "Administrative action to update an account's handle",
|
||||
input: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
|
@ -8,6 +8,7 @@ import { CID } from 'multiformats/cid'
|
||||
import * as ComAtprotoRepoStrongRef from '../repo/strongRef'
|
||||
import * as ComAtprotoModerationDefs from '../moderation/defs'
|
||||
import * as ComAtprotoServerDefs from '../server/defs'
|
||||
import * as ComAtprotoLabelDefs from '../label/defs'
|
||||
|
||||
export interface ActionView {
|
||||
id: number
|
||||
@ -195,6 +196,7 @@ export interface RepoViewDetail {
|
||||
relatedRecords: {}[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
invitedBy?: ComAtprotoServerDefs.InviteCode
|
||||
invites?: ComAtprotoServerDefs.InviteCode[]
|
||||
[k: string]: unknown
|
||||
@ -257,6 +259,7 @@ export interface RecordViewDetail {
|
||||
cid: string
|
||||
value: {}
|
||||
blobs: BlobView[]
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
repo: RepoView
|
||||
|
@ -21,6 +21,7 @@ import * as ComAtprotoAdminResolveModerationReports from './types/com/atproto/ad
|
||||
import * as ComAtprotoAdminReverseModerationAction from './types/com/atproto/admin/reverseModerationAction'
|
||||
import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRepos'
|
||||
import * as ComAtprotoAdminTakeModerationAction from './types/com/atproto/admin/takeModerationAction'
|
||||
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
|
||||
import * as ComAtprotoIdentityResolveHandle from './types/com/atproto/identity/resolveHandle'
|
||||
import * as ComAtprotoIdentityUpdateHandle from './types/com/atproto/identity/updateHandle'
|
||||
import * as ComAtprotoLabelQueryLabels from './types/com/atproto/label/queryLabels'
|
||||
@ -36,6 +37,7 @@ import * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord'
|
||||
import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob'
|
||||
import * as ComAtprotoServerCreateAccount from './types/com/atproto/server/createAccount'
|
||||
import * as ComAtprotoServerCreateInviteCode from './types/com/atproto/server/createInviteCode'
|
||||
import * as ComAtprotoServerCreateInviteCodes from './types/com/atproto/server/createInviteCodes'
|
||||
import * as ComAtprotoServerCreateSession from './types/com/atproto/server/createSession'
|
||||
import * as ComAtprotoServerDeleteAccount from './types/com/atproto/server/deleteAccount'
|
||||
import * as ComAtprotoServerDeleteSession from './types/com/atproto/server/deleteSession'
|
||||
@ -54,6 +56,7 @@ import * as ComAtprotoSyncGetHead from './types/com/atproto/sync/getHead'
|
||||
import * as ComAtprotoSyncGetRecord from './types/com/atproto/sync/getRecord'
|
||||
import * as ComAtprotoSyncGetRepo from './types/com/atproto/sync/getRepo'
|
||||
import * as ComAtprotoSyncListBlobs from './types/com/atproto/sync/listBlobs'
|
||||
import * as ComAtprotoSyncListRepos from './types/com/atproto/sync/listRepos'
|
||||
import * as ComAtprotoSyncNotifyOfUpdate from './types/com/atproto/sync/notifyOfUpdate'
|
||||
import * as ComAtprotoSyncRequestCrawl from './types/com/atproto/sync/requestCrawl'
|
||||
import * as ComAtprotoSyncSubscribeRepos from './types/com/atproto/sync/subscribeRepos'
|
||||
@ -253,6 +256,16 @@ export class AdminNS {
|
||||
const nsid = 'com.atproto.admin.takeModerationAction' // @ts-ignore
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
|
||||
updateAccountHandle<AV extends AuthVerifier>(
|
||||
cfg: ConfigOf<
|
||||
AV,
|
||||
ComAtprotoAdminUpdateAccountHandle.Handler<ExtractAuth<AV>>
|
||||
>,
|
||||
) {
|
||||
const nsid = 'com.atproto.admin.updateAccountHandle' // @ts-ignore
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
export class IdentityNS {
|
||||
@ -405,6 +418,16 @@ export class ServerNS {
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
|
||||
createInviteCodes<AV extends AuthVerifier>(
|
||||
cfg: ConfigOf<
|
||||
AV,
|
||||
ComAtprotoServerCreateInviteCodes.Handler<ExtractAuth<AV>>
|
||||
>,
|
||||
) {
|
||||
const nsid = 'com.atproto.server.createInviteCodes' // @ts-ignore
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
|
||||
createSession<AV extends AuthVerifier>(
|
||||
cfg: ConfigOf<AV, ComAtprotoServerCreateSession.Handler<ExtractAuth<AV>>>,
|
||||
) {
|
||||
@ -548,6 +571,13 @@ export class SyncNS {
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
|
||||
listRepos<AV extends AuthVerifier>(
|
||||
cfg: ConfigOf<AV, ComAtprotoSyncListRepos.Handler<ExtractAuth<AV>>>,
|
||||
) {
|
||||
const nsid = 'com.atproto.sync.listRepos' // @ts-ignore
|
||||
return this._server.xrpc.method(nsid, cfg)
|
||||
}
|
||||
|
||||
notifyOfUpdate<AV extends AuthVerifier>(
|
||||
cfg: ConfigOf<AV, ComAtprotoSyncNotifyOfUpdate.Handler<ExtractAuth<AV>>>,
|
||||
) {
|
||||
|
@ -364,6 +364,13 @@ export const schemaDict = {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.admin.defs#moderationDetail',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
invitedBy: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.server.defs#inviteCode',
|
||||
@ -461,6 +468,13 @@ export const schemaDict = {
|
||||
ref: 'lex:com.atproto.admin.defs#blobView',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
indexedAt: {
|
||||
type: 'string',
|
||||
format: 'datetime',
|
||||
@ -1049,6 +1063,33 @@ export const schemaDict = {
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoAdminUpdateAccountHandle: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.admin.updateAccountHandle',
|
||||
defs: {
|
||||
main: {
|
||||
type: 'procedure',
|
||||
description: "Administrative action to update an account's handle",
|
||||
input: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['did', 'handle'],
|
||||
properties: {
|
||||
did: {
|
||||
type: 'string',
|
||||
format: 'did',
|
||||
},
|
||||
handle: {
|
||||
type: 'string',
|
||||
format: 'handle',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoIdentityResolveHandle: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.identity.resolveHandle',
|
||||
@ -1113,44 +1154,40 @@ export const schemaDict = {
|
||||
label: {
|
||||
type: 'object',
|
||||
description: 'Metadata tag on an atproto resource (eg, repo or record)',
|
||||
key: 'tid',
|
||||
record: {
|
||||
type: 'object',
|
||||
required: ['src', 'uri', 'val', 'cts'],
|
||||
properties: {
|
||||
src: {
|
||||
type: 'string',
|
||||
format: 'did',
|
||||
description: 'DID of the actor who created this label',
|
||||
},
|
||||
uri: {
|
||||
type: 'string',
|
||||
format: 'uri',
|
||||
description:
|
||||
'AT URI of the record, repository (account), or other resource which this label applies to',
|
||||
},
|
||||
cid: {
|
||||
type: 'string',
|
||||
format: 'cid',
|
||||
description:
|
||||
"optionally, CID specifying the specific version of 'uri' resource this label applies to",
|
||||
},
|
||||
val: {
|
||||
type: 'string',
|
||||
maxLength: 128,
|
||||
description:
|
||||
'the short string name of the value or type of this label',
|
||||
},
|
||||
neg: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'if true, this is a negation label, overwriting a previous label',
|
||||
},
|
||||
cts: {
|
||||
type: 'string',
|
||||
format: 'datetime',
|
||||
description: 'timestamp when this label was created',
|
||||
},
|
||||
required: ['src', 'uri', 'val', 'cts'],
|
||||
properties: {
|
||||
src: {
|
||||
type: 'string',
|
||||
format: 'did',
|
||||
description: 'DID of the actor who created this label',
|
||||
},
|
||||
uri: {
|
||||
type: 'string',
|
||||
format: 'uri',
|
||||
description:
|
||||
'AT URI of the record, repository (account), or other resource which this label applies to',
|
||||
},
|
||||
cid: {
|
||||
type: 'string',
|
||||
format: 'cid',
|
||||
description:
|
||||
"optionally, CID specifying the specific version of 'uri' resource this label applies to",
|
||||
},
|
||||
val: {
|
||||
type: 'string',
|
||||
maxLength: 128,
|
||||
description:
|
||||
'the short string name of the value or type of this label',
|
||||
},
|
||||
neg: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'if true, this is a negation label, overwriting a previous label',
|
||||
},
|
||||
cts: {
|
||||
type: 'string',
|
||||
format: 'datetime',
|
||||
description: 'timestamp when this label was created',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -2049,6 +2086,51 @@ export const schemaDict = {
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoServerCreateInviteCodes: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.server.createInviteCodes',
|
||||
defs: {
|
||||
main: {
|
||||
type: 'procedure',
|
||||
description: 'Create an invite code.',
|
||||
input: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['codeCount', 'useCount'],
|
||||
properties: {
|
||||
codeCount: {
|
||||
type: 'integer',
|
||||
default: 1,
|
||||
},
|
||||
useCount: {
|
||||
type: 'integer',
|
||||
},
|
||||
forAccount: {
|
||||
type: 'string',
|
||||
format: 'did',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['codes'],
|
||||
properties: {
|
||||
codes: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoServerCreateSession: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.server.createSession',
|
||||
@ -2060,7 +2142,7 @@ export const schemaDict = {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['password'],
|
||||
required: ['identifier', 'password'],
|
||||
properties: {
|
||||
identifier: {
|
||||
type: 'string',
|
||||
@ -2726,6 +2808,63 @@ export const schemaDict = {
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoSyncListRepos: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.sync.listRepos',
|
||||
defs: {
|
||||
main: {
|
||||
type: 'query',
|
||||
description: 'List dids and root cids of hosted repos',
|
||||
parameters: {
|
||||
type: 'params',
|
||||
properties: {
|
||||
limit: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
maximum: 1000,
|
||||
default: 500,
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['repos'],
|
||||
properties: {
|
||||
cursor: {
|
||||
type: 'string',
|
||||
},
|
||||
repos: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.sync.listRepos#repo',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
repo: {
|
||||
type: 'object',
|
||||
required: ['did', 'head'],
|
||||
properties: {
|
||||
did: {
|
||||
type: 'string',
|
||||
format: 'did',
|
||||
},
|
||||
head: {
|
||||
type: 'string',
|
||||
format: 'cid',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ComAtprotoSyncNotifyOfUpdate: {
|
||||
lexicon: 1,
|
||||
id: 'com.atproto.sync.notifyOfUpdate',
|
||||
@ -4657,6 +4796,7 @@ export const ids = {
|
||||
'com.atproto.admin.reverseModerationAction',
|
||||
ComAtprotoAdminSearchRepos: 'com.atproto.admin.searchRepos',
|
||||
ComAtprotoAdminTakeModerationAction: 'com.atproto.admin.takeModerationAction',
|
||||
ComAtprotoAdminUpdateAccountHandle: 'com.atproto.admin.updateAccountHandle',
|
||||
ComAtprotoIdentityResolveHandle: 'com.atproto.identity.resolveHandle',
|
||||
ComAtprotoIdentityUpdateHandle: 'com.atproto.identity.updateHandle',
|
||||
ComAtprotoLabelDefs: 'com.atproto.label.defs',
|
||||
@ -4675,6 +4815,7 @@ export const ids = {
|
||||
ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob',
|
||||
ComAtprotoServerCreateAccount: 'com.atproto.server.createAccount',
|
||||
ComAtprotoServerCreateInviteCode: 'com.atproto.server.createInviteCode',
|
||||
ComAtprotoServerCreateInviteCodes: 'com.atproto.server.createInviteCodes',
|
||||
ComAtprotoServerCreateSession: 'com.atproto.server.createSession',
|
||||
ComAtprotoServerDefs: 'com.atproto.server.defs',
|
||||
ComAtprotoServerDeleteAccount: 'com.atproto.server.deleteAccount',
|
||||
@ -4697,6 +4838,7 @@ export const ids = {
|
||||
ComAtprotoSyncGetRecord: 'com.atproto.sync.getRecord',
|
||||
ComAtprotoSyncGetRepo: 'com.atproto.sync.getRepo',
|
||||
ComAtprotoSyncListBlobs: 'com.atproto.sync.listBlobs',
|
||||
ComAtprotoSyncListRepos: 'com.atproto.sync.listRepos',
|
||||
ComAtprotoSyncNotifyOfUpdate: 'com.atproto.sync.notifyOfUpdate',
|
||||
ComAtprotoSyncRequestCrawl: 'com.atproto.sync.requestCrawl',
|
||||
ComAtprotoSyncSubscribeRepos: 'com.atproto.sync.subscribeRepos',
|
||||
|
@ -8,6 +8,7 @@ import { CID } from 'multiformats/cid'
|
||||
import * as ComAtprotoRepoStrongRef from '../repo/strongRef'
|
||||
import * as ComAtprotoModerationDefs from '../moderation/defs'
|
||||
import * as ComAtprotoServerDefs from '../server/defs'
|
||||
import * as ComAtprotoLabelDefs from '../label/defs'
|
||||
|
||||
export interface ActionView {
|
||||
id: number
|
||||
@ -195,6 +196,7 @@ export interface RepoViewDetail {
|
||||
relatedRecords: {}[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
invitedBy?: ComAtprotoServerDefs.InviteCode
|
||||
invites?: ComAtprotoServerDefs.InviteCode[]
|
||||
[k: string]: unknown
|
||||
@ -257,6 +259,7 @@ export interface RecordViewDetail {
|
||||
cid: string
|
||||
value: {}
|
||||
blobs: BlobView[]
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
repo: RepoView
|
||||
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* GENERATED CODE - DO NOT MODIFY
|
||||
*/
|
||||
import express from 'express'
|
||||
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
||||
import { lexicons } from '../../../../lexicons'
|
||||
import { isObj, hasProp } from '../../../../util'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { HandlerAuth } from '@atproto/xrpc-server'
|
||||
|
||||
export interface QueryParams {}
|
||||
|
||||
export interface InputSchema {
|
||||
did: string
|
||||
handle: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export interface HandlerInput {
|
||||
encoding: 'application/json'
|
||||
body: InputSchema
|
||||
}
|
||||
|
||||
export interface HandlerError {
|
||||
status: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
export type HandlerOutput = HandlerError | void
|
||||
export type Handler<HA extends HandlerAuth = never> = (ctx: {
|
||||
auth: HA
|
||||
params: QueryParams
|
||||
input: HandlerInput
|
||||
req: express.Request
|
||||
res: express.Response
|
||||
}) => Promise<HandlerOutput> | HandlerOutput
|
@ -7,7 +7,21 @@ import { isObj, hasProp } from '../../../../util'
|
||||
import { CID } from 'multiformats/cid'
|
||||
|
||||
/** Metadata tag on an atproto resource (eg, repo or record) */
|
||||
export interface Label {}
|
||||
export interface Label {
|
||||
/** DID of the actor who created this label */
|
||||
src: string
|
||||
/** AT URI of the record, repository (account), or other resource which this label applies to */
|
||||
uri: string
|
||||
/** optionally, CID specifying the specific version of 'uri' resource this label applies to */
|
||||
cid?: string
|
||||
/** the short string name of the value or type of this label */
|
||||
val: string
|
||||
/** if true, this is a negation label, overwriting a previous label */
|
||||
neg?: boolean
|
||||
/** timestamp when this label was created */
|
||||
cts: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export function isLabel(v: unknown): v is Label {
|
||||
return (
|
||||
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* GENERATED CODE - DO NOT MODIFY
|
||||
*/
|
||||
import express from 'express'
|
||||
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
||||
import { lexicons } from '../../../../lexicons'
|
||||
import { isObj, hasProp } from '../../../../util'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { HandlerAuth } from '@atproto/xrpc-server'
|
||||
|
||||
export interface QueryParams {}
|
||||
|
||||
export interface InputSchema {
|
||||
codeCount: number
|
||||
useCount: number
|
||||
forAccount?: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export interface OutputSchema {
|
||||
codes: string[]
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export interface HandlerInput {
|
||||
encoding: 'application/json'
|
||||
body: InputSchema
|
||||
}
|
||||
|
||||
export interface HandlerSuccess {
|
||||
encoding: 'application/json'
|
||||
body: OutputSchema
|
||||
}
|
||||
|
||||
export interface HandlerError {
|
||||
status: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
export type HandlerOutput = HandlerError | HandlerSuccess
|
||||
export type Handler<HA extends HandlerAuth = never> = (ctx: {
|
||||
auth: HA
|
||||
params: QueryParams
|
||||
input: HandlerInput
|
||||
req: express.Request
|
||||
res: express.Response
|
||||
}) => Promise<HandlerOutput> | HandlerOutput
|
@ -12,7 +12,7 @@ export interface QueryParams {}
|
||||
|
||||
export interface InputSchema {
|
||||
/** Handle or other identifier supported by the server for the authenticating user. */
|
||||
identifier?: string
|
||||
identifier: string
|
||||
password: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* GENERATED CODE - DO NOT MODIFY
|
||||
*/
|
||||
import express from 'express'
|
||||
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
||||
import { lexicons } from '../../../../lexicons'
|
||||
import { isObj, hasProp } from '../../../../util'
|
||||
import { CID } from 'multiformats/cid'
|
||||
import { HandlerAuth } from '@atproto/xrpc-server'
|
||||
|
||||
export interface QueryParams {
|
||||
limit: number
|
||||
cursor?: string
|
||||
}
|
||||
|
||||
export type InputSchema = undefined
|
||||
|
||||
export interface OutputSchema {
|
||||
cursor?: string
|
||||
repos: Repo[]
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export type HandlerInput = undefined
|
||||
|
||||
export interface HandlerSuccess {
|
||||
encoding: 'application/json'
|
||||
body: OutputSchema
|
||||
}
|
||||
|
||||
export interface HandlerError {
|
||||
status: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
export type HandlerOutput = HandlerError | HandlerSuccess
|
||||
export type Handler<HA extends HandlerAuth = never> = (ctx: {
|
||||
auth: HA
|
||||
params: QueryParams
|
||||
input: HandlerInput
|
||||
req: express.Request
|
||||
res: express.Response
|
||||
}) => Promise<HandlerOutput> | HandlerOutput
|
||||
|
||||
export interface Repo {
|
||||
did: string
|
||||
head: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export function isRepo(v: unknown): v is Repo {
|
||||
return (
|
||||
isObj(v) &&
|
||||
hasProp(v, '$type') &&
|
||||
v.$type === 'com.atproto.sync.listRepos#repo'
|
||||
)
|
||||
}
|
||||
|
||||
export function validateRepo(v: unknown): ValidationResult {
|
||||
return lexicons.validate('com.atproto.sync.listRepos#repo', v)
|
||||
}
|
@ -201,20 +201,20 @@ export async function generateMockSetup(env: DevEnv) {
|
||||
.insertInto('label')
|
||||
.values([
|
||||
{
|
||||
sourceDid: ctx.cfg.labelerDid,
|
||||
subjectUri: labeledPost.uri,
|
||||
subjectCid: labeledPost.cid,
|
||||
value: 'nudity',
|
||||
negated: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
src: ctx.cfg.labelerDid,
|
||||
uri: labeledPost.uri,
|
||||
cid: labeledPost.cid,
|
||||
val: 'nudity',
|
||||
neg: 0,
|
||||
cts: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
sourceDid: ctx.cfg.labelerDid,
|
||||
subjectUri: filteredPost.uri,
|
||||
subjectCid: filteredPost.cid,
|
||||
value: 'dmca-violation',
|
||||
negated: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
src: ctx.cfg.labelerDid,
|
||||
uri: filteredPost.uri,
|
||||
cid: filteredPost.cid,
|
||||
val: 'dmca-violation',
|
||||
neg: 0,
|
||||
cts: new Date().toISOString(),
|
||||
},
|
||||
])
|
||||
.execute()
|
||||
|
@ -14,6 +14,7 @@ export default function (server: Server, ctx: AppContext) {
|
||||
|
||||
const moderationAction = await db.transaction(async (dbTxn) => {
|
||||
const moderationTxn = services.moderation(dbTxn)
|
||||
const labelTxn = services.appView.label(dbTxn)
|
||||
const now = new Date()
|
||||
|
||||
const existing = await moderationTxn.getAction(id)
|
||||
@ -53,6 +54,16 @@ export default function (server: Server, ctx: AppContext) {
|
||||
})
|
||||
}
|
||||
|
||||
// invert creates & negates
|
||||
const negate = result.createLabelVals?.split(' ')
|
||||
const create = result.negateLabelVals?.split(' ')
|
||||
await labelTxn.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
result.subjectUri ?? result.subjectDid,
|
||||
result.subjectCid,
|
||||
{ create, negate },
|
||||
)
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { Server } from '../../../../lexicon'
|
||||
import AppContext from '../../../../context'
|
||||
import { TAKEDOWN } from '../../../../lexicon/types/com/atproto/admin/defs'
|
||||
import { getSubject, getAction } from '../moderation/util'
|
||||
import { InvalidRequestError } from '@atproto/xrpc-server'
|
||||
|
||||
export default function (server: Server, ctx: AppContext) {
|
||||
server.com.atproto.admin.takeModerationAction({
|
||||
@ -11,16 +12,29 @@ export default function (server: Server, ctx: AppContext) {
|
||||
handler: async ({ input }) => {
|
||||
const { db, services } = ctx
|
||||
const moderationService = services.moderation(db)
|
||||
const { action, subject, reason, createdBy, subjectBlobCids } = input.body
|
||||
const {
|
||||
action,
|
||||
subject,
|
||||
reason,
|
||||
createdBy,
|
||||
createLabelVals,
|
||||
negateLabelVals,
|
||||
subjectBlobCids,
|
||||
} = input.body
|
||||
|
||||
validateLabels([...(createLabelVals ?? []), ...(negateLabelVals ?? [])])
|
||||
|
||||
const moderationAction = await db.transaction(async (dbTxn) => {
|
||||
const authTxn = services.auth(dbTxn)
|
||||
const moderationTxn = services.moderation(dbTxn)
|
||||
const labelTxn = services.appView.label(dbTxn)
|
||||
|
||||
const result = await moderationTxn.logAction({
|
||||
action: getAction(action),
|
||||
subject: getSubject(subject),
|
||||
subjectBlobCids: subjectBlobCids?.map((cid) => CID.parse(cid)) ?? [],
|
||||
createLabelVals,
|
||||
negateLabelVals,
|
||||
createdBy,
|
||||
reason,
|
||||
})
|
||||
@ -49,6 +63,13 @@ export default function (server: Server, ctx: AppContext) {
|
||||
})
|
||||
}
|
||||
|
||||
await labelTxn.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
result.subjectUri ?? result.subjectDid,
|
||||
result.subjectCid,
|
||||
{ create: createLabelVals, negate: negateLabelVals },
|
||||
)
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
@ -59,3 +80,15 @@ export default function (server: Server, ctx: AppContext) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const validateLabels = (labels: string[]) => {
|
||||
for (const label of labels) {
|
||||
for (const char of badChars) {
|
||||
if (label.includes(char)) {
|
||||
throw new InvalidRequestError(`Invalid label: ${label}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const badChars = [' ', ',', ';', `'`, `"`]
|
||||
|
@ -2,6 +2,7 @@ import { AtUri } from '@atproto/uri'
|
||||
import Database from '../../../db'
|
||||
import { Label } from '../../../lexicon/types/com/atproto/label/defs'
|
||||
import { ids } from '../../../lexicon/lexicons'
|
||||
import { sql } from 'kysely'
|
||||
|
||||
export type Labels = Record<string, Label[]>
|
||||
|
||||
@ -12,34 +13,86 @@ export class LabelService {
|
||||
return (db: Database) => new LabelService(db)
|
||||
}
|
||||
|
||||
async getLabelsForSubjects(subjects: string[]): Promise<Labels> {
|
||||
async formatAndCreate(
|
||||
src: string,
|
||||
uri: string,
|
||||
cid: string | null,
|
||||
labels: { create?: string[]; negate?: string[] },
|
||||
) {
|
||||
const { create = [], negate = [] } = labels
|
||||
const toCreate = create.map((val) => ({
|
||||
src,
|
||||
uri,
|
||||
cid: cid ?? undefined,
|
||||
val,
|
||||
neg: false,
|
||||
cts: new Date().toISOString(),
|
||||
}))
|
||||
const toNegate = negate.map((val) => ({
|
||||
src,
|
||||
uri,
|
||||
cid: cid ?? undefined,
|
||||
val,
|
||||
neg: true,
|
||||
cts: new Date().toISOString(),
|
||||
}))
|
||||
await this.createLabels([...toCreate, ...toNegate])
|
||||
}
|
||||
|
||||
async createLabels(labels: Label[]) {
|
||||
if (labels.length < 1) return
|
||||
const dbVals = labels.map((l) => ({
|
||||
...l,
|
||||
cid: l.cid ?? '',
|
||||
neg: (l.neg ? 1 : 0) as 1 | 0,
|
||||
}))
|
||||
const { ref } = this.db.db.dynamic
|
||||
const excluded = (col: string) => ref(`excluded.${col}`)
|
||||
await this.db.db
|
||||
.insertInto('label')
|
||||
.values(dbVals)
|
||||
.onConflict((oc) =>
|
||||
oc.columns(['src', 'uri', 'cid', 'val']).doUpdateSet({
|
||||
neg: sql`${excluded('neg')}`,
|
||||
cts: sql`${excluded('cts')}`,
|
||||
}),
|
||||
)
|
||||
.execute()
|
||||
}
|
||||
|
||||
async getLabelsForSubjects(
|
||||
subjects: string[],
|
||||
includeNeg?: boolean,
|
||||
): Promise<Labels> {
|
||||
if (subjects.length < 1) return {}
|
||||
const res = await this.db.db
|
||||
.selectFrom('label')
|
||||
.where('label.subjectUri', 'in', subjects)
|
||||
.where('label.uri', 'in', subjects)
|
||||
.if(!includeNeg, (qb) => qb.where('neg', '=', 0))
|
||||
.selectAll()
|
||||
.execute()
|
||||
return res.reduce((acc, cur) => {
|
||||
acc[cur.subjectUri] ??= []
|
||||
acc[cur.subjectUri].push({
|
||||
src: cur.sourceDid,
|
||||
uri: cur.subjectUri,
|
||||
cid: cur.subjectCid ?? undefined,
|
||||
val: cur.value,
|
||||
neg: cur.negated === 1, // @TODO update in appview
|
||||
cts: cur.createdAt,
|
||||
acc[cur.uri] ??= []
|
||||
acc[cur.uri].push({
|
||||
...cur,
|
||||
cid: cur.cid === '' ? undefined : cur.cid,
|
||||
neg: cur.neg === 1, // @TODO update in appview
|
||||
})
|
||||
return acc
|
||||
}, {} as Labels)
|
||||
}
|
||||
|
||||
async getLabelsForProfiles(dids: string[]): Promise<Labels> {
|
||||
// gets labels for both did & profile record
|
||||
async getLabelsForProfiles(
|
||||
dids: string[],
|
||||
includeNeg?: boolean,
|
||||
): Promise<Labels> {
|
||||
if (dids.length < 1) return {}
|
||||
const profileUris = dids.map((did) =>
|
||||
AtUri.make(did, ids.AppBskyActorProfile, 'self').toString(),
|
||||
)
|
||||
const subjects = [...dids, ...profileUris]
|
||||
const labels = await this.getLabelsForSubjects(subjects)
|
||||
const labels = await this.getLabelsForSubjects(subjects, includeNeg)
|
||||
// combine labels for profile + did
|
||||
return Object.keys(labels).reduce((acc, cur) => {
|
||||
const did = cur.startsWith('at://') ? new AtUri(cur).hostname : cur
|
||||
@ -49,13 +102,16 @@ export class LabelService {
|
||||
}, {} as Labels)
|
||||
}
|
||||
|
||||
async getLabels(subject: string): Promise<Label[]> {
|
||||
const labels = await this.getLabelsForSubjects([subject])
|
||||
async getLabels(subject: string, includeNeg?: boolean): Promise<Label[]> {
|
||||
const labels = await this.getLabelsForSubjects([subject], includeNeg)
|
||||
return labels[subject] ?? []
|
||||
}
|
||||
|
||||
async getLabelsForProfile(did: string): Promise<Label[]> {
|
||||
const labels = await this.getLabelsForProfiles([did])
|
||||
async getLabelsForProfile(
|
||||
did: string,
|
||||
includeNeg?: boolean,
|
||||
): Promise<Label[]> {
|
||||
const labels = await this.getLabelsForProfiles([did], includeNeg)
|
||||
return labels[did] ?? []
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
import { Kysely } from 'kysely'
|
||||
|
||||
const moderationActionTable = 'moderation_action'
|
||||
|
||||
export async function up(db: Kysely<unknown>): Promise<void> {
|
||||
await db.schema
|
||||
.alterTable(moderationActionTable)
|
||||
.addColumn('createLabelVals', 'varchar')
|
||||
.execute()
|
||||
|
||||
await db.schema
|
||||
.alterTable(moderationActionTable)
|
||||
.addColumn('negateLabelVals', 'varchar')
|
||||
.execute()
|
||||
|
||||
await db.schema.dropTable('label').execute()
|
||||
|
||||
await db.schema
|
||||
.createTable('label')
|
||||
.addColumn('src', 'varchar', (col) => col.notNull())
|
||||
.addColumn('uri', 'varchar', (col) => col.notNull())
|
||||
.addColumn('cid', 'varchar', (col) => col.notNull())
|
||||
.addColumn('val', 'varchar', (col) => col.notNull())
|
||||
.addColumn('neg', 'int2', (col) => col.notNull()) // @TODO convert to boolean in appview
|
||||
.addColumn('cts', 'varchar', (col) => col.notNull())
|
||||
.addPrimaryKeyConstraint('label_pkey', ['src', 'uri', 'cid', 'val'])
|
||||
.execute()
|
||||
|
||||
await db.schema
|
||||
.createIndex('label_uri_index')
|
||||
.on('label')
|
||||
.column('uri')
|
||||
.execute()
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<unknown>): Promise<void> {
|
||||
await db.schema
|
||||
.alterTable(moderationActionTable)
|
||||
.dropColumn('createLabelVals')
|
||||
.execute()
|
||||
|
||||
await db.schema
|
||||
.alterTable(moderationActionTable)
|
||||
.dropColumn('negateLabelVals')
|
||||
.execute()
|
||||
|
||||
await db.schema.dropTable('label').execute()
|
||||
|
||||
await db.schema
|
||||
.createTable('label')
|
||||
.addColumn('sourceDid', 'varchar', (col) => col.notNull())
|
||||
.addColumn('subjectUri', 'varchar', (col) => col.notNull())
|
||||
.addColumn('subjectCid', 'varchar')
|
||||
.addColumn('value', 'varchar', (col) => col.notNull())
|
||||
.addColumn('negated', 'int2', (col) => col.notNull()) // @TODO convert to boolean in appview
|
||||
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
||||
.addPrimaryKeyConstraint('label_pkey', [
|
||||
'sourceDid',
|
||||
'subjectUri',
|
||||
'subjectCid',
|
||||
'value',
|
||||
])
|
||||
.execute()
|
||||
|
||||
await db.schema
|
||||
.createIndex('label_subject_uri_index')
|
||||
.on('label')
|
||||
.column('subjectUri')
|
||||
.execute()
|
||||
}
|
@ -38,3 +38,4 @@ export * as _20230328T214311005Z from './20230328T214311005Z-rework-seq'
|
||||
export * as _20230406T185855842Z from './20230406T185855842Z-feed-item-init'
|
||||
export * as _20230411T175730759Z from './20230411T175730759Z-drop-message-queue'
|
||||
export * as _20230411T180247652Z from './20230411T180247652Z-labels'
|
||||
export * as _20230412T231807162Z from './20230412T231807162Z-moderation-action-labels'
|
||||
|
@ -1,12 +1,12 @@
|
||||
export const tableName = 'label'
|
||||
|
||||
export interface Label {
|
||||
sourceDid: string
|
||||
subjectUri: string
|
||||
subjectCid: string | null
|
||||
value: string
|
||||
negated: 0 | 1 // @TODO convert to boolean in app-view
|
||||
createdAt: string
|
||||
src: string
|
||||
uri: string
|
||||
cid: string
|
||||
val: string
|
||||
neg: 0 | 1 // @TODO convert to boolean in app-view
|
||||
cts: string
|
||||
}
|
||||
|
||||
export type PartialDB = { [tableName]: Label }
|
||||
|
@ -21,6 +21,8 @@ export interface ModerationAction {
|
||||
subjectDid: string
|
||||
subjectUri: string | null
|
||||
subjectCid: string | null
|
||||
createLabelVals: string | null
|
||||
negateLabelVals: string | null
|
||||
reason: string
|
||||
createdAt: string
|
||||
createdBy: string
|
||||
|
@ -37,13 +37,13 @@ export abstract class Labeler {
|
||||
const labels = await this.labelRecord(obj)
|
||||
if (labels.length < 1) return
|
||||
const cid = await cidForRecord(obj)
|
||||
const rows = labels.map((value) => ({
|
||||
sourceDid: this.labelerDid,
|
||||
subjectUri: uri.toString(),
|
||||
subjectCid: cid.toString(),
|
||||
value,
|
||||
negated: 0 as const,
|
||||
createdAt: new Date().toISOString(),
|
||||
const rows = labels.map((val) => ({
|
||||
src: this.labelerDid,
|
||||
uri: uri.toString(),
|
||||
cid: cid.toString(),
|
||||
val,
|
||||
neg: 0 as const,
|
||||
cts: new Date().toISOString(),
|
||||
}))
|
||||
|
||||
await this.db.db
|
||||
|
@ -364,6 +364,13 @@ export const schemaDict = {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.admin.defs#moderationDetail',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
invitedBy: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.server.defs#inviteCode',
|
||||
@ -461,6 +468,13 @@ export const schemaDict = {
|
||||
ref: 'lex:com.atproto.admin.defs#blobView',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'ref',
|
||||
ref: 'lex:com.atproto.label.defs#label',
|
||||
},
|
||||
},
|
||||
indexedAt: {
|
||||
type: 'string',
|
||||
format: 'datetime',
|
||||
@ -1055,7 +1069,7 @@ export const schemaDict = {
|
||||
defs: {
|
||||
main: {
|
||||
type: 'procedure',
|
||||
description: 'Administrative action to update an accounts handle',
|
||||
description: "Administrative action to update an account's handle",
|
||||
input: {
|
||||
encoding: 'application/json',
|
||||
schema: {
|
||||
|
@ -8,6 +8,7 @@ import { CID } from 'multiformats/cid'
|
||||
import * as ComAtprotoRepoStrongRef from '../repo/strongRef'
|
||||
import * as ComAtprotoModerationDefs from '../moderation/defs'
|
||||
import * as ComAtprotoServerDefs from '../server/defs'
|
||||
import * as ComAtprotoLabelDefs from '../label/defs'
|
||||
|
||||
export interface ActionView {
|
||||
id: number
|
||||
@ -195,6 +196,7 @@ export interface RepoViewDetail {
|
||||
relatedRecords: {}[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
invitedBy?: ComAtprotoServerDefs.InviteCode
|
||||
invites?: ComAtprotoServerDefs.InviteCode[]
|
||||
[k: string]: unknown
|
||||
@ -257,6 +259,7 @@ export interface RecordViewDetail {
|
||||
cid: string
|
||||
value: {}
|
||||
blobs: BlobView[]
|
||||
labels?: ComAtprotoLabelDefs.Label[]
|
||||
indexedAt: string
|
||||
moderation: ModerationDetail
|
||||
repo: RepoView
|
||||
|
@ -178,6 +178,8 @@ export class ModerationService {
|
||||
subject: { did: string } | { uri: AtUri; cid: CID }
|
||||
subjectBlobCids?: CID[]
|
||||
reason: string
|
||||
createLabelVals?: string[]
|
||||
negateLabelVals?: string[]
|
||||
createdBy: string
|
||||
createdAt?: Date
|
||||
}): Promise<ModerationActionRow> {
|
||||
@ -190,6 +192,8 @@ export class ModerationService {
|
||||
subjectBlobCids,
|
||||
createdAt = new Date(),
|
||||
} = info
|
||||
const createLabelVals = info.createLabelVals?.join()
|
||||
const negateLabelVals = info.negateLabelVals?.join()
|
||||
|
||||
// Resolve subject info
|
||||
let subjectInfo: SubjectInfo
|
||||
@ -248,6 +252,8 @@ export class ModerationService {
|
||||
reason,
|
||||
createdAt: createdAt.toISOString(),
|
||||
createdBy,
|
||||
createLabelVals,
|
||||
negateLabelVals,
|
||||
...subjectInfo,
|
||||
})
|
||||
.returningAll()
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
BlobView,
|
||||
} from '../../lexicon/types/com/atproto/admin/defs'
|
||||
import { OutputSchema as ReportOutput } from '../../lexicon/types/com/atproto/moderation/createReport'
|
||||
import { Label } from '../../lexicon/types/com/atproto/label/defs'
|
||||
import { ModerationAction, ModerationReport } from '../../db/tables/moderation'
|
||||
import { AccountService } from '../account'
|
||||
import { RecordService } from '../record'
|
||||
@ -127,9 +128,10 @@ export class ModerationViews {
|
||||
.execute(),
|
||||
this.services.account(this.db).getAccountInviteCodes(repo.did),
|
||||
])
|
||||
const [reports, actions] = await Promise.all([
|
||||
const [reports, actions, labels] = await Promise.all([
|
||||
this.report(reportResults),
|
||||
this.action(actionResults),
|
||||
this.labels(repo.did),
|
||||
])
|
||||
return {
|
||||
...repo,
|
||||
@ -139,6 +141,7 @@ export class ModerationViews {
|
||||
actions,
|
||||
},
|
||||
invites: inviteCodes,
|
||||
labels,
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,10 +242,11 @@ export class ModerationViews {
|
||||
.selectAll()
|
||||
.execute(),
|
||||
])
|
||||
const [reports, actions, blobs] = await Promise.all([
|
||||
const [reports, actions, blobs, labels] = await Promise.all([
|
||||
this.report(reportResults),
|
||||
this.action(actionResults),
|
||||
this.blob(record.blobCids),
|
||||
this.labels(record.uri),
|
||||
])
|
||||
return {
|
||||
...record,
|
||||
@ -252,6 +256,7 @@ export class ModerationViews {
|
||||
reports,
|
||||
actions,
|
||||
},
|
||||
labels,
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +319,8 @@ export class ModerationViews {
|
||||
reason: res.reason,
|
||||
createdAt: res.createdAt,
|
||||
createdBy: res.createdBy,
|
||||
createLabelVals: res.createLabelVals?.split(' '),
|
||||
negateLabelVals: res.negateLabelVals?.split(' '),
|
||||
reversal:
|
||||
res.reversedAt !== null &&
|
||||
res.reversedBy !== null &&
|
||||
@ -350,6 +357,8 @@ export class ModerationViews {
|
||||
action: action.action,
|
||||
subject,
|
||||
subjectBlobs,
|
||||
createLabelVals: action.createLabelVals,
|
||||
negateLabelVals: action.negateLabelVals,
|
||||
reason: action.reason,
|
||||
createdAt: action.createdAt,
|
||||
createdBy: action.createdBy,
|
||||
@ -538,6 +547,21 @@ export class ModerationViews {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// @TODO: call into label service instead on AppView
|
||||
async labels(subject: string, includeNeg?: boolean): Promise<Label[]> {
|
||||
const res = await this.db.db
|
||||
.selectFrom('label')
|
||||
.where('label.uri', '=', subject)
|
||||
.if(!includeNeg, (qb) => qb.where('neg', '=', 0))
|
||||
.selectAll()
|
||||
.execute()
|
||||
return res.map((l) => ({
|
||||
...l,
|
||||
cid: l.cid === '' ? undefined : l.cid,
|
||||
neg: l.neg === 1,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
type RepoResult = DidHandle & RepoRoot
|
||||
|
@ -5,7 +5,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "Y",
|
||||
"resolvedReportIds": Array [
|
||||
6,
|
||||
|
@ -33,7 +33,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -47,7 +55,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -622,7 +638,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -821,7 +845,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -982,7 +1014,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
|
@ -128,11 +128,12 @@ describe('labeler', () => {
|
||||
await ctx.db.db
|
||||
.insertInto('label')
|
||||
.values({
|
||||
sourceDid: labelerDid,
|
||||
subjectUri: aliceDid,
|
||||
value: 'repo-label',
|
||||
negated: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
src: labelerDid,
|
||||
uri: aliceDid,
|
||||
cid: '',
|
||||
val: 'repo-label',
|
||||
neg: 0,
|
||||
cts: new Date().toISOString(),
|
||||
})
|
||||
.execute()
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AtpAgent from '@atproto/api'
|
||||
import AtpAgent, { ComAtprotoAdminTakeModerationAction } from '@atproto/api'
|
||||
import { AtUri } from '@atproto/uri'
|
||||
import {
|
||||
adminAuth,
|
||||
@ -696,6 +696,172 @@ describe('moderation', () => {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
it('negates an existing label and reverses.', async () => {
|
||||
const { ctx } = server
|
||||
const post = sc.posts[sc.dids.bob][0].ref
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
post.uriStr,
|
||||
post.cidStr,
|
||||
{ create: ['kittens'] },
|
||||
)
|
||||
const action = await actionWithLabels({
|
||||
negateLabelVals: ['kittens'],
|
||||
subject: {
|
||||
$type: 'com.atproto.repo.strongRef',
|
||||
uri: post.uriStr,
|
||||
cid: post.cidStr,
|
||||
},
|
||||
})
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
|
||||
await reverse(action.id)
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['kittens'])
|
||||
// Cleanup
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
post.uriStr,
|
||||
post.cidStr,
|
||||
{ negate: ['kittens'] },
|
||||
)
|
||||
})
|
||||
|
||||
it('no-ops when negating an already-negated label and reverses.', async () => {
|
||||
const { ctx } = server
|
||||
const post = sc.posts[sc.dids.bob][0].ref
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
const action = await actionWithLabels({
|
||||
negateLabelVals: ['bears'],
|
||||
subject: {
|
||||
$type: 'com.atproto.repo.strongRef',
|
||||
uri: post.uriStr,
|
||||
cid: post.cidStr,
|
||||
},
|
||||
})
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
|
||||
await reverse(action.id)
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['bears'])
|
||||
// Cleanup
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
post.uriStr,
|
||||
post.cidStr,
|
||||
{ negate: ['bears'] },
|
||||
)
|
||||
})
|
||||
|
||||
it('creates a non-existing label and reverses.', async () => {
|
||||
const post = sc.posts[sc.dids.bob][0].ref
|
||||
const action = await actionWithLabels({
|
||||
createLabelVals: ['puppies'],
|
||||
subject: {
|
||||
$type: 'com.atproto.repo.strongRef',
|
||||
uri: post.uriStr,
|
||||
cid: post.cidStr,
|
||||
},
|
||||
})
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['puppies'])
|
||||
await reverse(action.id)
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
|
||||
})
|
||||
|
||||
it('no-ops when creating an existing label and reverses.', async () => {
|
||||
const { ctx } = server
|
||||
const post = sc.posts[sc.dids.bob][0].ref
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
post.uriStr,
|
||||
post.cidStr,
|
||||
{ create: ['birds'] },
|
||||
)
|
||||
const action = await actionWithLabels({
|
||||
createLabelVals: ['birds'],
|
||||
subject: {
|
||||
$type: 'com.atproto.repo.strongRef',
|
||||
uri: post.uriStr,
|
||||
cid: post.cidStr,
|
||||
},
|
||||
})
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['birds'])
|
||||
await reverse(action.id)
|
||||
await expect(getRecordLabels(post.uriStr)).resolves.toEqual([])
|
||||
})
|
||||
|
||||
it('creates and negates labels on a repo.', async () => {
|
||||
const { ctx } = server
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
sc.dids.bob,
|
||||
null,
|
||||
{ create: ['kittens'] },
|
||||
)
|
||||
const action = await actionWithLabels({
|
||||
createLabelVals: ['puppies'],
|
||||
negateLabelVals: ['kittens'],
|
||||
subject: {
|
||||
$type: 'com.atproto.admin.defs#repoRef',
|
||||
did: sc.dids.bob,
|
||||
},
|
||||
})
|
||||
await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['puppies'])
|
||||
await reverse(action.id)
|
||||
await expect(getRepoLabels(sc.dids.bob)).resolves.toEqual(['kittens'])
|
||||
})
|
||||
|
||||
async function actionWithLabels(
|
||||
opts: Partial<ComAtprotoAdminTakeModerationAction.InputSchema> & {
|
||||
subject: ComAtprotoAdminTakeModerationAction.InputSchema['subject']
|
||||
},
|
||||
) {
|
||||
const result = await agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
createdBy: 'did:example:admin',
|
||||
reason: 'Y',
|
||||
...opts,
|
||||
},
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
return result.data
|
||||
}
|
||||
|
||||
async function reverse(actionId: number) {
|
||||
await agent.api.com.atproto.admin.reverseModerationAction(
|
||||
{
|
||||
id: actionId,
|
||||
createdBy: 'did:example:admin',
|
||||
reason: 'Y',
|
||||
},
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async function getRecordLabels(uri: string) {
|
||||
const result = await agent.api.com.atproto.admin.getRecord(
|
||||
{ uri },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
const labels = result.data.labels ?? []
|
||||
return labels.map((l) => l.val)
|
||||
}
|
||||
|
||||
async function getRepoLabels(did: string) {
|
||||
const result = await agent.api.com.atproto.admin.getRepo(
|
||||
{ did },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
const labels = result.data.labels ?? []
|
||||
return labels.map((l) => l.val)
|
||||
}
|
||||
})
|
||||
|
||||
describe('blob takedown', () => {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { ids } from '../../src/lexicon/lexicons'
|
||||
import { FLAG } from '../../src/lexicon/types/com/atproto/admin/defs'
|
||||
import { adminAuth } from '../_util'
|
||||
import { SeedClient } from './client'
|
||||
import usersSeed from './users'
|
||||
|
||||
@ -99,6 +101,23 @@ export default async (sc: SeedClient) => {
|
||||
await sc.repost(carol, sc.posts[dan][1].ref)
|
||||
await sc.repost(dan, sc.posts[alice][1].ref)
|
||||
|
||||
await sc.agent.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: FLAG,
|
||||
subject: {
|
||||
$type: 'com.atproto.admin.defs#repoRef',
|
||||
did: dan,
|
||||
},
|
||||
createdBy: 'did:example:admin',
|
||||
reason: 'test',
|
||||
createLabelVals: ['repo-action-label'],
|
||||
},
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: { authorization: adminAuth() },
|
||||
},
|
||||
)
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(2)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(2)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(8)",
|
||||
"muted": false,
|
||||
@ -594,7 +602,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -1008,7 +1024,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -1021,7 +1045,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -1178,7 +1210,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -1208,7 +1248,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(1)",
|
||||
"muted": true,
|
||||
@ -1366,7 +1414,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(1)",
|
||||
"muted": true,
|
||||
@ -1572,7 +1628,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(2)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(2)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
|
@ -20,7 +20,15 @@ Object {
|
||||
"actor": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
|
@ -40,7 +40,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -146,7 +154,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -605,7 +621,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -745,7 +769,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -905,7 +937,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -1046,7 +1086,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -1186,7 +1234,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
@ -1346,7 +1402,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(6)",
|
||||
"muted": false,
|
||||
|
@ -8,7 +8,15 @@ Object {
|
||||
"followsCount": 1,
|
||||
"handle": "dan.test",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"postsCount": 2,
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
@ -76,7 +84,15 @@ Array [
|
||||
"followersCount": 1,
|
||||
"followsCount": 1,
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(3)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"postsCount": 2,
|
||||
"viewer": Object {
|
||||
"followedBy": "record(4)",
|
||||
@ -112,7 +128,15 @@ Object {
|
||||
"followersCount": 1,
|
||||
"followsCount": 1,
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"postsCount": 2,
|
||||
"viewer": Object {
|
||||
"followedBy": "record(0)",
|
||||
|
@ -13,7 +13,15 @@ Array [
|
||||
Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(0)",
|
||||
"muted": false,
|
||||
@ -68,7 +76,15 @@ Array [
|
||||
Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(0)",
|
||||
"muted": false,
|
||||
|
@ -17,165 +17,37 @@ Array [
|
||||
"cid": "cids(0)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 0,
|
||||
"likeCount": 3,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"reply": Object {
|
||||
"parent": Object {
|
||||
"cid": "cids(2)",
|
||||
"uri": "record(2)",
|
||||
},
|
||||
"root": Object {
|
||||
"cid": "cids(1)",
|
||||
"uri": "record(1)",
|
||||
},
|
||||
},
|
||||
"text": "thanks bob",
|
||||
"text": "again",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"replyCount": 2,
|
||||
"repostCount": 1,
|
||||
"uri": "record(0)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"reason": Object {
|
||||
"$type": "app.bsky.feed.defs#reasonRepost",
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "carol.test",
|
||||
"labels": Array [],
|
||||
"handle": "dan.test",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(5)",
|
||||
"following": "record(4)",
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(3)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 0,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"reply": Object {
|
||||
"parent": Object {
|
||||
"cid": "cids(1)",
|
||||
"uri": "record(1)",
|
||||
},
|
||||
"root": Object {
|
||||
"cid": "cids(1)",
|
||||
"uri": "record(1)",
|
||||
},
|
||||
},
|
||||
"text": "of course",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(3)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
"reply": Object {
|
||||
"parent": Object {
|
||||
"author": Object {
|
||||
"avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg",
|
||||
"did": "user(0)",
|
||||
"displayName": "ali",
|
||||
"handle": "alice.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(1)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 3,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "again",
|
||||
},
|
||||
"replyCount": 2,
|
||||
"repostCount": 1,
|
||||
"uri": "record(1)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
"root": Object {
|
||||
"author": Object {
|
||||
"avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg",
|
||||
"did": "user(0)",
|
||||
"displayName": "ali",
|
||||
"handle": "alice.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(1)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 3,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "again",
|
||||
},
|
||||
"replyCount": 2,
|
||||
"repostCount": 1,
|
||||
"uri": "record(1)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg",
|
||||
"did": "user(0)",
|
||||
"displayName": "ali",
|
||||
"handle": "alice.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(4)",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record#view",
|
||||
"record": Object {
|
||||
"$type": "app.bsky.embed.record#viewNotFound",
|
||||
"uri": "record(7)",
|
||||
},
|
||||
},
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cid": "cids(4)",
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "record(6)",
|
||||
"val": "test-label",
|
||||
},
|
||||
],
|
||||
"likeCount": 2,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record",
|
||||
"record": Object {
|
||||
"cid": "cids(5)",
|
||||
"uri": "record(7)",
|
||||
},
|
||||
},
|
||||
"text": "yoohoo label_me",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(6)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
@ -193,105 +65,26 @@ Array [
|
||||
"cid": "cids(1)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 3,
|
||||
"likeCount": 0,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "again",
|
||||
},
|
||||
"replyCount": 2,
|
||||
"repostCount": 1,
|
||||
"uri": "record(1)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "carol.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(5)",
|
||||
"following": "record(4)",
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(6)",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.recordWithMedia#view",
|
||||
"media": Object {
|
||||
"$type": "app.bsky.embed.images#view",
|
||||
"images": Array [
|
||||
Object {
|
||||
"alt": "tests/image/fixtures/key-landscape-small.jpg",
|
||||
"fullsize": "https://pds.public.url/image/AiDXkxVbgBksxb1nfiRn1m6S4K8_mee6o8r-UGLNzOM/rs:fit:2000:2000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg",
|
||||
"thumb": "https://pds.public.url/image/uc7FGfiGv0mMqmk9XiqHXrIhNymLHaex7Ge8nEhmXqo/rs:fit:1000:1000:1:0/plain/bafkreigy5p3xxceipk2o6nqtnugpft26ol6yleqhboqziino7axvdngtci@jpeg",
|
||||
},
|
||||
Object {
|
||||
"alt": "tests/image/fixtures/key-alt.jpg",
|
||||
"fullsize": "https://pds.public.url/image/xC2No-8rKVDIwIMmCiEBm9EiGLDBBOpf36PHoGf-GDw/rs:fit:2000:2000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg",
|
||||
"thumb": "https://pds.public.url/image/g7yazUpNwN8LKumZ2Zmn_ptQbtMLs1Pti5-GDn7H8_8/rs:fit:1000:1000:1:0/plain/bafkreifdklbbcdsyanjz3oqe5pf2omuq5ansthokxlbleagg3eenx62h7e@jpeg",
|
||||
},
|
||||
],
|
||||
},
|
||||
"record": Object {
|
||||
"record": Object {
|
||||
"$type": "app.bsky.embed.record#viewNotFound",
|
||||
"uri": "record(9)",
|
||||
"reply": Object {
|
||||
"parent": Object {
|
||||
"cid": "cids(2)",
|
||||
"uri": "record(3)",
|
||||
},
|
||||
"root": Object {
|
||||
"cid": "cids(0)",
|
||||
"uri": "record(0)",
|
||||
},
|
||||
},
|
||||
},
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 2,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.recordWithMedia",
|
||||
"media": Object {
|
||||
"$type": "app.bsky.embed.images",
|
||||
"images": Array [
|
||||
Object {
|
||||
"alt": "tests/image/fixtures/key-landscape-small.jpg",
|
||||
"image": Object {
|
||||
"$type": "blob",
|
||||
"mimeType": "image/jpeg",
|
||||
"ref": Object {
|
||||
"$link": "cids(7)",
|
||||
},
|
||||
"size": 4114,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"alt": "tests/image/fixtures/key-alt.jpg",
|
||||
"image": Object {
|
||||
"$type": "blob",
|
||||
"mimeType": "image/jpeg",
|
||||
"ref": Object {
|
||||
"$link": "cids(8)",
|
||||
},
|
||||
"size": 12736,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
"record": Object {
|
||||
"record": Object {
|
||||
"cid": "cids(9)",
|
||||
"uri": "record(9)",
|
||||
},
|
||||
},
|
||||
},
|
||||
"text": "hi im carol",
|
||||
"text": "thanks bob",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(8)",
|
||||
"viewer": Object {
|
||||
"like": "record(10)",
|
||||
},
|
||||
"uri": "record(2)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
@ -306,7 +99,234 @@ Array [
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(10)",
|
||||
"cid": "cids(3)",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record#view",
|
||||
"record": Object {
|
||||
"$type": "app.bsky.embed.record#viewRecord",
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(4)",
|
||||
"embeds": Array [
|
||||
Object {
|
||||
"$type": "app.bsky.embed.record#view",
|
||||
"record": Object {
|
||||
"$type": "app.bsky.embed.record#viewNotFound",
|
||||
"uri": "record(6)",
|
||||
},
|
||||
},
|
||||
],
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"uri": "record(5)",
|
||||
"value": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record",
|
||||
"record": Object {
|
||||
"cid": "cids(5)",
|
||||
"uri": "record(6)",
|
||||
},
|
||||
},
|
||||
"facets": Array [
|
||||
Object {
|
||||
"features": Array [
|
||||
Object {
|
||||
"$type": "app.bsky.richtext.facet#mention",
|
||||
"did": "user(0)",
|
||||
},
|
||||
],
|
||||
"index": Object {
|
||||
"byteEnd": 18,
|
||||
"byteStart": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
"text": "@alice.bluesky.xyz is the best",
|
||||
},
|
||||
},
|
||||
},
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cid": "cids(3)",
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "record(4)",
|
||||
"val": "test-label",
|
||||
},
|
||||
],
|
||||
"likeCount": 2,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record",
|
||||
"record": Object {
|
||||
"cid": "cids(4)",
|
||||
"uri": "record(5)",
|
||||
},
|
||||
},
|
||||
"text": "yoohoo label_me",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(4)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg",
|
||||
"did": "user(0)",
|
||||
"displayName": "ali",
|
||||
"handle": "alice.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(0)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 3,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "again",
|
||||
},
|
||||
"replyCount": 2,
|
||||
"repostCount": 1,
|
||||
"uri": "record(0)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(4)",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record#view",
|
||||
"record": Object {
|
||||
"$type": "app.bsky.embed.record#viewNotFound",
|
||||
"uri": "record(6)",
|
||||
},
|
||||
},
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 0,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"embed": Object {
|
||||
"$type": "app.bsky.embed.record",
|
||||
"record": Object {
|
||||
"cid": "cids(5)",
|
||||
"uri": "record(6)",
|
||||
},
|
||||
},
|
||||
"facets": Array [
|
||||
Object {
|
||||
"features": Array [
|
||||
Object {
|
||||
"$type": "app.bsky.richtext.facet#mention",
|
||||
"did": "user(0)",
|
||||
},
|
||||
],
|
||||
"index": Object {
|
||||
"byteEnd": 18,
|
||||
"byteStart": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
"text": "@alice.bluesky.xyz is the best",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 1,
|
||||
"uri": "record(5)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(6)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 0,
|
||||
"record": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "dan here!",
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(7)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"post": Object {
|
||||
"author": Object {
|
||||
"avatar": "https://pds.public.url/image/KzkHFsMRQ6oAKCHCRKFA1H-rDdc7VOtvEVpUJ82TwyQ/rs:fill:1000:1000:1:0/plain/bafkreiaivizp4xldojmmpuzmiu75cmea7nq56dnntnuhzhsjcb63aou5ei@jpeg",
|
||||
"did": "user(0)",
|
||||
"displayName": "ali",
|
||||
"handle": "alice.test",
|
||||
"labels": Array [],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
},
|
||||
"cid": "cids(7)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"likeCount": 0,
|
||||
@ -317,7 +337,7 @@ Array [
|
||||
},
|
||||
"replyCount": 0,
|
||||
"repostCount": 0,
|
||||
"uri": "record(11)",
|
||||
"uri": "record(8)",
|
||||
"viewer": Object {},
|
||||
},
|
||||
},
|
||||
@ -357,7 +377,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -612,7 +640,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -852,7 +888,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -866,7 +910,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -1441,7 +1493,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -1640,7 +1700,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -1801,7 +1869,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -2015,7 +2091,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(1)",
|
||||
"muted": false,
|
||||
@ -2605,7 +2689,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"followedBy": "record(1)",
|
||||
"muted": false,
|
||||
@ -2992,7 +3084,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -3433,7 +3533,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(0)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -3774,7 +3882,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -3974,7 +4090,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -4131,7 +4255,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"muted": false,
|
||||
},
|
||||
@ -4224,7 +4356,15 @@ Array [
|
||||
"by": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -4406,7 +4546,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -4567,7 +4715,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
@ -4728,7 +4884,15 @@ Array [
|
||||
"author": Object {
|
||||
"did": "user(1)",
|
||||
"handle": "dan.test",
|
||||
"labels": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(1)",
|
||||
"val": "repo-action-label",
|
||||
},
|
||||
],
|
||||
"viewer": Object {
|
||||
"following": "record(1)",
|
||||
"muted": false,
|
||||
|
@ -5,7 +5,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReports": Array [
|
||||
Object {
|
||||
@ -15,8 +15,8 @@ Object {
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -33,7 +33,7 @@ Object {
|
||||
"moderation": Object {
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
},
|
||||
},
|
||||
"repo": Object {
|
||||
@ -76,7 +76,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReports": Array [
|
||||
Object {
|
||||
@ -86,8 +86,8 @@ Object {
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -101,7 +101,7 @@ Object {
|
||||
"reasonType": "com.atproto.moderation.defs#reasonSpam",
|
||||
"reportedBy": "user(2)",
|
||||
"resolvedByActionIds": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
|
@ -6,7 +6,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
1,
|
||||
@ -32,7 +32,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 5,
|
||||
"id": 6,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
3,
|
||||
@ -47,7 +47,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -61,7 +61,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
1,
|
||||
@ -87,7 +87,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 6,
|
||||
"id": 7,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -100,7 +100,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 5,
|
||||
"id": 6,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
3,
|
||||
@ -115,7 +115,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 4,
|
||||
"id": 5,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -129,7 +129,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 3,
|
||||
"id": 4,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -143,7 +143,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -157,7 +157,7 @@ Array [
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
1,
|
||||
@ -174,5 +174,21 @@ Array [
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createLabelVals": Array [
|
||||
"repo-action-label",
|
||||
],
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"reason": "test",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "user(2)",
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -12,7 +12,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
2,
|
||||
@ -28,7 +28,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
2,
|
||||
@ -54,7 +54,7 @@ Object {
|
||||
"moderation": Object {
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
},
|
||||
},
|
||||
"repo": Object {
|
||||
@ -102,7 +102,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#flag",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [
|
||||
2,
|
||||
|
@ -8,7 +8,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -27,7 +27,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
5,
|
||||
6,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
@ -52,7 +52,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -82,7 +82,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [
|
||||
5,
|
||||
6,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
@ -107,7 +107,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -133,7 +133,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -152,7 +152,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
5,
|
||||
6,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
@ -165,7 +165,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
3,
|
||||
4,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
@ -179,7 +179,7 @@ Array [
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(0)",
|
||||
"resolvedByActionIds": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
|
@ -6,13 +6,14 @@ Object {
|
||||
"blobs": Array [],
|
||||
"cid": "cids(0)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"moderation": Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -26,7 +27,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"reversal": Object {
|
||||
@ -44,7 +45,7 @@ Object {
|
||||
],
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
},
|
||||
"reports": Array [
|
||||
Object {
|
||||
@ -113,13 +114,14 @@ Object {
|
||||
"blobs": Array [],
|
||||
"cid": "cids(0)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [],
|
||||
"moderation": Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -133,7 +135,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"reversal": Object {
|
||||
@ -151,7 +153,132 @@ Object {
|
||||
],
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
},
|
||||
"reports": Array [
|
||||
Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"id": 2,
|
||||
"reason": "defamation",
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"cid": "cids(0)",
|
||||
"uri": "record(0)",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"id": 1,
|
||||
"reasonType": "com.atproto.moderation.defs#reasonSpam",
|
||||
"reportedBy": "user(2)",
|
||||
"resolvedByActionIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"cid": "cids(0)",
|
||||
"uri": "record(0)",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
"repo": Object {
|
||||
"account": Object {
|
||||
"email": "alice@test.com",
|
||||
},
|
||||
"did": "user(0)",
|
||||
"handle": "alice.test",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"moderation": Object {},
|
||||
"relatedRecords": Array [
|
||||
Object {
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"avatar": Object {
|
||||
"$type": "blob",
|
||||
"mimeType": "image/jpeg",
|
||||
"ref": Object {
|
||||
"$link": "cids(1)",
|
||||
},
|
||||
"size": 3976,
|
||||
},
|
||||
"description": "its me!",
|
||||
"displayName": "ali",
|
||||
},
|
||||
],
|
||||
},
|
||||
"uri": "record(0)",
|
||||
"value": Object {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"text": "hey there",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`pds admin get record view serves labels. 1`] = `
|
||||
Object {
|
||||
"blobCids": Array [],
|
||||
"blobs": Array [],
|
||||
"cid": "cids(0)",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cid": "cids(0)",
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "record(0)",
|
||||
"val": "kittens",
|
||||
},
|
||||
Object {
|
||||
"cid": "cids(0)",
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "record(0)",
|
||||
"val": "puppies",
|
||||
},
|
||||
],
|
||||
"moderation": Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"cid": "cids(0)",
|
||||
"uri": "record(0)",
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"reversal": Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"reason": "X",
|
||||
},
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.repo.strongRef",
|
||||
"cid": "cids(0)",
|
||||
"uri": "record(0)",
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
],
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 3,
|
||||
},
|
||||
"reports": Array [
|
||||
Object {
|
||||
|
@ -9,13 +9,14 @@ Object {
|
||||
"handle": "alice.test",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"invites": Array [],
|
||||
"labels": Array [],
|
||||
"moderation": Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
@ -28,7 +29,7 @@ Object {
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 1,
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"reversal": Object {
|
||||
@ -45,7 +46,114 @@ Object {
|
||||
],
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
},
|
||||
"reports": Array [
|
||||
Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"id": 2,
|
||||
"reason": "defamation",
|
||||
"reasonType": "com.atproto.moderation.defs#reasonOther",
|
||||
"reportedBy": "user(1)",
|
||||
"resolvedByActionIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "user(0)",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"id": 1,
|
||||
"reasonType": "com.atproto.moderation.defs#reasonSpam",
|
||||
"reportedBy": "user(2)",
|
||||
"resolvedByActionIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "user(0)",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
"relatedRecords": Array [
|
||||
Object {
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"avatar": Object {
|
||||
"$type": "blob",
|
||||
"mimeType": "image/jpeg",
|
||||
"ref": Object {
|
||||
"$link": "cids(0)",
|
||||
},
|
||||
"size": 3976,
|
||||
},
|
||||
"description": "its me!",
|
||||
"displayName": "ali",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`pds admin get repo view serves labels. 1`] = `
|
||||
Object {
|
||||
"account": Object {
|
||||
"email": "alice@test.com",
|
||||
},
|
||||
"did": "user(0)",
|
||||
"handle": "alice.test",
|
||||
"indexedAt": "1970-01-01T00:00:00.000Z",
|
||||
"invites": Array [],
|
||||
"labels": Array [
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "kittens",
|
||||
},
|
||||
Object {
|
||||
"cts": "1970-01-01T00:00:00.000Z",
|
||||
"neg": false,
|
||||
"src": "did:example:labeler",
|
||||
"uri": "user(0)",
|
||||
"val": "puppies",
|
||||
},
|
||||
],
|
||||
"moderation": Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 3,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "user(0)",
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
Object {
|
||||
"action": "com.atproto.admin.defs#acknowledge",
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"id": 2,
|
||||
"reason": "X",
|
||||
"resolvedReportIds": Array [],
|
||||
"reversal": Object {
|
||||
"createdAt": "1970-01-01T00:00:00.000Z",
|
||||
"createdBy": "did:example:admin",
|
||||
"reason": "X",
|
||||
},
|
||||
"subject": Object {
|
||||
"$type": "com.atproto.admin.defs#repoRef",
|
||||
"did": "user(0)",
|
||||
},
|
||||
"subjectBlobCids": Array [],
|
||||
},
|
||||
],
|
||||
"currentAction": Object {
|
||||
"action": "com.atproto.admin.defs#takedown",
|
||||
"id": 3,
|
||||
},
|
||||
"reports": Array [
|
||||
Object {
|
||||
|
@ -76,16 +76,18 @@ describe('pds admin get moderation action view', () => {
|
||||
})
|
||||
|
||||
it('gets moderation action for a repo.', async () => {
|
||||
// id 2 because id 1 is in seed client
|
||||
const result = await agent.api.com.atproto.admin.getModerationAction(
|
||||
{ id: 1 },
|
||||
{ id: 2 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
expect(forSnapshot(result.data)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('gets moderation action for a record.', async () => {
|
||||
// id 3 because id 1 is in seed client
|
||||
const result = await agent.api.com.atproto.admin.getModerationAction(
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
expect(forSnapshot(result.data)).toMatchSnapshot()
|
||||
|
@ -165,7 +165,7 @@ describe('pds admin get moderation actions view', () => {
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
|
||||
expect(full.data.actions.length).toEqual(6)
|
||||
expect(full.data.actions.length).toEqual(7) // extra one because of seed client
|
||||
expect(results(paginatedAll)).toEqual(results([full.data]))
|
||||
})
|
||||
})
|
||||
|
@ -8,17 +8,24 @@ import {
|
||||
REASONOTHER,
|
||||
REASONSPAM,
|
||||
} from '../../../src/lexicon/types/com/atproto/moderation/defs'
|
||||
import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../../_util'
|
||||
import {
|
||||
runTestServer,
|
||||
forSnapshot,
|
||||
CloseFn,
|
||||
adminAuth,
|
||||
TestServerInfo,
|
||||
} from '../../_util'
|
||||
import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get record view', () => {
|
||||
let server: TestServerInfo
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await runTestServer({
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'views_admin_get_record',
|
||||
})
|
||||
close = server.close
|
||||
@ -113,4 +120,29 @@ describe('pds admin get record view', () => {
|
||||
)
|
||||
await expect(promise).rejects.toThrow('Record not found')
|
||||
})
|
||||
|
||||
it('serves labels.', async () => {
|
||||
const { ctx } = server
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
sc.posts[sc.dids.alice][0].ref.uriStr,
|
||||
sc.posts[sc.dids.alice][0].ref.cidStr,
|
||||
{ create: ['kittens', 'puppies', 'birds'] },
|
||||
)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
sc.posts[sc.dids.alice][0].ref.uriStr,
|
||||
sc.posts[sc.dids.alice][0].ref.cidStr,
|
||||
{ negate: ['birds'] },
|
||||
)
|
||||
const result = await agent.api.com.atproto.admin.getRecord(
|
||||
{
|
||||
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
||||
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
||||
},
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
expect(forSnapshot(result.data)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
@ -7,17 +7,24 @@ import {
|
||||
REASONOTHER,
|
||||
REASONSPAM,
|
||||
} from '../../../src/lexicon/types/com/atproto/moderation/defs'
|
||||
import { runTestServer, forSnapshot, CloseFn, adminAuth } from '../../_util'
|
||||
import {
|
||||
runTestServer,
|
||||
forSnapshot,
|
||||
CloseFn,
|
||||
adminAuth,
|
||||
TestServerInfo,
|
||||
} from '../../_util'
|
||||
import { SeedClient } from '../../seeds/client'
|
||||
import basicSeed from '../../seeds/basic'
|
||||
|
||||
describe('pds admin get repo view', () => {
|
||||
let server: TestServerInfo
|
||||
let agent: AtpAgent
|
||||
let close: CloseFn
|
||||
let sc: SeedClient
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await runTestServer({
|
||||
server = await runTestServer({
|
||||
dbPostgresSchema: 'views_admin_get_repo',
|
||||
})
|
||||
close = server.close
|
||||
@ -80,4 +87,26 @@ describe('pds admin get repo view', () => {
|
||||
)
|
||||
await expect(promise).rejects.toThrow('Repo not found')
|
||||
})
|
||||
|
||||
it('serves labels.', async () => {
|
||||
const { ctx } = server
|
||||
const labelingService = ctx.services.appView.label(ctx.db)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
sc.dids.alice,
|
||||
null,
|
||||
{ create: ['kittens', 'puppies', 'birds'] },
|
||||
)
|
||||
await labelingService.formatAndCreate(
|
||||
ctx.cfg.labelerDid,
|
||||
sc.dids.alice,
|
||||
null,
|
||||
{ negate: ['birds'] },
|
||||
)
|
||||
const result = await agent.api.com.atproto.admin.getRepo(
|
||||
{ did: sc.dids.alice },
|
||||
{ headers: { authorization: adminAuth() } },
|
||||
)
|
||||
expect(forSnapshot(result.data)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
@ -169,7 +169,7 @@ describe('timeline views', () => {
|
||||
|
||||
it('blocks posts, reposts, replies by actor takedown', async () => {
|
||||
const actionResults = await Promise.all(
|
||||
[bob, dan].map((did) =>
|
||||
[bob, carol].map((did) =>
|
||||
agent.api.com.atproto.admin.takeModerationAction(
|
||||
{
|
||||
action: TAKEDOWN,
|
||||
|
Loading…
x
Reference in New Issue
Block a user