Skip to content

Commit

Permalink
Add database backup/restore scripts (#324)
Browse files Browse the repository at this point in the history
  • Loading branch information
koistya authored Feb 26, 2019
1 parent 6d7d9a8 commit 45494e6
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 24 deletions.
34 changes: 18 additions & 16 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
# Include your project-specific ignores in this file
# See https://help.github.com/ignore-files/ for more about ignoring files

# Dependencies
/node_modules
/flow-typed
# Testing
/coverage
# Build output
build/

# Production
/build
/secrets
# Dependencies
node_modules/
flow-typed/

# Misc
.firebase/*
__generated__
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage/

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Expand All @@ -31,3 +23,13 @@ firebase-error.log*
!.vscode/snippets
!.vscode/launch.json
!.vscode/settings.json

# Misc
.firebase/*
__generated__
.DS_Store
.env.local
.env.*.local
.eslintcache
.yarn-integrity
backup.sql
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ Also, you need to be familiar with [HTML][html], [CSS][css], [JavaScript][js] ([
├── node_modules/ # 3rd-party libraries and utilities
├── public/ # Static files such as favicon.ico etc.
├── scripts/ # Automation scripts (yarn update-schema etc.)
├── seeds/ # Reference and seed data for the database
├── src/ # Application source code
│ ├── admin/ # Admin section (Dashboard, User Management etc.)
│ ├── common/ # Shared React components and HOCs
Expand Down Expand Up @@ -114,13 +113,13 @@ Then open [http://localhost:3000/](http://localhost:3000/) to see your app.<br>
$ yarn db-change # Create a new database migration file
$ yarn db-migrate # Migrate database to the latest version
$ yarn db-rollback # Rollback the latest migration
$ yarn db-save # Save data from database to JSON files
$ yarn db-seed # Seed database with previously saved data
$ yarn db-backup --env=prod # Write database backup to backup.sql
$ yarn db-restore # Restore database backup from backup.sql
$ yarn db # Open PostgreSQL shell (for testing/debugging)
```

**Note**: Appending `--env=prod`, `--env=test` flags to any of the commands above will force it to
use database connection settings from `.env.production` and/or `.env.test` file(s).
**Note**: Appending `--env=prod` or `--env=test` flags to any of the commands above will force it
to use database connection settings from `.env.production` or `.env.test` files.

### How to Test

Expand All @@ -134,7 +133,7 @@ $ yarn test # Run unit tests. Or, `yarn test -- --watch`

1. Create a new **Google Cloud** project and **Cloud SQL** database.
2. Configure authentication in **Firebase** dashboard.
3. Set Firebase project ID in `.firebaserc` file.
3. Set Google Cloud project ID in `package.json` file (see `scripts`).
4. Set API keys, secrets and other settings in `.env.production` file.
5. Migrate the database by running `yarn db-migrate --env=prod`.
6. Finally, deploy your application by running `yarn deploy-prod`.
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,18 @@
}
},
"scripts": {
"setup": "node ./scripts/setup",
"update-schema": "node ./scripts/update-schema",
"relay": "relay-compiler --src ./src --schema ./schema.graphql",
"setup": "node ./scripts/setup",
"prestart": "yarn relay",
"start": "react-app start",
"build": "react-app build",
"test": "react-app test",
"lint": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" .",
"lint-fix": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" --fix . && yarn run prettier --write \"**/*.{js,json}\"",
"db": "node ./scripts/db",
"db-save": "node ./scripts/db-save",
"db-backup": "node ./scripts/db-backup",
"db-restore": "node ./scripts/db-restore",
"db-change": "knex migrate:make",
"db-migrate": "knex migrate:latest",
"db-rollback": "knex migrate:rollback",
Expand Down
87 changes: 87 additions & 0 deletions scripts/db-backup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* React Starter Kit for Firebase
* https://github.com/kriasoft/react-firebase-starter
* Copyright (c) 2015-present Kriasoft | MIT License
*/

const fs = require('fs');
const readline = require('readline');
const cp = require('child_process');
const { EOL } = require('os');

// Load environment variables (PGHOST, PGUSER, etc.)
require('../knexfile');

// Ensure that the SSL key file has correct permissions
if (process.env.PGSSLKEY) {
cp.spawnSync('chmod', ['0600', process.env.PGSSLKEY], { stdio: 'inherit' });
}

// Get the list of database tables
let cmd = cp.spawnSync(
'psql',
[
'--no-align',
'--tuples-only',
'--record-separator=|',
'--command',
"SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'",
],
{
stdio: ['inherit', 'pipe', 'inherit'],
},
);

if (cmd.status !== 0) {
console.error('Failed to read the list of database tables.');
process.exit(cmd.status);
}

const tables = cmd.stdout
.toString('utf8')
.trim()
.split('|')
.filter(x => x !== 'migrations' && x !== 'migrations_lock')
.map(x => `public."${x}"`)
.join(', ');

// Dump the database
cmd = cp
.spawn(
'pg_dump',
[
'--data-only',
'--no-owner',
'--no-privileges',
'--column-inserts',
'--disable-triggers',
'--exclude-table=migrations',
'--exclude-table=migrations_lock',
'--exclude-table=migrations_id_seq',
'--exclude-table=migrations_lock_index_seq',
...process.argv.slice(2).filter(x => !x.startsWith('--env')),
],
{
stdio: ['pipe', 'pipe', 'inherit'],
},
)
.on('exit', code => {
if (code !== 0) process.exit(code);
});

const out = fs.createWriteStream('backup.sql', { encoding: 'utf8' });
const rl = readline.createInterface({ input: cmd.stdout, terminal: false });

rl.on('line', line => {
// Some (system) triggers cannot be disabled in a cloud environment
// "DISABLE TRIGGER ALL" => "DISABLE TRIGGER USER"
if (line.endsWith(' TRIGGER ALL;')) {
out.write(`${line.substr(0, line.length - 5)} USER;${EOL}`, 'utf8');
}
// Add a command that truncates all the database tables
else if (line.startsWith('SET row_security')) {
out.write(`${line}${EOL}${EOL}TRUNCATE TABLE ${tables} CASCADE;${EOL}`);
} else {
out.write(`${line}${EOL}`, 'utf8');
}
});
28 changes: 28 additions & 0 deletions scripts/db-restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* React Starter Kit for Firebase
* https://github.com/kriasoft/react-firebase-starter
* Copyright (c) 2015-present Kriasoft | MIT License
*/

const cp = require('child_process');

// Load environment variables (PGHOST, PGUSER, etc.)
require('../knexfile');

// Ensure that the SSL key file has correct permissions
if (process.env.PGSSLKEY) {
cp.spawnSync('chmod', ['0600', process.env.PGSSLKEY], { stdio: 'inherit' });
}

cp.spawn(
'psql',
[
'--file=backup.sql',
'--echo-errors',
'--no-readline',
...process.argv.slice(2).filter(x => !x.startsWith('--env')),
],
{
stdio: 'inherit',
},
).on('exit', process.exit);

0 comments on commit 45494e6

Please sign in to comment.