Skip to content

Commit

Permalink
feat: add support for getting an account's plan (storacha#564)
Browse files Browse the repository at this point in the history
add a new function to the keyring context that lets clients get an
account's billing plan
  • Loading branch information
travis authored Oct 31, 2023
1 parent aacf2e5 commit 9565452
Show file tree
Hide file tree
Showing 21 changed files with 378 additions and 535 deletions.
4 changes: 2 additions & 2 deletions packages/w3ui/examples/react/w3console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"devDependencies": {
"@preact/preset-vite": "^2.4.0",
"@types/blueimp-md5": "^2.18.0",
"@ucanto/core": "^8.0.0",
"@ucanto/interface": "^8.0.0",
"@ucanto/core": "^9.0.0",
"@ucanto/interface": "^9.0.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.4",
Expand Down
8 changes: 4 additions & 4 deletions packages/w3ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@
"@types/jest": "^29.4.0",
"@types/jsdom": "^20.0.1",
"@types/react": "^18.0.26",
"@ucanto/client": "^8.0.0",
"@ucanto/server": "^8.0.1",
"@ucanto/transport": "^8.0.0",
"@ucanto/client": "^9.0.0",
"@ucanto/server": "^9.0.1",
"@ucanto/transport": "^9.0.0",
"@web-std/file": "^3.0.2",
"@web3-storage/capabilities": "^7.0.0",
"@web3-storage/capabilities": "^11.1.0",
"esm": "^3.2.25",
"fake-indexeddb": "^4.0.1",
"hd-scripts": "^4.1.0",
Expand Down
7 changes: 4 additions & 3 deletions packages/w3ui/packages/keyring-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
},
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/keyring-core",
"dependencies": {
"@ucanto/interface": "^8.0.0",
"@ucanto/principal": "^8.0.0",
"@web3-storage/access": "^15.2.0"
"@ucanto/interface": "^9.0.0",
"@ucanto/principal": "^9.0.0",
"@web3-storage/access": "^16.3.0",
"@web3-storage/did-mailto": "^2.0.2"
},
"eslintConfig": {
"extends": [
Expand Down
39 changes: 28 additions & 11 deletions packages/w3ui/packages/keyring-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Abilities, AgentMeta, Service } from '@web3-storage/access/types'
import type { Abilities, AgentMeta, PlanGetFailure, PlanGetSuccess, Service } from '@web3-storage/access/types'
import type {
Capability,
DID,
Expand All @@ -9,17 +9,20 @@ import type {
Delegation,
UCANOptions
} from '@ucanto/interface'
import { Agent, authorizeWaitAndClaim } from '@web3-storage/access/agent'
import { Agent as AccessAgent, authorizeWaitAndClaim, getAccountPlan } from '@web3-storage/access/agent'
import { StoreIndexedDB } from '@web3-storage/access/stores/store-indexeddb'
import * as RSASigner from '@ucanto/principal/rsa'
import * as Ucanto from '@ucanto/interface'
import * as DidMailto from '@web3-storage/did-mailto'

export { Agent, Abilities, AgentMeta, Service }
export { Abilities, AgentMeta, Service }
export const authorize = authorizeWaitAndClaim

const DB_NAME = 'w3ui'
const DB_STORE_NAME = 'keyring'
export const W3UI_ACCOUNT_LOCALSTORAGE_KEY = 'w3ui-account-email'

export type Agent = AccessAgent & { store: StoreIndexedDB }
export type PlanGetResult = Ucanto.Result<PlanGetSuccess, PlanGetFailure | Ucanto.Failure>
/**
* A Space is the core organizational structure of web3-storage,
* similar to a bucket in S3 but with some special properties.
Expand Down Expand Up @@ -101,6 +104,11 @@ export interface RegisterSpaceOpts {
provider?: DID<'web'>
}

export type Email = `${string}@${string}`
export interface Plan {
product?: DID
}

export interface KeyringContextActions {
/**
* Load the user agent and all stored data from secure storage.
Expand All @@ -119,7 +127,7 @@ export interface KeyringContextActions {
/**
* Authorize this device to act as the account linked to email.
*/
authorize: (email: `${string}@${string}`) => Promise<void>
authorize: (email: Email) => Promise<void>
/**
* Abort an ongoing account authorization.
*/
Expand All @@ -136,7 +144,7 @@ export interface KeyringContextActions {
* Register the current space and store in secure storage. Automatically sets the
* newly registered space as the current space.
*/
registerSpace: (email: string, opts?: RegisterSpaceOpts) => Promise<void>
registerSpace: (email: Email, opts?: RegisterSpaceOpts) => Promise<void>
/**
* Get all the proofs matching the capabilities. Proofs are delegations with
* an audience matching the agent DID.
Expand All @@ -155,6 +163,10 @@ export interface KeyringContextActions {
* Import a proof that delegates `*` ability on a space to this agent
*/
addSpace: (proof: Delegation) => Promise<void>
/**
* Get the plan
*/
getPlan: (email: Email) => Promise<PlanGetResult>
}

export type CreateDelegationOptions = Omit<UCANOptions, 'audience'> & {
Expand Down Expand Up @@ -191,6 +203,13 @@ export function getSpaces (agent: Agent): Space[] {
return spaces
}

/**
* Get plan of the account identified by the given email.
*/
export async function getPlan (agent: Agent, email: Email): Promise<Ucanto.Result<PlanGetSuccess, PlanGetFailure | Ucanto.Failure>> {
return await getAccountPlan(agent, DidMailto.fromEmail(email))
}

export interface CreateAgentOptions extends ServiceConfig {}

/**
Expand All @@ -200,20 +219,18 @@ export interface CreateAgentOptions extends ServiceConfig {}
export async function createAgent (
options: CreateAgentOptions = {}
): Promise<Agent> {
const dbName = `${DB_NAME}${
options.servicePrincipal != null ? '@' + options.servicePrincipal.did() : ''
}`
const dbName = `${DB_NAME}${options.servicePrincipal != null ? '@' + options.servicePrincipal.did() : ''}`
const store = new StoreIndexedDB(dbName, {
dbVersion: 1,
dbStoreName: DB_STORE_NAME
})
const raw = await store.load()
if (raw != null) {
return Object.assign(Agent.from(raw, { ...options, store }), { store })
return Object.assign(AccessAgent.from(raw, { ...options, store }), { store })
}
const principal = await RSASigner.generate()
return Object.assign(
await Agent.create({ principal }, { ...options, store }),
await AccessAgent.create({ principal }, { ...options, store }),
{ store }
)
}
2 changes: 1 addition & 1 deletion packages/w3ui/packages/keyring-core/test/agent.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test, expect } from 'vitest'
import 'fake-indexeddb/auto'

import { createAgent } from '../src/index'
import { createAgent } from '../src/index.js'

test('createAgent', async () => {
const agent = await createAgent()
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/react-keyring/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"devDependencies": {
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@ucanto/interface": "^8.0.0",
"@ucanto/principal": "^8.0.0"
"@ucanto/interface": "^9.0.0",
"@ucanto/principal": "^9.0.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
Expand Down
3 changes: 2 additions & 1 deletion packages/w3ui/packages/react-keyring/src/Authenticator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const AuthenticatorContext = createContext<AuthenticatorContextValue>([
throw new Error('missing keyring context provider')
},
addSpace: async () => {},
authorize: async () => {}
authorize: async () => {},
getPlan: async () => ({ error: { name: 'KeyringContextMissing', message: 'missing keyring context provider' } })
}
])

Expand Down
20 changes: 15 additions & 5 deletions packages/w3ui/packages/react-keyring/src/providers/Keyring.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type {
KeyringContextState,
KeyringContextActions,
ServiceConfig,
RegisterSpaceOpts
RegisterSpaceOpts,
Email,
Plan,
PlanGetResult
} from '@w3ui/keyring-core'
import type {
Capability,
Expand All @@ -23,11 +26,12 @@ import {
Space,
getCurrentSpace as getCurrentSpaceInAgent,
getSpaces,
getPlan as getPlanWithAgent,
CreateDelegationOptions,
W3UI_ACCOUNT_LOCALSTORAGE_KEY
} from '@w3ui/keyring-core'

export { KeyringContextState, KeyringContextActions }
export { Plan, KeyringContextState, KeyringContextActions }

export type KeyringContextValue = [
state: KeyringContextState,
Expand Down Expand Up @@ -56,7 +60,8 @@ export const keyringContextDefaultValue: KeyringContextValue = [
createDelegation: async () => {
throw new Error('missing keyring context provider')
},
addSpace: async () => {}
addSpace: async () => {},
getPlan: async () => ({ error: { name: 'KeyringContextMissing', message: 'missing keyring context provider' } })
}
]

Expand Down Expand Up @@ -161,7 +166,6 @@ export function KeyringProvider ({

const resetAgent = async (): Promise<void> => {
const agent = await getAgent()
// @ts-expect-error TODO expose store from access client
await Promise.all([agent.store.reset(), unloadAgent()])
}

Expand Down Expand Up @@ -193,6 +197,11 @@ export function KeyringProvider ({
await agent.importSpaceFromDelegation(proof)
}

const getPlan = async (email: Email): Promise<PlanGetResult> => {
const agent = await getAgent()
return await getPlanWithAgent(agent, email)
}

const state = {
space,
spaces,
Expand All @@ -210,7 +219,8 @@ export function KeyringProvider ({
setCurrentSpace,
getProofs,
createDelegation,
addSpace
addSpace,
getPlan
}

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/w3ui/packages/react-uploader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"dependencies": {
"@w3ui/react-keyring": "workspace:^6.0.1",
"@w3ui/uploader-core": "workspace:^",
"@web3-storage/capabilities": "^7.0.0",
"@web3-storage/capabilities": "^11.1.0",
"ariakit-react-utils": "0.17.0-next.27"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/w3ui/packages/react-uploads-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"dependencies": {
"@w3ui/react-keyring": "workspace:^6.0.1",
"@w3ui/uploads-list-core": "workspace:^",
"@web3-storage/capabilities": "^7.0.0",
"@web3-storage/capabilities": "^11.1.0",
"ariakit-react-utils": "0.17.0-next.27"
},
"peerDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/solid-keyring/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
},
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/solid-keyring",
"dependencies": {
"@ucanto/interface": "^8.0.0",
"@ucanto/principal": "^8.0.0",
"@ucanto/interface": "^9.0.0",
"@ucanto/principal": "^9.0.0",
"@w3ui/keyring-core": "workspace:^5.0.1"
},
"peerDependencies": {
Expand Down
17 changes: 13 additions & 4 deletions packages/w3ui/packages/solid-keyring/src/providers/Keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import type {
ServiceConfig,
CreateDelegationOptions,
Agent,
Abilities
Abilities,
Email,
PlanGetResult
} from '@w3ui/keyring-core'
import type { Delegation, Capability, DID, Principal } from '@ucanto/interface'

Expand All @@ -21,6 +23,7 @@ import {
createAgent,
getCurrentSpace as getCurrentSpaceInAgent,
getSpaces,
getPlan as getPlanWithAgent,
W3UI_ACCOUNT_LOCALSTORAGE_KEY
} from '@w3ui/keyring-core'

Expand Down Expand Up @@ -55,7 +58,8 @@ export const AuthContext: Context<KeyringContextValue> = createContext<KeyringCo
throw new Error('missing keyring context provider')
},
addSpace: async () => {},
authorize: async () => {}
authorize: async () => {},
getPlan: async () => ({ error: { name: 'KeyringContextMissing', message: 'missing keyring context provider' } })
}
])

Expand Down Expand Up @@ -158,7 +162,6 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (

const resetAgent = async (): Promise<void> => {
const agent = await getAgent()
// @ts-expect-error TODO: expose store in access client
await Promise.all([agent.store.reset(), unloadAgent()])
}

Expand All @@ -167,6 +170,11 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (
return agent.proofs(caps)
}

const getPlan = async (email: Email): Promise<PlanGetResult> => {
const agent = await getAgent()
return await getPlanWithAgent(agent, email)
}

const createDelegation = async (
audience: Principal,
abilities: Abilities[],
Expand Down Expand Up @@ -201,7 +209,8 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (
createDelegation,
addSpace,
authorize,
cancelAuthorize
cancelAuthorize,
getPlan
}

return createComponent(AuthContext.Provider, {
Expand Down
2 changes: 1 addition & 1 deletion packages/w3ui/packages/solid-uploader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"dependencies": {
"@w3ui/solid-keyring": "workspace:^5.0.1",
"@w3ui/uploader-core": "workspace:^",
"@web3-storage/capabilities": "^7.0.0",
"@web3-storage/capabilities": "^11.1.0",
"multiformats": "^11.0.1"
},
"peerDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/solid-uploads-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
"dependencies": {
"@w3ui/solid-keyring": "workspace:^5.0.1",
"@w3ui/uploads-list-core": "workspace:^",
"@web3-storage/capabilities": "^7.0.0"
"@web3-storage/capabilities": "^11.1.0"
},
"peerDependencies": {
"solid-js": "^1.7.8"
},
"devDependencies": {
"@ucanto/interface": "^8.0.0"
"@ucanto/interface": "^9.0.0"
},
"eslintConfig": {
"extends": [
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/uploader-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
},
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/uploader-core",
"dependencies": {
"@ucanto/interface": "^8.0.0",
"@web3-storage/upload-client": "^9.1.0",
"@ucanto/interface": "^9.0.0",
"@web3-storage/upload-client": "^11.1.0",
"multiformats": "^11.0.1"
},
"eslintConfig": {
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/uploads-list-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
},
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/uploads-list-core",
"dependencies": {
"@ucanto/interface": "^8.0.0",
"@web3-storage/upload-client": "^9.1.0"
"@ucanto/interface": "^9.0.0",
"@web3-storage/upload-client": "^11.1.0"
},
"eslintConfig": {
"extends": [
Expand Down
4 changes: 2 additions & 2 deletions packages/w3ui/packages/vue-keyring/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"vue": "^3.0.0"
},
"devDependencies": {
"@ucanto/interface": "^8.0.0",
"@ucanto/principal": "^8.0.0"
"@ucanto/interface": "^9.0.0",
"@ucanto/principal": "^9.0.0"
},
"eslintConfig": {
"extends": [
Expand Down
Loading

0 comments on commit 9565452

Please sign in to comment.