UNPKG

3.24 kBJavaScriptView Raw
1'use strict'
2
3const co = require('co')
4const cli = require('heroku-cli-util')
5
6function dropboxURL (url) {
7 // Force a dump file to download instead of rendering as HTML
8 // file by specifying the dl=1 param for Dropbox URL
9 if (url.match(/https?:\/\/www.dropbox.com/) && !url.endsWith('dl=1')) {
10 if (url.endsWith('dl=0')) url = url.replace('dl=0', 'dl=1')
11 else if (url.includes('?')) url += '&dl=1'
12 else url += '?dl=1'
13 }
14 return url
15}
16
17function * run (context, heroku) {
18 const pgbackups = require('../../lib/pgbackups')(context, heroku)
19 const fetcher = require('../../lib/fetcher')(heroku)
20 const host = require('../../lib/host')
21 const sortBy = require('lodash.sortby')
22
23 const {app, args, flags} = context
24 const interval = Math.max(3, parseInt(flags['wait-interval'])) || 3
25 const db = yield fetcher.addon(app, args.database)
26
27 let backupURL
28 let backupName = args.backup
29
30 if (backupName && backupName.match(/^https?:\/\//)) {
31 backupURL = dropboxURL(backupName)
32 } else {
33 let backupApp
34 if (backupName && backupName.match(/::/)) {
35 [backupApp, backupName] = backupName.split('::')
36 } else {
37 backupApp = app
38 }
39 let transfers = yield heroku.get(`/client/v11/apps/${backupApp}/transfers`, {host: host()})
40 let backups = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'gof3r')
41 let backup
42 if (backupName) {
43 backup = backups.find(b => pgbackups.transfer.name(b) === backupName)
44 if (!backup) throw new Error(`Backup ${cli.color.cyan(backupName)} not found for ${cli.color.app(backupApp)}`)
45 if (!backup.succeeded) throw new Error(`Backup ${cli.color.cyan(backupName)} for ${cli.color.app(backupApp)} did not complete successfully`)
46 } else {
47 backup = sortBy(backups.filter(b => b.succeeded), 'finished_at').pop()
48 if (!backup) throw new Error(`No backups for ${cli.color.app(backupApp)}. Capture one with ${cli.color.cmd('heroku pg:backups:capture')}`)
49 backupName = pgbackups.transfer.name(backup)
50 }
51 backupURL = backup.to_url
52 }
53
54 yield cli.confirmApp(app, flags.confirm)
55 let restore
56 yield cli.action(`Starting restore of ${cli.color.cyan(backupName)} to ${cli.color.addon(db.name)}`, co(function * () {
57 restore = yield heroku.post(`/client/v11/databases/${db.id}/restores`, {
58 body: {backup_url: backupURL},
59 host: host(db)
60 })
61 }))
62 cli.log(`
63Use Ctrl-C at any time to stop monitoring progress; the backup will continue restoring.
64Use ${cli.color.cmd('heroku pg:backups')} to check progress.
65Stop a running restore with ${cli.color.cmd('heroku pg:backups:cancel')}.
66`)
67
68 yield pgbackups.wait('Restoring', restore.uuid, interval, flags.verbose, db.app.name)
69}
70
71module.exports = {
72 topic: 'pg',
73 command: 'backups:restore',
74 description: 'restore a backup (default latest) to a database',
75 help: 'defaults to saving the latest database to DATABASE_URL',
76 needsApp: true,
77 needsAuth: true,
78 args: [
79 {name: 'backup', optional: true},
80 {name: 'database', optional: true}
81 ],
82 flags: [
83 {name: 'wait-interval', hasValue: true},
84 {name: 'verbose', char: 'v'},
85 {name: 'confirm', char: 'c', hasValue: true}
86 ],
87 run: cli.command({preauth: true}, co.wrap(run))
88}