UNPKG

@atproto/ozone

Version:

Backend service for moderating the Bluesky network.

157 lines (136 loc) 4.36 kB
import assert from 'node:assert' import { ToolsOzoneTeamDefs } from '@atproto/api' import { AuthRequiredError } from '@atproto/xrpc-server' import { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier' import { AppContext } from '../../context' import { Member } from '../../db/schema/member' import { Server } from '../../lexicon' import { SettingService } from '../../setting/service' import { settingValidators } from '../../setting/validators' export default function (server: Server, ctx: AppContext) { server.tools.ozone.setting.upsertOption({ auth: ctx.authVerifier.modOrAdminToken, handler: async ({ input, auth }) => { const access = auth.credentials const db = ctx.db const { key, value, description, managerRole, scope } = input.body const serviceDid = ctx.cfg.service.did let ownerDid = serviceDid if (scope === 'personal' && access.type !== 'moderator') { throw new AuthRequiredError( 'Must use moderator auth to create or update a personal setting', ) } // if the caller is using moderator auth and storing personal setting // use the caller's DID as the owner if (scope === 'personal' && access.type === 'moderator') { ownerDid = access.iss } const now = new Date() const baseOption = { key, value, did: ownerDid, createdBy: ownerDid, lastUpdatedBy: ownerDid, description: description || '', createdAt: now, updatedAt: now, } const settingService = ctx.settingService(db) if (scope === 'personal') { await settingService.upsert({ ...baseOption, scope: 'personal', managerRole: null, }) } else { const manageableRoles = getRolesForInstanceOption(access) const existingSetting = await getExistingSetting( settingService, ownerDid, key, 'instance', ) if ( existingSetting?.managerRole && !manageableRoles.includes(existingSetting.managerRole) ) { throw new AuthRequiredError(`Not permitted to update setting ${key}`) } const option = { ...baseOption, scope: 'instance' as const, managerRole: getManagerRole(managerRole), } if (settingValidators.has(key)) { await settingValidators.get(key)?.(option) } await settingService.upsert(option) } const newOption = await getExistingSetting( settingService, ownerDid, key, scope, ) assert(newOption, 'Failed to get the updated setting') return { encoding: 'application/json', body: { option: settingService.view(newOption), }, } }, }) } const getExistingSetting = async ( settingService: SettingService, did: string, key: string, scope: string, ) => { const result = await settingService.query({ scope: scope === 'personal' ? 'personal' : 'instance', keys: [key], limit: 1, did, }) return result.options[0] } const getRolesForInstanceOption = ( access: AdminTokenOutput['credentials'] | ModeratorOutput['credentials'], ) => { const fullPermission = [ ToolsOzoneTeamDefs.ROLEADMIN, ToolsOzoneTeamDefs.ROLEMODERATOR, ToolsOzoneTeamDefs.ROLETRIAGE, ToolsOzoneTeamDefs.ROLEVERIFIER, ] if (access.type === 'admin_token') { return fullPermission } if (access.isAdmin) { return fullPermission } if (access.isModerator) { return [ToolsOzoneTeamDefs.ROLEMODERATOR, ToolsOzoneTeamDefs.ROLETRIAGE] } if (access.isVerifier) { return [ToolsOzoneTeamDefs.ROLEVERIFIER] } return [ToolsOzoneTeamDefs.ROLETRIAGE] } const getManagerRole = (role?: string) => { let managerRole: Member['role'] | null = null if (role === ToolsOzoneTeamDefs.ROLEADMIN) { managerRole = ToolsOzoneTeamDefs.ROLEADMIN } else if (role === ToolsOzoneTeamDefs.ROLEMODERATOR) { managerRole = ToolsOzoneTeamDefs.ROLEMODERATOR } else if (role === ToolsOzoneTeamDefs.ROLETRIAGE) { managerRole = ToolsOzoneTeamDefs.ROLETRIAGE } else if (role === ToolsOzoneTeamDefs.ROLEVERIFIER) { managerRole = ToolsOzoneTeamDefs.ROLEVERIFIER } return managerRole }