feat: recognize ens domains (#351)
* feat: recognize ens domains * refactor: added ens recognition and more tests * fix: validation mechanism to accept ENS and CIDs * feat: support non-ascii characters for ENS * fix: asset summary component to support ENS issue
This commit is contained in:
Generated
+36
-28
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.15.0",
|
"version": "0.15.0",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "^3.3.4",
|
"@ethersphere/bee-js": "^4.1.1",
|
||||||
"@ethersphere/manifest-js": "1.1.0",
|
"@ethersphere/manifest-js": "1.1.0",
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@material-ui/core": "4.12.3",
|
"@material-ui/core": "4.12.3",
|
||||||
@@ -2219,10 +2219,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersphere/bee-js": {
|
"node_modules/@ethersphere/bee-js": {
|
||||||
"version": "3.3.4",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-4.1.1.tgz",
|
||||||
"integrity": "sha512-uxH0kR31tE8IKVON7jyQHANZ/5edlU8OUCz/qyRft1YeS+L4HzEHAD3bN58iE842nt6Iz/fekrJ7yk1ONIcDBg==",
|
"integrity": "sha512-4hbgi7rHnku+2pv352z7txV6IhP+2iipCKaQS4sOFhjieui44LE0Lox8gy5tud9tY2wRN2wY6LTqvo2EaYrVRQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@types/readable-stream": "^2.3.13",
|
"@types/readable-stream": "^2.3.13",
|
||||||
"bufferutil": "^4.0.6",
|
"bufferutil": "^4.0.6",
|
||||||
"elliptic": "^6.5.4",
|
"elliptic": "^6.5.4",
|
||||||
@@ -2235,12 +2236,12 @@
|
|||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"utf-8-validate": "^5.0.9",
|
"utf-8-validate": "^5.0.9",
|
||||||
"web-streams-polyfill": "^4.0.0-beta.1",
|
"web-streams-polyfill": "^4.0.0-beta.1",
|
||||||
"ws": "^8.5.0"
|
"ws": "^8.6.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"bee": "1.5.1-d0a77598",
|
"bee": "1.6.0-6ceadd35",
|
||||||
"beeApiVersion": "3.0.0",
|
"beeApiVersion": "3.0.1",
|
||||||
"beeDebugApiVersion": "2.0.0",
|
"beeDebugApiVersion": "2.0.1",
|
||||||
"node": ">=12.0.0",
|
"node": ">=12.0.0",
|
||||||
"npm": ">=6.0.0"
|
"npm": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@@ -2254,9 +2255,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersphere/bee-js/node_modules/ws": {
|
"node_modules/@ethersphere/bee-js/node_modules/ws": {
|
||||||
"version": "8.5.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz",
|
||||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
"integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@@ -7461,14 +7462,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001278",
|
"version": "1.0.30001344",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
|
||||||
"integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==",
|
"integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": {
|
"funding": [
|
||||||
"type": "opencollective",
|
{
|
||||||
"url": "https://opencollective.com/browserslist"
|
"type": "opencollective",
|
||||||
}
|
"url": "https://opencollective.com/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"node_modules/capture-exit": {
|
"node_modules/capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -30872,10 +30879,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersphere/bee-js": {
|
"@ethersphere/bee-js": {
|
||||||
"version": "3.3.4",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-4.1.1.tgz",
|
||||||
"integrity": "sha512-uxH0kR31tE8IKVON7jyQHANZ/5edlU8OUCz/qyRft1YeS+L4HzEHAD3bN58iE842nt6Iz/fekrJ7yk1ONIcDBg==",
|
"integrity": "sha512-4hbgi7rHnku+2pv352z7txV6IhP+2iipCKaQS4sOFhjieui44LE0Lox8gy5tud9tY2wRN2wY6LTqvo2EaYrVRQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@types/readable-stream": "^2.3.13",
|
"@types/readable-stream": "^2.3.13",
|
||||||
"bufferutil": "^4.0.6",
|
"bufferutil": "^4.0.6",
|
||||||
"elliptic": "^6.5.4",
|
"elliptic": "^6.5.4",
|
||||||
@@ -30888,7 +30896,7 @@
|
|||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"utf-8-validate": "^5.0.9",
|
"utf-8-validate": "^5.0.9",
|
||||||
"web-streams-polyfill": "^4.0.0-beta.1",
|
"web-streams-polyfill": "^4.0.0-beta.1",
|
||||||
"ws": "^8.5.0"
|
"ws": "^8.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"web-streams-polyfill": {
|
"web-streams-polyfill": {
|
||||||
@@ -30897,9 +30905,9 @@
|
|||||||
"integrity": "sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ=="
|
"integrity": "sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ=="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.5.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz",
|
||||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
"integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34893,9 +34901,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001278",
|
"version": "1.0.30001344",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
|
||||||
"integrity": "sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==",
|
"integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "^3.3.4",
|
"@ethersphere/bee-js": "^4.1.1",
|
||||||
"@ethersphere/manifest-js": "1.1.0",
|
"@ethersphere/manifest-js": "1.1.0",
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@material-ui/core": "4.12.3",
|
"@material-ui/core": "4.12.3",
|
||||||
|
|||||||
@@ -1,25 +1,32 @@
|
|||||||
import * as swarmCid from '@ethersphere/swarm-cid'
|
import * as swarmCid from '@ethersphere/swarm-cid'
|
||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
|
import { Utils } from '@ethersphere/bee-js'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isWebsite?: boolean
|
isWebsite?: boolean
|
||||||
hash: string
|
reference: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssetSummary({ isWebsite, hash }: Props): ReactElement {
|
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
|
||||||
|
const isHash = Utils.isHexString(reference) && reference.length === 64
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<ExpandableListItemKey label="Swarm hash" value={hash} />
|
{isHash && <ExpandableListItemKey label="Swarm hash" value={reference} />}
|
||||||
<ExpandableListItemLink label="Share on Swarm Gateway" value={`https://gateway.ethswarm.org/access/${hash}`} />
|
{!isHash && <ExpandableListItemLink label="ENS" value={reference} />}
|
||||||
{isWebsite && (
|
<ExpandableListItemLink
|
||||||
|
label="Share on Swarm Gateway"
|
||||||
|
value={`https://gateway.ethswarm.org/access/${reference}`}
|
||||||
|
/>
|
||||||
|
{isWebsite && isHash && (
|
||||||
<ExpandableListItemLink
|
<ExpandableListItemLink
|
||||||
label="BZZ Link"
|
label="BZZ Link"
|
||||||
value={`https://${swarmCid.encodeManifestReference(hash).toString()}.bzz.link`}
|
value={`https://${swarmCid.encodeManifestReference(reference).toString()}.bzz.link`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { History } from '../../components/History'
|
|||||||
import { Context, defaultUploadOrigin } from '../../providers/File'
|
import { Context, defaultUploadOrigin } from '../../providers/File'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { recognizeSwarmHash } from '../../utils'
|
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
|
||||||
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||||
import { FileNavigation } from './FileNavigation'
|
import { FileNavigation } from './FileNavigation'
|
||||||
|
|
||||||
@@ -23,10 +23,17 @@ export function Download(): ReactElement {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const validateChange = (value: string) => {
|
const validateChange = (value: string) => {
|
||||||
if (Utils.isHexString(value, 64) || Utils.isHexString(value, 128) || !value.trim().length) {
|
if (
|
||||||
|
Utils.isHexString(value, 64) ||
|
||||||
|
Utils.isHexString(value, 128) ||
|
||||||
|
!value.trim().length ||
|
||||||
|
regexpEns.test(value)
|
||||||
|
) {
|
||||||
setReferenceError(undefined)
|
setReferenceError(undefined)
|
||||||
} else {
|
} else {
|
||||||
setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters.')
|
setReferenceError(
|
||||||
|
'Incorrect format of swarm hash. Expected 64 or 128 hexstring characters, bzz.link url or ENS domain.',
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +90,7 @@ export function Download(): ReactElement {
|
|||||||
confirmLabelDisabled={Boolean(referenceError) || loading}
|
confirmLabelDisabled={Boolean(referenceError) || loading}
|
||||||
placeholder="e.g. 31fb0362b1a42536134c86bc58b97ac0244e5c6630beec3e27c2d1cecb38c605"
|
placeholder="e.g. 31fb0362b1a42536134c86bc58b97ac0244e5c6630beec3e27c2d1cecb38c605"
|
||||||
expandedOnly
|
expandedOnly
|
||||||
mapperFn={value => recognizeSwarmHash(value)}
|
mapperFn={value => recognizeEnsOrSwarmHash(value)}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
<History title="Download History" localStorageKey={HISTORY_KEYS.DOWNLOAD_HISTORY} />
|
<History title="Download History" localStorageKey={HISTORY_KEYS.DOWNLOAD_HISTORY} />
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export function Share(): ReactElement {
|
|||||||
<AssetPreview metadata={metadata} previewUri={preview} />
|
<AssetPreview metadata={metadata} previewUri={preview} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<AssetSummary isWebsite={metadata?.isWebsite} hash={reference} />
|
<AssetSummary isWebsite={metadata?.isWebsite} reference={reference} />
|
||||||
</Box>
|
</Box>
|
||||||
<DownloadActionBar
|
<DownloadActionBar
|
||||||
onOpen={onOpen}
|
onOpen={onOpen}
|
||||||
|
|||||||
+56
-8
@@ -1,4 +1,4 @@
|
|||||||
import { extractSwarmHash, extractSwarmCid, recognizeSwarmHash } from './index'
|
import { extractSwarmHash, extractSwarmCid, extractEns, recognizeEnsOrSwarmHash } from './index'
|
||||||
|
|
||||||
interface TestObject {
|
interface TestObject {
|
||||||
input: string
|
input: string
|
||||||
@@ -122,16 +122,64 @@ describe('extractSwarmCid', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('recognizeSwarmHash', () => {
|
const correctEns: TestObject[] = [
|
||||||
test('should correctly extract hash', () => {
|
{
|
||||||
;[...correctHashes, ...correctCids].forEach(({ input, expectedOutput }) => {
|
input: 'test.eth',
|
||||||
const hash = recognizeSwarmHash(input)
|
expectedOutput: 'test.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 't-est.eth',
|
||||||
|
expectedOutput: 't-est.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://test.eth/whatever',
|
||||||
|
expectedOutput: 'test.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'https://alice.test.eth?whatever',
|
||||||
|
expectedOutput: 'alice.test.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'swarm.example.eth/?id=1&page=2',
|
||||||
|
expectedOutput: 'swarm.example.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://swarm.example.eth#up',
|
||||||
|
expectedOutput: 'swarm.example.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://site.eth:8008',
|
||||||
|
expectedOutput: 'site.eth',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const wrongEns: string[] = ['http://test.ethereum/whatever']
|
||||||
|
|
||||||
|
describe('extractEns', () => {
|
||||||
|
test('should correctly extract ens domain', () => {
|
||||||
|
correctEns.forEach(({ input, expectedOutput }) => {
|
||||||
|
const hash = extractEns(input)
|
||||||
expect(hash).toBe(expectedOutput)
|
expect(hash).toBe(expectedOutput)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('should not extract hash from incorrect inputs but instead return them', () => {
|
test('should not extract ens from incorrect inputs', () => {
|
||||||
;[...wrongHashes, ...wrongCids].forEach(url => {
|
wrongEns.forEach(url => {
|
||||||
const hash = recognizeSwarmHash(url)
|
const hash = extractEns(url)
|
||||||
|
expect(hash).toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('recognizeEnsOrSwarmHash', () => {
|
||||||
|
test('should correctly extract hash or ens', () => {
|
||||||
|
;[...correctHashes, ...correctCids, ...correctEns].forEach(({ input, expectedOutput }) => {
|
||||||
|
const hash = recognizeEnsOrSwarmHash(input)
|
||||||
|
expect(hash).toBe(expectedOutput)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('should not extract hash or ens from incorrect inputs but instead return them', () => {
|
||||||
|
;[...wrongHashes, ...wrongCids, ...wrongEns].forEach(url => {
|
||||||
|
const hash = recognizeEnsOrSwarmHash(url)
|
||||||
expect(hash).toBe(url)
|
expect(hash).toBe(url)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+12
-2
@@ -144,8 +144,18 @@ export function extractSwarmCid(s: string): string | undefined {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function recognizeSwarmHash(value: string): string {
|
// Matches any number of subdomain with .eth
|
||||||
return extractSwarmHash(value) || extractSwarmCid(value) || value
|
// e.g. this.is.just-a-test.eth
|
||||||
|
export const regexpEns = /((?:(?:[^-./?:\s][^./?:\s]{0,61}[^-./?:\s]|[^-./?:\s]{1,2})\.)+eth)(?:$|[/?:#].*)/i
|
||||||
|
|
||||||
|
export function extractEns(value: string): string | undefined {
|
||||||
|
const matches = value.match(regexpEns)
|
||||||
|
|
||||||
|
return (matches && matches[1]) || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recognizeEnsOrSwarmHash(value: string): string {
|
||||||
|
return extractEns(value) || extractSwarmHash(value) || extractSwarmCid(value) || value
|
||||||
}
|
}
|
||||||
|
|
||||||
export function uuidV4(): string {
|
export function uuidV4(): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user