Skip to content

Commit

Permalink
feat(snekfinder): add open-storage-gateway backend
Browse files Browse the repository at this point in the history
Add the OSGBackend that communicates with osg.snek.at to store files.
For that most functionality from IPFSBackend is moved to the abstract
Backend. The OSGBackend and the IPFSBackend are both subclasses of the
Backend class and implement seperate upload methods.
  • Loading branch information
schettn authored Nov 21, 2021
1 parent 7edd4e0 commit 1612b80
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 66 deletions.
53 changes: 1 addition & 52 deletions packages/snek-finder/src/backends/IPFSBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,16 @@ export const ipfs = ipfsClient.create({
})

export class IPFSBackend extends Backend {
public initBackendLink!: string
public onBackendLinkChange!: (link: string) => void

constructor(public indexKey: string = 'snek-finder-ipfs-backend') {
super()
this.indexKey = indexKey
}

async init() {
const response = await (await fetch(this.initBackendLink)).json()

await this.writeIndex(response)
}

async readIndex() {
if (window) {
const getIndexData = () => {
const indexData = window.localStorage.getItem(this.indexKey)

return indexData && JSON.parse(indexData)
}

let indexData = getIndexData()

if (!indexData) {
await this.init()
indexData = getIndexData()
}

return {data: indexData}
} else {
throw new Error(
'window not defined, make sure to load this script in the browser'
)
}
}

async writeIndex(index: object) {
if (window) {
// make a file from index including date in name
const indexData = JSON.stringify(index)
const indexFile = new File([indexData], `${Date.now()}.json`)
const indexUrl = await this.upload(indexFile)

this.onBackendLinkChange(indexUrl)

window.localStorage.setItem(this.indexKey, indexData)
} else {
throw new Error(
'window not defined, make sure to load this script in the browser'
)
}
}

async upload(file: File) {
const {cid} = await ipfs.add({path: file.name, content: file.stream()})

return `https://cloudflare-ipfs.com/ipfs/${cid.toString()}`
}
}

const backend = new IPFSBackend('snek-finder-ipfs-backend-root')

export default backend
export default new IPFSBackend('snek-finder-ipfs-backend-root')
26 changes: 26 additions & 0 deletions packages/snek-finder/src/backends/OSGBackend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Backend} from './backend'

export class OSGBackend extends Backend {
constructor(public indexKey: string = 'snek-finder-osg-backend') {
super()
this.indexKey = indexKey
}

async upload(file: File) {
const url = 'https://osg.snek.at/storage'

const formData = new FormData()
formData.append('file', file)

const resp = await fetch(url, {
body: formData,
method: 'POST'
})

const json = await resp.json()

return `${url}/${json.file_id}`
}
}

export default new OSGBackend('snek-finder-osg-backend-root')
55 changes: 53 additions & 2 deletions packages/snek-finder/src/backends/backend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
export abstract class Backend {
abstract readIndex(): Promise<any>
abstract writeIndex(index: object): Promise<void>
public initBackendLink?: string
public onBackendLinkChange!: (link: string) => void

public abstract indexKey: string

abstract upload(file: File): Promise<any>

async init() {
if (this.initBackendLink) {
const response = await (await fetch(this.initBackendLink)).json()

await this.writeIndex(response)
}
}

async readIndex() {
if (window) {
const getIndexData = () => {
const indexData = window.localStorage.getItem(this.indexKey)

return indexData && JSON.parse(indexData)
}

let indexData = getIndexData()

if (!indexData) {
await this.init()
indexData = getIndexData()
}

return {data: indexData}
} else {
throw new Error(
'window not defined, make sure to load this script in the browser'
)
}
}

async writeIndex(index: object) {
if (window) {
// make a file from index including date in name
const indexData = JSON.stringify(index)
const indexFile = new File([indexData], `${Date.now()}.json`)
const indexUrl = await this.upload(indexFile)

this.onBackendLinkChange(indexUrl)

window.localStorage.setItem(this.indexKey, indexData)
} else {
throw new Error(
'window not defined, make sure to load this script in the browser'
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type SnekStudioProps = {
/**
* Called when the editor should be closed with saving the editing state
*/
onComplete(dataURL: string): void
onComplete(file: Blob | null, dataURL: string): void
/**
* Called when the editor should be closed without saving the editing state
*/
Expand Down Expand Up @@ -39,9 +39,9 @@ const SnekStudio: React.FC<SnekStudioProps> = props => {
src={props.src}
onClose={props.onClose}
onComplete={({canvas}: {canvas: HTMLCanvasElement}) => {
const dataURL = canvas.toDataURL('image/png')

props.onComplete(dataURL)
canvas.toBlob(blob => {
props.onComplete(blob, canvas.toDataURL('image/png'))
})
}}
onBeforeComplete={() => false}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react'

import SnekFinder, {SnekFinderProps} from '.'
import IPFSBackend from '../../backends/IPFSBackend'
import OSGBackend from '../../backends/OSGBackend'

export default {
title: 'Applications/SnekFinder',
Expand All @@ -11,7 +12,7 @@ export default {

const Template: Story<SnekFinderProps> = args => <SnekFinder {...args} />

export const Primary: Story<SnekFinderProps> = Template.bind({})
export const IPFS: Story<SnekFinderProps> = Template.bind({})

IPFSBackend.onBackendLinkChange = (link: string) => {
console.log(link)
Expand All @@ -20,13 +21,30 @@ IPFSBackend.onBackendLinkChange = (link: string) => {
IPFSBackend.initBackendLink =
'https://cloudflare-ipfs.com/ipfs/QmSw2QEGRx9PzBXsxt5HoKiong1hkWYN8pNwLKqwNPgaiR'

Primary.args = {backend: IPFSBackend}
IPFS.args = {backend: IPFSBackend}

export const Selector: Story<SnekFinderProps> = Template.bind({})
export const IPFSSelector: Story<SnekFinderProps> = Template.bind({})

Selector.args = {
IPFSSelector.args = {
backend: IPFSBackend,
mode: 'selector',
onSelectorSelect: item => console.log(item),
onSelectorClose: () => console.log('close')
}

export const OpenStorage: Story<SnekFinderProps> = Template.bind({})

OSGBackend.onBackendLinkChange = (link: string) => {
console.log(link)
}

OpenStorage.args = {backend: OSGBackend, mode: 'browser'}

export const OpenStorageSelector: Story<SnekFinderProps> = Template.bind({})

OpenStorageSelector.args = {
backend: OSGBackend,
mode: 'selector',
onSelectorSelect: item => console.log(item),
onSelectorClose: () => console.log('close')
}
20 changes: 16 additions & 4 deletions packages/snek-finder/src/containers/SnekFinder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,23 @@ const SnekFinder: React.FC<SnekFinderProps> = ({backend, ...props}) => {
{showModal && showModal.type === 'SNEK_STUDIO' && file && (
<SnekStudio
src={file.src}
onComplete={src => {
const newData = update(data, {[showModal.uuid]: {src: {$set: src}}})
onComplete={async (blob, dataURL) => {
// convert dataUri to blob

setData(newData)
backend.writeIndex(newData)
setData(update(data, {[showModal.uuid]: {src: {$set: dataURL}}}))

// upload blob to backend
if (blob) {
const url = await backend.upload(new File([blob], file.name))

const newData = update(data, {
[showModal.uuid]: {src: {$set: url}}
})

setData(newData)

backend.writeIndex(newData)
}
}}
onClose={() => setShowModal({...showModal, type: 'IMAGE_VIEWER'})}
/>
Expand Down

0 comments on commit 1612b80

Please sign in to comment.