Skip to content

Commit

Permalink
feat: sdk and cli
Browse files Browse the repository at this point in the history
  • Loading branch information
hugomrdias committed Jul 31, 2022
1 parent 305b2d3 commit 2373447
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 189 deletions.
17 changes: 9 additions & 8 deletions packages/access-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
"license": "(Apache-2.0 OR MIT)",
"dependencies": {
"@ipld/dag-ucan": "1.7.0-beta",
"@ucanto/authority": "link:/Users/hd/code/ucanto/packages/authority",
"@ucanto/client": "link:/Users/hd/code/ucanto/packages/client",
"@ucanto/core": "link:/Users/hd/code/ucanto/packages/core",
"@ucanto/interface": "link:/Users/hd/code/ucanto/packages/interface",
"@ucanto/server": "link:/Users/hd/code/ucanto/packages/server",
"@ucanto/transport": "link:/Users/hd/code/ucanto/packages/transport",
"@ucanto/validator": "link:/Users/hd/code/ucanto/packages/validator",
"@ucanto/authority": "^0.4.5",
"@ucanto/client": "^0.5.4",
"@ucanto/core": "^0.5.4",
"@ucanto/interface": "^0.6.2",
"@ucanto/server": "^0.6.4",
"@ucanto/transport": "^0.6.3",
"@ucanto/validator": "^0.5.5",
"@web3-storage/w3access": "workspace:^",
"@web3-storage/worker-utils": "0.2.0-dev",
"multiformats": "^9.6.5",
Expand Down Expand Up @@ -65,7 +65,8 @@
"BRANCH": "readonly",
"DEBUG": "readonly",
"ACCOUNTS": "writable",
"VALIDATIONS": "writable"
"VALIDATIONS": "writable",
"BUCKET": "writable"
}
},
"eslintIgnore": [
Expand Down
2 changes: 2 additions & 0 deletions packages/access-api/src/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {}
declare global {
const ACCOUNTS: KVNamespace
const VALIDATIONS: KVNamespace
const BUCKET: R2Bucket
}

export interface RouteContext {
Expand All @@ -15,6 +16,7 @@ export interface RouteContext {
keypair: SigningAuthority
config: typeof config
url: URL
event: FetchEvent
}

export type Handler = (
Expand Down
16 changes: 2 additions & 14 deletions packages/access-api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import { errorHandler } from '@web3-storage/worker-utils/error'
import { notFound } from '@web3-storage/worker-utils/response'
import { Router } from '@web3-storage/worker-utils/router'
import { validate } from './routes/validate.js'
import { upload } from './routes/upload.js'

/** @type Router<import('./bindings.js').RouteContext> */
const r = new Router({ onNotFound: notFound })
r.add('options', '*', preflight)
r.add('get', '/version', version)
r.add('post', '/upload', upload)

r.add('get', '/validate', validate)
r.add('post', '/', async (request, env) => {
Expand All @@ -27,20 +29,6 @@ r.add('post', '/', async (request, env) => {
catch: (/** @type {string | Error} */ err) => {
env.log.error(err)
},
canIssue: (
/** @type {{ with: any; can: string; }} */ capability,
/** @type {import("@ucanto/interface").DID<unknown>} */ issuer
) => {
if (capability.with === issuer || issuer === env.keypair.did()) {
return true
}

if (capability.can === 'identity/validate') {
return true
}

return false
},
})

const rsp = await server.request({
Expand Down
22 changes: 22 additions & 0 deletions packages/access-api/src/routes/upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @param {import('@web3-storage/worker-utils/router').ParsedRequest} req
* @param {import('../bindings.js').RouteContext} env
*/
export async function upload(req, env) {
let size = 0
const checkStream = new TransformStream({
transform(chunk, controller) {
size += chunk.length
// eslint-disable-next-line no-console
console.log('🚀 ~ file: upload.js ~ line 10 ~ transform ~ size', size)
controller.enqueue(chunk)
},
})

if (!req.body) {
throw new Error('no body')
}

await BUCKET.put(`file-${Date.now()}`, req.body?.pipeThrough(checkStream))
return new Response(String(size))
}
2 changes: 1 addition & 1 deletion packages/access-api/src/utils/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ export function getContext(event, params) {

const keypair = SigningAuthority.parse(config.PRIVATE_KEY)
const url = new URL(event.request.url)
return { params, log, keypair, config, url }
return { params, log, keypair, config, url, event }
}
7 changes: 7 additions & 0 deletions packages/access-api/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ compatibility_date = "2022-06-26"
compatibility_flags = ["url_standard"]
no_bundle = true

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "access-test"

kv_namespaces = [
{ binding = "ACCOUNTS", id = "e9fad7e04b254bf49206e08e50074387", preview_id = "e9fad7e04b254bf49206e08e50074387" },
{ binding = "VALIDATIONS", id = "62e57652625c44a3b1fef2f840ffc882", preview_id = "62e57652625c44a3b1fef2f840ffc882" },
Expand All @@ -21,3 +25,6 @@ DEBUG = "true"
[build]
command = "scripts/cli.js build"
watch_dir = "src"

[miniflare]
r2_persist = "./data/" # Defaults to ./.mf/r2
19 changes: 10 additions & 9 deletions packages/access/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dist/src/types"
],
"capabilities": [
"dist/src/capabilities.d.ts"
"dist/src/capabilities"
]
}
},
Expand All @@ -42,17 +42,18 @@
],
"dependencies": {
"@ipld/dag-ucan": "1.7.0-beta",
"@ucanto/authority": "link:/Users/hd/code/ucanto/packages/authority",
"@ucanto/client": "link:/Users/hd/code/ucanto/packages/client",
"@ucanto/core": "link:/Users/hd/code/ucanto/packages/core",
"@ucanto/interface": "link:/Users/hd/code/ucanto/packages/interface",
"@ucanto/server": "link:/Users/hd/code/ucanto/packages/server",
"@ucanto/transport": "link:/Users/hd/code/ucanto/packages/transport",
"@ucanto/validator": "link:/Users/hd/code/ucanto/packages/validator",
"@ucanto/authority": "^0.4.5",
"@ucanto/client": "^0.5.4",
"@ucanto/core": "^0.5.4",
"@ucanto/interface": "^0.6.2",
"@ucanto/server": "^0.6.4",
"@ucanto/transport": "^0.6.3",
"@ucanto/validator": "^0.5.5",
"@web-std/fetch": "^4.1.0",
"conf": "^10.1.2",
"ora": "^6.1.2",
"p-retry": "^5.1.1"
"p-retry": "^5.1.1",
"undici": "^5.8.0"
},
"devDependencies": {
"@types/assert": "^1.5.6",
Expand Down
23 changes: 23 additions & 0 deletions packages/access/src/capabilities-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Capability, DID, Link } from '@ucanto/interface'

export interface StoreAdd extends Capability<'store/add', DID> {
link?: Link
}

export interface StoreRemove extends Capability<'store/remove', DID> {
link?: Link
}

export interface StoreList extends Capability<'store/list', DID> {}

export interface IdentityValidate extends Capability<'identity/validate', DID> {
as: `mailto:${string}`
}

export interface IdentityRegister
extends Capability<'identity/register', `mailto:${string}`> {
as: DID
}

export interface IdentityIdentify
extends Capability<'identity/identify', DID> {}
2 changes: 1 addition & 1 deletion packages/access/src/capabilities-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function equalWith(child, parent) {
}

/**
* @template {Types.ParsedCapability<"store/add"|"store/remove", Types.URI<'did:'>, {link?: import('./types').Link<unknown, number, number, 0|1>}>} T
* @template {Types.ParsedCapability<"store/add"|"store/remove", Types.URI<'did:'>, {link?: Types.Link<unknown, number, number, 0|1>}>} T
* @param {T} claimed
* @param {T} delegated
*/
Expand Down
106 changes: 63 additions & 43 deletions packages/access/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import fs from 'fs'
import Conf from 'conf'
import ora from 'ora'
import * as Keypair from '@ucanto/authority'
import * as UCAN from '@ipld/dag-ucan'
// @ts-ignore
// eslint-disable-next-line no-unused-vars
import * as Types from '@ucanto/interface'
import * as Access from './index.js'
import path from 'path'
import undici from 'undici'
import { Transform } from 'stream'

const NAME = 'w3access'
const pkg = JSON.parse(
Expand All @@ -23,19 +22,8 @@ const config = new Conf({
projectSuffix: '',
})

/**
* @param {string} jwtUCAN
*/
async function validate(jwtUCAN) {
// @ts-ignore
const ucan = UCAN.parse(jwtUCAN)

await UCAN.verifySignature(ucan, Keypair.Authority.parse(ucan.issuer.did()))

return ucan
}

const prog = sade(NAME)
const url = 'https://auth-dev.dag.haus'

prog.version(pkg.version)

Expand All @@ -44,34 +32,18 @@ prog
.describe('Create or save a keypair to the config.')
.option('--force', 'Override config with new keypair.', false)
.option('--private-key', 'Create new keypair with private key.')
.option('--ucan', 'UCAN issued by the service to your DID.')
.action(async (opts) => {
const spinner = ora('Creating new keypair').start()
try {
const privateKey = /** @type {string | undefined} */ (
config.get('private-key')
)

/**
* @param {string} ucan
* @param {import('@ucanto/interface').SigningAuthority} kp
*/
async function validateAndSaveUcan(ucan, kp) {
if (ucan) {
const r = await validate(ucan)
if (kp.did() !== r.audience.did()) {
throw new Error('UCAN does not match keypair DID.')
}
config.set('ucan', ucan)
}
}

// Save or override keypair
if (opts['private-key']) {
const kp = Keypair.parse(opts['private-key'])
config.set('private-key', opts['private-key'])
config.set('did', kp.did())
await validateAndSaveUcan(opts.ucan, kp)
spinner.succeed(`Keypair created and saved to ${config.path}`)
return
}
Expand All @@ -86,7 +58,6 @@ prog
}

if (privateKey) {
await validateAndSaveUcan(opts.ucan, Keypair.parse(privateKey))
spinner.succeed(
`Your already have a private key in your config, use --force to override.`
)
Expand All @@ -103,32 +74,81 @@ prog
prog
.command('register')
.describe("Register with the service using config's keypair.")
.option('--name', 'Service name.', 'nft.storage')
.option('--key', 'Service API key.', '')
.option('--url', 'Service URL.', url)
.action(async (opts) => {
const spinner = ora('Registering with the service').start()
try {
if (!config.get('private-key')) {
spinner.fail(
`You dont have a private key saved yet, run "${NAME} keypair"`
`You dont have a private key saved yet, run "${NAME} init"`
)
process.exit(1)
}

// @ts-ignore
const kp = Keypair.parse(config.get('private-key'))

await Access.validateAndRegister({
url: new URL('http://127.0.0.1:8787'),
issuer: kp,
const issuer = Keypair.parse(config.get('private-key'))
const url = new URL(opts.url)
await Access.validate({
url,
issuer,
caveats: {
as: 'mailto:hugo@dag.house',
},
onAwait: () => {
spinner.text = 'Waiting for email validation...'
})

spinner.text = 'Waiting for email validation...'
const proof = await Access.pullRegisterDelegation({
issuer,
url,
})

await Access.register({
url,
issuer,
proof,
})

spinner.succeed('Registration done.')
} catch (error) {
console.error(error)
// @ts-ignore
spinner.fail(error.message)
process.exit(1)
}
})

prog
.command('upload <file>')
.describe("Register with the service using config's keypair.")
.option('--url', 'Service URL.', url)
.action(async (file, opts) => {
const spinner = ora('Registering with the service').start()
try {
if (!config.get('private-key')) {
spinner.fail(
`You dont have a private key saved yet, run "${NAME} init"`
)
process.exit(1)
}

// @ts-ignore
const url = new URL(opts.url)

const stream = fs.createReadStream(path.resolve(file))
const checkStream = new Transform({
transform(chunk, encoding, callback) {
console.log(chunk.length)
callback(undefined, chunk)
},
})

const rsp = await undici.fetch(`${url}upload`, {
body: stream.pipe(checkStream),
method: 'POST',
})

console.log(await rsp.text())

spinner.succeed('Registration done.')
} catch (error) {
console.error(error)
Expand Down
Loading

0 comments on commit 2373447

Please sign in to comment.