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

fix(files): prefer subdomain gw in copied share links #2255

Merged
merged 28 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cdde888
Fix issue #2244: Updated public gateway settings, added subdomain gat…
acul71 Sep 3, 2024
b3cbe79
Merge pull request #1 from ipfs/main
acul71 Sep 3, 2024
3373d44
Update src/bundles/gateway.js
acul71 Sep 4, 2024
c3de5c3
Update src/bundles/gateway.js
acul71 Sep 4, 2024
b0d128f
Update src/settings/SettingsPage.js
acul71 Sep 4, 2024
7a64e72
Update src/settings/SettingsPage.js
acul71 Sep 4, 2024
fda5381
Incorporated review suggestions for issue #2244: Updated constants, r…
acul71 Sep 4, 2024
64e95f6
Merge branch 'main' into fix-issue-2244
SgtPooki Sep 4, 2024
b013645
Update src/bundles/gateway.js
acul71 Sep 4, 2024
08d2073
Update src/bundles/gateway.js
acul71 Sep 4, 2024
39840d0
Update src/bundles/gateway.js
acul71 Sep 4, 2024
318285e
Update src/bundles/gateway.js
acul71 Sep 4, 2024
ef940f6
Update src/lib/files.js
acul71 Sep 4, 2024
8cdafe6
Update src/lib/files.test.js
acul71 Sep 4, 2024
e22ddb0
Update src/lib/files.test.js
acul71 Sep 4, 2024
61f8e20
Update src/settings/SettingsPage.js
acul71 Sep 4, 2024
a31026c
Update src/lib/files.test.js
acul71 Sep 4, 2024
0d8fc4b
Update src/components/public-subdomain-gateway-form/PublicSubdomainGa…
acul71 Sep 4, 2024
5cab67d
Update src/components/public-subdomain-gateway-form/PublicSubdomainGa…
acul71 Sep 4, 2024
c3c96e2
Update src/components/public-subdomain-gateway-form/PublicSubdomainGa…
acul71 Sep 4, 2024
05e4841
Update src/components/public-subdomain-gateway-form/PublicSubdomainGa…
acul71 Sep 4, 2024
5c9ec59
Update src/components/public-subdomain-gateway-form/PublicSubdomainGa…
acul71 Sep 4, 2024
9b512a4
nit: moved kind helper functions outside the test blocks
acul71 Sep 4, 2024
7c96305
Fix submit subdomain gateway check validity test
acul71 Sep 4, 2024
ee529a9
Update src/bundles/gateway.js
acul71 Sep 5, 2024
d8e81bc
Update src/lib/files.test.js
acul71 Sep 5, 2024
2de3310
fix: settings.test.js workaround for no button
acul71 Sep 5, 2024
44a43be
fix(settings): avoid probing dweb.link on load
lidel Sep 10, 2024
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
3 changes: 3 additions & 0 deletions public/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"placeholder": "Enter a URL (http://127.0.0.1:5001) or a Multiaddr (/ip4/127.0.0.1/tcp/5001)"
},
"publicGatewayForm": {
"placeholder": "Enter a URL (https://ipfs.io)"
},
"publicSubdomainGatewayForm": {
"placeholder": "Enter a URL (https://dweb.link)"
},
"terms": {
Expand Down
3 changes: 2 additions & 1 deletion public/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"translationProjectLink": "Join the IPFS Translation Project"
},
"apiDescription": "<0>If your node is configured with a <1>custom Kubo RPC API address</1>, including a port other than the default 5001, enter it here.</0>",
"publicGatewayDescription": "<0>Choose which <1>public gateway</1> you want to use when generating shareable links.</0>",
"publicSubdomainGatewayDescription": "<0>Select a default <1>Subdomain Gateway</1> for generating shareable links.</0>",
"publicPathGatewayDescription": "<0>Select a fallback <1>Path Gateway</1> for generating shareable links for CIDs that exceed the 63-character DNS limit.</0>",
"cliDescription": "<0>Enable this option to display a \"view code\" <1></1> icon next to common IPFS commands. Clicking it opens a modal with that command's CLI code, so you can paste it into the IPFS command-line interface in your terminal.</0>",
"cliModal": {
"extraNotesJsonConfig": "If you've made changes to the config in this page's code editor that you'd like to save, click the download icon next to the copy button to download it as a JSON file."
Expand Down
4 changes: 3 additions & 1 deletion src/bundles/files/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
* @property {function():string} selectApiUrl
* @property {function():string} selectGatewayUrl
* @property {function():string} selectPublicGateway
* @property {function():string} selectPublicSubdomainGateway
*
* @typedef {Object} UnkonwActions
* @property {function(string):Promise<unknown>} doUpdateHash
Expand Down Expand Up @@ -422,7 +423,8 @@
doFilesShareLink: (files) => perform(ACTIONS.SHARE_LINK, async (ipfs, { store }) => {
// ensureMFS deliberately omitted here, see https://github.com/ipfs/ipfs-webui/issues/1744 for context.
const publicGateway = store.selectPublicGateway()
return getShareableLink(files, publicGateway, ipfs)
const publicSubdomainGateway = store.selectPublicSubdomainGateway()
return getShareableLink(files, publicGateway, publicSubdomainGateway, ipfs)

Check warning on line 427 in src/bundles/files/actions.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/files/actions.js#L426-L427

Added lines #L426 - L427 were not covered by tests
}),

/**
Expand Down
101 changes: 93 additions & 8 deletions src/bundles/gateway.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { readSetting, writeSetting } from './local-storage.js'

export const DEFAULT_GATEWAY = 'https://ipfs.io' // TODO: switch to dweb.link when https://github.com/ipfs/kubo/issues/7318
// export const DEFAULT_GATEWAY = 'https://ipfs.io' // TODO: switch to dweb.link when https://github.com/ipfs/kubo/issues/7318
export const DEFAULT_PATH_GATEWAY = 'https://ipfs.io'
acul71 marked this conversation as resolved.
Show resolved Hide resolved
export const DEFAULT_SUBDOMAIN_GATEWAY = 'https://dweb.link'
const IMG_HASH_1PX = 'bafkreib6wedzfupqy7qh44sie42ub4mvfwnfukmw6s2564flajwnt4cvc4' // 1x1.png
const IMG_ARRAY = [
{ id: 'IMG_HASH_1PX', name: '1x1.png', hash: 'bafybeibwzifw52ttrkqlikfzext5akxu7lz4xiwjgwzmqcpdzmp3n5vnbe' },
{ id: 'IMG_HASH_1PX', name: '1x1.png', hash: IMG_HASH_1PX },
{ id: 'IMG_HASH_1PXID', name: '1x1.png', hash: 'bafkqax4jkbheodikdifaaaaabveuqrcsaaaaaaiaaaaacaidaaaaajo3k3faaaaaanieyvcfaaaabj32hxnaaaaaaf2fetstabaonwdgaaaaacsjiravicgxmnqaaaaaaiaadyrbxqzqaaaaabeuktsevzbgbaq' },
{ id: 'IMG_HASH_FAVICON', name: 'favicon.ico', hash: 'bafkreihc7efnl2prri6j6krcopelxms3xsh7undpsjqbfsasm7ikiyha4i' }
]

const readPublicGatewaySetting = () => {
const setting = readSetting('ipfsPublicGateway')
return setting || DEFAULT_GATEWAY
return setting || DEFAULT_PATH_GATEWAY

Check warning on line 15 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L15

Added line #L15 was not covered by tests
}

const readPublicSubdomainGatewaySetting = () => {
const setting = readSetting('ipfsPublicSubdomainGateway')
return setting || DEFAULT_SUBDOMAIN_GATEWAY

Check warning on line 20 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L19-L20

Added lines #L19 - L20 were not covered by tests
}

const init = () => ({
availableGateway: null,
publicGateway: readPublicGatewaySetting()
publicGateway: readPublicGatewaySetting(),
publicSubdomainGateway: readPublicSubdomainGatewaySetting()
})

export const checkValidHttpUrl = (value) => {
Expand All @@ -25,7 +34,7 @@
} catch (_) {
return false
}

console.log(`URL is valid: ${url}, url.protocol=${url.protocol}`) // lucas

Check warning on line 37 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L37

Added line #L37 was not covered by tests
return url.protocol === 'http:' || url.protocol === 'https:'
}

Expand Down Expand Up @@ -58,12 +67,12 @@
return true
}

let timer = setTimeout(() => { if (timeout()) reject(new Error()) }, imgCheckTimeout)
let timer = setTimeout(() => { if (timeout()) reject(new Error(`Image load timed out after ${imgCheckTimeout / 1000} seconds for URL: ${imgUrl}`)) }, imgCheckTimeout)

Check warning on line 70 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L70

Added line #L70 was not covered by tests
const img = new Image()

img.onerror = () => {
timeout()
reject(new Error())
reject(new Error(`Failed to load image from URL: ${imgUrl}`))

Check warning on line 75 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L75

Added line #L75 was not covered by tests
}

img.onload = () => {
Expand All @@ -76,6 +85,71 @@
})
}

/**
* Checks if a given URL redirects to a subdomain that starts with a specific hash.
*
* @param {URL} url - The URL to check for redirection.
* @throws {Error} Throws an error if the URL does not redirect to the expected subdomain.
* @returns {Promise<void>} A promise that resolves if the URL redirects correctly, otherwise it throws an error.
*/
async function expectSubdomainRedirect (url) {
// Detecting redirects on remote Origins is extra tricky,
// but we seem to be able to access xhr.responseURL which is enough to see
// if paths are redirected to subdomains.

const { url: responseUrl } = await fetch(url.toString())
const { hostname } = new URL(responseUrl)

Check warning on line 101 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L100-L101

Added lines #L100 - L101 were not covered by tests

if (!hostname.startsWith(IMG_HASH_1PX)) {
const msg = `Expected ${url.toString()} to redirect to subdomain '${IMG_HASH_1PX}' but instead received '${responseUrl}'`
console.error(msg)
throw new Error(msg)

Check warning on line 106 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L103-L106

Added lines #L103 - L106 were not covered by tests
}
}

/**
* Checks if an image can be loaded from a given URL within a specified timeout.
*
* @param {URL} imgUrl - The URL of the image to be loaded.
* @returns {Promise<void>} A promise that resolves if the image loads successfully within the timeout, otherwise it rejects with an error.
*/
async function checkViaImgUrl (imgUrl) {
try {
await checkImgSrcPromise(imgUrl)

Check warning on line 118 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L117-L118

Added lines #L117 - L118 were not covered by tests
} catch (error) {
throw new Error(`Error or timeout when attempting to load img from '${imgUrl.toString()}'`)

Check warning on line 120 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L120

Added line #L120 was not covered by tests
}
}

/**
* Checks if a given gateway URL is functioning correctly by verifying image loading and redirection.
*
* @param {string} gatewayUrl - The URL of the gateway to be checked.
* @returns {Promise<boolean>} A promise that resolves to true if the gateway is functioning correctly, otherwise false.
*/
export async function checkSubdomainGateway (gatewayUrl) {
let imgSubdomainUrl
let imgRedirectedPathUrl
try {
const gwUrl = new URL(gatewayUrl)
imgSubdomainUrl = new URL(`${gwUrl.protocol}//${IMG_HASH_1PX}.ipfs.${gwUrl.hostname}/?now=${Date.now()}&filename=1x1.png#x-ipfs-companion-no-redirect`)
imgRedirectedPathUrl = new URL(`${gwUrl.protocol}//${gwUrl.hostname}/ipfs/${IMG_HASH_1PX}?now=${Date.now()}&filename=1x1.png#x-ipfs-companion-no-redirect`)

Check warning on line 136 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L133-L136

Added lines #L133 - L136 were not covered by tests
} catch (err) {
console.error('Invalid URL:', err)
return false

Check warning on line 139 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L138-L139

Added lines #L138 - L139 were not covered by tests
}
return await checkViaImgUrl(imgSubdomainUrl)
.then(async () => expectSubdomainRedirect(imgRedirectedPathUrl))

Check warning on line 142 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L141-L142

Added lines #L141 - L142 were not covered by tests
.then(() => {
console.log(`Gateway at '${gatewayUrl}' is functioning correctly (verified image loading and redirection)`)
return true

Check warning on line 145 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L144-L145

Added lines #L144 - L145 were not covered by tests
})
.catch((err) => {
console.error(err)
return false

Check warning on line 149 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L148-L149

Added lines #L148 - L149 were not covered by tests
})
}

const bundle = {
name: 'gateway',

Expand All @@ -88,6 +162,10 @@
return { ...state, publicGateway: action.payload }
}

if (action.type === 'SET_PUBLIC_SUBDOMAIN_GATEWAY') {
return { ...state, publicSubdomainGateway: action.payload }

Check warning on line 166 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L165-L166

Added lines #L165 - L166 were not covered by tests
}

return state
},

