Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
language: go

go:
- 1.3.1
- 1.3.1

before_script:
- source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list
- wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add -
- sudo apt-get update
- sudo apt-get install rethinkdb
- rethinkdb --bind all &
1 change: 1 addition & 0 deletions db/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var databaseNames = []string{
"prod",
"staging",
"dev",
"test",
}

// Setup configures the RethinkDB server
Expand Down
12 changes: 9 additions & 3 deletions env/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package env

// Flags contains values of flags which are important in the whole API
type Flags struct {
BindAddress string
APIVersion string
LogFormatterType string
BindAddress string
APIVersion string
LogFormatterType string
ForceColors bool

SessionDuration int
ClassicRegistration bool

RethinkDBURL string
RethinkDBKey string
RethinkDBDatabase string
}
182 changes: 21 additions & 161 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@ import (
"net"
"net/http"
"os"
"time"

"github.com/Sirupsen/logrus"
"github.com/dancannon/gorethink"
"github.com/lavab/glogrus"

"github.com/namsral/flag"
"github.com/zenazn/goji/graceful"
"github.com/zenazn/goji/web"
"github.com/zenazn/goji/web/middleware"

"github.com/lavab/api/db"
"github.com/lavab/api/env"
"github.com/lavab/api/routes"
"github.com/lavab/api/setup"
)

// TODO: "Middleware that implements a few quick security wins"
Expand All @@ -35,7 +30,7 @@ var (
rethinkdbURL = flag.String("rethinkdb_url", func() string {
address := os.Getenv("RETHINKDB_PORT_28015_TCP_ADDR")
if address == "" {
address = "localhost"
address = "127.0.0.1"
}
return address + ":28015"
}(), "Address of the RethinkDB database")
Expand All @@ -55,182 +50,47 @@ func main() {

// Put config into the environment package
env.Config = &env.Flags{
BindAddress: *bindAddress,
APIVersion: *apiVersion,
LogFormatterType: *logFormatterType,
SessionDuration: *sessionDuration,
ClassicRegistration: *classicRegistration,
}

// Set up a new logger
log := logrus.New()
BindAddress: *bindAddress,
APIVersion: *apiVersion,
LogFormatterType: *logFormatterType,
ForceColors: *forceColors,

// Set the formatter depending on the passed flag's value
if *logFormatterType == "text" {
log.Formatter = &logrus.TextFormatter{
ForceColors: *forceColors,
}
} else if *logFormatterType == "json" {
log.Formatter = &logrus.JSONFormatter{}
}

// Pass it to the environment package
env.Log = log

// Set up the database
rethinkOpts := gorethink.ConnectOpts{
Address: *rethinkdbURL,
AuthKey: *rethinkdbKey,
MaxIdle: 10,
IdleTimeout: time.Second * 10,
}
err := db.Setup(rethinkOpts)
if err != nil {
log.WithFields(logrus.Fields{
"error": err,
}).Fatal("Unable to set up the database")
}

// Initialize the actual connection
rethinkOpts.Database = *rethinkdbDatabase
rethinkSession, err := gorethink.Connect(rethinkOpts)
if err != nil {
log.WithFields(logrus.Fields{
"error": err,
}).Fatal("Unable to connect to the database")
}

// Put the RethinkDB session into the environment package
env.Rethink = rethinkSession
ClassicRegistration: *classicRegistration,
SessionDuration: *sessionDuration,

// Initialize the tables
env.Tokens = &db.TokensTable{
RethinkCRUD: db.NewCRUDTable(
rethinkSession,
rethinkOpts.Database,
"tokens",
),
}
env.Accounts = &db.AccountsTable{
RethinkCRUD: db.NewCRUDTable(
rethinkSession,
rethinkOpts.Database,
"accounts",
),
Tokens: env.Tokens,
}
env.Keys = &db.KeysTable{
RethinkCRUD: db.NewCRUDTable(
rethinkSession,
rethinkOpts.Database,
"keys",
),
}
env.Contacts = &db.ContactsTable{
RethinkCRUD: db.NewCRUDTable(
rethinkSession,
rethinkOpts.Database,
"contacts",
),
RethinkDBURL: *rethinkdbURL,
RethinkDBKey: *rethinkdbKey,
RethinkDBDatabase: *rethinkdbDatabase,
}

// Create a new goji mux
mux := web.New()

// Include the most basic middlewares:
// - RequestID assigns an unique ID for each request in order to identify errors.
// - Glogrus logs each request
// - Recoverer prevents panics from crashing the API
// - AutomaticOptions automatically responds to OPTIONS requests
mux.Use(middleware.RequestID)
mux.Use(glogrus.NewGlogrus(log, "api"))
mux.Use(middleware.Recoverer)
mux.Use(middleware.AutomaticOptions)

// Set up an auth'd mux
auth := web.New()
auth.Use(routes.AuthMiddleware)

// Index route
mux.Get("/", routes.Hello)

// Accounts
auth.Get("/accounts", routes.AccountsList)
mux.Post("/accounts", routes.AccountsCreate)
auth.Get("/accounts/:id", routes.AccountsGet)
auth.Put("/accounts/:id", routes.AccountsUpdate)
auth.Delete("/accounts/:id", routes.AccountsDelete)
auth.Post("/accounts/:id/wipe-data", routes.AccountsWipeData)

// Tokens
auth.Get("/tokens", routes.TokensGet)
mux.Post("/tokens", routes.TokensCreate)
auth.Delete("/tokens", routes.TokensDelete)
auth.Delete("/tokens/:id", routes.TokensDelete)

// Threads
auth.Get("/threads", routes.ThreadsList)
auth.Get("/threads/:id", routes.ThreadsGet)
auth.Put("/threads/:id", routes.ThreadsUpdate)

// Emails
auth.Get("/emails", routes.EmailsList)
auth.Post("/emails", routes.EmailsCreate)
auth.Get("/emails/:id", routes.EmailsGet)
auth.Put("/emails/:id", routes.EmailsUpdate)
auth.Delete("/emails/:id", routes.EmailsDelete)

// Labels
auth.Get("/labels", routes.LabelsList)
auth.Post("/labels", routes.LabelsCreate)
auth.Get("/labels/:id", routes.LabelsGet)
auth.Put("/labels/:id", routes.LabelsUpdate)
auth.Delete("/labels/:id", routes.LabelsDelete)

// Contacts
auth.Get("/contacts", routes.ContactsList)
auth.Post("/contacts", routes.ContactsCreate)
auth.Get("/contacts/:id", routes.ContactsGet)
auth.Put("/contacts/:id", routes.ContactsUpdate)
auth.Delete("/contacts/:id", routes.ContactsDelete)

// Keys
mux.Get("/keys", routes.KeysList)
auth.Post("/keys", routes.KeysCreate)
mux.Get("/keys/:id", routes.KeysGet)
auth.Post("/keys/:id/vote", routes.KeysVote)

// Merge the muxes
mux.Handle("/*", auth)

// Compile the routes
mux.Compile()
// Generate a mux
mux := setup.PrepareMux(env.Config)

// Make the mux handle every request
http.Handle("/", mux)

// Log that we're starting the server
log.WithFields(logrus.Fields{
"address": *bindAddress,
env.Log.WithFields(logrus.Fields{
"address": env.Config.BindAddress,
}).Info("Starting the HTTP server")

// Initialize the goroutine listening to signals passed to the app
graceful.HandleSignals()

// Pre-graceful shutdown event
graceful.PreHook(func() {
log.Info("Received a singnal, stopping the application")
env.Log.Info("Received a singnal, stopping the application")
})

// Post-shutdown event
graceful.PostHook(func() {
log.Info("Stopped the application")
env.Log.Info("Stopped the application")
})

// Listen to the passed address
listener, err := net.Listen("tcp", *bindAddress)
listener, err := net.Listen("tcp", env.Config.BindAddress)
if err != nil {
log.WithFields(logrus.Fields{
env.Log.WithFields(logrus.Fields{
"error": err,
"address": *bindAddress,
}).Fatal("Cannot set up a TCP listener")
Expand All @@ -240,7 +100,7 @@ func main() {
err = graceful.Serve(listener, http.DefaultServeMux)
if err != nil {
// Don't use .Fatal! We need the code to shut down properly.
log.Error(err)
env.Log.Error(err)
}

// If code reaches this place, it means that it was forcefully closed.
Expand Down
Loading