1- import { core , useStore , server , dataBrowser } from '@tomic/react' ;
1+ import {
2+ core ,
3+ useStore ,
4+ server ,
5+ dataBrowser ,
6+ generateKeyPair ,
7+ } from '@tomic/react' ;
28import { useState , useCallback , FormEvent , FC , useEffect , useId } from 'react' ;
39import { styled } from 'styled-components' ;
410import { stringToSlug } from '../../../../../helpers/stringToSlug' ;
@@ -16,6 +22,27 @@ import { CustomResourceDialogProps } from '../../useNewResourceUI';
1622import { useCreateAndNavigate } from '../../../../../hooks/useCreateAndNavigate' ;
1723import { useSettings } from '../../../../../helpers/AppSettings' ;
1824
25+ const DRIVE_PUBLIC_KEY = 'https://atomicdata.dev/properties/drive/publicKey' ;
26+ const DRIVE_PRIVATE_KEY = 'https://atomicdata.dev/properties/drive/privateKey' ;
27+ const DRIVE_HASH = 'https://atomicdata.dev/properties/drive/hash' ;
28+
29+ function decodeBase64 ( base64 : string ) : Uint8Array {
30+ const binary = atob ( base64 ) ;
31+ const bytes = new Uint8Array ( binary . length ) ;
32+
33+ for ( let i = 0 ; i < binary . length ; i ++ ) {
34+ bytes [ i ] = binary . charCodeAt ( i ) ;
35+ }
36+
37+ return bytes ;
38+ }
39+
40+ function toHex ( bytes : Uint8Array ) : string {
41+ return Array . from ( bytes )
42+ . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) )
43+ . join ( '' ) ;
44+ }
45+
1946export const NewDriveDialog : FC < CustomResourceDialogProps > = ( {
2047 onClose,
2148 onCreated,
@@ -38,27 +65,44 @@ export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
3865 'No agent set in the Store, required when creating a Drive' ,
3966 ) ;
4067 }
68+ const driveKeys = await generateKeyPair ( ) ;
69+ const drivePublicKeyBytes = decodeBase64 ( driveKeys . publicKey ) ;
70+ const prefix = new TextEncoder ( ) . encode ( 'atomicdata.drive' ) ;
71+ const hashInput = new Uint8Array (
72+ prefix . length + drivePublicKeyBytes . length ,
73+ ) ;
74+ hashInput . set ( prefix , 0 ) ;
75+ hashInput . set ( drivePublicKeyBytes , prefix . length ) ;
76+ const digest = await crypto . subtle . digest ( 'SHA-256' , hashInput ) ;
77+ const driveHash = toHex ( new Uint8Array ( digest ) . slice ( 0 , 16 ) ) ;
4178
4279 await createAndNavigate (
4380 server . classes . drive ,
4481 {
4582 [ core . properties . name ] : name ,
4683 [ core . properties . write ] : [ agent . subject ] ,
4784 [ core . properties . read ] : [ agent . subject ] ,
85+ [ DRIVE_PUBLIC_KEY ] : driveKeys . publicKey ,
86+ [ DRIVE_PRIVATE_KEY ] : driveKeys . privateKey ,
87+ [ DRIVE_HASH ] : driveHash ,
4888 } ,
4989 {
5090 noParent : true ,
5191 skipNavigation,
5292 onCreated : async resource => {
5393 // Add drive to the agents drive list.
54- const agentResource = await store . getResource ( agent . subject ! ) ;
55- agentResource . push ( server . properties . drives , [ resource . subject ] ) ;
56- await agentResource . save ( ) ;
94+ // DID agents may not have a local resource, so we ignore errors here.
95+ try {
96+ const agentResource = await store . getResource ( agent . subject ! ) ;
97+ agentResource . push ( server . properties . drives , [ resource . subject ] ) ;
98+ await agentResource . save ( ) ;
99+ } catch ( _e ) {
100+ // Agent resource update failed (e.g., DID agents don't have a local resource)
101+ }
57102
58103 // Create a default ontology.
59104 const ontologyName = stringToSlug ( name . trim ( ) ) ;
60105 const ontology = await store . newResource ( {
61- subject : `${ resource . subject } /defaultOntology` ,
62106 isA : core . classes . ontology ,
63107 parent : resource . subject ,
64108 propVals : {
0 commit comments