Update CLI repo management to use leveldb blockstore

This commit is contained in:
Paul Frazee 2022-02-03 17:03:36 -06:00
parent c3b3eb473f
commit 0a2bb3b03a
5 changed files with 2967 additions and 3166 deletions
cli/src
commands
lib
yarn.lock

@ -1,7 +1,7 @@
import { service } from '@bluesky-demo/common'
import * as ucan from 'ucans'
import cmd from '../../lib/command.js'
import { readRepo, writeRepo } from '../../lib/repo.js'
import { Repo } from '../../lib/repo.js'
import { REPO_PATH } from '../../lib/env.js'
export default cmd({
@ -11,10 +11,11 @@ export default cmd({
args: [{name: 'user', optional: true}],
async command (args) {
const user = args._[0]
const repo = await readRepo(REPO_PATH)
const repo = await Repo.load(REPO_PATH)
const store = await repo.getLocalUserStore()
// TODO - handle per-user, handle merged feed
// TODO - format output
console.log(repo.store.posts)
console.log(store.posts)
}
})

@ -1,7 +1,7 @@
import { service } from '@bluesky-demo/common'
import * as ucan from 'ucans'
import cmd from '../../lib/command.js'
import { readRepo, writeRepo } from '../../lib/repo.js'
import { Repo } from '../../lib/repo.js'
import { REPO_PATH } from '../../lib/env.js'
export default cmd({
@ -16,15 +16,18 @@ export default cmd({
process.exit(1)
}
const repo = await readRepo(REPO_PATH)
const repo = await Repo.load(REPO_PATH)
const store = await repo.getLocalUserStore()
await repo.store.addPost({
console.log('Creating post...')
await store.addPost({
user: repo.account.name,
text
})
await repo.rootCidFile.put(store.root)
console.log('Posting to server...')
const car = await repo.store.getCarFile()
console.log('Uploading to server...')
const car = await store.getCarFile()
const blueskyDid = await service.getServerDid()
const token = await ucan.build({
audience: blueskyDid,
@ -35,8 +38,5 @@ export default cmd({
}]
})
await service.updateUser(car, ucan.encode(token))
console.log('Updating local store...')
await writeRepo(REPO_PATH, repo)
}
})

@ -1,7 +1,6 @@
import prompt from 'prompt'
import { promises as fsp } from 'fs'
import { writeNewRepo } from '../../lib/repo.js'
import { service, UserStore } from '@bluesky-demo/common'
import { Repo } from '../../lib/repo.js'
import { service } from '@bluesky-demo/common'
import * as ucan from 'ucans'
import cmd from '../../lib/command.js'
import { REPO_PATH } from '../../lib/env.js'
@ -53,42 +52,20 @@ export default cmd({
})).question
}
try {
await fsp.mkdir(REPO_PATH, {recursive: true})
} catch (e: any) {
console.error(`Failed to create repo at ${REPO_PATH}`)
console.error(e.toString())
process.exit(1)
}
console.log('Generating keys...')
const keypair = await ucan.EdKeypair.create({exportable: true})
const userDID = keypair.did()
const userStore = await UserStore.create(username, keypair)
const carFileBuf = await userStore.getCarFile()
console.log('Writing repo...')
await writeNewRepo(
REPO_PATH,
await keypair.export(),
carFileBuf,
{
name: username,
server,
did: userDID
}
)
console.log('Generating repo...')
const repo = await Repo.createNew(REPO_PATH, username, server)
if (register) {
console.log('Registering with server...')
try {
// TODO - service needs to use `server`
const userStore = await repo.getLocalUserStore()
const blueskyDid = await service.getServerDid()
const token = await ucan.build({
audience: blueskyDid,
issuer: keypair
issuer: repo.keypair
})
await service.register(carFileBuf, ucan.encode(token))
await service.register(await userStore.getCarFile(), ucan.encode(token))
} catch (e: any) {
console.error(`Failed to register with server`)
console.error(e.toString())
@ -99,6 +76,6 @@ export default cmd({
console.log('')
console.log(`Repo created at ${REPO_PATH}`)
console.log(`DID: ${userDID}`)
console.log(`DID: ${repo.account.did}`)
}
})

@ -1,6 +1,7 @@
import path from 'path'
import { promises as fsp } from 'fs'
import { UserStore, MemoryDB } from '@bluesky-demo/common'
import { UserStore, Blockstore } from '@bluesky-demo/common'
import { CID } from 'multiformats/cid'
import * as ucan from 'ucans'
export interface AccountJson {
@ -9,32 +10,79 @@ export interface AccountJson {
did: string
}
export interface Repo {
keypair: ucan.EdKeypair
account: AccountJson
store: UserStore
export class Repo {
constructor(
public keypair: ucan.EdKeypair,
public account: AccountJson,
public blockstore: Blockstore,
public rootCidFile: RootCidFile
) {}
static async createNew (repoPath: string, name: string, server: string): Promise<Repo> {
try {
await fsp.mkdir(repoPath, {recursive: true})
} catch (e: any) {
console.error(`Failed to create repo at ${repoPath}`)
console.error(e.toString())
process.exit(1)
}
const keypair = await ucan.EdKeypair.create({exportable: true})
const userDID = keypair.did()
const account: AccountJson = {
name,
server,
did: userDID
}
const blockstore = new Blockstore(path.join(repoPath, 'blockstore'))
await fsp.writeFile(path.join(repoPath, 'scdp.key'), await keypair.export(), 'utf-8')
await fsp.writeFile(path.join(repoPath, 'account.json'), JSON.stringify(account, null, 2), 'utf-8')
const rootCidFile = new RootCidFile(path.join(repoPath, 'root.cid'))
const localUserStore = await UserStore.create(name, blockstore, keypair)
await rootCidFile.put(localUserStore.root)
return new Repo(
keypair,
account,
blockstore,
rootCidFile
)
}
static async load (repoPath: string): Promise<Repo> {
const secretKeyStr = await readFile(repoPath, 'scdp.key', 'utf-8') as string
const account = await readAccountFile(repoPath, 'account.json')
const blockstore = new Blockstore(path.join(repoPath, 'blockstore'))
const keypair = ucan.EdKeypair.fromSecretKey(secretKeyStr)
const rootCidFile = new RootCidFile(path.join(repoPath, 'root.cid'))
return new Repo(
keypair,
account,
blockstore,
rootCidFile
)
}
async getLocalUserStore (): Promise<UserStore> {
return this.getUserStore(await this.rootCidFile.get())
}
async getUserStore (cid: CID): Promise<UserStore> {
return UserStore.get(cid, this.blockstore, this.keypair) // TODO !!!! only pass in keypair if this is the local user! (waiting on PR to make keypair optional)
}
}
export async function writeNewRepo (repoPath: string, secretKeyStr: string, carFileBuf: Uint8Array, accountJson: AccountJson) {
// TODO- correct?
await fsp.writeFile(path.join(repoPath, 'scdp.key'), secretKeyStr, 'utf-8')
await fsp.writeFile(path.join(repoPath, 'blocks.car'), carFileBuf)
await fsp.writeFile(path.join(repoPath, 'account.json'), JSON.stringify(accountJson, null, 2), 'utf-8')
}
export async function writeRepo (repoPath: string, repo: Repo) {
await fsp.writeFile(path.join(repoPath, 'blocks.car'), await repo.store.getCarFile())
}
export async function readRepo (repoPath: string): Promise<Repo> {
const secretKeyStr = await readFile(repoPath, 'scdp.key', 'utf-8') as string
const carFileBuf = await readFile(repoPath, 'blocks.car') as Uint8Array
const account = await readAccountFile(repoPath, 'account.json')
const keypair = ucan.EdKeypair.fromSecretKey(secretKeyStr)
return {
keypair,
account,
store: await UserStore.fromCarFile(carFileBuf, MemoryDB.getGlobal(), keypair)
class RootCidFile {
constructor (public path: string) {}
async get () {
const str = await fsp.readFile(this.path, 'utf-8')
if (!str) throw new Error(`No root.cid file found`)
return CID.parse(str)
}
async put (cid: CID) {
await fsp.writeFile(this.path, cid.toString(), 'utf-8')
}
}

5973
yarn.lock

File diff suppressed because it is too large Load Diff