Introducing LevelDB, currently handling only users
This commit is contained in:
parent
efc6246685
commit
868a4e3abd
|
@ -106,7 +106,7 @@ func apiAuthHandler(f func(*config.Options, *happydns.User, httprouter.Params, i
|
|||
err: err,
|
||||
status: http.StatusUnauthorized,
|
||||
}.WriteResponse(w)
|
||||
} else if std, err := storage.MainStore.GetUser(int(session.IdUser)); err != nil {
|
||||
} else if std, err := storage.UsersStore.GetUser(int(session.IdUser)); err != nil {
|
||||
APIErrorResponse{
|
||||
err: err,
|
||||
status: http.StatusUnauthorized,
|
||||
|
|
|
@ -52,7 +52,7 @@ func dummyAuth(_ httprouter.Params, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
if user, err := storage.MainStore.GetUserByEmail(lf.Email); err != nil {
|
||||
if user, err := storage.UsersStore.GetUserByEmail(lf.Email); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func checkAuth(_ httprouter.Params, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
if user, err := storage.MainStore.GetUserByEmail(lf.Email); err != nil {
|
||||
if user, err := storage.UsersStore.GetUserByEmail(lf.Email); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func registerUser(opts *config.Options, p httprouter.Params, body io.Reader) Res
|
|||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else if err := storage.MainStore.CreateUser(user); err != nil {
|
||||
} else if err := storage.UsersStore.CreateUser(user); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -6,4 +6,5 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/miekg/dns v1.1.29
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
)
|
||||
|
|
17
go.sum
17
go.sum
|
@ -1,18 +1,31 @@
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
|
@ -21,3 +34,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
19
main.go
19
main.go
|
@ -3,14 +3,17 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happydns/api"
|
||||
"git.happydns.org/happydns/config"
|
||||
"git.happydns.org/happydns/storage"
|
||||
"git.happydns.org/happydns/storage/mysql"
|
||||
leveldb "git.happydns.org/happydns/storage/leveldb"
|
||||
mysql "git.happydns.org/happydns/storage/mysql"
|
||||
)
|
||||
|
||||
type ResponseWriterPrefix struct {
|
||||
|
@ -58,6 +61,8 @@ func StripPrefix(opts *config.Options, h http.Handler) http.Handler {
|
|||
func main() {
|
||||
var err error
|
||||
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
// Load and parse options
|
||||
var opts *config.Options
|
||||
if opts, err = config.ConsolidateConfig(); err != nil {
|
||||
|
@ -66,17 +71,27 @@ func main() {
|
|||
|
||||
// Initialize contents
|
||||
log.Println("Opening database...")
|
||||
if store, err := database.NewMySQLStorage(opts.DSN); err != nil {
|
||||
if store, err := mysql.NewMySQLStorage(opts.DSN); err != nil {
|
||||
log.Fatal("Cannot open the database: ", err)
|
||||
} else {
|
||||
storage.MainStore = store
|
||||
}
|
||||
defer storage.MainStore.Close()
|
||||
|
||||
if store, err := leveldb.NewLevelDBStorage("happydns.db"); err != nil {
|
||||
log.Fatal("Cannot open the database: ", err)
|
||||
} else {
|
||||
storage.UsersStore = store
|
||||
}
|
||||
defer storage.UsersStore.Close()
|
||||
|
||||
log.Println("Do database migrations...")
|
||||
if err = storage.MainStore.DoMigration(); err != nil {
|
||||
log.Fatal("Cannot migrate database: ", err)
|
||||
}
|
||||
if err = storage.UsersStore.DoMigration(); err != nil {
|
||||
log.Fatal("Cannot migrate users database: ", err)
|
||||
}
|
||||
|
||||
// Serve content
|
||||
log.Println("Ready, listening on", opts.Bind)
|
||||
|
|
|
@ -33,3 +33,17 @@ type Storage interface {
|
|||
DeleteZone(z *happydns.Zone) error
|
||||
ClearZones() error
|
||||
}
|
||||
|
||||
type UserStorage interface {
|
||||
DoMigration() error
|
||||
Close() error
|
||||
|
||||
GetUsers() (happydns.Users, error)
|
||||
GetUser(id int) (*happydns.User, error)
|
||||
GetUserByEmail(email string) (*happydns.User, error)
|
||||
UserExists(email string) bool
|
||||
CreateUser(user *happydns.User) error
|
||||
UpdateUser(user *happydns.User) error
|
||||
DeleteUser(user *happydns.User) error
|
||||
ClearUsers() error
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package database // import "happydns.org/storage/leveldb"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
type LevelDBStorage struct {
|
||||
db *leveldb.DB
|
||||
}
|
||||
|
||||
// NewMySQLStorage establishes the connection to the database
|
||||
func NewLevelDBStorage(path string) (*LevelDBStorage, error) {
|
||||
if db, err := leveldb.OpenFile(path, nil); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &LevelDBStorage{db}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DoMigration() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) Close() error {
|
||||
return s.db.Close()
|
||||
}
|
||||
|
||||
func decodeData(data []byte, v interface{}) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) get(key string, v interface{}) error {
|
||||
data, err := s.db.Get([]byte(key), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decodeData(data, v)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) put(key string, v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.db.Put([]byte(key), data, nil)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) findKey(prefix string) (key string, id int64, err error) {
|
||||
found := true
|
||||
for found {
|
||||
id = rand.Int63()
|
||||
key = fmt.Sprintf("%s%d", prefix, id)
|
||||
|
||||
found, err = s.db.Has([]byte(key), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) delete(key string) error {
|
||||
return s.db.Delete([]byte(key), nil)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) search(prefix string) iterator.Iterator {
|
||||
return s.db.NewIterator(util.BytesPrefix([]byte(prefix)), nil)
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happydns/model"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) GetUsers() (users happydns.Users, err error) {
|
||||
iter := s.search("user-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var u happydns.User
|
||||
|
||||
err = decodeData(iter.Value(), &u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
users = append(users, &u)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetUser(id int) (u *happydns.User, err error) {
|
||||
u = &happydns.User{}
|
||||
err = s.get(fmt.Sprintf("user-%d", id), &u)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetUserByEmail(email string) (u *happydns.User, err error) {
|
||||
var users happydns.Users
|
||||
|
||||
users, err = s.GetUsers()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Email == email {
|
||||
u = user
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unable to find user with email address '%s'.", email)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UserExists(email string) bool {
|
||||
users, err := s.GetUsers()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Email == email {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateUser(u *happydns.User) error {
|
||||
key, id, err := s.findKey("user-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.Id = id
|
||||
return s.put(key, u)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateUser(u *happydns.User) error {
|
||||
return s.put(fmt.Sprintf("user-%d", u.Id), u)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteUser(u *happydns.User) error {
|
||||
return s.delete(fmt.Sprintf("user-%d", u.Id))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearUsers() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("user-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -72,6 +72,7 @@ func (s *MySQLStorage) DoMigration() error {
|
|||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
package database // import "happydns.org/database"
|
||||
|
||||
const schemaVersion = 1
|
||||
const schemaVersion = 2
|
||||
|
||||
var schemaRevisions = map[uint16]string{
|
||||
1: `CREATE TABLE schema_version (
|
||||
|
@ -35,5 +35,20 @@ CREATE TABLE zones (
|
|||
storage_facility ENUM("live", "history") NOT NULL DEFAULT "live",
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`,
|
||||
2: `ALTER TABLE user_sessions
|
||||
DROP FOREIGN KEY user_sessions_ibfk_1;
|
||||
|
||||
ALTER TABLE zones
|
||||
DROP FOREIGN KEY zones_ibfk_1;
|
||||
|
||||
ALTER TABLE users
|
||||
CHANGE id_user id_user BIGINT NOT NULL AUTO_INCREMENT;
|
||||
|
||||
ALTER TABLE user_sessions
|
||||
CHANGE id_user id_user BIGINT NOT NULL;
|
||||
|
||||
ALTER TABLE zones
|
||||
CHANGE id_user id_user BIGINT NOT NULL;
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
ALTER TABLE user_sessions
|
||||
DROP FOREIGN KEY user_sessions_ibfk_1;
|
||||
|
||||
ALTER TABLE zones
|
||||
DROP FOREIGN KEY zones_ibfk_1;
|
||||
|
||||
ALTER TABLE users
|
||||
CHANGE id_user id_user BIGINT NOT NULL AUTO_INCREMENT;
|
||||
|
||||
ALTER TABLE user_sessions
|
||||
CHANGE id_user id_user BIGINT NOT NULL;
|
||||
|
||||
ALTER TABLE zones
|
||||
CHANGE id_user id_user BIGINT NOT NULL;
|
|
@ -1,3 +1,6 @@
|
|||
package storage
|
||||
|
||||
var MainStore Storage
|
||||
var (
|
||||
MainStore Storage
|
||||
UsersStore UserStorage
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue