Skip to content

Commit 82a4b44

Browse files
committed
pds
1 parent 110e2d4 commit 82a4b44

File tree

161 files changed

+12650
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

161 files changed

+12650
-12
lines changed

packages/pds/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
"test:sqlite-only": "jest --testPathIgnorePatterns /tests/proxied/*",
3030
"test:log": "tail -50 test.log | pino-pretty",
3131
"test:updateSnapshot": "../dev-infra/with-test-redis-and-db.sh jest --updateSnapshot",
32-
"migration:create": "ts-node ./bin/migration-create.ts"
32+
"migration:create": "ts-node ./bin/migration-create.ts",
33+
"schemagen": "cd ./src/authz/spicedb && make gen",
34+
"spicedb:validate": "zed validate ./src/authz/spicedb/schema/atproto.zed",
35+
"spicedb:preview": "zed preview schema compile ./src/authz/spicedb/schema/atproto.zed",
36+
"spicedb:compile": "zed preview schema compile ./src/authz/spicedb/schema/atproto.zed --out atproto.zed"
3337
},
3438
"dependencies": {
3539
"@atproto-labs/fetch-node": "workspace:*",
@@ -44,12 +48,13 @@
4448
"@atproto/identity": "workspace:^",
4549
"@atproto/lexicon": "workspace:^",
4650
"@atproto/lexicon-resolver": "workspace:^",
47-
"@atproto/oauth-scopes": "workspace:^",
4851
"@atproto/oauth-provider": "workspace:^",
52+
"@atproto/oauth-scopes": "workspace:^",
4953
"@atproto/repo": "workspace:^",
5054
"@atproto/syntax": "workspace:^",
5155
"@atproto/xrpc": "workspace:^",
5256
"@atproto/xrpc-server": "workspace:^",
57+
"@authzed/authzed-node": "^1.5.1",
5358
"@did-plc/lib": "^0.0.4",
5459
"@hapi/address": "^5.1.1",
5560
"@paralleldrive/cuid2": "^2.2.2",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
pushd src/authz/spicedb/schema
5+
6+
echo 'export const spicedbSchema = `' > atproto.ts
7+
8+
zed preview schema compile atproto.zed --out compiled.zed
9+
cat compiled.zed >> atproto.ts
10+
rm compiled.zed
11+
12+
echo '`;' >> atproto.ts
13+

packages/pds/src/account-manager/oauth-store.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ import * as deviceHelper from './helpers/device'
5757
import * as lexiconHelper from './helpers/lexicon'
5858
import * as tokenHelper from './helpers/token'
5959
import * as usedRefreshTokenHelper from './helpers/used-refresh-token'
60+
import { v1 as spice } from '@authzed/authzed-node'
61+
import { createRelationship } from '../authz/spicedb'
62+
import { aturi2spicedb } from '../space'
6063

6164
/**
6265
* This class' purpose is to implement the interface needed by the OAuthProvider
@@ -78,6 +81,7 @@ export class OAuthStore
7881
private readonly plcRotationKey: Keypair,
7982
private readonly publicUrl: string,
8083
private readonly recoveryDidKey: string | null,
84+
private readonly spicedbClient?: spice.ZedPromiseClientInterface,
8185
) {}
8286

8387
private get db() {
@@ -152,9 +156,26 @@ export class OAuthStore
152156
try {
153157
await this.actorStore.create(did, signingKey)
154158
try {
159+
// DUAL WRITE
160+
// (1) create repo
155161
const commit = await this.actorStore.transact(did, (actorTxn) =>
156162
actorTxn.repo.createRepo([]),
163+
// TODO, write root space and relationship records to the root space in the database
157164
)
165+
// (2) set account as owner over root space
166+
if (this.spicedbClient) {
167+
try {
168+
await createRelationship(this.spicedbClient,
169+
`space:${aturi2spicedb(`${did}/root`)}`,
170+
'owner',
171+
`user:${aturi2spicedb(did)}`,
172+
)
173+
} catch (err) {
174+
// temp, dev-env is creating duplicate accounts
175+
// or not cleaning up properly (shortcut? re: but not spicedb)
176+
console.error('failed to set user as owner of root space', err)
177+
}
178+
}
158179

159180
await this.plcClient.sendOperation(did, op)
160181

packages/pds/src/actor-store/actor-store-reader.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { ActorDb } from './db'
55
import { PreferenceReader } from './preference/reader'
66
import { RecordReader } from './record/reader'
77
import { RepoReader } from './repo/reader'
8+
import { SpaceReader } from './space/reader'
89

910
export class ActorStoreReader {
1011
public readonly repo: RepoReader
1112
public readonly record: RecordReader
1213
public readonly pref: PreferenceReader
14+
public readonly space: SpaceReader
1315

1416
constructor(
1517
public readonly did: string,
@@ -22,6 +24,7 @@ export class ActorStoreReader {
2224
this.repo = new RepoReader(db, blobstore)
2325
this.record = new RecordReader(db)
2426
this.pref = new PreferenceReader(db)
27+
this.space = new SpaceReader(db)
2528

2629
// Invoke "keypair" once. Also avoids leaking "this" as keypair context.
2730
let keypairPromise: Promise<Keypair>

packages/pds/src/actor-store/actor-store-transactor.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { ActorDb } from './db'
44
import { PreferenceTransactor } from './preference/transactor'
55
import { RecordTransactor } from './record/transactor'
66
import { RepoTransactor } from './repo/transactor'
7+
import { SpaceTransactor } from './space/transactor'
78

89
export class ActorStoreTransactor {
910
public readonly record: RecordTransactor
1011
public readonly repo: RepoTransactor
1112
public readonly pref: PreferenceTransactor
13+
public readonly space: SpaceTransactor
1214

1315
constructor(
1416
public readonly did: string,
@@ -27,5 +29,12 @@ export class ActorStoreTransactor {
2729
keypair,
2830
resources.backgroundQueue,
2931
)
32+
this.space = new SpaceTransactor(
33+
db,
34+
blobstore,
35+
did,
36+
keypair,
37+
resources.backgroundQueue,
38+
)
3039
}
3140
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { Kysely } from 'kysely'
2+
3+
export async function up(db: Kysely<unknown>): Promise<void> {
4+
5+
await db.schema
6+
.createTable('space')
7+
.addColumn('uri', 'varchar', (col) => col.primaryKey())
8+
.addColumn('cid', 'varchar', (col) => col.notNull())
9+
// the account that created or updated the record
10+
.addColumn('did', 'varchar', (col) => col.notNull())
11+
.addColumn('parent', 'varchar', (col) => col.notNull())
12+
.addColumn('space', 'varchar', (col) => col.notNull())
13+
.addColumn('collection', 'varchar', (col) => col.notNull())
14+
.addColumn('rkey', 'varchar', (col) => col.notNull())
15+
.addColumn('record', 'varchar', (col) => col.notNull())
16+
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
17+
.addColumn('takedownRef', 'varchar')
18+
// #futurology for edits w/ history
19+
.addColumn('version', 'varchar')
20+
.execute()
21+
22+
await db.schema
23+
.createIndex('space_space_idx')
24+
.on('space')
25+
.column('space')
26+
.execute()
27+
await db.schema
28+
.createIndex('space_collection_idx')
29+
.on('space')
30+
.column('collection')
31+
.execute()
32+
await db.schema
33+
.createIndex('space_rkey_idx')
34+
.on('space')
35+
.column('rkey')
36+
.execute()
37+
await db.schema
38+
.createIndex('space_cid_idx')
39+
.on('space')
40+
.column('cid')
41+
.execute()
42+
await db.schema
43+
.createIndex('space_did_idx')
44+
.on('space')
45+
.column('did')
46+
.execute()
47+
48+
await db.schema
49+
.createTable('space_blob')
50+
.addColumn('cid', 'varchar', (col) => col.primaryKey())
51+
.addColumn('space', 'varchar', (col) => col.notNull())
52+
.addColumn('mimeType', 'varchar', (col) => col.notNull())
53+
.addColumn('size', 'integer', (col) => col.notNull())
54+
.addColumn('tempKey', 'varchar')
55+
.addColumn('metadata', 'varchar') // JSON object with details like dimensions, playrate, ... mimeType dependent
56+
.addColumn('createdAt', 'varchar', (col) => col.notNull())
57+
.addColumn('takedownRef', 'varchar')
58+
.execute()
59+
await db.schema
60+
.createIndex('space_blob_space_idx')
61+
.on('space_blob')
62+
.column('space')
63+
.execute()
64+
await db.schema
65+
.createIndex('space_blob_tempkey_idx')
66+
.on('space_blob')
67+
.column('tempKey')
68+
.execute()
69+
70+
await db.schema
71+
.createTable('space_record_blob')
72+
.addColumn('blobCid', 'varchar', (col) => col.notNull())
73+
.addColumn('recordUri', 'varchar', (col) => col.notNull())
74+
.addPrimaryKeyConstraint(`space_record_blob_pkey`, ['blobCid', 'recordUri'])
75+
.execute()
76+
77+
}
78+
79+
export async function down(db: Kysely<unknown>): Promise<void> {
80+
await db.schema.dropTable('space_record_blob').execute()
81+
await db.schema.dropTable('space_blob').execute()
82+
await db.schema.dropTable('space').execute()
83+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as init from './001-init'
2+
import * as spaces from './002-spaces'
23

34
export default {
45
'001': init,
6+
'002': spaces,
57
}

packages/pds/src/actor-store/db/schema/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ import * as record from './record'
55
import * as recordBlob from './record-blob'
66
import * as repoBlock from './repo-block'
77
import * as repoRoot from './repo-root'
8+
import * as space from './space'
9+
import * as spaceBacklink from './space-backlink'
10+
import * as spaceBlob from './space-blob'
11+
import * as spaceRecordBlob from './space-record-blob'
812

913
export type DatabaseSchema = accountPref.PartialDB &
1014
repoRoot.PartialDB &
1115
record.PartialDB &
1216
backlink.PartialDB &
1317
repoBlock.PartialDB &
1418
blob.PartialDB &
15-
recordBlob.PartialDB
19+
recordBlob.PartialDB &
20+
space.PartialDB &
21+
spaceBacklink.PartialDB &
22+
spaceBlob.PartialDB &
23+
spaceRecordBlob.PartialDB
1624

1725
export type { AccountPref } from './account-pref'
1826
export type { RepoRoot } from './repo-root'
@@ -21,3 +29,7 @@ export type { Backlink } from './backlink'
2129
export type { RepoBlock } from './repo-block'
2230
export type { Blob } from './blob'
2331
export type { RecordBlob } from './record-blob'
32+
export type { Space } from './space'
33+
export type { SpaceBacklink } from './space-backlink'
34+
export type { SpaceBlob } from './space-blob'
35+
export type { SpaceRecordBlob } from './space-record-blob'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface SpaceBacklink {
2+
uri: string
3+
path: string
4+
linkTo: string
5+
}
6+
7+
export const tableName = 'space_backlink'
8+
9+
export type PartialDB = { [tableName]: SpaceBacklink }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export interface SpaceBlob {
2+
cid: string
3+
space: string
4+
mimeType: string
5+
size: number
6+
tempKey: string | null
7+
width: number | null
8+
height: number | null
9+
createdAt: string
10+
takedownRef: string | null
11+
}
12+
13+
export const tableName = 'space_blob'
14+
15+
export type PartialDB = { [tableName]: SpaceBlob }

0 commit comments

Comments
 (0)