Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"yarn": ">=999.0.0",
"npm": ">=999.0.0"
},
"version": "2.33.2",
"version": "2.33.3",
"private": true,
"license": "AGPL-3.0-or-later",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/Calendar/src/SiteAdaptor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const site: Plugin.SiteAdaptor.Definition = {
category: 'dapp',
recommendFeature,
description: recommendFeature.description,
tutorialLink: 'https://mask.notion.site/Calendar-Web3-Daily-Digest-a59647e6e43a454eaccedd627fe50d3c',
},
],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,14 @@ enum ProviderTabs {
All = 'All',
IPFS = 'IPFS',
Arweave = 'Arweave',
Load = 'Load Network',
}

const TabToProviderMap: Record<ProviderTabs, Provider | null> = {
[ProviderTabs.All]: null,
[ProviderTabs.IPFS]: Provider.IPFS,
[ProviderTabs.Arweave]: Provider.Arweave,
[ProviderTabs.Load]: Provider.Load,
}

const providers = getEnumAsArray(ProviderTabs)
Expand Down
33 changes: 14 additions & 19 deletions packages/plugins/FileService/src/SiteAdaptor/components/Terms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,23 @@ export function Terms() {
</Typography>
<Typography variant="body2" className={classes.introduction}>
<Trans>
Web3 File Service is a decentralized storage service provided by Mask Network. It allows users
to store files in different decentralized networks. This feature is powered by Mask Network's
partner file storage protocols such as IPFS and Arweave. It supports files in PDF, DOC, JPG,
PNG, MP3, MP4 and other formats. At present, the maximum file size is 10 MB.
Web3 File Service is a decentralized storage solution provided by Mask Network that allows users
to store files across multiple decentralized networks. This service is powered by Mask Networks
partner protocols, including IPFS, Arweave, and Load Network. It supports various file formats
such as PDF, DOC, JPG, PNG, MP3, MP4, and more, with a maximum file size of 10MB per upload.
<br />
Through the Web3 File Service, users can upload files to different decentralized networks and
choose whether or not to encrypt them, thereby generating files with different levels of
confidentiality. Mask Network users can share these files to social platforms via generated
links. Using encryption helps protect file security and prevent privacy breaches.
<br />
You can store files in multiple decentralized networks through the Web3 file service. When
uploading files, you can choose to encrypt or decrypt them. According to the selected encryption
method, you can obtain two file links with and without encryption. Users of Mask Network can
share files to social platforms through this link. By using the encryption, you can ensure the
security of your files and prevent privacy leakage.
Please note that anyone with the link can access and share the file. Due to the immutable nature
of decentralized storage systems, uploaded files cannot be deleted or modified, so please
exercise caution when uploading files containing personal information.
<br />
<br />
It should be noted that any user who has the link can download and share the file. With the
characteristics of decentralized file storage systems, your uploaded files can never be deleted
or tampered. Be caution when uploading files with personal privacy.
<br />
<br />
The Web3 File Service provided by Mask Network enables individuals to be free from data
restrictions imposed by traditional social platforms, enabling free encrypted transmission and
sharing of files. At present, the service is provisionally free for all users with Mask Network.
Mask Network will provide updates on costs users may have to pay in the future.
Mask Network’s Web3 File Service is designed to help users break free from the data limitations
of traditional social platforms, enabling secure and decentralized file sharing. The service is
currently free for all Mask Network users. Any future costs will be announced in advance.
</Trans>
</Typography>
<Typography variant="body2" className={classes.footNote}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// todo:the issue is potentially the file size limit

import { Icons } from '@masknet/icons'
import { UploadDropArea } from '@masknet/shared'
import { makeStyles } from '@masknet/theme'
import { Checkbox, FormControlLabel, Radio, Typography } from '@mui/material'
import { useCallback, useMemo, useState, type ReactNode } from 'react'
import { MAX_FILE_SIZE } from '../../constants.js'
import { MAX_FILE_SIZE, MAX_FILE_SIZE_LOAD } from '../../constants.js'
import { downloadFile } from '../../helpers.js'
import { Provider } from '../../types.js'
import { useFileManagement } from '../contexts/index.js'
Expand Down Expand Up @@ -79,6 +81,8 @@ export function UploadFile() {
const [provider, setProvider] = useState<Provider>(Provider.Arweave)
const { recentFiles, uploadingFiles, uploadFile, attachToPost } = useFileManagement()

const FILE_SIZE = provider === Provider.Load ? MAX_FILE_SIZE_LOAD : MAX_FILE_SIZE

const files = useMemo(() => {
return [...uploadingFiles, ...recentFiles]
}, [uploadingFiles, recentFiles])
Expand All @@ -92,6 +96,10 @@ export function UploadFile() {
provider: Provider.IPFS,
name: <Trans>IPFS</Trans>,
},
{
provider: Provider.Load,
name: <Trans>Load Network</Trans>,
},
]

const onSelectFile = useCallback(
Expand All @@ -103,7 +111,7 @@ export function UploadFile() {

return (
<section className={classes.container}>
<UploadDropArea className={classes.uploadArea} maxFileSize={MAX_FILE_SIZE} onSelectFile={onSelectFile} />
<UploadDropArea className={classes.uploadArea} maxFileSize={FILE_SIZE} onSelectFile={onSelectFile} />
<div className={classes.options}>
{providers.map((config: ProviderConfig) => (
<FormControlLabel
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/FileService/src/SiteAdaptor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const definition: Plugin.SiteAdaptor.Definition = {
),
name,
iconFilterColor,
tutorialLink: 'https://realmasknetwork.notion.site/8c8fe1efce5a48b49739a38f4ea8c60f',
tutorialLink: 'https://mask.notion.site/Web3-File-Service-ee60e0e79a674019a0560de16144ecb3',
}
})(),
],
Expand Down
116 changes: 116 additions & 0 deletions packages/plugins/FileService/src/Worker/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { isEmpty } from 'lodash-es'
import { Attachment } from '@dimensiondev/common-protocols'
import { encodeText } from '@masknet/kit'
import { LANDING_PAGE, Provider } from '../constants.js'
import type { ProviderAgent, LandingPageMetadata, AttachmentOptions } from '../types.js'
import { makeFileKeySigned } from '../helpers.js'

const LOAD_GATEWAY_URL = 'https://load0.network/resolve'
const LOAD_UPLOAD_ENDPOINT = 'https://load0.network/upload'
const API_KEY = 'load_acc_ep4bep0uGlmUXMu46BxM0uWXKsqL5M5w' // move to env

class LoadAgent implements ProviderAgent {
static providerName = 'Load Network'
private uploadController?: AbortController

init() {
this.uploadController = new AbortController()
}

async makeAttachment(options: AttachmentOptions) {
this.init()
const passphrase = options.key ? encodeText(options.key) : undefined
const encoded = await Attachment.encode(passphrase, {
block: options.block,
mime: isEmpty(options.type) ? 'application/octet-stream' : options.type,
metadata: null,
})

const effectiveType = isEmpty(options.type) ? 'application/octet-stream' : options.type
const effectiveName = options.name || 'unnamed_file'
const payloadTxID = await this.makePayload(encoded, effectiveType, effectiveName)
return payloadTxID
}

async *upload(id: string) {
try {
// Since we're using optimistic upload, we can yield progress immediately
// The actual upload to Load Network happens in the background
yield 50
yield 100
} catch (error) {
console.error('Upload progress tracking failed:', error)
throw error
} finally {
if (this.uploadController) {
this.uploadController.abort()
this.uploadController = undefined
}
}
}

async uploadLandingPage(metadata: LandingPageMetadata) {
this.init()
const linkPrefix = LOAD_GATEWAY_URL
const encodedMetadata = JSON.stringify({
name: metadata.name,
size: metadata.size,
provider: Provider.Load,
link: `${linkPrefix}/${metadata.txId}`,
signed: await makeFileKeySigned(metadata.key),
createdAt: new Date().toISOString(),
})
const response = await fetch(LANDING_PAGE)
const text = await response.text()
const replaced = text
.replace('Arweave', LoadAgent.providerName)
.replace('Over Arweave', `Over ${LoadAgent.providerName}`)
.replace('__METADATA__', encodedMetadata)

const data = encodeText(replaced)

const landingPageTxId = await this.makePayload(data, 'text/html', `${metadata.name}-landing.html`)

return landingPageTxId
}

async makePayload(data: Uint8Array, type: string, fileName: string = 'file.dat') {
this.init()

try {
const blob = new Blob([data], { type })
const headers = {
'Content-Type': type,
Filename: fileName,
'App-Name': 'Maskbook',
'X-Load-Authorization': API_KEY,
}

const response = await fetch(LOAD_UPLOAD_ENDPOINT, {
method: 'POST',
headers,
body: blob,
signal: this.uploadController?.signal,
})

if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`)
}

const { optimistic_hash } = await response.json()
return optimistic_hash
} catch (error) {
const errorMessage = `Load Network upload failed: ${error instanceof Error ? error.message : String(error)}`
console.error('Load Network detailed error:', errorMessage)

const enhancedError = new Error(errorMessage)
if (error instanceof Error && error.stack) {
enhancedError.stack = error.stack
}

throw enhancedError
}
}
}

export default new LoadAgent()
2 changes: 2 additions & 0 deletions packages/plugins/FileService/src/Worker/service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { type AttachmentOptions, type LandingPageMetadata, Provider, type ProviderAgent } from '../types.js'
import arweave from './arweave.js'
import ipfs from './ipfs.js'
import load from './load.js'

const allProviders: Record<Provider, ProviderAgent> = {
[Provider.Arweave]: arweave,
[Provider.IPFS]: ipfs,
[Provider.Load]: load,
}

export async function makeAttachment(provider: Provider, options: AttachmentOptions) {
Expand Down
2 changes: 2 additions & 0 deletions packages/plugins/FileService/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const META_KEY_2 = 'com.maskbook.fileservice:2'
export const META_KEY_3 = 'com.maskbook.fileservice:3'

export const MAX_FILE_SIZE = 10 * 1000 * 1000
export const MAX_FILE_SIZE_LOAD = 10 * 1024 * 1024

export const LANDING_PAGE = 'https://files.r2d2.to/partner/arweave/landing-page.html'
export const RECOVERY_PAGE = 'https://fileservice.r2d2.to/recover'
Expand All @@ -19,4 +20,5 @@ export const enum RoutePaths {
export const enum Provider {
IPFS = 'ipfs',
Arweave = 'arweave',
Load = 'load',
}
1 change: 1 addition & 0 deletions packages/plugins/FileService/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const resolveGatewayAPI = createLookupTableResolver<Provider, string>(
{
[Provider.Arweave]: 'https://arweave.net',
[Provider.IPFS]: 'https://mask.infura-ipfs.io/ipfs',
[Provider.Load]: 'https://load0.network/resolve',
},
() => 'Unknown provider',
)
Expand Down
4 changes: 4 additions & 0 deletions packages/plugins/FileService/src/locale/en-US.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading