AWS Elastic Beanstalk+Node.js+Express+Passport+MySQLでアカウント管理（その２）

[前回](http://qiita.com/items/f9f4ccd62513f22e6c56)からのつづき。

続いて、アカウントを管理するクラスを書いておく。

```javascript:accounts.js

var mysql = require('mysql');
var account_model = require('./account');
var Account = account_model.Account;

// create connection to mysql
function createConnection(){
  return sqlConnection =  mysql.createConnection({
    host     : process.env.RDS_HOSTNAME,
    user     : process.env.RDS_USERNAME,
    password : process.env.RDS_PASSWORD,
    port     : process.env.RDS_PORT,
    database : 'ebdb'  // Elastic Beanstalk用RDSの詳細画面にある"DB Name:"の名前
  });
};

function createAccountWithTransaction(connection, username, password, email, sex, description, callback){
  console.log('start transaction');
  var query = 'start transaction';
  connection.query(query, function(err, result){
    if(err){
      // start transactionに失敗。
      console.log('cannot start transaction.\n    ' + err);
      callback(err);
      return;
    }

    console.log('inserting profile...');
    query = "insert into PROFILES (EMAIL, SEX, DESCRIPTION) values (?, ?, ?)";
    connection.query(query, [email, sex, description], function(err, result){
      if(err){
        // プロフィールの登録に失敗。
        console.log('cannot insert profile');
        console.log(err);
        callback(err);
        return;  
      }
      console.log('profile inserted.');

      query = 'select last_insert_id() as solution from PROFILES';
      connection.query(query, function(err, rows, fields){
        if(err){
          // max(ID)の取得に失敗。
          console.log('cannot select last_insert_id as solution from PROFILES');
          // アカウントの登録に失敗。ロールバックする。
          query = 'rollback';
          connection.query(query, function(e){
            if(e){
              // rollbackに失敗した旨をコールバックで伝達
              callback(e);
            }else{
              // アカウントの登録に失敗したことをコールバックで伝達
              callback(err);
            }
          });
          return;
        }
        //var id = rows[0].solution + 1;
        var id = rows[0].solution;
        console.log('last_insert_id = ' + id);

        console.log('inserting account...');
        query = "insert into ACCOUNTS (ID, USERNAME, PASSWORD, PROFILEID, CREATED) values (?, ?, ?, ?, now())";
        connection.query(query, [ id, username, password, id], function(err, result){
          if(err){
            // アカウントの登録に失敗。ロールバックする。
            query = 'rollback';
            connection.query(query, function(e){
              if(e){
                // rollbackに失敗した旨をコールバックで伝達
                callback(e);
              }else{
                // アカウントの登録に失敗したことをコールバックで伝達
                callback(err);
              }
            });

          }else{
            // アカウントとプロフィールの登録に成功したのでcommit
            query = 'commit';
            connection.query(query, function(err){
              // commit自体が失敗した場合は
              // Account & Profileの登録が反映されないが
              // エラーが発生したことをコールバックで伝達。（エラーがない場合は,err=nullなので気にしない）
              console.log('query complete: add account');
              callback(err);
            });
          }
        });

      });
    });
  });
}

/*
 * method query
 *
 * @arg queryString -- query string
 * @arg callback -- callback function.
 *                         callback(err, rows, fields)
 */
function query(queryString, callback){
  // create connection to mysql
  var connection = createConnection();
  console.log('connection created.');

  // connection.connect は connection.queryが勝手にしてくれるからここでは呼ばない。
  // むしろ呼ぶと「２度接続した」とかいろいろエラーになる。

  // query
  console.log('calling connection.query: ' + queryString);
  connection.query(queryString, callback);
  console.log('called.');
};
exports.query = query;


/*
 * util methods
 */
function findById(id, callback) {
  var queryString = "SELECT * FROM ACCOUNTS WHERE ACCOUNTS.ID = '" + id + "'";

  query(queryString, function(err, rows, fields){
    if(err){
      callback(new Error('query error in findById.'));
      throw err;
    }

    if(rows.length > 0){
      var account = new Account(rows[0].ID, rows[0].USERNAME, rows[0].PASSWORD, rows[0].PROFILEID, rows[0].CREATED);
      callback(null, account);
    }else{
      callback(new Error('User ' + id + ' does not exist'));
    }
  });
}
exports.findById = findById;

function findByUsername(username, callback) {
  var queryString = "SELECT * FROM ACCOUNTS WHERE ACCOUNTS.USERNAME = '" + username + "'";
  console.log('findByUsername query:' + queryString);

  query(queryString, function(err, rows, fields){
    if(err){
      console.log('error: ' + err);
      callback(new Error('query error in findByUsername.'));
      throw err;
    }

    console.log('findByUsername querring. rows = ' + rows + ', fields = ' + fields);
    if(rows.length > 0){
      var account = new Account(rows[0].ID, rows[0].USERNAME, rows[0].PASSWORD, rows[0].PROFILEID, rows[0].CREATED);
      console.log('accout');
      console.log(account);
      //return callback(null, account);
      callback(null, account);
    }else{
      //return callback(null, null);
      callback(null, null);
    }
  });
}
exports.findByUsername = findByUsername;

function createAccount(username, password, email, sex, description, callback){
  console.log('username = ' + username + ', password = ' + password);

  // create connection to mysql
  var connection = createConnection();

  // connection.connect は connection.queryが勝手にしてくれるからここでは呼ばない。
  // むしろ呼ぶと「２度接続した」とかいろいろエラーになる。

  // query -- すでに使われているアカウント名かどうかチェックする。   
  var queryString = "SELECT * FROM ACCOUNTS WHERE ACCOUNTS.USERNAME = '" + username + "'";
  connection.query(queryString, function(err, rows, fields){
    if(err){
      console.log('query error in createAccount');
      callback(err);
      return;
    }

    createAccountWithTransaction(connection, username, password, email, sex, description, function(err){
      if(err){
        console.log('createAccountWithTransaction error');
        callback(err);
        return;
      }

      console.log('success: create account. user:' + username);
      callback(null); // success
    });

  });
}
exports.createAccount = createAccount;

/*
 * method signup
 */
exports.signup = function signup(req, res, callback){
  if(req.body.username && req.body.password){
    var username = req.body.username;
    //var password = des.crypt(req.body.password);
    var password = req.body.password;
    var email    = req.body.email;
    var sex      = req.body.sex || 'other';
    var description = req.body.description || '';

    createAccount(username, password, email, sex, description, function(err){
      if(err){
        callback(err);
      }else{
        callback(null);
      }
    });
  }
};

```

やたら、ログを吐いてるのは気にしない方向で…

[続く](http://qiita.com/items/5ee926ad1a65ed0536e3)