Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT]feat: use arfs-js as filesystems for repositories #125

Draft
wants to merge 4 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@uiw/react-codemirror": "^4.21.11",
"@uiw/react-md-editor": "^3.23.5",
"ardb": "^1.1.10",
"arfs-js": "^1.2.6",
"arweave": "^1.14.4",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const CONTRACT_TX_ID = 'w5ZU15Y2cLzZlu3jewauIlnzbKw-OAxbN9G5TbuuiDQ'
export const CONTRACT_TX_ID = 'NLIpqKz21gA68AQUL7zDorx7MIYHTfrrSbXDn2vhm-I'
export const VITE_GA_TRACKING_ID = 'G-L433HSR0D0'
export const AMPLITUDE_TRACKING_ID = '92a463755ed8c8b96f0f2353a37b7b2'
export const PL_REPO_ID = '6ace6247-d267-463d-b5bd-7e50d98c3693'
Expand Down
12 changes: 10 additions & 2 deletions src/helpers/getArrayBufSize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
export function getArrayBufSize(arrayBuffer: ArrayBuffer): GetArrayBufSizeReturnType {
const byteSize = arrayBuffer.byteLength
export function getRepoSize(arrayBufferOrSize: ArrayBuffer | number): GetArrayBufSizeReturnType {
let byteSize = 0

if (arrayBufferOrSize instanceof ArrayBuffer) {
byteSize = arrayBufferOrSize.byteLength
}

if (typeof arrayBufferOrSize === 'number') {
byteSize = arrayBufferOrSize
}

if (byteSize >= 1073741824) {
return {
Expand Down
51 changes: 51 additions & 0 deletions src/lib/arfs/arfsSingleton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ArFS, BiFrost, Drive } from 'arfs-js'

let instance: ArFSSingleton
let driveInstance: Drive | null = null
let bifrostInstance: BiFrost | null = null
let arfsInstance: ArFS | null = null

class ArFSSingleton {
driveInstance: Drive | null = null
bifrostInstance: BiFrost | null = null
arfsInstance: ArFS | null = null

constructor() {
if (instance) {
throw new Error('You can only create one instance!')
}
// eslint-disable-next-line @typescript-eslint/no-this-alias
instance = this
}

getInstance() {
return this
}

getBifrostInstance() {
return bifrostInstance
}

getArfsInstance() {
return arfsInstance
}

getDriveInstance() {
return driveInstance
}

setDrive(drive: Drive) {
driveInstance = drive
}

setBifrost(bifrost: BiFrost) {
bifrostInstance = bifrost
}

setArFS(arfs: ArFS) {
arfsInstance = arfs
}
}

const singletonArfs = Object.freeze(new ArFSSingleton())
export default singletonArfs
7 changes: 7 additions & 0 deletions src/lib/arfs/getArFS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ArFS } from 'arfs-js'

export function getArFS() {
const arfs = new ArFS({ wallet: 'use_wallet' })

return arfs
}
7 changes: 7 additions & 0 deletions src/lib/arfs/getBifrost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ArFS, BiFrost, Drive } from 'arfs-js'

export function getBifrost(drive: Drive, arfs: ArFS) {
const bifrost = new BiFrost(drive, arfs)

return bifrost
}
12 changes: 9 additions & 3 deletions src/lib/git/helpers/fsWithName.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import LightningFS from '@isomorphic-git/lightning-fs'
// import LightningFS from '@isomorphic-git/lightning-fs'

import singletonArfs from '@/lib/arfs/arfsSingleton'

export function fsWithName(name: string) {
return new LightningFS(name)
const bifrost = singletonArfs.getBifrostInstance()

if (!bifrost) throw new Error('Bifrost uninitialized.')
console.log({ name })
return bifrost.fs
}

export type FSType = ReturnType<typeof fsWithName>
export type FSType = ReturnType<typeof fsWithName>
98 changes: 49 additions & 49 deletions src/lib/git/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,64 +33,64 @@ const arweave = new Arweave({
protocol: 'https'
})

export async function postNewRepo({ id, title, description, file, owner, visibility }: any) {
const publicKey = await getActivePublicKey()
export async function postNewRepo({ id, dataTxId, title, description, visibility }: any) {
// const publicKey = await getActivePublicKey()

const userSigner = await getSigner()

let data = (await toArrayBuffer(file)) as ArrayBuffer
// let data = (await toArrayBuffer(file)) as ArrayBuffer

const inputTags = [
{ name: 'App-Name', value: 'Protocol.Land' },
{ name: 'Content-Type', value: file.type },
{ name: 'Creator', value: owner },
{ name: 'Title', value: title },
{ name: 'Description', value: description },
{ name: 'Repo-Id', value: id },
{ name: 'Type', value: 'repo-create' },
{ name: 'Visibility', value: visibility }
] as Tag[]
// const inputTags = [
kranthicodes marked this conversation as resolved.
Show resolved Hide resolved
// { name: 'App-Name', value: 'Protocol.Land' },
// { name: 'Content-Type', value: file.type },
// { name: 'Creator', value: owner },
// { name: 'Title', value: title },
// { name: 'Description', value: description },
// { name: 'Repo-Id', value: id },
// { name: 'Type', value: 'repo-create' },
// { name: 'Visibility', value: visibility }
// ] as Tag[]

let privateStateTxId = ''
if (visibility === 'private') {
const pubKeyArray = [strToJwkPubKey(publicKey)]
// Encrypt
const { aesKey, encryptedFile, iv } = await encryptFileWithAesGcm(data)
const encryptedAesKeysArray = await encryptAesKeyWithPublicKeys(aesKey, pubKeyArray)
// // Store 'encrypted', 'iv', and 'encryptedKeyArray' securely

const privateState = {
version: '0.1',
iv,
encKeys: encryptedAesKeysArray,
pubKeys: [publicKey]
}
const privateStateTxId = ''
// if (visibility === 'private') {
// const pubKeyArray = [strToJwkPubKey(publicKey)]
// // Encrypt
// const { aesKey, encryptedFile, iv } = await encryptFileWithAesGcm(data)
// const encryptedAesKeysArray = await encryptAesKeyWithPublicKeys(aesKey, pubKeyArray)
// // // Store 'encrypted', 'iv', and 'encryptedKeyArray' securely

const privateInputTags = [
{ name: 'App-Name', value: 'Protocol.Land' },
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Type', value: 'private-state' },
{ name: 'ID', value: id }
] as Tag[]
// const privateState = {
// version: '0.1',
// iv,
// encKeys: encryptedAesKeysArray,
// pubKeys: [publicKey]
// }

const privateStateTxResponse = await signAndSendTx(JSON.stringify(privateState), privateInputTags, userSigner)
// const privateInputTags = [
// { name: 'App-Name', value: 'Protocol.Land' },
// { name: 'Content-Type', value: 'application/json' },
// { name: 'Type', value: 'private-state' },
// { name: 'ID', value: id }
// ] as Tag[]

if (!privateStateTxResponse) {
throw new Error('Failed to post Private State')
}
// const privateStateTxResponse = await signAndSendTx(JSON.stringify(privateState), privateInputTags, userSigner)

privateStateTxId = privateStateTxResponse
// if (!privateStateTxResponse) {
// throw new Error('Failed to post Private State')
// }

data = encryptedFile
}
// privateStateTxId = privateStateTxResponse

await waitFor(500)
// data = encryptedFile
// }

const dataTxResponse = await signAndSendTx(data, inputTags, userSigner, true)
// await waitFor(500)

if (!dataTxResponse) {
throw new Error('Failed to post Git repository')
}
// const dataTxResponse = await signAndSendTx(data, inputTags, userSigner, true)

// if (!dataTxResponse) {
// throw new Error('Failed to post Git repository')
// }

const contract = await getWarpContract(CONTRACT_TX_ID, userSigner)

Expand All @@ -100,13 +100,13 @@ export async function postNewRepo({ id, title, description, file, owner, visibil
id,
name: title,
description,
dataTxId: dataTxResponse,
dataTxId,
visibility,
privateStateTxId
}
})

return { txResponse: dataTxResponse }
return { txResponse: id }
}

export async function updateGithubSync({ id, currentGithubSync, githubSync }: any) {
Expand Down Expand Up @@ -398,9 +398,9 @@ export async function createNewRepo(title: string, fs: FSType, owner: string, id

await waitFor(1000)

const repoBlob = await packGitRepo({ fs, dir })
// const repoBlob = await packGitRepo({ fs, dir })

return { repoBlob, commit: sha }
return { commit: sha }
} catch (error) {
console.error('failed to create repo')
}
Expand Down
29 changes: 15 additions & 14 deletions src/pages/home/components/NewRepoModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import { useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import SVG from 'react-inlinesvg'
import { useNavigate } from 'react-router-dom'
import { v4 as uuidv4 } from 'uuid'
// import { v4 as uuidv4 } from 'uuid'
import * as yup from 'yup'

import CloseCrossIcon from '@/assets/icons/close-cross.svg'
import { Button } from '@/components/common/buttons'
import CostEstimatesToolTip from '@/components/CostEstimatesToolTip'
import { trackGoogleAnalyticsEvent } from '@/helpers/google-analytics'
import { withAsync } from '@/helpers/withAsync'
import { getArFS } from '@/lib/arfs/getArFS'
import { getBifrost } from '@/lib/arfs/getBifrost'
import { createNewRepo, postNewRepo } from '@/lib/git'
import { fsWithName } from '@/lib/git/helpers/fsWithName'
// import { fsWithName } from '@/lib/git/helpers/fsWithName'
import { useGlobalStore } from '@/stores/globalStore'
import { isRepositoryNameAvailable } from '@/stores/repository-core/actions/repoMeta'

Expand Down Expand Up @@ -57,7 +59,8 @@ export default function NewRepoModal({ setIsOpen, isOpen }: NewRepoModalProps) {
async function handleCreateBtnClick(data: yup.InferType<typeof schema>) {
setIsSubmitting(true)

const id = uuidv4()
const arfs = getArFS()

const { title, description } = data
const owner = authState.address || 'Protocol.Land user'

Expand All @@ -70,28 +73,26 @@ export default function NewRepoModal({ setIsOpen, isOpen }: NewRepoModalProps) {
}

try {
const fs = fsWithName(id)
const createdRepo = await createNewRepo(title, fs, owner, id)

if (createdRepo && createdRepo.commit && createdRepo.repoBlob) {
const { repoBlob } = createdRepo
const drive = await arfs.drive.create(title)
const bifrost = getBifrost(drive, arfs)
const createdRepo = await createNewRepo(title, bifrost.fs, owner, drive.driveId!)

if (createdRepo && createdRepo.commit) {
const result = await postNewRepo({
id,
id: drive.driveId!,
title,
description,
file: repoBlob,
owner: authState.address,
visibility
visibility,
dataTxId: drive.id!
})

if (result.txResponse) {
trackGoogleAnalyticsEvent('Repository', 'Successfully created a repo', 'Create new repo', {
repo_id: id,
repo_id: drive.id!,
repo_name: title
})

navigate(`/repository/${id}`)
navigate(`/repository/${drive.driveId!}`)
}
}
} catch (error) {
Expand Down
6 changes: 2 additions & 4 deletions src/stores/pull-request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ const createPullRequestSlice: StateCreator<CombinedSlices, [['zustand/immer', ne
return error || !response || !response.result ? null : response.result
}

const loadRepoWithStatus = async ({ id, dataTxId, uploadStrategy, privateStateTxId }: Repo) => {
const { error, response } = await withAsync(() =>
loadRepository(id, dataTxId, uploadStrategy, privateStateTxId)
)
const loadRepoWithStatus = async ({ id }: Repo) => {
const { error, response } = await withAsync(() => loadRepository(id))
return !error && response && response.success ? { success: true } : { success: false }
}

Expand Down
Loading