Explorar o código

feat(create): Better error reporting on DB connection issues.

Closes #90
Michael Bromley %!s(int64=6) %!d(string=hai) anos
pai
achega
1a7dc054b8

+ 1 - 1
packages/create/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/create",
-  "version": "0.1.0",
+  "version": "0.1.6",
   "license": "MIT",
   "bin": {
     "create": "./index.js"

+ 14 - 8
packages/create/src/create-vendure-app.ts

@@ -10,6 +10,7 @@ import { Observable } from 'rxjs';
 
 import { gatherUserResponses } from './gather-user-responses';
 import {
+    checkDbConnection,
     checkNodeVersion,
     checkThatNpmCanReadCwd,
     getDependencies,
@@ -81,19 +82,19 @@ async function createApp(name: string | undefined, useNpm: boolean, logLevel: Lo
             title: 'Installing dependencies',
             task: (() => {
                 return new Observable(subscriber => {
+                    subscriber.next('Creating package.json');
                     fs.writeFileSync(
                         path.join(root, 'package.json'),
                         JSON.stringify(packageJsonContents, null, 2) + os.EOL,
                     );
-                    subscriber.next('Created package.json');
                     const { dependencies, devDependencies } = getDependencies(usingTs, dbType);
 
+                    subscriber.next(`Installing ${dependencies.join(', ')}`);
                     installPackages(root, useYarn, dependencies, false, logLevel)
-                        .then(() => subscriber.next(`Installed ${dependencies.join(', ')}`))
                         .then(() => {
                             if (devDependencies.length) {
-                                return installPackages(root, useYarn, devDependencies, true, logLevel)
-                                    .then(() => subscriber.next(`Installed ${devDependencies.join(', ')}`));
+                                subscriber.next(`Installing ${devDependencies.join(', ')}`);
+                                return installPackages(root, useYarn, devDependencies, true, logLevel);
                             }
                         })
                         .then(() => subscriber.complete());
@@ -117,7 +118,10 @@ async function createApp(name: string | undefined, useNpm: boolean, logLevel: Lo
                         .then(() => {
                             subscriber.next(`Created index`);
                             if (usingTs) {
-                                return fs.copyFile(assetPath('tsconfig.template.json'), path.join(root, 'tsconfig.json'));
+                                return fs.copyFile(
+                                    assetPath('tsconfig.template.json'),
+                                    path.join(root, 'tsconfig.json'),
+                                );
                             }
                         })
                         .then(() => createDirectoryStructure(root))
@@ -153,8 +157,9 @@ async function createApp(name: string | undefined, useNpm: boolean, logLevel: Lo
 
                     const initialDataPath = path.join(assetsDir, 'initial-data.json');
                     const port = await detectPort(3000);
-                    const bootstrapFn = () =>
-                        bootstrap({
+                    const bootstrapFn = async () => {
+                        await checkDbConnection(config.dbConnectionOptions, root);
+                        return bootstrap({
                             ...config,
                             port,
                             silent: logLevel === 'silent',
@@ -166,13 +171,13 @@ async function createApp(name: string | undefined, useNpm: boolean, logLevel: Lo
                                 importAssetsDir: path.join(assetsDir, 'images'),
                             },
                         });
+                    };
                     let app: any;
                     if (populateProducts) {
                         app = await populate(
                             bootstrapFn,
                             initialDataPath,
                             path.join(assetsDir, 'products.csv'),
-                            path.join(assetsDir, 'images'),
                         );
                     } else {
                         app = await populate(bootstrapFn, initialDataPath);
@@ -190,6 +195,7 @@ async function createApp(name: string | undefined, useNpm: boolean, logLevel: Lo
         await tasks.run();
     } catch (e) {
         console.error(chalk.red(e));
+        process.exit(1);
     }
     const startCommand = useYarn ? 'yarn start' : 'npm run start';
     console.log();

+ 79 - 0
packages/create/src/helpers.ts

@@ -258,3 +258,82 @@ function dbDriverPackage(dbType: DbType): string {
             return '';
     }
 }
+
+/**
+ * Checks that the specified DB connection options are working (i.e. a connection can be
+ * established) and that the named database exists.
+ */
+export function checkDbConnection(options: any, root: string): Promise<true> {
+    switch (options.type) {
+        case 'mysql':
+            return checkMysqlDbExists(options, root);
+        case 'postgres':
+            return checkPostgresDbExists(options, root);
+        default:
+            return Promise.resolve(true);
+    }
+}
+
+async function checkMysqlDbExists(options: any, root: string): Promise<true> {
+    const mysql = await import(path.join(root, 'node_modules/mysql'));
+    const connectionOptions = {
+        host: options.host,
+        user: options.username,
+        password: options.password,
+        port: options.port,
+        database: options.database,
+    };
+    const connection = mysql.createConnection(connectionOptions);
+
+    return new Promise<boolean>((resolve, reject) => {
+        connection.connect((err: any) => {
+            if (err) {
+                if (err.code === 'ER_BAD_DB_ERROR') {
+                    throwDatabaseDoesNotExist(options.database);
+                }
+                throwConnectionError(err);
+            }
+            resolve(true);
+        });
+    }).then(() => {
+        return new Promise((resolve, reject) => {
+            connection.end((err: any) => {
+                resolve(true);
+            });
+        });
+    });
+}
+
+async function checkPostgresDbExists(options: any, root: string): Promise<true> {
+    const { Client } = await import(path.join(root, 'node_modules/pg'));
+    const connectionOptions = {
+        host: options.host,
+        user: options.username,
+        password: options.password,
+        port: options.port,
+        database: options.database,
+    };
+    const client = new Client(connectionOptions);
+
+    try {
+        await client.connect();
+    } catch (e) {
+        if (e.code === '3D000') {
+            throwDatabaseDoesNotExist(options.database);
+        }
+        throwConnectionError(e);
+        await client.end();
+        throw e;
+    }
+    await client.end();
+    return true;
+}
+
+function throwConnectionError(err: any) {
+    throw new Error(`Could not connect to the database. ` +
+        `Please check the connection settings in your Vendure config.\n[${err.message || err.toString()}]`);
+}
+
+function throwDatabaseDoesNotExist(name: string) {
+    throw new Error(`Database "${name}" does not exist. Please create the database and then try again.`);
+}