Skip to content

Commit

Permalink
feat(naming): support subdomain dnslink on cloudflare
Browse files Browse the repository at this point in the history
fix #5
  • Loading branch information
agentofuser committed Jun 25, 2019
1 parent 7fd847b commit 0cd3e12
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 54 deletions.
65 changes: 48 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ The goal of `@agentofuser/ipfs-deploy` is to make it as easy as possible to

## Table of Contents

- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [Security](#security)
- [Background](#background)
- [Contributors](#contributors)
- [Who's Using](#users)
- [License](#license)
1. [@agentofuser/ipfs-deploy](#agentofuseripfs-deploy)
1. [Table of Contents](#Table-of-Contents)
2. [Install](#Install)
1. [No install:](#No-install)
3. [Usage](#Usage)
4. [API](#API)
5. [Security](#Security)
6. [Background](#Background)
7. [Contributors](#Contributors)
8. [Users](#Users)
9. [License](#License)

## Install

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

After setting up your Cloudflare and Pinata accounts, in your website's
repository root, create or edit the file `.env` with your domain and
credentials:
repository root, create or edit the file `.env` with your credentials, zone,
and record information:

```
IPFS_DEPLOY_SITE_DOMAIN=
```bash
# pinata credentials
IPFS_DEPLOY_PINATA__API_KEY=
IPFS_DEPLOY_PINATA__SECRET_API_KEY=

# cloudflare credentials
IPFS_DEPLOY_CLOUDFLARE__API_EMAIL=
IPFS_DEPLOY_CLOUDFLARE__API_KEY=

# cloudflare dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=
IPFS_DEPLOY_CLOUDFLARE__RECORD=
```

(**Don't** commit it to source control unless you know what you're doing.)
Example with top-level domain:

```bash
# cloudflare dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.agentofuser.com
```

Example with subdomain:

```bash
# cloudflare dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.test.agentofuser.com
```

Note the 2 `_` after `PINATA` and `CLOUDFLARE`.

(**Don't** commit the `.env` file to source control unless you know what you're
doing.)

```
$ echo '.env' >> .gitignore
Expand Down Expand Up @@ -177,15 +205,18 @@ const deploy = require('@agentofuser/ipfs-deploy')
try {
const deployOptions = {
publicDirPath: argv.path,
copyHttpGatewayUrlToClipboard: !argv.noClipboard,
open: !argv.O,
remotePinners: argv.p,
dnsProviders: argv.d,
copyHttpGatewayUrlToClipboard:
!(argv.clipboard === false) && !argv.C && !argv.noClipboard,
open: !(argv.open === false) && !argv.O && !argv.noOpen,
remotePinners: argv.pinner,
dnsProviders: argv.dns,
siteDomain: argv.siteDomain,
credentials: {
cloudflare: {
apiKey: argv.cloudflare && argv.cloudflare.apiKey,
apiEmail: argv.cloudflare && argv.cloudflare.apiEmail,
zone: argv.cloudflare && argv.cloudflare.zone,
record: argv.cloudflare && argv.cloudflare.record,
},
pinata: {
apiKey: argv.pinata && argv.pinata.apiKey,
Expand Down
2 changes: 2 additions & 0 deletions bin/ipfs-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ async function main() {
cloudflare: {
apiKey: argv.cloudflare && argv.cloudflare.apiKey,
apiEmail: argv.cloudflare && argv.cloudflare.apiEmail,
zone: argv.cloudflare && argv.cloudflare.zone,
record: argv.cloudflare && argv.cloudflare.record,
},
pinata: {
apiKey: argv.pinata && argv.pinata.apiKey,
Expand Down
122 changes: 88 additions & 34 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const { logError } = require('./src/logging')

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

const white = chalk.whiteBright

Expand Down Expand Up @@ -64,48 +65,81 @@ async function openUrl(url) {
const spinner = ora()
spinner.start('πŸ„ Opening web browser…')
const childProcess = await doOpen(url)
spinner.succeed('πŸ„ Opened web browser (call with -O to disable.)')
spinner.succeed('πŸ„ Opened URL on web browser (call with -O to disable):')
spinner.info(linkUrl(url))
return childProcess
}

async function updateCloudflareDns(siteDomain, { apiEmail, apiKey }, hash) {
// returns (sub)domain deployed to or null when error
async function updateCloudflareDns(
siteDomain,
{ apiEmail, apiKey, zone, record },
hash
) {
let result
const spinner = ora()

spinner.start(`πŸ“‘ Beaming new hash to DNS provider ${white('Cloudflare')}…`)
if (fp.some(_.isEmpty)([siteDomain, apiEmail, apiKey])) {
if (fp.some(_.isEmpty)([apiEmail, apiKey])) {
spinner.fail('πŸ’” Missing arguments for Cloudflare API.')
spinner.warn('🧐 Check if these environment variables are present:')
logError(`
IPFS_DEPLOY_SITE_DOMAIN
IPFS_DEPLOY_CLOUDFLARE__API_EMAIL
IPFS_DEPLOY_CLOUDFLARE__API_KEY
(Note the 2 '_' after "CLOUDFLARE".)
You can put them in a .env file if you want and they will be picked up.
`)
}
if (_.isEmpty(siteDomain) && fp.some(_.isEmpty)([zone, record])) {
spinner.fail('πŸ’” Missing arguments for Cloudflare API.')
spinner.warn('🧐 Check if these environment variables are present:')
logError(`
IPFS_DEPLOY_CLOUDFLARE__ZONE
IPFS_DEPLOY_CLOUDFLARE__RECORD
(Note the 2 '_' after "CLOUDFLARE".)
You can put them in a .env file if you want and they will be picked up.
Example with top-level domain:
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.agentofuser.com
Example with subdomain:
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.test.agentofuser.com
`)
result = null
} else {
try {
const api = {
email: apiEmail,
key: apiKey,
}
const api = {
email: apiEmail,
key: apiKey,
}

const opts = {
record: siteDomain,
zone: siteDomain,
link: `/ipfs/${hash}`,
}
const opts = {
zone: zone || siteDomain,
record: record || `_dnslink.${siteDomain}`,
link: `/ipfs/${hash}`,
}

try {
const content = await updateCloudflareDnslink(api, opts)
spinner.succeed('πŸ™Œ SUCCESS!')
spinner.info(`πŸ”„ Updated DNS TXT ${white(opts.record)} to:`)
spinner.info(`πŸ”— ${white(content)}`)

result = opts.record
.split('.')
.slice(1)
.join('.')
} catch (e) {
spinner.fail("πŸ’” Updating Cloudflare DNS didn't work.")
logError(e)
result = null
}

return siteDomain
}

return result
}

async function showSize(path) {
Expand All @@ -118,7 +152,9 @@ async function showSize(path) {
})
const kibi = byteSize(size, { units: 'iec' })
const readableSize = `${kibi.value} ${kibi.unit}`
spinner.succeed(`🚚 ${chalk.blue(path)} weighs ${readableSize}.`)
spinner.succeed(
`🚚 Directory ${chalk.blue(path)} weighs ${readableSize}.`
)
return readableSize
} catch (e) {
spinner.fail("βš– Couldn't calculate website size.")
Expand All @@ -144,9 +180,9 @@ async function addToInfura(publicDirPath) {
recursive: true,
})
spinner.succeed("πŸ“Œ It's pinned to Infura now with hash:")
const hash = response[response.length - 1].hash
spinner.info(`πŸ”— ${hash}`)
return hash
const pinnedHash = response[response.length - 1].hash
spinner.info(linkCid(pinnedHash, 'infura'))
return pinnedHash
} catch (e) {
spinner.fail("πŸ’” Uploading to Infura didn't work.")
logError(e)
Expand All @@ -160,7 +196,7 @@ function copyUrlToClipboard(url) {
try {
clipboardy.writeSync(url)
spinner.succeed('πŸ“‹ Copied HTTP gateway URL to clipboard:')
spinner.info(`πŸ”— ${chalk.green(url)}`)
spinner.info(linkUrl(url))
return url
} catch (e) {
spinner.fail('⚠️ Could not copy URL to clipboard.')
Expand All @@ -180,6 +216,8 @@ async function deploy({
cloudflare: {
apiEmail,
apiKey,
zone,
record,
},
pinata: {
apiKey,
Expand Down Expand Up @@ -213,7 +251,10 @@ async function deploy({
if (remotePinners.includes('pinata')) {
const addToPinata = setupPinata(credentials.pinata)
const pinataHash = await addToPinata(publicDirPath, {
name: siteDomain || __dirname,
name:
(credentials.cloudflare && credentials.cloudflare.record) ||
siteDomain ||
__dirname,
})

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

if (successfulRemotePinners.length > 0) {
const pinnedHash = Object.values(pinnedHashes)[0]
const isEqual = hash => hash === pinnedHash
const isEqual = pinnedHash => pinnedHash === pinnedHash
if (!fp.every(isEqual)(Object.values(pinnedHashes))) {
const spinner = ora()
spinner.fail('β‰  Found inconsistency in pinned hashes:')
logError(pinnedHashes)
return undefined
}

const gatewayUrl = httpGatewayUrl(pinnedHash, successfulRemotePinners[0])

if (copyHttpGatewayUrlToClipboard) {
copyUrlToClipboard(gatewayUrl)
}

let dnslinkedHostname
if (dnsProviders.includes('cloudflare')) {
await updateCloudflareDns(siteDomain, credentials.cloudflare, pinnedHash)
dnslinkedHostname = await updateCloudflareDns(
siteDomain,
credentials.cloudflare,
pinnedHash
)
}

if (open && _.isEmpty(dnsProviders)) {
await openUrl(gatewayUrl)
const gatewayUrls = successfulRemotePinners.map(pinner =>
httpGatewayUrl(pinnedHash, pinner)
)

if (open) {
gatewayUrls.forEach(async gatewayUrl => await openUrl(gatewayUrl))

if (dnslinkedHostname) {
await openUrl(`https://${dnslinkedHostname}`)
}
}
if (open && !_.isEmpty(dnsProviders)) {
await openUrl(`https://${siteDomain}`)

if (copyHttpGatewayUrlToClipboard) {
if (dnslinkedHostname) {
copyUrlToClipboard(`https://${dnslinkedHostname}`)
} else {
copyUrlToClipboard(gatewayUrls[0])
}
}

return pinnedHash
} else {
logError('Failed to deploy.')
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"ora": "^3.4.0",
"prettier": "^1.18.2",
"recursive-fs": "^1.1.2",
"terminal-link": "^1.3.0",
"trammel": "^2.1.0",
"update-notifier": "^3.0.0",
"yargs": "^13.2.4"
Expand Down
8 changes: 5 additions & 3 deletions src/pinata.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ const fs = require('fs')
const FormData = require('form-data')
const recursive = require('recursive-fs')
const ora = require('ora')

const { logError } = require('./logging')
const { linkCid } = require('./utils/pure-fns')

const chalk = require('chalk')
const white = chalk.whiteBright
Expand Down Expand Up @@ -50,9 +52,9 @@ module.exports.setupPinata = ({ apiKey, secretApiKey }) => {
})

spinner.succeed("πŸ“Œ It's pinned to Pinata now with hash:")
const hash = response.data.IpfsHash
spinner.info(`πŸ”— ${hash}`)
return hash
const pinnedHash = response.data.IpfsHash
spinner.info(linkCid(pinnedHash, 'infura'))
return pinnedHash
} catch (e) {
spinner.fail("πŸ’” Uploading to Pinata didn't work.")
logError(e)
Expand Down
16 changes: 16 additions & 0 deletions src/utils/pure-fns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const terminalLink = require('terminal-link')
const chalk = require('chalk')

const httpGatewayUrl = require('../gateway')

function linkCid(cid, gatewayProvider) {
return `πŸ”— ${chalk.green(
terminalLink(cid, httpGatewayUrl(cid, gatewayProvider))
)}`
}

function linkUrl(url) {
return `πŸ”— ${chalk.green(terminalLink(url, url))}`
}

module.exports = { linkCid, linkUrl }

0 comments on commit 0cd3e12

Please sign in to comment.