Skip to content

Commit

Permalink
Added support for Bolt and MongoDB
Browse files Browse the repository at this point in the history
  • Loading branch information
josephspurrier committed Feb 1, 2016
1 parent e452e33 commit c44af62
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 52 deletions.
32 changes: 21 additions & 11 deletions README.md
Expand Up @@ -3,31 +3,39 @@ Basic MVC Web Application in Golang

This project demonstrates how to structure and build a website using the Go language without a framework.

## Quick Start with MySQL

To download, run the following command:

~~~
go get github.com/josephspurrier/gowebapp
~~~

Start MySQL and import config/mysql.sql to create the database and tables.
## Quick Start with Bolt

The gowebapp.db file will be created once you start the application.

Build and run from the root directory. Open your web browser to: http://localhost. You should see the welcome page.

Navigate to the login page, and then to the register page. Create a new user and you should be able to login. That's it.

Open config/config.json and edit the Database section so the connection information matches your MySQL instance.
## Quick Start with MongoDB

Start MongoDB.

Open config/config.json and edit the Database section so the connection information matches your MongoDB instance. Also, change Type from Bolt to MongoDB.

Build and run from the root directory. Open your web browser to: http://localhost. You should see the welcome page.

Navigate to the login page, and then to the register page. Create a new user and you should be able to login. That's it.

## Switching to MongoDB
## Quick Start with MySQL

Start MySQL and import config/mysql.sql to create the database and tables.

A few people have asked for MongoDB so I made a few code modifications here:
https://gist.github.com/josephspurrier/7742f8e863ee46dd12ba
Open config/config.json and edit the Database section so the connection information matches your MySQL instance. Also, change Type from Bolt to MySQL.

And a few things to keep in mind:
Build and run from the root directory. Open your web browser to: http://localhost. You should see the welcome page.

* in login.go, use this to check for no results found: if err == model.ErrNoResult
* And to get the ID, use the User.Id() func
Navigate to the login page, and then to the register page. Create a new user and you should be able to login. That's it.

## Overview

