initial commit

This commit is contained in:
dhax 2017-09-25 18:23:11 +02:00
commit 93d8310491
46 changed files with 3379 additions and 0 deletions

66
database/accountStore.go Normal file
View file

@ -0,0 +1,66 @@
package database
import (
"github.com/dhax/go-base/models"
"github.com/go-pg/pg"
)
type AccountStore struct {
db *pg.DB
}
func NewAccountStore(db *pg.DB) *AccountStore {
return &AccountStore{
db: db,
}
}
func (s *AccountStore) Get(id int) (*models.Account, error) {
a := models.Account{ID: id}
err := s.db.Model(&a).
Where("account.id = ?id").
Column("account.*", "Profile", "Token").
First()
return &a, err
}
func (s *AccountStore) Update(a *models.Account) error {
_, err := s.db.Model(a).
Column("email", "name").
Update()
return err
}
func (s *AccountStore) Delete(a *models.Account) error {
err := s.db.RunInTransaction(func(tx *pg.Tx) error {
if _, err := tx.Model(&models.Token{}).
Where("account_id = ?", a.ID).
Delete(); err != nil {
return err
}
if _, err := tx.Model(&models.Profile{}).
Where("account_id = ?", a.ID).
Delete(); err != nil {
return err
}
return tx.Delete(a)
})
return err
}
func (s *AccountStore) UpdateToken(t *models.Token) error {
_, err := s.db.Model(t).
Column("identifier").
Update()
return err
}
func (s *AccountStore) DeleteToken(t *models.Token) error {
err := s.db.Delete(t)
return err
}
func (s *AccountStore) UpdateProfile(p *models.Profile) error {
err := s.db.Update(p)
return err
}

View file

@ -0,0 +1,84 @@
package database
import (
"errors"
"github.com/dhax/go-base/models"
"github.com/go-pg/pg"
)
var (
ErrUniqueEmailConstraint = errors.New("email already registered")
)
type AdmAccountStore struct {
db *pg.DB
}
func NewAdmAccountStore(db *pg.DB) *AdmAccountStore {
return &AdmAccountStore{
db: db,
}
}
func (s *AdmAccountStore) List(f models.AccountFilter) (*[]models.Account, int, error) {
var a []models.Account
count, err := s.db.Model(&a).
Apply(f.Filter).
SelectAndCount()
if err != nil {
return nil, 0, err
}
return &a, count, nil
}
func (s *AdmAccountStore) Create(a *models.Account) error {
count, _ := s.db.Model(a).
Where("email = ?email").
Count()
if count != 0 {
return ErrUniqueEmailConstraint
}
err := s.db.RunInTransaction(func(tx *pg.Tx) error {
err := tx.Insert(a)
if err != nil {
return err
}
p := &models.Profile{
AccountID: a.ID,
}
return tx.Insert(p)
})
return err
}
func (s *AdmAccountStore) Get(id int) (*models.Account, error) {
a := models.Account{ID: id}
err := s.db.Select(&a)
return &a, err
}
func (s *AdmAccountStore) Update(a *models.Account) error {
err := s.db.Update(a)
return err
}
func (s *AdmAccountStore) Delete(a *models.Account) error {
err := s.db.RunInTransaction(func(tx *pg.Tx) error {
if _, err := tx.Model(&models.Token{}).
Where("account_id = ?", a.ID).
Delete(); err != nil {
return err
}
if _, err := tx.Model(&models.Profile{}).
Where("account_id = ?", a.ID).
Delete(); err != nil {
return err
}
return tx.Delete(a)
})
return err
}

84
database/authStore.go Normal file
View file

@ -0,0 +1,84 @@
package database
import (
"time"
"github.com/dhax/go-base/models"
"github.com/go-pg/pg"
)
type AuthStore struct {
db *pg.DB
}
func NewAuthStore(db *pg.DB) *AuthStore {
return &AuthStore{
db: db,
}
}
func (s *AuthStore) GetByID(id int) (*models.Account, error) {
a := models.Account{ID: id}
err := s.db.Model(&a).
Column("account.*").
Where("id = ?id").
First()
return &a, err
}
func (s *AuthStore) GetByEmail(e string) (*models.Account, error) {
a := models.Account{Email: e}
err := s.db.Model(&a).
Column("id", "active", "email", "name").
Where("email = ?email").
First()
return &a, err
}
func (s *AuthStore) GetByRefreshToken(t string) (*models.Account, *models.Token, error) {
token := models.Token{Token: t}
err := s.db.Model(&token).
Where("token = ?token").
First()
if err != nil {
return nil, nil, err
}
a := models.Account{ID: token.AccountID}
err = s.db.Model(&a).
Column("account.*").
Where("id = ?id").
First()
return &a, &token, err
}
func (s *AuthStore) UpdateAccount(a *models.Account) error {
_, err := s.db.Model(a).
Column("last_login").
Update()
return err
}
func (s *AuthStore) SaveRefreshToken(t *models.Token) error {
var err error
if t.ID == 0 {
err = s.db.Insert(t)
} else {
err = s.db.Update(t)
}
return err
}
func (s *AuthStore) DeleteRefreshToken(t *models.Token) error {
err := s.db.Delete(t)
return err
}
func (s *AuthStore) PurgeExpiredToken() error {
_, err := s.db.Model(&models.Token{}).
Where("expiry < ?", time.Now()).
Delete()
return err
}

View file

