Skip to content

Commit

Permalink
Merge branch 'main' into feat/services
Browse files Browse the repository at this point in the history
  • Loading branch information
travis committed Feb 28, 2025
2 parents 7706e68 + 131cd56 commit 2e44995
Show file tree
Hide file tree
Showing 19 changed files with 1,089 additions and 365 deletions.
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@
"dependencies": {
"@atproto/api": "^0.13.35",
"@atproto/oauth-client-browser": "^0.3.9",
"@headlessui/react": "^2.2.0",
"@heroicons/react": "^2.2.0",
"@tailwindcss/postcss": "^4.0.9",
"@tanstack/react-query": "^5.66.0",
"@w3ui/react": "^2.5.5",
"@web3-storage/w3up-client": "^17.1.2",
"dexie": "^4.0.11",
"dexie-react-hooks": "^1.1.7",
"next": "15.1.6",
"postcss": "^8.5.2",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"tailwindcss": "^4.0.9"
},
"devDependencies": {
"@cloudflare/next-on-pages": "^1.13.7",
Expand All @@ -31,8 +37,6 @@
"@types/react-dom": "^18",
"eslint": "^9",
"eslint-config-next": "15.1.6",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5",
"wrangler": "^3.109.3"
}
Expand Down
795 changes: 483 additions & 312 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
"@tailwindcss/postcss": {},
},
};

Expand Down
Binary file added public/bluesky-storacha.webp
Binary file not shown.
19 changes: 19 additions & 0 deletions src/app/backups/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Blobs, Repos } from "@/components/Backups"

export const runtime = 'edge'

export interface BackupsProps {
params: Promise<{ id: string }>
}

export default async function Backups ({ params }: BackupsProps) {
const id = (await params).id

return (
<div className="bg-white/80 p-10 h-screen">
<h1 className="text-xl font-mono font-bold uppercase">Backup {id}</h1>
<Repos id={id} className='py-4 w-full' />
<Blobs id={id} className='py-4 w-full' />
</div>
)
}
55 changes: 55 additions & 0 deletions src/app/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client'

import Dexie, { type EntityTable } from 'dexie'

interface Backup {
id: number;
accountDid: string;
createdAt: Date;
}

interface Repo {
cid: string;
backupId: number;
accountDid: string;
}

interface Blob {
cid: string;
backupId: number;
accountDid: string;
}

interface Commit {
accountDid: string;
commitRev: string;
}

const db = new Dexie('storacha-bluesky-backups') as Dexie & {
backups: EntityTable<
Backup,
'id'
>;
repos: EntityTable<
Repo,
'cid'
>;
blobs: EntityTable<
Blob,
'cid'
>;
commits: EntityTable<
Commit,
'accountDid'
>;
};

// Schema declaration:
db.version(1).stores({
backups: 'id++, accountDid, createdAt',
repos: 'cid, backupId, accountDid',
blobs: 'cid, backupId, accountDid',
commits: 'accountDid, commitRev'
});

export default db
12 changes: 9 additions & 3 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";

.btn {
@apply px-2 py-1 border rounded-lg cursor-pointer hover:bg-white;
}

.ipt {
@apply px-2 py-1 border rounded-lg;
}
3 changes: 2 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import RootProviders from "./providers";
import './globals.css'