Expand All @@ -98,9 +176,16 @@
dispatch({ type: 'SET_PUBLIC_GATEWAY', payload: address })
},

doUpdatePublicSubdomainGateway: (address) => async ({ dispatch }) => {
await writeSetting('ipfsPublicSubdomainGateway', address)
dispatch({ type: 'SET_PUBLIC_SUBDOMAIN_GATEWAY', payload: address })

Check warning on line 181 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L179-L181

Added lines #L179 - L181 were not covered by tests
},

selectAvailableGateway: (state) => state?.gateway?.availableGateway,

selectPublicGateway: (state) => state?.gateway?.publicGateway
selectPublicGateway: (state) => state?.gateway?.publicGateway,

Check warning on line 186 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L186

Added line #L186 was not covered by tests

selectPublicSubdomainGateway: (state) => state?.gateway?.publicSubdomainGateway

Check warning on line 188 in src/bundles/gateway.js

View check run for this annotation

Codecov / codecov/patch

src/bundles/gateway.js#L188

Added line #L188 was not covered by tests
}

export default bundle
10 changes: 6 additions & 4 deletions src/components/public-gateway-form/PublicGatewayForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { connect } from 'redux-bundler-react'
import { withTranslation } from 'react-i18next'
import Button from '../button/Button.js'
import { checkValidHttpUrl, checkViaImgSrc, DEFAULT_GATEWAY } from '../../bundles/gateway.js'
import { checkValidHttpUrl, checkViaImgSrc, DEFAULT_PATH_GATEWAY } from '../../bundles/gateway.js'