@ -0,0 +1,65 @@
package migrate
import (
"fmt"
"github.com/go-pg/migrations"
)
const AccountTable = `
CREATE TABLE accounts (
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT current_timestamp,
updated_at timestamp with time zone DEFAULT current_timestamp,
last_login timestamp with time zone NOT NULL DEFAULT current_timestamp,
email text NOT NULL UNIQUE,
name text NOT NULL,
active boolean NOT NULL DEFAULT TRUE,
roles text[] NOT NULL DEFAULT '{"user"}',
PRIMARY KEY (id)
)`
const TokenTable = `
CREATE TABLE tokens (
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT current_timestamp,
updated_at timestamp with time zone NOT NULL DEFAULT current_timestamp,
account_id int NOT NULL REFERENCES accounts(id),
token text NOT NULL UNIQUE,
expiry timestamp with time zone NOT NULL,
mobile boolean NOT NULL DEFAULT FALSE,
identifier text,
PRIMARY KEY (id)
)`
func init() {
up := []string{
AccountTable,
TokenTable,
}
down := []string{
`DROP TABLE tokens`,
`DROP TABLE accounts`,
}
migrations.Register(func(db migrations.DB) error {
fmt.Println("creating initial tables")
for _, q := range up {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
}, func(db migrations.DB) error {
fmt.Println("dropping initial tables")
for _, q := range down {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
})
}

View file

@ -0,0 +1,48 @@
package migrate
import (
"fmt"
"github.com/go-pg/migrations"
)
const bootstrapAdminAccount = `
INSERT INTO accounts (id, email, name, active, roles)
VALUES (DEFAULT, 'admin@boot.io', 'Admin Boot', true, '{admin}')
`
const bootstrapUserAccount = `
INSERT INTO accounts (id, email, name, active)
VALUES (DEFAULT, 'user@boot.io', 'User Boot', true)
`
func init() {
up := []string{
bootstrapAdminAccount,
bootstrapUserAccount,
}
down := []string{
`TRUNCATE accounts CASCADE`,
}
migrations.Register(func(db migrations.DB) error {
fmt.Println("add bootstrap accounts")
for _, q := range up {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
}, func(db migrations.DB) error {
fmt.Println("truncate accounts cascading")
for _, q := range down {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
})
}

View file

@ -0,0 +1,53 @@
package migrate
import (
"fmt"
"github.com/go-pg/migrations"
)
const ProfileTable = `
CREATE TABLE profiles (
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT current_timestamp,
updated_at timestamp with time zone NOT NULL DEFAULT current_timestamp,
account_id int NOT NULL REFERENCES accounts(id),
theme text NOT NULL DEFAULT 'default',
PRIMARY KEY (id)
)`
const bootstrapAccountProfiles = `
INSERT INTO profiles(account_id) VALUES(1);
INSERT INTO profiles(account_id) VALUES(2);
`
func init() {
up := []string{
ProfileTable,
bootstrapAccountProfiles,
}
down := []string{
`DROP TABLE profiles`,
}
migrations.Register(func(db migrations.DB) error {
fmt.Println("create profile table")
for _, q := range up {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
}, func(db migrations.DB) error {
fmt.Println("drop profile table")
for _, q := range down {
_, err := db.Exec(q)
if err != nil {
return err
}
}
return nil
})
}

70
database/migrate/main.go Normal file
View file

@ -0,0 +1,70 @@
package migrate
import (
"fmt"
"os"
"github.com/dhax/go-base/database"
"github.com/go-pg/migrations"
"github.com/go-pg/pg"
)
func Migrate(args []string) {
db, err := database.DBConn()
if err != nil {
panic(err)
}
err = db.RunInTransaction(func(tx *pg.Tx) error {
oldVersion, newVersion, err := migrations.Run(tx, args...)
if err != nil {
return err
}
if newVersion != oldVersion {
fmt.Printf("migrated from version %d to %d\n", oldVersion, newVersion)
} else {
fmt.Printf("version is %d\n", oldVersion)
}
return nil
})
if err != nil {
exitf(err.Error())
}
}
func Reset() {
db, err := database.DBConn()
if err != nil {
exitf(err.Error())
}
version, err := migrations.Version(db)
if err != nil {
exitf(err.Error())
}
err = db.RunInTransaction(func(tx *pg.Tx) error {
for version != 0 {
oldVersion, newVersion, err := migrations.Run(tx, "down")
if err != nil {
return err
}
fmt.Printf("migrated from version %d to %d\n", oldVersion, newVersion)
version = newVersion
}
return nil
})
if err != nil {
exitf(err.Error())
}
}
func errorf(s string, args ...interface{}) {
fmt.Fprintf(os.Stderr, s+"\n", args...)
}
func exitf(s string, args ...interface{}) {
errorf(s, args...)
os.Exit(1)
}

44
database/postgres.go Normal file
View file

@ -0,0 +1,44 @@
package database
import (
"fmt"
"time"
"github.com/spf13/viper"
"github.com/go-pg/pg"
)
// DBConn returns a postgres connection pool
func DBConn() (*pg.DB, error) {
opts, err := pg.ParseURL(viper.GetString("database_url"))
if err != nil {
return nil, err
}
db := pg.Connect(opts)
if err := checkConn(db); err != nil {
return nil, err
}
if viper.GetBool("db_debug") {
db.OnQueryProcessed(func(event *pg.QueryProcessedEvent) {
query, err := event.FormattedQuery()
if err != nil {
panic(err)
}
fmt.Printf("%s %s\n", time.Since(event.StartTime), query)
})
}
return db, nil
}
func checkConn(db *pg.DB) error {
var n int
if _, err := db.QueryOne(pg.Scan(&n), "SELECT 1"); err != nil {
return err
}
return nil
}