func hapiSignupSubmit(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Name     string
		Email    string
		Password string
		FID      string
		Admin bool
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeErrorInternal(w, err)
		return
	}
	if len(req.Name) == 0 {
		writeErrorBadRequest(w, errors.New("You must enter a name."))
		return
	}
	if len(req.Email) == 0 ||
		!strings.ContainsAny(req.Email, "@") ||
		!strings.ContainsAny(req.Email, ".") {
		writeErrorBadRequest(w, errors.New("You must enter a valid email."))
		return
	}
	if len(req.Password) < 8 {
		writeErrorBadRequest(w, errors.New("Your password must be at least 8 characters."))
		return
	}
	req.FID = regexNum.ReplaceAllString(req.FID, "")
	if len(req.FID) < 10 {
		writeErrorBadRequest(w, errors.New("Your phone number must be at least 10 digits."))
		return
	}
	if req.FID[0] != '1' {
		if len(req.FID) >= 11 {
			writeErrorBadRequest(w, errors.New("Invalid country code. Currently only American numbers are supported."))
			return
		}
		req.FID = "+1" + req.FID
	} else {
		req.FID = "+" + req.FID
	}
	var admin bool
	if req.Admin {
		var count int
		q := `SELECT COUNT(*) FROM users WHERE admin=TRUE`
		if err := db.Get(&count, q); err != nil {
			writeErrorInternal(w, err)
			return
		}
		if count > 0 {
			writeErrorBadRequest(w, errors.New("invalid param Admin"))
			return
		}
		admin = true
	}
	user := &dt.User{
		Name:  req.Name,
		Email: req.Email,
		Password: req.Password,
		Trainer:  false,
		Admin:    admin,
	}
	err := user.Create(db, dt.FlexIDType(2), req.FID)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	csrfToken, err := createCSRFToken(user)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	header, token, err := getAuthToken(user)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	resp := struct {
		ID        uint64
		Email     string
		Scopes    []string
		AuthToken string
		IssuedAt  int64
		CSRFToken string
	}{
		ID:        user.ID,
		Email:     user.Email,
		Scopes:    header.Scopes,
		AuthToken: token,
		IssuedAt:  header.IssuedAt,
		CSRFToken: csrfToken,
	}
	resp.ID = user.ID
	log.Info("user signed up. id", user.ID)
	writeBytes(w, resp)
}
hapiSignupSubmit signs up a user after server-side validation of all
passed in values.
package core
import (
	"crypto/hmac"
	"crypto/sha512"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
	"golang.org/x/crypto/bcrypt"
	w "golang.org/x/net/websocket"
	"github.com/itsabot/abot/core/log"
	"github.com/itsabot/abot/core/websocket"
	"github.com/itsabot/abot/shared/datatypes"
	"github.com/itsabot/abot/shared/interface/emailsender"
	"github.com/itsabot/abot/shared/prefs"
	"github.com/julienschmidt/httprouter"
)
var tmplLayout *template.Template
var ws = websocket.NewAtomicWebSocketSet()
var ErrInvalidUserPass = errors.New("Invalid username/password combination")
var regexNum = regexp.MustCompile(`\D+`)
func newRouter() *httprouter.Router {
	router := httprouter.New()
	router.ServeFiles("/public/*filepath", http.Dir("public"))
	if os.Getenv("ABOT_ENV") != "production" {
		initCMDGroup(router)
	}
	router.HandlerFunc("GET", "/", hIndex)
	router.HandlerFunc("POST", "/", hMain)
	router.HandlerFunc("OPTIONS", "/", hOptions)
	router.NotFound = http.HandlerFunc(hIndex)
	router.HandlerFunc("POST", "/api/login.json", hapiLoginSubmit)
	router.HandlerFunc("POST", "/api/logout.json", hapiLogoutSubmit)
	router.HandlerFunc("POST", "/api/signup.json", hapiSignupSubmit)
	router.HandlerFunc("POST", "/api/forgot_password.json", hapiForgotPasswordSubmit)
	router.HandlerFunc("POST", "/api/reset_password.json", hapiResetPasswordSubmit)
	router.HandlerFunc("GET", "/api/admin_exists.json", hapiAdminExists)
	router.HandlerFunc("GET", "/api/user/profile.json", hapiProfile)
	router.HandlerFunc("PUT", "/api/user/profile.json", hapiProfileView)
	router.HandlerFunc("GET", "/api/admin/plugins.json", hapiPlugins)
	router.HandlerFunc("GET", "/api/admin/conversations_need_training.json", hapiConversationsNeedTraining)
	router.Handle("GET", "/api/admin/conversations/:uid/:fid/:fidt/:off", hapiConversation)
	router.HandlerFunc("PATCH", "/api/admin/conversations.json", hapiConversationsUpdate)
	router.HandlerFunc("POST", "/api/admins/send_message.json", hapiSendMessage)
	router.HandlerFunc("GET", "/api/admins.json", hapiAdmins)
	router.HandlerFunc("PUT", "/api/admins.json", hapiAdminsUpdate)
	router.HandlerFunc("GET", "/api/admin/remote_tokens.json", hapiRemoteTokens)
	router.HandlerFunc("POST", "/api/admin/remote_tokens.json", hapiRemoteTokensSubmit)
	router.HandlerFunc("DELETE", "/api/admin/remote_tokens.json", hapiRemoteTokensDelete)
	router.HandlerFunc("PUT", "/api/admin/settings.json", hapiSettingsUpdate)
	router.HandlerFunc("GET", "/api/admin/dashboard.json", hapiDashboard)
	return router
}
func hIndex(w http.ResponseWriter, r *http.Request) {
	var err error
	env := os.Getenv("ABOT_ENV")
	if env != "production" && env != "test" {
		p := filepath.Join("assets", "html", "layout.html")
		tmplLayout, err = template.ParseFiles(p)
		if err != nil {
			writeErrorInternal(w, err)
			return
		}
		if err = compileAssets(); err != nil {
			writeErrorInternal(w, err)
			return
		}
	}
	data := struct {
		IsProd     bool
		ItsAbotURL string
	}{
		IsProd:     os.Getenv("ABOT_ENV") == "production",
		ItsAbotURL: os.Getenv("ITSABOT_URL"),
	}
	if err = tmplLayout.Execute(w, data); err != nil {
		writeErrorInternal(w, err)
	}
}
func hMain(w http.ResponseWriter, r *http.Request) {
	errMsg := "Something went wrong with my wiring... I'll get that fixed up soon."
	ret, err := ProcessText(r)
	if err != nil {
		if len(ret) > 0 {
			ret = errMsg
		}
		log.Info("failed to process text.", err)
	}
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Access-Control-Allow-Origin")
	_, err = fmt.Fprint(w, ret)
	if err != nil {
		writeErrorInternal(w, err)
	}
}
func hOptions(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Access-Control-Allow-Origin")
	w.WriteHeader(http.StatusOK)
}
func hapiLogoutSubmit(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("id")
	if err != nil {
		writeError(w, err)
		return
	}
	uid := cookie.Value
	if uid == "null" {
		http.Error(w, "id was null", http.StatusBadRequest)
		return
	}
	q := `DELETE FROM sessions WHERE userid=$1`
	if _, err = db.Exec(q, uid); err != nil {
		writeError(w, err)
		return
	}
	w.WriteHeader(http.StatusOK)
}
func hapiLoginSubmit(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Email    string
		Password string
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeErrorInternal(w, err)
		return
	}
	var u struct {
		ID       uint64
		Password []byte
		Admin    bool
	}
	q := `SELECT id, password, admin FROM users WHERE email=$1`
	err := db.Get(&u, q, req.Email)
	if err == sql.ErrNoRows {
		writeErrorAuth(w, ErrInvalidUserPass)
		return
	} else if err != nil {
		writeErrorInternal(w, err)
		return
	}
	if u.ID == 0 {
		writeErrorAuth(w, ErrInvalidUserPass)
		return
	}
	err = bcrypt.CompareHashAndPassword(u.Password, []byte(req.Password))
	if err == bcrypt.ErrMismatchedHashAndPassword || err == bcrypt.ErrHashTooShort {
		writeErrorAuth(w, ErrInvalidUserPass)
		return
	} else if err != nil {
		writeErrorInternal(w, err)
		return
	}
	user := &dt.User{
		ID:    u.ID,
		Email: req.Email,
		Admin: u.Admin,
	}
	csrfToken, err := createCSRFToken(user)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	header, token, err := getAuthToken(user)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	resp := struct {
		ID        uint64
		Email     string
		Scopes    []string
		AuthToken string
		IssuedAt  int64
		CSRFToken string
	}{
		ID:        user.ID,
		Email:     user.Email,
		Scopes:    header.Scopes,
		AuthToken: token,
		IssuedAt:  header.IssuedAt,
		CSRFToken: csrfToken,
	}
	writeBytes(w, resp)
}