const PublicGatewayForm = ({ t, doUpdatePublicGateway, publicGateway }) => {
const [value, setValue] = useState(publicGateway)
Expand Down Expand Up @@ -39,8 +39,8 @@

const onReset = async (event) => {
event.preventDefault()
setValue(DEFAULT_GATEWAY)
doUpdatePublicGateway(DEFAULT_GATEWAY)
setValue(DEFAULT_PATH_GATEWAY)
doUpdatePublicGateway(DEFAULT_PATH_GATEWAY)

Check warning on line 43 in src/components/public-gateway-form/PublicGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-gateway-form/PublicGatewayForm.js#L42-L43

Added lines #L42 - L43 were not covered by tests
}

const onKeyPress = (event) => {
Expand All @@ -63,15 +63,17 @@
/>
<div className='tr'>
<Button
id='public-path-gateway-reset-button'
minWidth={100}
height={40}
bg='bg-charcoal'
className='tc'
disabled={value === DEFAULT_GATEWAY}
disabled={value === DEFAULT_PATH_GATEWAY}
onClick={onReset}>
{t('app:actions.reset')}
</Button>
<Button
id='public-path-gateway-submit-button'
minWidth={100}
height={40}
className='mt2 mt0-l ml2-l tc'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useState, useEffect } from 'react'
import { connect } from 'redux-bundler-react'
import { withTranslation } from 'react-i18next'
import Button from '../button/Button.js'
import { checkValidHttpUrl, checkSubdomainGateway, DEFAULT_SUBDOMAIN_GATEWAY } from '../../bundles/gateway.js'

const PublicSubdomainGatewayForm = ({ t, doUpdatePublicSubdomainGateway, publicSubdomainGateway }) => {
const [value, setValue] = useState(publicSubdomainGateway)
const initialIsValidGatewayUrl = !checkValidHttpUrl(value)
const [isValidGatewayUrl, setIsValidGatewayUrl] = useState(initialIsValidGatewayUrl)

Check warning on line 10 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L7-L10

Added lines #L7 - L10 were not covered by tests

// Updates the border of the input to indicate validity
useEffect(() => {
const validateUrl = async () => {
try {
const isValid = await checkSubdomainGateway(value)
setIsValidGatewayUrl(isValid)

Check warning on line 17 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L13-L17

Added lines #L13 - L17 were not covered by tests
} catch (error) {
console.error('Error checking subdomain gateway:', error)
setIsValidGatewayUrl(false)

Check warning on line 20 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L19-L20

Added lines #L19 - L20 were not covered by tests
}
}

validateUrl()

Check warning on line 24 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L24

Added line #L24 was not covered by tests
}, [value])

const onChange = (event) => setValue(event.target.value)

Check warning on line 27 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L27

Added line #L27 was not covered by tests

const onSubmit = async (event) => {
event.preventDefault()

Check warning on line 30 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L29-L30

Added lines #L29 - L30 were not covered by tests

let isValid = false
try {
isValid = await checkSubdomainGateway(value)
setIsValidGatewayUrl(true)

Check warning on line 35 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L32-L35

Added lines #L32 - L35 were not covered by tests
} catch (e) {
setIsValidGatewayUrl(false)
return

Check warning on line 38 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L37-L38

Added lines #L37 - L38 were not covered by tests
}

isValid && doUpdatePublicSubdomainGateway(value)

Check warning on line 41 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L41

Added line #L41 was not covered by tests
}

const onReset = async (event) => {
event.preventDefault()
setValue(DEFAULT_SUBDOMAIN_GATEWAY)
doUpdatePublicSubdomainGateway(DEFAULT_SUBDOMAIN_GATEWAY)

Check warning on line 47 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L44-L47

Added lines #L44 - L47 were not covered by tests
}

const onKeyPress = (event) => {
if (event.key === 'Enter') {
onSubmit(event)

Check warning on line 52 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L50-L52

Added lines #L50 - L52 were not covered by tests
}
}

return (

Check warning on line 56 in src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js

View check run for this annotation

Codecov / codecov/patch

src/components/public-subdomain-gateway-form/PublicSubdomainGatewayForm.js#L56

Added line #L56 was not covered by tests
<form onSubmit={onSubmit}>
<input
id='public-subdomain-gateway'
aria-label={t('terms.publicSubdomainGateway')}
placeholder={t('publicSubdomainGatewayForm.placeholder')}
type='text'
className={`w-100 lh-copy monospace f5 pl1 pv1 mb2 charcoal input-reset ba b--black-20 br1 ${!isValidGatewayUrl ? 'focus-outline-red b--red-muted' : 'focus-outline-green b--green-muted'}`}
onChange={onChange}
onKeyPress={onKeyPress}
value={value}
/>
<div className='tr'>
<Button
id='public-subdomain-gateway-reset-button'
minWidth={100}
height={40}
bg='bg-charcoal'
className='tc'
disabled={value === DEFAULT_SUBDOMAIN_GATEWAY}
onClick={onReset}>
{t('app:actions.reset')}
</Button>
<Button
id='public-subdomain-gateway-submit-button'
minWidth={100}
height={40}
className='mt2 mt0-l ml2-l tc'
disabled={!isValidGatewayUrl || value === publicSubdomainGateway}>
{t('actions.submit')}
</Button>
</div>
</form>
)
}

export default connect(
'doUpdatePublicSubdomainGateway',
'selectPublicSubdomainGateway',
withTranslation('app')(PublicSubdomainGatewayForm)
)
30 changes: 24 additions & 6 deletions src/lib/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,15 @@ export async function getDownloadLink (files, gatewayUrl, ipfs) {
}

/**
* @param {FileStat[]} files
* @param {string} gatewayUrl
* @param {IPFSService} ipfs
* @returns {Promise<string>}
* Generates a shareable link for the provided files using a subdomain gateway as default or a path gateway as fallback.
*
* @param {FileStat[]} files - An array of file objects with their respective CIDs and names.
* @param {string} gatewayUrl - The URL of the default IPFS gateway.
* @param {string} subdomainGatewayUrl - The URL of the subdomain gateway.
* @param {IPFSService} ipfs - The IPFS service instance for interacting with the IPFS network.
* @returns {Promise<string>} - A promise that resolves to the shareable link for the provided files.
*/
export async function getShareableLink (files, gatewayUrl, ipfs) {
export async function getShareableLink (files, gatewayUrl, subdomainGatewayUrl, ipfs) {
let cid
let filename

Expand All @@ -111,7 +114,22 @@ export async function getShareableLink (files, gatewayUrl, ipfs) {
cid = await makeCIDFromFiles(files, ipfs)
}

return `${gatewayUrl}/ipfs/${cid}${filename || ''}`
const url = new URL(subdomainGatewayUrl)

/**
* dweb.link (subdomain isolation) is listed first as the new default option.
* However, ipfs.io (path gateway fallback) is also listed for CIDs that cannot be represented in a 63-character DNS label.
* This allows users to customize both the subdomain and path gateway they use, with the subdomain gateway being used by default whenever possible.
*/
let shareableLink = ''
const base32Cid = cid.toV1().toString()
if (base32Cid.length < 64) {
shareableLink = `${url.protocol}//${base32Cid}.ipfs.${url.host}${filename || ''}`
} else {
shareableLink = `${gatewayUrl}/ipfs/${cid}${filename || ''}`
}

return shareableLink
}

/**
Expand Down
Loading
Loading