Expand Down Expand Up @@ -274,7 +282,9 @@ if !recaptcha.Verified(r) {

It's a good idea to abstract the database layer out so if you need to make
changes, you don't have to look through business logic to find the queries. All
the queries are stored in the models folder:
the queries are stored in the models folder.

The user.go file at the root of the model directory is a compliation of all the queries for each database type.

Connect to the database (only once needed in your application):

Expand Down
9 changes: 8 additions & 1 deletion config/config.json
@@ -1,6 +1,13 @@
{
"Database": {
"Type": "MySQL",
"Type": "Bolt",
"Bolt": {
"Path": "gowebapp.db"
},
"MongoDB": {
"URL": "127.0.0.1",
"Database": "gowebapp"
},
"MySQL": {
"Username": "root",
"Password": "",
Expand Down
5 changes: 2 additions & 3 deletions controller/login.go
@@ -1,7 +1,6 @@
package controller

import (
"database/sql"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -72,7 +71,7 @@ func LoginPOST(w http.ResponseWriter, r *http.Request) {
result, err := model.UserByEmail(email)

// Determine if user exists
if err == sql.ErrNoRows {
if err == model.ErrNoResult {
loginAttempt(sess)
sess.AddFlash(view.Flash{"Password is incorrect - Attempt: " + fmt.Sprintf("%v", sess.Values[sessLoginAttempt]), view.FlashWarning})
sess.Save(r, w)
Expand All @@ -90,7 +89,7 @@ func LoginPOST(w http.ResponseWriter, r *http.Request) {
// Login successfully
session.Empty(sess)
sess.AddFlash(view.Flash{"Login successful!", view.FlashSuccess})
sess.Values["id"] = result.Id
sess.Values["id"] = result.ID()
sess.Values["email"] = email
sess.Values["first_name"] = result.First_name
sess.Save(r, w)
Expand Down
5 changes: 2 additions & 3 deletions controller/register.go
@@ -1,7 +1,6 @@
package controller

import (
"database/sql"
"log"
"net/http"

Expand Down Expand Up @@ -70,9 +69,9 @@ func RegisterPOST(w http.ResponseWriter, r *http.Request) {
}

// Get database result
_, err := model.UserIdByEmail(email)
_, err := model.UserByEmail(email)

if err == sql.ErrNoRows { // If success (no user exists with that email)
if err == model.ErrNoResult { // If success (no user exists with that email)
ex := model.UserCreate(first_name, last_name, email, password)
// Will only error if there is a problem with the query
if ex != nil {
Expand Down
2 changes: 1 addition & 1 deletion gowebapp.go
Expand Up @@ -63,7 +63,7 @@ var config = &configuration{}

// configuration contains the application settings
type configuration struct {
Database database.Databases `json:"Database"`
Database database.DatabaseInfo `json:"Database"`
Email email.SMTPInfo `json:"Email"`
Recaptcha recaptcha.RecaptchaInfo `json:"Recaptcha"`
Server server.Server `json:"Server"`
Expand Down
80 changes: 80 additions & 0 deletions model/bolt/user.go
@@ -0,0 +1,80 @@
package model

import (
"errors"
"time"

"github.com/josephspurrier/gowebapp/shared/database"

"gopkg.in/mgo.v2/bson"
)

// *****************************************************************************
// User
// *****************************************************************************

// User table contains the information for each user
type User struct {
ObjectId bson.ObjectId `bson:"_id"`
First_name string `db:"first_name" bson:"first_name"`
Last_name string `db:"last_name" bson:"last_name"`
Email string `db:"email" bson:"email"`
Password string `db:"password" bson:"password"`
Status_id uint8 `db:"status_id" bson:"status_id"`
Created_at time.Time `db:"created_at" bson:"created_at"`
Updated_at time.Time `db:"updated_at" bson:"updated_at"`
Deleted uint8 `db:"deleted" bson:"deleted"`
}

var (
ErrCode = errors.New("Case statement in code is not correct.")
ErrNoResult = errors.New("Result not found.")
ErrUnavailable = errors.New("Database is unavailable.")
)

// Id returns the user id
func (u *User) ID() string {
return u.ObjectId.Hex()
}

// standardizeErrors returns the same error regardless of the database used
func standardizeError(err error) error {
return err
}

// UserByEmail gets user information from email
func UserByEmail(email string) (User, error) {
var err error

result := User{}

err = database.View("user", email, &result)
if err != nil {
err = ErrNoResult
}

return result, standardizeError(err)
}

// UserCreate creates user
func UserCreate(first_name, last_name, email, password string) error {
var err error

now := time.Now()

user := &User{
ObjectId: bson.NewObjectId(),
First_name: first_name,
Last_name: last_name,
Email: email,
Password: password,
Status_id: 1,
Created_at: now,
Updated_at: now,
Deleted: 0,
}

err = database.Update("user", user.Email, &user)

return standardizeError(err)
}
96 changes: 96 additions & 0 deletions model/mongo/user.go
@@ -0,0 +1,96 @@
package model

import (
"errors"
"time"

"github.com/josephspurrier/gowebapp/shared/database"

"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)

// *****************************************************************************
// User
// *****************************************************************************

// User table contains the information for each user
type User struct {
ObjectId bson.ObjectId `bson:"_id"`
First_name string `db:"first_name" bson:"first_name"`
Last_name string `db:"last_name" bson:"last_name"`
Email string `db:"email" bson:"email"`
Password string `db:"password" bson:"password"`
Status_id uint8 `db:"status_id" bson:"status_id"`
Created_at time.Time `db:"created_at" bson:"created_at"`
Updated_at time.Time `db:"updated_at" bson:"updated_at"`
Deleted uint8 `db:"deleted" bson:"deleted"`
}

var (
ErrCode = errors.New("Case statement in code is not correct.")
ErrNoResult = errors.New("Result not found.")
ErrUnavailable = errors.New("Database is unavailable.")
)

// Id returns the user id
func (u *User) ID() string {
return u.ObjectId.Hex()
}

// standardizeErrors returns the same error regardless of the database used
func standardizeError(err error) error {
if err == mgo.ErrNotFound {
return ErrNoResult
}

return err
}

// UserByEmail gets user information from email
func UserByEmail(email string) (User, error) {
var err error

result := User{}

if database.CheckConnection() {
session := database.Mongo.Copy()
defer session.Close()
c := session.DB(database.ReadConfig().MongoDB.Database).C("user")
err = c.Find(bson.M{"email": email}).One(&result)
} else {
err = ErrUnavailable
}

return result, standardizeError(err)
}

// UserCreate creates user
func UserCreate(first_name, last_name, email, password string) error {
var err error

now := time.Now()

if database.CheckConnection() {
session := database.Mongo.Copy()
defer session.Close()
c := session.DB(database.ReadConfig().MongoDB.Database).C("user")

user := &User{
ObjectId: bson.NewObjectId(),
First_name: first_name,
Last_name: last_name,
Email: email,
Password: password,
Status_id: 1,
Created_at: now,
Updated_at: now,
Deleted: 0,
}
err = c.Insert(user)
} else {
err = ErrUnavailable
}

return standardizeError(err)
}
76 changes: 76 additions & 0 deletions model/sql/user.go
@@ -0,0 +1,76 @@
package model

import (
"database/sql"
"errors"
"fmt"
"time"

"github.com/josephspurrier/gowebapp/shared/database"
)

// *****************************************************************************
// User
// *****************************************************************************

// User table contains the information for each user
type User struct {
Id uint32 `db:"id"`
First_name string `db:"first_name"`
Last_name string `db:"last_name"`
Email string `db:"email"`
Password string `db:"password"`
Status_id uint8 `db:"status_id"`
Created_at time.Time `db:"created_at"`
Updated_at time.Time `db:"updated_at"`
Deleted uint8 `db:"deleted"`
}

// User_status table contains every possible user status (active/inactive)
type User_status struct {
Id uint8 `db:"id"`
Status string `db:"status"`
Created_at time.Time `db:"created_at"`
Updated_at time.Time `db:"updated_at"`
Deleted uint8 `db:"deleted"`
}

var (
ErrCode = errors.New("Case statement in code is not correct.")
ErrNoResult = errors.New("Result not found.")
ErrUnavailable = errors.New("Database is unavailable.")
)

// Id returns the user id
func (u *User) ID() string {
return fmt.Sprintf("%v", u.Id)
}

// standardizeErrors returns the same error regardless of the database used
func standardizeError(err error) error {
if err == sql.ErrNoRows {
return ErrNoResult
}

return err
}

// UserByEmail gets user information from email
func UserByEmail(email string) (User, error) {
result := User{}
err := database.Sql.Get(&result, "SELECT id, password, status_id, first_name FROM user WHERE email = ? LIMIT 1", email)
return result, err
}

// UserIdByEmail gets user id from email
func UserIdByEmail(email string) (User, error) {
result := User{}
err := database.Sql.Get(&result, "SELECT id FROM user WHERE email = ? LIMIT 1", email)
return result, err
}

// UserCreate creates user
func UserCreate(first_name, last_name, email, password string) error {
_, err := database.Sql.Exec("INSERT INTO user (first_name, last_name, email, password) VALUES (?,?,?,?)", first_name, last_name, email, password)
return err
}

0 comments on commit c44af62

Please sign in to comment.