export default function RootLayout({
children,
Expand All @@ -9,7 +10,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body>
<body className="bg-[url(/bluesky-storacha.webp)] bg-contain backdrop-blur-xs">
<RootProviders>{children}</RootProviders>
</body>
</html>
Expand Down
27 changes: 20 additions & 7 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import BackupButton from "@/components/BackupButton";
import BlueskyAuthenticator from "@/components/BlueskyAuthenticator";
import StorachaAuthenticator from "@/components/StorachaAuthenticator";
import { Backups } from "@/components/Backups";

export default function Home () {
return (
<div>
<h1>Bluesky Backup Webapp</h1>
<p>Lets get started</p>
<h4>Bluesky Auth</h4>
<BlueskyAuthenticator />
<div className="bg-white/80 p-10 h-screen">
<h1 className="text-lg mb-8 font-bold font-mono uppercase">Bluesky Backups</h1>
<div className="mb-4">
<h4 className="text-sm font-bold font-mono uppercase">Bluesky Auth</h4>
<div className="p-2 bg-white/50 rounded-lg">
<BlueskyAuthenticator />
</div>
</div>

<h4>Storacha Auth</h4>
<StorachaAuthenticator />
<div className="mb-4">
<h4 className="text-sm font-bold font-mono uppercase">Storacha Auth</h4>
<div className="p-2 bg-white/50 rounded-lg">
<StorachaAuthenticator />
</div>
</div>

<BackupButton />

<Backups className="mt-16" />
</div>
)
}
31 changes: 11 additions & 20 deletions src/components/Authenticator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ export function AuthenticationForm (): ReactNode {
const [{ submitted }] = useAuthenticator()
return (
<div className='authenticator'>
<Authenticator.Form className='text-zinc-950 bg-grad rounded-xl shadow-md px-10 pt-8 pb-8'>
<div>
<label className='block mb-2 uppercase text-xs font-semibold tracking-wider m-1 font-mono' htmlFor='authenticator-email'>Email</label>
<Authenticator.EmailInput className='text-black py-2 px-2 rounded block mb-4 border border-gray-800 w-80 shadow-md' id='authenticator-email' required />
</div>
<div className='text-center mt-4'>
<button
className='inline-block bg-zinc-950 hover:outline text-white font-bold text-sm px-6 py-2 rounded-full whitespace-nowrap'
type='submit'
disabled={submitted}
>
Authorize
</button>
</div>
<Authenticator.Form className='flex flex-row'>
<Authenticator.EmailInput className='ipt w-82' placeholder="Email" required />
<button
className='btn'
type='submit'
disabled={submitted}>
Authorize
</button>
</Authenticator.Form>
</div >
)
Expand All @@ -28,16 +22,13 @@ export function AuthenticationForm (): ReactNode {
export function AuthenticationSubmitted (): ReactNode {
const [{ email }] = useAuthenticator()
return (
<div className='authenticator'>
<div className='bg-grad rounded-xl shadow-md px-10 pt-8 pb-8'>
<h1 className='text-xl font-semibold'>Verify your email address!</h1>
<p className='pt-2 pb-4'>
<div>
<p className='py-1'>
Click the link in the email we sent to <span className='font-semibold tracking-wide'>{email}</span> to authorize this agent.
</p>
<Authenticator.CancelButton className='inline-block bg-zinc-950 hover:outline text-white font-bold text-sm px-6 py-2 rounded-full whitespace-nowrap' >
<Authenticator.CancelButton className='btn pb-2' >
Cancel
</Authenticator.CancelButton>
</div>
</div>
)
}
Expand Down
110 changes: 110 additions & 0 deletions src/components/BackupButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client'

import { useBskyAuthContext } from "@/contexts"
import { backup, BackupMetadataStore } from "@/lib/bluesky"
import { Space, useW3 } from "@w3ui/react"
import { useState } from "react"
import { SpaceFinder } from "./SpaceFinder"
import db from "@/app/db"

const backupMetadataStore: BackupMetadataStore = {
async setLatestCommit (accountDid, commitRev) {
await db.commits.put({ accountDid, commitRev })
},
async addRepo (cid, backupId, accountDid) {
await db.repos.put({ cid, backupId, accountDid })
},
async addBlob (cid, backupId, accountDid) {
await db.blobs.put({ cid, backupId, accountDid })
},
async addBackup (accountDid) {
return await db.backups.add({ accountDid, createdAt: new Date() })
}
}

export interface BackupButtonProps {
backupMetadataStore: BackupMetadataStore
}

export default function BackupButton () {
const [isBackingUp, setIsBackingUp] = useState(false)
const [selectedSpace, setSelectedSpace] = useState<Space>()
const [storacha] = useW3()
const bluesky = useBskyAuthContext()
const backupEvents = new EventTarget()
const space = selectedSpace ?? storacha?.spaces[0]
async function onClick () {
if (space && bluesky.userProfile && bluesky.agent && storacha.client) {
await storacha.client.setCurrentSpace(space.did())

setIsBackingUp(true)
await backup(bluesky.userProfile, bluesky.agent, storacha.client, backupMetadataStore, { eventTarget: backupEvents })
setIsBackingUp(false)
} else {
console.log('not backing up, profile, agent, client:', bluesky.userProfile, bluesky.agent, storacha.client)
}
}
const userAuthenticatedToBothServices = bluesky.userProfile && (storacha.accounts.length > 0)
const [backupProgressComponent, setBackupProgressComponent] = useState(
<>
Backing up your Bluesky account...
</>
)
backupEvents.addEventListener('repo:fetching', () => {
setBackupProgressComponent(
<>
Backing up your Bluesky account...
</>
)
})
backupEvents.addEventListener('repo:fetching', () => {
setBackupProgressComponent(
<>
Backing up your Bluesky account...
</>
)
})
backupEvents.addEventListener('repo:uploaded', () => {
setBackupProgressComponent(
<>
Backup started...
</>
)
})
backupEvents.addEventListener('blob:fetching', (e) => {
const { i: loaded, count: total } = (e as CustomEvent).detail ?? { i: 0, count: 1 }
const percentComplete = Math.floor((loaded / total) * 100)
setBackupProgressComponent(
<div>
<h3>Backing up your Bluesky account...</h3>
<div className='relative flex flex-row justify-start border'>
<div className='bg-black h-4' style={{ width: `${percentComplete}%` }}>
</div>
</div>
</div>
)
})
return userAuthenticatedToBothServices ? (
isBackingUp ? (
<div>
{backupProgressComponent}
</div>
) : (
<div>
<p>Please choose the Storacha space where you&apos;d like to back up your Bluesky account:</p>
{storacha.spaces && (storacha.spaces.length > 0) && (
<SpaceFinder
selected={space} setSelected={setSelectedSpace} spaces={storacha.spaces}
className="w-52" />
)}
<button
onClick={onClick} disabled={!space}
className="btn">
{space ? "Back It Up!" : "Please Pick a Space"}
</button>
</div>
)
) : (
<div>Please authenticate to both Bluesky and Storacha to continue.</div>
)
}
Loading

0 comments on commit 2e44995

Please # to comment.