Skip to content

Commit 0cd3e12

Browse files
committed
feat(naming): support subdomain dnslink on cloudflare
fix #5
1 parent 7fd847b commit 0cd3e12

File tree

6 files changed

+160
-54
lines changed

6 files changed

+160
-54
lines changed

β€ŽREADME.mdβ€Ž

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ The goal of `@agentofuser/ipfs-deploy` is to make it as easy as possible to
1515

1616
## Table of Contents
1717

18-
- [Install](#install)
19-
- [Usage](#usage)
20-
- [API](#api)
21-
- [Security](#security)
22-
- [Background](#background)
23-
- [Contributors](#contributors)
24-
- [Who's Using](#users)
25-
- [License](#license)
18+
1. [@agentofuser/ipfs-deploy](#agentofuseripfs-deploy)
19+
1. [Table of Contents](#Table-of-Contents)
20+
2. [Install](#Install)
21+
1. [No install:](#No-install)
22+
3. [Usage](#Usage)
23+
4. [API](#API)
24+
5. [Security](#Security)
25+
6. [Background](#Background)
26+
7. [Contributors](#Contributors)
27+
8. [Users](#Users)
28+
9. [License](#License)
2629

2730
## Install
2831

@@ -116,18 +119,43 @@ https://pinata.cloud/documentation#GettingStarted
116119
pinning service used.)
117120

118121
After setting up your Cloudflare and Pinata accounts, in your website's
119-
repository root, create or edit the file `.env` with your domain and
120-
credentials:
122+
repository root, create or edit the file `.env` with your credentials, zone,
123+
and record information:
121124

122-
```
123-
IPFS_DEPLOY_SITE_DOMAIN=
125+
```bash
126+
# pinata credentials
124127
IPFS_DEPLOY_PINATA__API_KEY=
125128
IPFS_DEPLOY_PINATA__SECRET_API_KEY=
129+
130+
# cloudflare credentials
126131
IPFS_DEPLOY_CLOUDFLARE__API_EMAIL=
127132
IPFS_DEPLOY_CLOUDFLARE__API_KEY=
133+
134+
# cloudflare dns info
135+
IPFS_DEPLOY_CLOUDFLARE__ZONE=
136+
IPFS_DEPLOY_CLOUDFLARE__RECORD=
128137
```
129138

130-
(**Don't** commit it to source control unless you know what you're doing.)
139+
Example with top-level domain:
140+
141+
```bash
142+
# cloudflare dns info
143+
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
144+
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.agentofuser.com
145+
```
146+
147+
Example with subdomain:
148+
149+
```bash
150+
# cloudflare dns info
151+
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
152+
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.test.agentofuser.com
153+
```
154+
155+
Note the 2 `_` after `PINATA` and `CLOUDFLARE`.
156+
157+
(**Don't** commit the `.env` file to source control unless you know what you're
158+
doing.)
131159

132160
```
133161
$ echo '.env' >> .gitignore
@@ -177,15 +205,18 @@ const deploy = require('@agentofuser/ipfs-deploy')
177205
try {
178206
const deployOptions = {
179207
publicDirPath: argv.path,
180-
copyHttpGatewayUrlToClipboard: !argv.noClipboard,
181-
open: !argv.O,
182-
remotePinners: argv.p,
183-
dnsProviders: argv.d,
208+
copyHttpGatewayUrlToClipboard:
209+
!(argv.clipboard === false) && !argv.C && !argv.noClipboard,
210+
open: !(argv.open === false) && !argv.O && !argv.noOpen,
211+
remotePinners: argv.pinner,
212+
dnsProviders: argv.dns,
184213
siteDomain: argv.siteDomain,
185214
credentials: {
186215
cloudflare: {
187216
apiKey: argv.cloudflare && argv.cloudflare.apiKey,
188217
apiEmail: argv.cloudflare && argv.cloudflare.apiEmail,
218+
zone: argv.cloudflare && argv.cloudflare.zone,
219+
record: argv.cloudflare && argv.cloudflare.record,
189220
},
190221
pinata: {
191222
apiKey: argv.pinata && argv.pinata.apiKey,

β€Žbin/ipfs-deploy.jsβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ async function main() {
101101
cloudflare: {
102102
apiKey: argv.cloudflare && argv.cloudflare.apiKey,
103103
apiEmail: argv.cloudflare && argv.cloudflare.apiEmail,
104+
zone: argv.cloudflare && argv.cloudflare.zone,
105+
record: argv.cloudflare && argv.cloudflare.record,
104106
},
105107
pinata: {
106108
apiKey: argv.pinata && argv.pinata.apiKey,

β€Žindex.jsβ€Ž

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { logError } = require('./src/logging')
1515

1616
const httpGatewayUrl = require('./src/gateway')
1717
const { setupPinata } = require('./src/pinata')
18+
const { linkCid, linkUrl } = require('./src/utils/pure-fns')
1819

1920
const white = chalk.whiteBright
2021

@@ -64,48 +65,81 @@ async function openUrl(url) {
6465
const spinner = ora()
6566
spinner.start('πŸ„ Opening web browser…')
6667
const childProcess = await doOpen(url)
67-
spinner.succeed('πŸ„ Opened web browser (call with -O to disable.)')
68+
spinner.succeed('πŸ„ Opened URL on web browser (call with -O to disable):')
69+
spinner.info(linkUrl(url))
6870
return childProcess
6971
}
7072

71-
async function updateCloudflareDns(siteDomain, { apiEmail, apiKey }, hash) {
73+
// returns (sub)domain deployed to or null when error
74+
async function updateCloudflareDns(
75+
siteDomain,
76+
{ apiEmail, apiKey, zone, record },
77+
hash
78+
) {
79+
let result
7280
const spinner = ora()
7381

7482
spinner.start(`πŸ“‘ Beaming new hash to DNS provider ${white('Cloudflare')}…`)
75-
if (fp.some(_.isEmpty)([siteDomain, apiEmail, apiKey])) {
83+
if (fp.some(_.isEmpty)([apiEmail, apiKey])) {
7684
spinner.fail('πŸ’” Missing arguments for Cloudflare API.')
7785
spinner.warn('🧐 Check if these environment variables are present:')
7886
logError(`
79-
IPFS_DEPLOY_SITE_DOMAIN
8087
IPFS_DEPLOY_CLOUDFLARE__API_EMAIL
8188
IPFS_DEPLOY_CLOUDFLARE__API_KEY
8289
90+
(Note the 2 '_' after "CLOUDFLARE".)
91+
You can put them in a .env file if you want and they will be picked up.
92+
`)
93+
}
94+
if (_.isEmpty(siteDomain) && fp.some(_.isEmpty)([zone, record])) {
95+
spinner.fail('πŸ’” Missing arguments for Cloudflare API.')
96+
spinner.warn('🧐 Check if these environment variables are present:')
97+
logError(`
98+
IPFS_DEPLOY_CLOUDFLARE__ZONE
99+
IPFS_DEPLOY_CLOUDFLARE__RECORD
100+
101+
(Note the 2 '_' after "CLOUDFLARE".)
83102
You can put them in a .env file if you want and they will be picked up.
103+
104+
Example with top-level domain:
105+
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
106+
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.agentofuser.com
107+
108+
Example with subdomain:
109+
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
110+
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.test.agentofuser.com
84111
`)
112+
result = null
85113
} else {
86-
try {
87-
const api = {
88-
email: apiEmail,
89-
key: apiKey,
90-
}
114+
const api = {
115+
email: apiEmail,
116+
key: apiKey,
117+
}
91118

92-
const opts = {
93-
record: siteDomain,
94-
zone: siteDomain,
95-
link: `/ipfs/${hash}`,
96-
}
119+
const opts = {
120+
zone: zone || siteDomain,
121+
record: record || `_dnslink.${siteDomain}`,
122+
link: `/ipfs/${hash}`,
123+
}
97124

125+
try {
98126
const content = await updateCloudflareDnslink(api, opts)
99127
spinner.succeed('πŸ™Œ SUCCESS!')
100128
spinner.info(`πŸ”„ Updated DNS TXT ${white(opts.record)} to:`)
101129
spinner.info(`πŸ”— ${white(content)}`)
130+
131+
result = opts.record
132+
.split('.')
133+
.slice(1)
134+
.join('.')
102135
} catch (e) {
103136
spinner.fail("πŸ’” Updating Cloudflare DNS didn't work.")
104137
logError(e)
138+
result = null
105139
}
106-
107-
return siteDomain
108140
}
141+
142+
return result
109143
}
110144

111145
async function showSize(path) {
@@ -118,7 +152,9 @@ async function showSize(path) {
118152
})
119153
const kibi = byteSize(size, { units: 'iec' })
120154
const readableSize = `${kibi.value} ${kibi.unit}`
121-
spinner.succeed(`🚚 ${chalk.blue(path)} weighs ${readableSize}.`)
155+
spinner.succeed(
156+
`🚚 Directory ${chalk.blue(path)} weighs ${readableSize}.`
157+
)
122158
return readableSize
123159
} catch (e) {
124160
spinner.fail("βš– Couldn't calculate website size.")
@@ -144,9 +180,9 @@ async function addToInfura(publicDirPath) {
144180
recursive: true,
145181
})
146182
spinner.succeed("πŸ“Œ It's pinned to Infura now with hash:")
147-
const hash = response[response.length - 1].hash
148-
spinner.info(`πŸ”— ${hash}`)
149-
return hash
183+
const pinnedHash = response[response.length - 1].hash
184+
spinner.info(linkCid(pinnedHash, 'infura'))
185+
return pinnedHash
150186
} catch (e) {
151187
spinner.fail("πŸ’” Uploading to Infura didn't work.")
152188
logError(e)
@@ -160,7 +196,7 @@ function copyUrlToClipboard(url) {
160196
try {
161197
clipboardy.writeSync(url)
162198
spinner.succeed('πŸ“‹ Copied HTTP gateway URL to clipboard:')
163-
spinner.info(`πŸ”— ${chalk.green(url)}`)
199+
spinner.info(linkUrl(url))
164200
return url
165201
} catch (e) {
166202
spinner.fail('⚠️ Could not copy URL to clipboard.')
@@ -180,6 +216,8 @@ async function deploy({
180216
cloudflare: {
181217
apiEmail,
182218
apiKey,
219+
zone,
220+
record,
183221
},
184222
pinata: {
185223
apiKey,
@@ -213,7 +251,10 @@ async function deploy({
213251
if (remotePinners.includes('pinata')) {
214252
const addToPinata = setupPinata(credentials.pinata)
215253
const pinataHash = await addToPinata(publicDirPath, {
216-
name: siteDomain || __dirname,
254+
name:
255+
(credentials.cloudflare && credentials.cloudflare.record) ||
256+
siteDomain ||
257+
__dirname,
217258
})
218259

219260
if (pinataHash) {
@@ -224,30 +265,43 @@ async function deploy({
224265

225266
if (successfulRemotePinners.length > 0) {
226267
const pinnedHash = Object.values(pinnedHashes)[0]
227-
const isEqual = hash => hash === pinnedHash
268+
const isEqual = pinnedHash => pinnedHash === pinnedHash
228269
if (!fp.every(isEqual)(Object.values(pinnedHashes))) {
229270
const spinner = ora()
230271
spinner.fail('β‰  Found inconsistency in pinned hashes:')
231272
logError(pinnedHashes)
232273
return undefined
233274
}
234275

235-
const gatewayUrl = httpGatewayUrl(pinnedHash, successfulRemotePinners[0])
236-
237-
if (copyHttpGatewayUrlToClipboard) {
238-
copyUrlToClipboard(gatewayUrl)
239-
}
240-
276+
let dnslinkedHostname
241277
if (dnsProviders.includes('cloudflare')) {
242-
await updateCloudflareDns(siteDomain, credentials.cloudflare, pinnedHash)
278+
dnslinkedHostname = await updateCloudflareDns(
279+
siteDomain,
280+
credentials.cloudflare,
281+
pinnedHash
282+
)
243283
}
244284

245-
if (open && _.isEmpty(dnsProviders)) {
246-
await openUrl(gatewayUrl)
285+
const gatewayUrls = successfulRemotePinners.map(pinner =>
286+
httpGatewayUrl(pinnedHash, pinner)
287+
)
288+
289+
if (open) {
290+
gatewayUrls.forEach(async gatewayUrl => await openUrl(gatewayUrl))
291+
292+
if (dnslinkedHostname) {
293+
await openUrl(`https://${dnslinkedHostname}`)
294+
}
247295
}
248-
if (open && !_.isEmpty(dnsProviders)) {
249-
await openUrl(`https://${siteDomain}`)
296+
297+
if (copyHttpGatewayUrlToClipboard) {
298+
if (dnslinkedHostname) {
299+
copyUrlToClipboard(`https://${dnslinkedHostname}`)
300+
} else {
301+
copyUrlToClipboard(gatewayUrls[0])
302+
}
250303
}
304+
251305
return pinnedHash
252306
} else {
253307
logError('Failed to deploy.')

β€Žpackage.jsonβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"ora": "^3.4.0",
5353
"prettier": "^1.18.2",
5454
"recursive-fs": "^1.1.2",
55+
"terminal-link": "^1.3.0",
5556
"trammel": "^2.1.0",
5657
"update-notifier": "^3.0.0",
5758
"yargs": "^13.2.4"

β€Žsrc/pinata.jsβ€Ž

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ const fs = require('fs')
33
const FormData = require('form-data')
44
const recursive = require('recursive-fs')
55
const ora = require('ora')
6+
67
const { logError } = require('./logging')
8+
const { linkCid } = require('./utils/pure-fns')
79

810
const chalk = require('chalk')
911
const white = chalk.whiteBright
@@ -50,9 +52,9 @@ module.exports.setupPinata = ({ apiKey, secretApiKey }) => {
5052
})
5153

5254
spinner.succeed("πŸ“Œ It's pinned to Pinata now with hash:")
53-
const hash = response.data.IpfsHash
54-
spinner.info(`πŸ”— ${hash}`)
55-
return hash
55+
const pinnedHash = response.data.IpfsHash
56+
spinner.info(linkCid(pinnedHash, 'infura'))
57+
return pinnedHash
5658
} catch (e) {
5759
spinner.fail("πŸ’” Uploading to Pinata didn't work.")
5860
logError(e)

β€Žsrc/utils/pure-fns.jsβ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const terminalLink = require('terminal-link')
2+
const chalk = require('chalk')
3+
4+
const httpGatewayUrl = require('../gateway')
5+
6+
function linkCid(cid, gatewayProvider) {
7+
return `πŸ”— ${chalk.green(
8+
terminalLink(cid, httpGatewayUrl(cid, gatewayProvider))
9+
)}`
10+
}
11+
12+
function linkUrl(url) {
13+
return `πŸ”— ${chalk.green(terminalLink(url, url))}`
14+
}
15+
16+
module.exports = { linkCid, linkUrl }

0 commit comments

Comments
Β (0)