initial commit
This commit is contained in:
commit
93d8310491
46 changed files with 3379 additions and 0 deletions
66
database/accountStore.go
Normal file
66
database/accountStore.go
Normal 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
|
||||
}
|
||||
84
database/admAccountStore.go
Normal file
84
database/admAccountStore.go
Normal 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
84
database/authStore.go
Normal 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
|
||||
}
|
||||
65
database/migrate/1_initial.go
Normal file
65
database/migrate/1_initial.go
Normal 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
|
||||
})
|
||||
}
|
||||
48
database/migrate/2_bootstrap_users.go
Normal file
48
database/migrate/2_bootstrap_users.go
Normal 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
|
||||
})
|
||||
}
|
||||
53
database/migrate/3_add_profile_table.go
Normal file
53
database/migrate/3_add_profile_table.go
Normal 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
70
database/migrate/main.go
Normal 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
44
database/postgres.go
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue