Skip to content

Commit

Permalink
Merge pull request #1769 from bluesky-social/account-manager
Browse files Browse the repository at this point in the history
Account Manager
  • Loading branch information
dholms authored Nov 1, 2023
2 parents 8449ceb + 09f8837 commit 1544e73
Show file tree
Hide file tree
Showing 92 changed files with 1,565 additions and 1,338 deletions.
6 changes: 1 addition & 5 deletions packages/bsky/tests/handle-invalidation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,7 @@ describe('handle invalidation', () => {
it('deals with handle contention', async () => {
await backdateIndexedAt(bob)
// update alices handle so that the pds will let bob take her old handle
await network.pds.ctx.db.db
.updateTable('account')
.where('did', '=', alice)
.set({ handle: 'not-alice.test' })
.execute()
await network.pds.ctx.accountManager.updateHandle(alice, 'not-alice.test')

await pdsAgent.api.com.atproto.identity.updateHandle(
{
Expand Down
24 changes: 11 additions & 13 deletions packages/bsky/tests/indexing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,8 @@ describe('indexing', () => {

it('skips invalid records.', async () => {
const { db, services } = network.bsky.indexer.ctx
const { db: pdsDb, services: pdsServices } = network.pds.ctx
const { accountManager } = network.pds.ctx
// const { db: pdsDb, services: pdsServices } = network.pds.ctx
// Create a good and a bad post record
const writes = await Promise.all([
pdsRepo.prepareCreate({
Expand All @@ -517,9 +518,11 @@ describe('indexing', () => {
sc.dids.alice,
(store) => store.repo.processWrites(writes),
)
await pdsServices
.account(pdsDb)
.updateRepoRoot(sc.dids.alice, writeCommit.cid, writeCommit.rev)
await accountManager.updateRepoRoot(
sc.dids.alice,
writeCommit.cid,
writeCommit.rev,
)
await network.pds.ctx.sequencer.sequenceCommit(
sc.dids.alice,
writeCommit,
Expand Down Expand Up @@ -652,15 +655,10 @@ describe('indexing', () => {
)
await expect(getProfileBefore).resolves.toBeDefined()
// Delete account on pds
await pdsAgent.api.com.atproto.server.requestAccountDelete(undefined, {
headers: sc.getHeaders(alice),
})
const { token } = await network.pds.ctx.db.db
.selectFrom('email_token')
.selectAll()
.where('purpose', '=', 'delete_account')
.where('did', '=', alice)
.executeTakeFirstOrThrow()
const token = await network.pds.ctx.accountManager.createEmailToken(
alice,
'delete_account',
)
await pdsAgent.api.com.atproto.server.deleteAccount({
token,
did: alice,
Expand Down
1 change: 1 addition & 0 deletions packages/dev-env/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const ADMIN_PASSWORD = 'admin-pass'
export const MOD_PASSWORD = 'mod-pass'
export const TRIAGE_PASSWORD = 'triage-pass'
export const JWT_SECRET = 'jwt-secret'
19 changes: 11 additions & 8 deletions packages/dev-env/src/pds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import fs from 'node:fs/promises'
import getPort from 'get-port'
import * as ui8 from 'uint8arrays'
import * as pds from '@atproto/pds'
import { getMigrator } from '@atproto/pds/src/service-db'
import { Secp256k1Keypair, randomStr } from '@atproto/crypto'
import { AtpAgent } from '@atproto/api'
import { PdsConfig } from './types'
import { ADMIN_PASSWORD, MOD_PASSWORD, TRIAGE_PASSWORD } from './const'
import {
ADMIN_PASSWORD,
JWT_SECRET,
MOD_PASSWORD,
TRIAGE_PASSWORD,
} from './const'

export class TestPds {
constructor(
Expand Down Expand Up @@ -37,7 +41,7 @@ export class TestPds {
adminPassword: ADMIN_PASSWORD,
moderatorPassword: MOD_PASSWORD,
triagePassword: TRIAGE_PASSWORD,
jwtSecret: 'jwt-secret',
jwtSecret: JWT_SECRET,
serviceHandleDomains: ['.test'],
bskyAppViewUrl: 'https://appview.invalid',
bskyAppViewDid: 'did:example:invalid',
Expand All @@ -51,11 +55,6 @@ export class TestPds {

const server = await pds.PDS.create(cfg, secrets)

// Separate migration db on postgres in case migration changes some
// connection state that we need in the tests, e.g. "alter database ... set ..."
const migrator = getMigrator(server.ctx.db)
await migrator.migrateToLatestOrThrow()

await server.start()

return new TestPds(url, port, server)
Expand Down Expand Up @@ -88,6 +87,10 @@ export class TestPds {
}
}

jwtSecret() {
return JWT_SECRET
}

async processAll() {
await this.ctx.backgroundQueue.processAll()
}
Expand Down
15 changes: 15 additions & 0 deletions packages/pds/src/account-manager/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Database, Migrator } from '../../db'
import { DatabaseSchema } from './schema'
import migrations from './migrations'

export * from './schema'

export type AccountDb = Database<DatabaseSchema>

export const getDb = (location: string): AccountDb => {
return Database.sqlite(location)
}

export const getMigrator = (db: AccountDb) => {
return new Migrator(db.db, migrations)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,32 +56,37 @@ export async function up(db: Kysely<unknown>): Promise<void> {
.execute()

await db.schema
.createTable('account')
.createTable('actor')
.addColumn('did', 'varchar', (col) => col.primaryKey())
.addColumn('handle', 'varchar')
.addColumn('email', 'varchar', (col) => col.notNull())
.addColumn('passwordScrypt', 'varchar', (col) => col.notNull())
.addColumn('createdAt', 'varchar', (col) => col.notNull())
.addColumn('emailConfirmedAt', 'varchar')
.addColumn('invitesDisabled', 'int2', (col) => col.notNull().defaultTo(0))
.addColumn('takedownRef', 'varchar')
.execute()
await db.schema
.createIndex(`account_email_lower_idx`)
.createIndex(`actor_handle_lower_idx`)
.unique()
.on('account')
.expression(sql`lower("email")`)
.on('actor')
.expression(sql`lower("handle")`)
.execute()
await db.schema
.createIndex(`account_handle_lower_idx`)
.unique()
.on('account')
.expression(sql`lower("handle")`)
.createIndex('actor_cursor_idx')
.on('actor')
.columns(['createdAt', 'did'])
.execute()

await db.schema
.createIndex('account_cursor_idx')
.createTable('account')
.addColumn('did', 'varchar', (col) => col.primaryKey())
.addColumn('email', 'varchar', (col) => col.notNull())
.addColumn('passwordScrypt', 'varchar', (col) => col.notNull())
.addColumn('emailConfirmedAt', 'varchar')
.addColumn('invitesDisabled', 'int2', (col) => col.notNull().defaultTo(0))
.execute()
await db.schema
.createIndex(`account_email_lower_idx`)
.unique()
.on('account')
.columns(['createdAt', 'did'])
.expression(sql`lower("email")`)
.execute()

await db.schema
Expand All @@ -101,6 +106,7 @@ export async function up(db: Kysely<unknown>): Promise<void> {
export async function down(db: Kysely<unknown>): Promise<void> {
await db.schema.dropTable('email_token').execute()
await db.schema.dropTable('account').execute()
await db.schema.dropTable('actor').execute()
await db.schema.dropTable('repo_root').execute()
await db.schema.dropTable('refresh_token').execute()
await db.schema.dropTable('invite_code_use').execute()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import { Generated, Selectable } from 'kysely'

export interface Account {
did: string
handle: string | null
email: string
passwordScrypt: string
createdAt: string
emailConfirmedAt: string | null
invitesDisabled: Generated<0 | 1>
takedownRef: string | null
}

export type AccountEntry = Selectable<Account>
Expand Down
14 changes: 14 additions & 0 deletions packages/pds/src/account-manager/db/schema/actor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Selectable } from 'kysely'

export interface Actor {
did: string
handle: string | null
createdAt: string
takedownRef: string | null
}

export type ActorEntry = Selectable<Actor>

export const tableName = 'actor'

export type PartialDB = { [tableName]: Actor }
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import * as actor from './actor'
import * as account from './account'
import * as repoRoot from './repo-root'
import * as refreshToken from './refresh-token'
import * as appPassword from './app-password'
import * as inviteCode from './invite-code'
import * as emailToken from './email-token'

export type DatabaseSchema = account.PartialDB &
export type DatabaseSchema = actor.PartialDB &
account.PartialDB &
refreshToken.PartialDB &
appPassword.PartialDB &
repoRoot.PartialDB &
inviteCode.PartialDB &
emailToken.PartialDB

export type { Actor, ActorEntry } from './actor'
export type { Account, AccountEntry } from './account'
export type { RepoRoot } from './repo-root'
export type { RefreshToken } from './refresh-token'
Expand Down
Loading

0 comments on commit 1544e73

Please sign in to comment.