Refactor storage layer to use key-value template pattern
All checks were successful
continuous-integration/drone/tag Build is passing
All checks were successful
continuous-integration/drone/tag Build is passing
Extract common key-value storage operations into internal/storage/kvtpl/ template directory. This consolidates duplicated logic from both leveldb and inmemory implementations into reusable generic functions. Key changes: - Create kvtpl/ package with generic KV storage operations - Consolidate iterator implementation using generics - Update migration functions to use new template structure - Refactor tests to work with new storage interface This prepares the codebase for easier addition of new storage backends by providing a consistent template layer.
This commit is contained in:
parent
21b9072bba
commit
6add2f220e
45 changed files with 1283 additions and 1834 deletions
|
|
@ -1,129 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllAuthUsers() (happydns.Iterator[happydns.UserAuth], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.UserAuth](&s.authUsers), nil
|
||||
}
|
||||
|
||||
// ListAuthUsers retrieves the list of known Users.
|
||||
func (s *InMemoryStorage) ListAuthUsers() (happydns.UserAuths, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var users happydns.UserAuths
|
||||
for _, user := range s.authUsers {
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// GetAuthUser retrieves the User with the given identifier.
|
||||
func (s *InMemoryStorage) GetAuthUser(id happydns.Identifier) (*happydns.UserAuth, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
user, exists := s.authUsers[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrAuthUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetAuthUserByEmail retrieves the User with the given email address.
|
||||
func (s *InMemoryStorage) GetAuthUserByEmail(email string) (*happydns.UserAuth, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
userid, exists := s.authUsersByEmail[email]
|
||||
if !exists {
|
||||
return nil, happydns.ErrAuthUserNotFound
|
||||
}
|
||||
|
||||
user, exists := s.authUsers[userid.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrAuthUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// AuthUserExists checks if the given email address is already associated to an User.
|
||||
func (s *InMemoryStorage) AuthUserExists(email string) (bool, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
_, exists := s.authUsersByEmail[email]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateAuthUser creates a record in the database for the given User.
|
||||
func (s *InMemoryStorage) CreateAuthUser(user *happydns.UserAuth) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
user.Id, err = happydns.NewRandomIdentifier()
|
||||
s.authUsers[user.Id.String()] = user
|
||||
s.authUsersByEmail[user.Email] = user.Id
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateAuthUser updates the fields of the given User.
|
||||
func (s *InMemoryStorage) UpdateAuthUser(user *happydns.UserAuth) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.authUsers[user.Id.String()] = user
|
||||
s.authUsersByEmail[user.Email] = user.Id
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteAuthUser removes the given User from the database.
|
||||
func (s *InMemoryStorage) DeleteAuthUser(user *happydns.UserAuth) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.authUsers, user.Id.String())
|
||||
delete(s.authUsersByEmail, user.Email)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearAuthUsers deletes all AuthUsers present in the database.
|
||||
func (s *InMemoryStorage) ClearAuthUsers() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.authUsers = make(map[string]*happydns.UserAuth)
|
||||
s.authUsersByEmail = make(map[string]happydns.Identifier)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ package inmemory
|
|||
|
||||
import (
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -30,5 +31,10 @@ func init() {
|
|||
}
|
||||
|
||||
func Instantiate() (storage.Storage, error) {
|
||||
return NewInMemoryStorage()
|
||||
db, err := NewInMemoryStorage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kv.NewKVDatabase(db)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,16 +22,22 @@
|
|||
package inmemory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// InMemoryStorage implements the Storage interface using in-memory data structures.
|
||||
type InMemoryStorage struct {
|
||||
mu sync.Mutex
|
||||
data map[string][]byte // Generic key-value store for KVStorage interface
|
||||
authUsers map[string]*happydns.UserAuth
|
||||
authUsersByEmail map[string]happydns.Identifier
|
||||
domains map[string]*happydns.Domain
|
||||
|
|
@ -49,6 +55,7 @@ type InMemoryStorage struct {
|
|||
// NewInMemoryStorage creates a new instance of InMemoryStorage.
|
||||
func NewInMemoryStorage() (*InMemoryStorage, error) {
|
||||
return &InMemoryStorage{
|
||||
data: make(map[string][]byte),
|
||||
authUsers: make(map[string]*happydns.UserAuth),
|
||||
authUsersByEmail: make(map[string]happydns.Identifier),
|
||||
domains: make(map[string]*happydns.Domain),
|
||||
|
|
@ -85,3 +92,96 @@ func (s *InMemoryStorage) Close() error {
|
|||
// No connection to close for in-memory storage.
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeData decodes data from the interface (expected to be []byte) into v.
|
||||
func (s *InMemoryStorage) DecodeData(data interface{}, v interface{}) error {
|
||||
b, ok := data.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("data to decode are not in []byte format (%T)", data)
|
||||
}
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
// Has checks if a key exists in the storage.
|
||||
func (s *InMemoryStorage) Has(key string) (bool, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
_, exists := s.data[key]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// Get retrieves a value by key and decodes it into v.
|
||||
func (s *InMemoryStorage) Get(key string, v interface{}) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
data, exists := s.data[key]
|
||||
if !exists {
|
||||
return happydns.ErrNotFound
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// Put stores a value with the given key.
|
||||
func (s *InMemoryStorage) Put(key string, v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.data[key] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindIdentifierKey finds a unique key with the given prefix.
|
||||
func (s *InMemoryStorage) FindIdentifierKey(prefix string) (key string, id happydns.Identifier, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for {
|
||||
id, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key = fmt.Sprintf("%s%s", prefix, id.String())
|
||||
|
||||
if _, exists := s.data[key]; !exists {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes a key from the storage.
|
||||
func (s *InMemoryStorage) Delete(key string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.data, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search returns an iterator for all keys with the given prefix.
|
||||
func (s *InMemoryStorage) Search(prefix string) storage.Iterator {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Collect all matching keys
|
||||
var keys []string
|
||||
for k := range s.data {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort keys for consistent iteration order
|
||||
sort.Strings(keys)
|
||||
|
||||
// Copy data for the iterator to avoid concurrent access issues
|
||||
data := make(map[string][]byte, len(keys))
|
||||
for _, k := range keys {
|
||||
dataCopy := make([]byte, len(s.data[k]))
|
||||
copy(dataCopy, s.data[k])
|
||||
data[k] = dataCopy
|
||||
}
|
||||
|
||||
return NewKVIterator(keys, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllDomains() (happydns.Iterator[happydns.Domain], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.Domain](&s.domains), nil
|
||||
}
|
||||
|
||||
// ListDomains retrieves all Domains associated to the given User.
|
||||
func (s *InMemoryStorage) ListDomains(u *happydns.User) ([]*happydns.Domain, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var domains []*happydns.Domain
|
||||
for _, domain := range s.domains {
|
||||
if domain.Owner.Equals(u.Id) {
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// GetDomain retrieves the Domain with the given id and owned by the given User.
|
||||
func (s *InMemoryStorage) GetDomain(id happydns.Identifier) (*happydns.Domain, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
domain, exists := s.domains[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrDomainNotFound
|
||||
}
|
||||
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
// GetDomainByDN is like GetDomain but look for the domain name instead of identifier.
|
||||
func (s *InMemoryStorage) GetDomainByDN(u *happydns.User, dn string) (ret []*happydns.Domain, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, domain := range s.domains {
|
||||
if domain.DomainName == dn {
|
||||
ret = append(ret, domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil, happydns.ErrDomainNotFound
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDomain creates a record in the database for the given Domain.
|
||||
func (s *InMemoryStorage) CreateDomain(domain *happydns.Domain) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
domain.Id, err = happydns.NewRandomIdentifier()
|
||||
s.domains[domain.Id.String()] = domain
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateDomain updates the fields of the given Domain.
|
||||
func (s *InMemoryStorage) UpdateDomain(domain *happydns.Domain) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.domains[domain.Id.String()] = domain
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDomain removes the given Domain from the database.
|
||||
func (s *InMemoryStorage) DeleteDomain(domainid happydns.Identifier) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.domains, domainid.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearDomains deletes all Domains present in the database.
|
||||
func (s *InMemoryStorage) ClearDomains() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.domains = make(map[string]*happydns.Domain)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DOMAIN LOGS --------------------------------------------------
|
||||
|
||||
func (s *InMemoryStorage) ListAllDomainLogs() (happydns.Iterator[happydns.DomainLogWithDomainId], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.DomainLogWithDomainId](&s.domainLogs), nil
|
||||
}
|
||||
|
||||
// ListDomainLogs retrieves the logs for the given Domain.
|
||||
func (s *InMemoryStorage) ListDomainLogs(domain *happydns.Domain) (dlogs []*happydns.DomainLog, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, logid := range s.domainLogsByDomains[domain.Id.String()] {
|
||||
dlogs = append(dlogs, &s.domainLogs[logid.String()].DomainLog)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDomainLog creates a log entry for the given Domain.
|
||||
func (s *InMemoryStorage) CreateDomainLog(domain *happydns.Domain, log *happydns.DomainLog) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
log.Id, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.domainLogs[log.Id.String()] = &happydns.DomainLogWithDomainId{DomainLog: *log, DomainId: domain.Id}
|
||||
s.domainLogsByDomains[domain.Id.String()] = append(s.domainLogsByDomains[domain.Id.String()], &log.Id)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateDomainLog updates a log entry for the given Domain.
|
||||
func (s *InMemoryStorage) UpdateDomainLog(domain *happydns.Domain, log *happydns.DomainLog) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.domainLogs[log.Id.String()] = &happydns.DomainLogWithDomainId{DomainLog: *log, DomainId: domain.Id}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDomainLog deletes a log entry for the given Domain.
|
||||
func (s *InMemoryStorage) DeleteDomainLog(domain *happydns.Domain, log *happydns.DomainLog) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.domainLogs, log.Id.String())
|
||||
|
||||
i := slices.IndexFunc(s.domainLogsByDomains[domain.Id.String()], func(e *happydns.Identifier) bool {
|
||||
return e.Equals(log.Id)
|
||||
})
|
||||
if i == -1 {
|
||||
return happydns.ErrDomainLogNotFound
|
||||
}
|
||||
s.domainLogsByDomains[domain.Id.String()] = append(s.domainLogsByDomains[domain.Id.String()][:i], s.domainLogsByDomains[domain.Id.String()][i+1:]...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -21,81 +21,55 @@
|
|||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"iter"
|
||||
"maps"
|
||||
)
|
||||
|
||||
// InMemoryIterator provides an iterator over a map[string]*T.
|
||||
type InMemoryIterator[T any] struct {
|
||||
origin *map[string]*T
|
||||
next func() (string, *T, bool)
|
||||
stop func()
|
||||
key string
|
||||
item *T
|
||||
err error
|
||||
decode func([]byte, interface{}) error
|
||||
// KVIterator implements the storage.Iterator interface for in-memory KVStorage.
|
||||
type KVIterator struct {
|
||||
keys []string
|
||||
data map[string][]byte
|
||||
index int
|
||||
current string
|
||||
}
|
||||
|
||||
// NewInMemoryIterator constructs an iterator over a map[string]*T.
|
||||
// You can pass any map like map[string]*UserAuth, map[string]*Domain, etc.
|
||||
func NewInMemoryIterator[T any](m *map[string]*T) *InMemoryIterator[T] {
|
||||
next, stop := iter.Pull2(maps.All[map[string]*T](*m))
|
||||
return &InMemoryIterator[T]{
|
||||
origin: m,
|
||||
next: next,
|
||||
stop: stop,
|
||||
// NewKVIterator creates a new iterator for the given keys and data.
|
||||
func NewKVIterator(keys []string, data map[string][]byte) *KVIterator {
|
||||
return &KVIterator{
|
||||
keys: keys,
|
||||
data: data,
|
||||
index: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInMemoryIteratorCustomDecode creates a new LevelDBIterator instance for the given LevelDB iterator and decode function.
|
||||
func NewInMemoryIteratorCustomDecode[T any](m *map[string]*T, decodeFunc func([]byte, interface{}) error) *InMemoryIterator[T] {
|
||||
next, stop := iter.Pull2(maps.All[map[string]*T](*m))
|
||||
return &InMemoryIterator[T]{
|
||||
origin: m,
|
||||
next: next,
|
||||
stop: stop,
|
||||
decode: decodeFunc,
|
||||
// Next moves the iterator to the next item.
|
||||
func (it *KVIterator) Next() bool {
|
||||
it.index++
|
||||
if it.index >= len(it.keys) {
|
||||
return false
|
||||
}
|
||||
it.current = it.keys[it.index]
|
||||
return true
|
||||
}
|
||||
|
||||
// Next advances the iterator to the next item.
|
||||
func (it *InMemoryIterator[T]) Next() (valid bool) {
|
||||
it.key, it.item, valid = it.next()
|
||||
return
|
||||
// Key returns the current key.
|
||||
func (it *KVIterator) Key() string {
|
||||
if it.index < 0 || it.index >= len(it.keys) {
|
||||
return ""
|
||||
}
|
||||
return it.current
|
||||
}
|
||||
|
||||
// NextWithError advances the iterator to the next item.
|
||||
func (it *InMemoryIterator[T]) NextWithError() (valid bool) {
|
||||
it.key, it.item, valid = it.next()
|
||||
return
|
||||
// Value returns the current value.
|
||||
func (it *KVIterator) Value() interface{} {
|
||||
if it.index < 0 || it.index >= len(it.keys) {
|
||||
return []byte{}
|
||||
}
|
||||
return it.data[it.current]
|
||||
}
|
||||
|
||||
// Item returns the current item pointed to by the iterator.
|
||||
func (it *InMemoryIterator[T]) Item() *T {
|
||||
return it.item
|
||||
// Valid returns whether the iterator is at a valid position.
|
||||
func (it *KVIterator) Valid() bool {
|
||||
return it.index >= 0 && it.index < len(it.keys)
|
||||
}
|
||||
|
||||
// DropItem deletes the key currently pointed to by the iterator.
|
||||
func (it *InMemoryIterator[T]) DropItem() error {
|
||||
delete(*it.origin, it.key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Raw returns the raw (non-decoded) value at the current iterator position.
|
||||
// Should only be called after a successful call to Next().
|
||||
func (it *InMemoryIterator[T]) Raw() []byte {
|
||||
j, _ := json.Marshal(it.item)
|
||||
return j
|
||||
}
|
||||
|
||||
// Err returns any error encountered during iteration (always nil here).
|
||||
func (it *InMemoryIterator[T]) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// Close is a no-op for in-memory iterators, present to satisfy common interfaces.
|
||||
func (it *InMemoryIterator[T]) Close() {
|
||||
return
|
||||
// Release releases the iterator resources.
|
||||
func (it *KVIterator) Release() {
|
||||
// No resources to release for in-memory iterator
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllProviders() (happydns.Iterator[happydns.ProviderMessage], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.ProviderMessage](&s.providers), nil
|
||||
}
|
||||
|
||||
// ListProviders retrieves all providers owned by the given User.
|
||||
func (s *InMemoryStorage) ListProviders(u *happydns.User) (happydns.ProviderMessages, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var providers happydns.ProviderMessages
|
||||
for _, provider := range s.providers {
|
||||
if provider.Owner.Equals(u.Id) {
|
||||
providers = append(providers, provider)
|
||||
}
|
||||
}
|
||||
|
||||
return providers, nil
|
||||
}
|
||||
|
||||
// GetProvider retrieves the full Provider with the given identifier and owner.
|
||||
func (s *InMemoryStorage) GetProvider(id happydns.Identifier) (*happydns.ProviderMessage, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
provider, exists := s.providers[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrProviderNotFound
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// CreateProvider creates a record in the database for the given Provider.
|
||||
func (s *InMemoryStorage) CreateProvider(p *happydns.Provider) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
message, err := json.Marshal(p.Provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ProviderMeta.Id, err = happydns.NewRandomIdentifier()
|
||||
s.providers[p.ProviderMeta.Id.String()] = &happydns.ProviderMessage{
|
||||
ProviderMeta: p.ProviderMeta,
|
||||
Provider: message,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProvider updates the fields of the given Provider.
|
||||
func (s *InMemoryStorage) UpdateProvider(p *happydns.Provider) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
message, err := json.Marshal(p.Provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.providers[p.ProviderMeta.Id.String()] = &happydns.ProviderMessage{
|
||||
ProviderMeta: p.ProviderMeta,
|
||||
Provider: message,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProvider removes the given Provider from the database.
|
||||
func (s *InMemoryStorage) DeleteProvider(id happydns.Identifier) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.providers, id.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearProviders deletes all Providers present in the database.
|
||||
func (s *InMemoryStorage) ClearProviders() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.providers = make(map[string]*happydns.ProviderMessage)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllSessions() (happydns.Iterator[happydns.Session], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.Session](&s.sessions), nil
|
||||
}
|
||||
|
||||
// GetSession retrieves the Session with the given identifier.
|
||||
func (s *InMemoryStorage) GetSession(id string) (*happydns.Session, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
session, exists := s.sessions[id]
|
||||
if !exists {
|
||||
return nil, happydns.ErrSessionNotFound
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// ListAuthUserSessions retrieves all Session for the given AuthUser.
|
||||
func (s *InMemoryStorage) ListAuthUserSessions(user *happydns.UserAuth) (sessions []*happydns.Session, ess error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, session := range s.sessions {
|
||||
if user.Id.Equals(session.IdUser) {
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// ListUserSessions retrieves all Session for the given User.
|
||||
func (s *InMemoryStorage) ListUserSessions(userid happydns.Identifier) (sessions []*happydns.Session, ess error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, session := range s.sessions {
|
||||
if userid.Equals(session.IdUser) {
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// UpdateSession updates the fields of the given Session.
|
||||
func (s *InMemoryStorage) UpdateSession(session *happydns.Session) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.sessions[session.Id] = session
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSession removes the given Session from the database.
|
||||
func (s *InMemoryStorage) DeleteSession(id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.sessions, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearSessions deletes all Sessions present in the database.
|
||||
func (s *InMemoryStorage) ClearSessions() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.sessions = make(map[string]*happydns.Session)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllUsers() (happydns.Iterator[happydns.User], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.User](&s.users), nil
|
||||
}
|
||||
|
||||
// ListUsers retrieves the list of known Users.
|
||||
func (s *InMemoryStorage) ListUsers() (users []*happydns.User, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, user := range s.users {
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// GetUser retrieves the User with the given identifier.
|
||||
func (s *InMemoryStorage) GetUser(id happydns.Identifier) (*happydns.User, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
user, exists := s.users[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetUserByEmail retrieves the User with the given email address.
|
||||
func (s *InMemoryStorage) GetUserByEmail(email string) (*happydns.User, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
user, exists := s.usersByEmail[email]
|
||||
if !exists {
|
||||
return nil, happydns.ErrUserNotFound
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateUser updates the fields of the given User.
|
||||
func (s *InMemoryStorage) CreateOrUpdateUser(user *happydns.User) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.users[user.Id.String()] = user
|
||||
s.usersByEmail[user.Email] = user
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser removes the given User from the database.
|
||||
func (s *InMemoryStorage) DeleteUser(userid happydns.Identifier) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if user, exists := s.users[userid.String()]; exists {
|
||||
delete(s.users, userid.String())
|
||||
delete(s.usersByEmail, user.Email)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearUsers deletes all Users present in the database.
|
||||
func (s *InMemoryStorage) ClearUsers() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.users = make(map[string]*happydns.User)
|
||||
s.usersByEmail = make(map[string]*happydns.User)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *InMemoryStorage) ListAllZones() (happydns.Iterator[happydns.ZoneMessage], error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return NewInMemoryIterator[happydns.ZoneMessage](&s.zones), nil
|
||||
}
|
||||
|
||||
// GetZoneMeta retrieves metadata of the Zone with the given identifier.
|
||||
func (s *InMemoryStorage) GetZoneMeta(id happydns.Identifier) (*happydns.ZoneMeta, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
zone, exists := s.zones[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
return &zone.ZoneMeta, nil
|
||||
}
|
||||
|
||||
// GetZone retrieves the full Zone (including Services and metadata) which have the given identifier.
|
||||
func (s *InMemoryStorage) GetZone(id happydns.Identifier) (*happydns.ZoneMessage, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
zone, exists := s.zones[id.String()]
|
||||
if !exists {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
// CreateZone creates a record in the database for the given Zone.
|
||||
func (s *InMemoryStorage) CreateZone(zone *happydns.Zone) (err error) {
|
||||
zone.ZoneMeta.Id, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return s.UpdateZone(zone)
|
||||
}
|
||||
|
||||
// UpdateZone updates the fields of the given Zone.
|
||||
func (s *InMemoryStorage) UpdateZone(zone *happydns.Zone) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
zmsg := &happydns.ZoneMessage{
|
||||
ZoneMeta: zone.ZoneMeta,
|
||||
Services: map[happydns.Subdomain][]*happydns.ServiceMessage{},
|
||||
}
|
||||
|
||||
for subdn, services := range zone.Services {
|
||||
for _, service := range services {
|
||||
message, err := json.Marshal(service.Service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zmsg.Services[subdn] = append(zmsg.Services[subdn], &happydns.ServiceMessage{
|
||||
ServiceMeta: service.ServiceMeta,
|
||||
Service: message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
s.zones[zone.Id.String()] = zmsg
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteZone removes the given Zone from the database.
|
||||
func (s *InMemoryStorage) DeleteZone(id happydns.Identifier) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
delete(s.zones, id.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearZones deletes all Zones present in the database.
|
||||
func (s *InMemoryStorage) ClearZones() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.zones = make(map[string]*happydns.ZoneMessage)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"git.happydns.org/happyDomain/internal/usecase/session"
|
||||
"git.happydns.org/happyDomain/internal/usecase/user"
|
||||
"git.happydns.org/happyDomain/internal/usecase/zone"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type ProviderAndDomainStorage interface {
|
||||
|
|
@ -56,3 +57,22 @@ type Storage interface {
|
|||
// Close shutdown the connection with the database and releases all structure.
|
||||
Close() error
|
||||
}
|
||||
|
||||
type Iterator interface {
|
||||
Release()
|
||||
Next() bool
|
||||
Valid() bool
|
||||
Key() string
|
||||
Value() interface{}
|
||||
}
|
||||
|
||||
type KVStorage interface {
|
||||
Close() error
|
||||
DecodeData(i interface{}, v interface{}) error
|
||||
Has(key string) (bool, error)
|
||||
Get(key string, v interface{}) error
|
||||
Put(key string, v interface{}) error
|
||||
FindIdentifierKey(prefix string) (key string, id happydns.Identifier, err error)
|
||||
Delete(key string) error
|
||||
Search(prefix string) Iterator
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
|
|
@ -25,35 +25,33 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllAuthUsers() (happydns.Iterator[happydns.UserAuth], error) {
|
||||
iter := s.search("auth-")
|
||||
return NewLevelDBIterator[happydns.UserAuth](s.db, iter), nil
|
||||
func (s *KVStorage) ListAllAuthUsers() (happydns.Iterator[happydns.UserAuth], error) {
|
||||
iter := s.db.Search("auth-")
|
||||
return NewKVIterator[happydns.UserAuth](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getAuthUser(key string) (*happydns.UserAuth, error) {
|
||||
func (s *KVStorage) getAuthUser(key string) (*happydns.UserAuth, error) {
|
||||
u := &happydns.UserAuth{}
|
||||
err := s.get(key, &u)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
err := s.db.Get(key, &u)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrAuthUserNotFound
|
||||
}
|
||||
return u, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetAuthUser(id happydns.Identifier) (u *happydns.UserAuth, err error) {
|
||||
func (s *KVStorage) GetAuthUser(id happydns.Identifier) (u *happydns.UserAuth, err error) {
|
||||
return s.getAuthUser(fmt.Sprintf("auth-%s", id.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetAuthUserByEmail(email string) (*happydns.UserAuth, error) {
|
||||
func (s *KVStorage) GetAuthUserByEmail(email string) (*happydns.UserAuth, error) {
|
||||
users, err := s.ListAllAuthUsers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer users.Close()
|
||||
|
||||
for users.Next() {
|
||||
user := users.Item()
|
||||
|
|
@ -65,11 +63,12 @@ func (s *LevelDBStorage) GetAuthUserByEmail(email string) (*happydns.UserAuth, e
|
|||
return nil, fmt.Errorf("Unable to find user with email address '%s'.", email)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) AuthUserExists(email string) (bool, error) {
|
||||
func (s *KVStorage) AuthUserExists(email string) (bool, error) {
|
||||
users, err := s.ListAllAuthUsers()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer users.Close()
|
||||
|
||||
for users.Next() {
|
||||
user := users.Item()
|
||||
|
|
@ -81,46 +80,37 @@ func (s *LevelDBStorage) AuthUserExists(email string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateAuthUser(u *happydns.UserAuth) error {
|
||||
key, id, err := s.findIdentifierKey("auth-")
|
||||
func (s *KVStorage) CreateAuthUser(u *happydns.UserAuth) error {
|
||||
key, id, err := s.db.FindIdentifierKey("auth-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.Id = id
|
||||
return s.put(key, u)
|
||||
return s.db.Put(key, u)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateAuthUser(u *happydns.UserAuth) error {
|
||||
return s.put(fmt.Sprintf("auth-%s", u.Id.String()), u)
|
||||
func (s *KVStorage) UpdateAuthUser(u *happydns.UserAuth) error {
|
||||
return s.db.Put(fmt.Sprintf("auth-%s", u.Id.String()), u)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteAuthUser(u *happydns.UserAuth) error {
|
||||
return s.delete(fmt.Sprintf("auth-%s", u.Id.String()))
|
||||
func (s *KVStorage) DeleteAuthUser(u *happydns.UserAuth) error {
|
||||
return s.db.Delete(fmt.Sprintf("auth-%s", u.Id.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearAuthUsers() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
func (s *KVStorage) ClearAuthUsers() error {
|
||||
iter, err := s.ListAllAuthUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("auth-")), nil)
|
||||
defer iter.Release()
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
122
internal/storage/kvtpl/domain-log.go
Normal file
122
internal/storage/kvtpl/domain-log.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// DomainLogIterator wraps KVIterator to populate DomainId from the key
|
||||
type DomainLogIterator struct {
|
||||
*KVIterator[happydns.DomainLogWithDomainId]
|
||||
}
|
||||
|
||||
// NewDomainLogIterator creates a new DomainLogIterator
|
||||
func NewDomainLogIterator(db storage.KVStorage, iter storage.Iterator) *DomainLogIterator {
|
||||
return &DomainLogIterator{
|
||||
KVIterator: NewKVIterator[happydns.DomainLogWithDomainId](db, iter),
|
||||
}
|
||||
}
|
||||
|
||||
// Next advances the iterator and extracts the DomainId from the key
|
||||
func (it *DomainLogIterator) Next() bool {
|
||||
if it.KVIterator.Next() {
|
||||
// Extract domain ID from key
|
||||
key := it.Key()
|
||||
st := strings.Split(key, "|")
|
||||
if len(st) >= 3 && it.item != nil {
|
||||
domainId, err := happydns.NewIdentifierFromString(st[1])
|
||||
if err == nil {
|
||||
it.item.DomainId = domainId
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *KVStorage) ListAllDomainLogs() (happydns.Iterator[happydns.DomainLogWithDomainId], error) {
|
||||
iter := s.db.Search("domain.log|")
|
||||
return NewDomainLogIterator(s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) ListDomainLogs(domain *happydns.Domain) (logs []*happydns.DomainLog, err error) {
|
||||
iter := s.db.Search(fmt.Sprintf("domain.log|%s|", domain.Id.String()))
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var z happydns.DomainLog
|
||||
|
||||
err = s.db.DecodeData(iter.Value(), &z)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logs = append(logs, &z)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) getDomainLog(id string) (l *happydns.DomainLog, d *happydns.Domain, err error) {
|
||||
l = &happydns.DomainLog{}
|
||||
err = s.db.Get(id, l)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, nil, happydns.ErrDomainLogNotFound
|
||||
}
|
||||
|
||||
st := strings.Split(id, "|")
|
||||
if len(st) < 3 {
|
||||
return
|
||||
}
|
||||
|
||||
d = &happydns.Domain{}
|
||||
err = s.db.Get(fmt.Sprintf("domain-%s", st[1]), d)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, nil, happydns.ErrDomainNotFound
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) CreateDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
key, id, err := s.db.FindIdentifierKey(fmt.Sprintf("domain.log|%s|", d.Id.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Id = id
|
||||
return s.db.Put(key, l)
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
return s.db.Put(fmt.Sprintf("domain.log|%s|%s", d.Id.String(), l.Id.String()), l)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
return s.db.Delete(fmt.Sprintf("domain.log|%s|%s", d.Id.String(), l.Id.String()))
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
|
|
@ -26,25 +26,22 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllDomains() (happydns.Iterator[happydns.Domain], error) {
|
||||
iter := s.search("domain-")
|
||||
return NewLevelDBIterator[happydns.Domain](s.db, iter), nil
|
||||
func (s *KVStorage) ListAllDomains() (happydns.Iterator[happydns.Domain], error) {
|
||||
iter := s.db.Search("domain-")
|
||||
return NewKVIterator[happydns.Domain](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ListDomains(u *happydns.User) (domains []*happydns.Domain, err error) {
|
||||
iter := s.search("domain-")
|
||||
func (s *KVStorage) ListDomains(u *happydns.User) (domains []*happydns.Domain, err error) {
|
||||
iter := s.db.Search("domain-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var z happydns.Domain
|
||||
|
||||
err = decodeData(iter.Value(), &z)
|
||||
err = s.db.DecodeData(iter.Value(), &z)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -57,20 +54,20 @@ func (s *LevelDBStorage) ListDomains(u *happydns.User) (domains []*happydns.Doma
|
|||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getDomain(id string) (*happydns.Domain, error) {
|
||||
func (s *KVStorage) getDomain(id string) (*happydns.Domain, error) {
|
||||
domain := &happydns.Domain{}
|
||||
err := s.get(id, domain)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
err := s.db.Get(id, domain)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrDomainNotFound
|
||||
}
|
||||
return domain, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetDomain(id happydns.Identifier) (*happydns.Domain, error) {
|
||||
func (s *KVStorage) GetDomain(id happydns.Identifier) (*happydns.Domain, error) {
|
||||
return s.getDomain(fmt.Sprintf("domain-%s", id.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetDomainByDN(u *happydns.User, dn string) ([]*happydns.Domain, error) {
|
||||
func (s *KVStorage) GetDomainByDN(u *happydns.User, dn string) ([]*happydns.Domain, error) {
|
||||
domains, err := s.ListDomains(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -84,57 +81,48 @@ func (s *LevelDBStorage) GetDomainByDN(u *happydns.User, dn string) ([]*happydns
|
|||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil, leveldb.ErrNotFound
|
||||
return nil, happydns.ErrNotFound
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateDomain(z *happydns.Domain) error {
|
||||
key, id, err := s.findIdentifierKey("domain-")
|
||||
func (s *KVStorage) CreateDomain(z *happydns.Domain) error {
|
||||
key, id, err := s.db.FindIdentifierKey("domain-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
z.Id = id
|
||||
return s.put(key, z)
|
||||
return s.db.Put(key, z)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateDomain(z *happydns.Domain) error {
|
||||
return s.put(fmt.Sprintf("domain-%s", z.Id.String()), z)
|
||||
func (s *KVStorage) UpdateDomain(z *happydns.Domain) error {
|
||||
return s.db.Put(fmt.Sprintf("domain-%s", z.Id.String()), z)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteDomain(zId happydns.Identifier) error {
|
||||
return s.delete(fmt.Sprintf("domain-%s", zId.String()))
|
||||
func (s *KVStorage) DeleteDomain(zId happydns.Identifier) error {
|
||||
return s.db.Delete(fmt.Sprintf("domain-%s", zId.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearDomains() error {
|
||||
func (s *KVStorage) ClearDomains() error {
|
||||
err := s.ClearZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := s.db.OpenTransaction()
|
||||
iter, err := s.ListAllDomains()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("domain-")), nil)
|
||||
defer iter.Release()
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -22,27 +22,26 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) InsightsRun() error {
|
||||
return s.put("insights", time.Now())
|
||||
func (s *KVStorage) InsightsRun() error {
|
||||
return s.db.Put("insights", time.Now())
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) LastInsightsRun() (t *time.Time, instance happydns.Identifier, err error) {
|
||||
err = s.get("insights.instance-id", &instance)
|
||||
if err == leveldb.ErrNotFound {
|
||||
func (s *KVStorage) LastInsightsRun() (t *time.Time, instance happydns.Identifier, err error) {
|
||||
err = s.db.Get("insights.instance-id", &instance)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
// No instance ID defined, set one
|
||||
instance, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.put("insights.instance-id", instance)
|
||||
err = s.db.Put("insights.instance-id", instance)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -51,8 +50,8 @@ func (s *LevelDBStorage) LastInsightsRun() (t *time.Time, instance happydns.Iden
|
|||
}
|
||||
|
||||
t = new(time.Time)
|
||||
err = s.get("insights", &t)
|
||||
if err == leveldb.ErrNotFound {
|
||||
err = s.db.Get("insights", &t)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
t = nil
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
120
internal/storage/kvtpl/iterator.go
Normal file
120
internal/storage/kvtpl/iterator.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
)
|
||||
|
||||
// KVIterator is a generic implementation of Iterator for LevelDB.
|
||||
type KVIterator[T any] struct {
|
||||
db storage.KVStorage
|
||||
iter storage.Iterator
|
||||
err error
|
||||
item *T
|
||||
}
|
||||
|
||||
// NewKVIterator creates a new KVIterator instance for the given iterator.
|
||||
func NewKVIterator[T any](db storage.KVStorage, iter storage.Iterator) *KVIterator[T] {
|
||||
return &KVIterator[T]{
|
||||
db: db,
|
||||
iter: iter,
|
||||
}
|
||||
}
|
||||
|
||||
// Next moves the iterator to the next valid item.
|
||||
// Skips items that fail to decode and logs the error.
|
||||
func (it *KVIterator[T]) Next() bool {
|
||||
for it.iter.Next() {
|
||||
var value T
|
||||
err := it.db.DecodeData(it.iter.Value(), &value)
|
||||
if err != nil {
|
||||
log.Printf("KVIterator: error decoding item at key %q: %s", it.iter.Key(), err)
|
||||
it.err = err
|
||||
continue
|
||||
}
|
||||
it.item = &value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NextWithError advances the iterator to the next item, on decode error it doesn't continue to the next item.
|
||||
// Returns true if there is a next item, false otherwise.
|
||||
func (it *KVIterator[T]) NextWithError() bool {
|
||||
if it.iter.Next() {
|
||||
var value T
|
||||
err := it.db.DecodeData(it.iter.Value(), &value)
|
||||
if err != nil {
|
||||
it.err = err
|
||||
it.item = nil
|
||||
} else {
|
||||
it.err = nil
|
||||
it.item = &value
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Item returns the current item from the iterator.
|
||||
// Only valid after a successful call to Next().
|
||||
func (it *KVIterator[T]) Item() *T {
|
||||
return it.item
|
||||
}
|
||||
|
||||
// DropItem deletes the key currently pointed to by the iterator.
|
||||
func (it *KVIterator[T]) DropItem() error {
|
||||
if it.iter == nil || !it.iter.Valid() {
|
||||
return fmt.Errorf("DropItem: iterator is not valid")
|
||||
}
|
||||
return it.db.Delete(it.iter.Key())
|
||||
}
|
||||
|
||||
// Raw returns the raw (non-decoded) value at the current iterator position.
|
||||
// Should only be called after a successful call to Next().
|
||||
func (it *KVIterator[T]) Raw() interface{} {
|
||||
if it.iter == nil || !it.iter.Valid() {
|
||||
return []byte{}
|
||||
}
|
||||
return it.iter.Value()
|
||||
}
|
||||
|
||||
func (it *KVIterator[T]) Key() string {
|
||||
if it.iter == nil || !it.iter.Valid() {
|
||||
return ""
|
||||
}
|
||||
return it.iter.Key()
|
||||
}
|
||||
|
||||
// Err returns the first error encountered during iteration, if any.
|
||||
func (it *KVIterator[T]) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// Close releases resources held by the underlying LevelDB iterator.
|
||||
func (it *KVIterator[T]) Close() {
|
||||
it.iter.Release()
|
||||
}
|
||||
112
internal/storage/kvtpl/provider.go
Normal file
112
internal/storage/kvtpl/provider.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *KVStorage) ListAllProviders() (happydns.Iterator[happydns.ProviderMessage], error) {
|
||||
iter := s.db.Search("provider-")
|
||||
return NewKVIterator[happydns.ProviderMessage](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) getProviderMeta(id happydns.Identifier) (*happydns.ProviderMessage, error) {
|
||||
srcMsg := &happydns.ProviderMessage{}
|
||||
err := s.db.Get(id.String(), srcMsg)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrProviderNotFound
|
||||
}
|
||||
return srcMsg, err
|
||||
}
|
||||
|
||||
func (s *KVStorage) ListProviders(u *happydns.User) (srcs happydns.ProviderMessages, err error) {
|
||||
iter, err := s.ListAllProviders()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
srcMsg := iter.Item()
|
||||
if !bytes.Equal(srcMsg.Owner, u.Id) {
|
||||
continue
|
||||
}
|
||||
|
||||
srcs = append(srcs, srcMsg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) GetProvider(id happydns.Identifier) (*happydns.ProviderMessage, error) {
|
||||
var prvdMsg happydns.ProviderMessage
|
||||
err := s.db.Get(fmt.Sprintf("provider-%s", id.String()), &prvdMsg)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrProviderNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &prvdMsg, nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) CreateProvider(prvd *happydns.Provider) error {
|
||||
key, id, err := s.db.FindIdentifierKey("provider-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prvd.Id = id
|
||||
|
||||
return s.db.Put(key, prvd)
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateProvider(prvd *happydns.Provider) error {
|
||||
return s.db.Put(fmt.Sprintf("provider-%s", prvd.Id.String()), prvd)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteProvider(prvdId happydns.Identifier) error {
|
||||
return s.db.Delete(fmt.Sprintf("provider-%s", prvdId.String()))
|
||||
}
|
||||
|
||||
func (s *KVStorage) ClearProviders() error {
|
||||
iter, err := s.ListAllProviders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
107
internal/storage/kvtpl/session.go
Normal file
107
internal/storage/kvtpl/session.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *KVStorage) ListAllSessions() (happydns.Iterator[happydns.Session], error) {
|
||||
iter := s.db.Search("user.session-")
|
||||
return NewKVIterator[happydns.Session](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) getSession(id string) (*happydns.Session, error) {
|
||||
session := &happydns.Session{}
|
||||
err := s.db.Get(id, &session)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrSessionNotFound
|
||||
}
|
||||
return session, err
|
||||
}
|
||||
|
||||
func (s *KVStorage) GetSession(id string) (session *happydns.Session, err error) {
|
||||
return s.getSession(fmt.Sprintf("user.session-%s", id))
|
||||
}
|
||||
|
||||
func (s *KVStorage) ListAuthUserSessions(user *happydns.UserAuth) (sessions []*happydns.Session, err error) {
|
||||
iter := s.db.Search("user.session-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var session happydns.Session
|
||||
|
||||
err = s.db.DecodeData(iter.Value(), &session)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if session.IdUser.Equals(user.Id) {
|
||||
sessions = append(sessions, &session)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) ListUserSessions(userid happydns.Identifier) (sessions []*happydns.Session, err error) {
|
||||
iter := s.db.Search("user.session-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var session happydns.Session
|
||||
|
||||
err = s.db.DecodeData(iter.Value(), &session)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if session.IdUser.Equals(userid) {
|
||||
sessions = append(sessions, &session)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateSession(session *happydns.Session) error {
|
||||
return s.db.Put(fmt.Sprintf("user.session-%s", session.Id), session)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteSession(id string) error {
|
||||
return s.db.Delete(fmt.Sprintf("user.session-%s", id))
|
||||
}
|
||||
|
||||
func (s *KVStorage) ClearSessions() error {
|
||||
iter := s.db.Search("user.session-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
err := s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -19,37 +19,22 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package inmemory
|
||||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
)
|
||||
|
||||
// InsightsRun registers a insights process run just now.
|
||||
func (s *InMemoryStorage) InsightsRun() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
s.lastInsightsRun = &now
|
||||
|
||||
return nil
|
||||
type KVStorage struct {
|
||||
db storage.KVStorage
|
||||
}
|
||||
|
||||
// LastInsightsRun gets the last time insights process run.
|
||||
func (s *InMemoryStorage) LastInsightsRun() (*time.Time, happydns.Identifier, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.lastInsightsID == nil {
|
||||
instance, err := happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
s.lastInsightsID = instance
|
||||
}
|
||||
|
||||
return s.lastInsightsRun, s.lastInsightsID, nil
|
||||
func NewKVDatabase(impl storage.KVStorage) (storage.Storage, error) {
|
||||
return &KVStorage{
|
||||
impl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) Close() error {
|
||||
return s.db.Close()
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func migrateFrom0(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom0(s *KVStorage) (err error) {
|
||||
err = migrateFrom0_sourcesProvider(s)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -47,14 +47,20 @@ type sourceMeta struct {
|
|||
Comment string `json:"_comment,omitempty"`
|
||||
}
|
||||
|
||||
func migrateFrom0_sourcesProvider(s *LevelDBStorage) (err error) {
|
||||
iter := s.search("source-")
|
||||
func migrateFrom0_sourcesProvider(s *KVStorage) (err error) {
|
||||
iter := s.db.Search("source-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
src := iter.Value()
|
||||
for src[0] == '"' {
|
||||
err = decodeData(src, &src)
|
||||
srcRaw := iter.Value()
|
||||
src, ok := srcRaw.([]byte)
|
||||
if !ok {
|
||||
log.Printf("Migrating v0 -> v1: skip %s (not bytes)...", iter.Key())
|
||||
continue
|
||||
}
|
||||
|
||||
for len(src) > 0 && src[0] == '"' {
|
||||
err = s.db.DecodeData(src, &src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -63,7 +69,7 @@ func migrateFrom0_sourcesProvider(s *LevelDBStorage) (err error) {
|
|||
src = bytes.Replace(src, []byte("\"Source\":"), []byte("\"Provider\":"), 1)
|
||||
|
||||
var srcMeta sourceMeta
|
||||
err = decodeData(src, &srcMeta)
|
||||
err = s.db.DecodeData(src, &srcMeta)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -101,16 +107,16 @@ func migrateFrom0_sourcesProvider(s *LevelDBStorage) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
newKey := bytes.Replace(iter.Key(), []byte("source-"), []byte("provider-"), 1)
|
||||
newKey := string(bytes.Replace([]byte(iter.Key()), []byte("source-"), []byte("provider-"), 1))
|
||||
|
||||
log.Printf("Migrating v0 -> v1: %s to %s (%s)...", iter.Key(), newKey, newType)
|
||||
|
||||
err = s.db.Put(newKey, src, nil)
|
||||
err = s.db.Put(newKey, src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -119,18 +125,23 @@ func migrateFrom0_sourcesProvider(s *LevelDBStorage) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func migrateFrom0_reparentDomains(s *LevelDBStorage) (err error) {
|
||||
iter := s.search("domain-")
|
||||
func migrateFrom0_reparentDomains(s *KVStorage) (err error) {
|
||||
iter := s.db.Search("domain-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
domRaw := iter.Value()
|
||||
domstr, ok := domRaw.([]byte)
|
||||
if !ok {
|
||||
log.Printf("Migrating v0 -> v1: skip %s (not bytes)...", iter.Key())
|
||||
continue
|
||||
}
|
||||
|
||||
domstr = bytes.Replace(domstr, []byte("\"id_source\":"), []byte("\"id_provider\":"), 1)
|
||||
|
||||
log.Printf("Migrating v0 -> v1: %s...", iter.Key())
|
||||
|
||||
err = s.db.Put(iter.Key(), domstr, nil)
|
||||
err = s.db.Put(iter.Key(), domstr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ import (
|
|||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func migrateFrom1(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom1(s *KVStorage) (err error) {
|
||||
err = migrateFrom1_users_tree(s)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -61,19 +61,19 @@ func genUserIdv2(input int64) (string, []byte, error) {
|
|||
return hex.EncodeToString(decoded), decoded, err
|
||||
}
|
||||
|
||||
func migrateFrom1_users_tree(s *LevelDBStorage) (err error) {
|
||||
iter := s.search("user-")
|
||||
func migrateFrom1_users_tree(s *KVStorage) (err error) {
|
||||
iter := s.db.Search("user-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var user userV1
|
||||
err = decodeData(iter.Value(), &user)
|
||||
err = s.db.DecodeData(iter.Value(), &user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newId, idRaw, errr := genUserIdv2(user.Id)
|
||||
if err != nil {
|
||||
if errr != nil {
|
||||
log.Printf("Migrating v1 -> v2: %s: unable to calculate new ID: %s", iter.Key(), errr.Error())
|
||||
continue
|
||||
} else if len(idRaw) == 0 {
|
||||
|
|
@ -106,17 +106,17 @@ func migrateFrom1_users_tree(s *LevelDBStorage) (err error) {
|
|||
|
||||
log.Printf("Migrating v1 -> v2: %s to user-%x...", iter.Key(), idRaw)
|
||||
|
||||
err = s.put(fmt.Sprintf("user-%x", idRaw), newUser)
|
||||
err = s.db.Put(fmt.Sprintf("user-%x", idRaw), newUser)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.put(fmt.Sprintf("auth-%x", idRaw), user4auth)
|
||||
err = s.db.Put(fmt.Sprintf("auth-%x", idRaw), user4auth)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -127,27 +127,35 @@ func migrateFrom1_users_tree(s *LevelDBStorage) (err error) {
|
|||
migrateFrom1_provider(s, user.Id, newId),
|
||||
migrateFrom1_zone(s, user.Id, newId),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func migrateFrom1_domains(s *LevelDBStorage, oldUserId int64, newUserId string) (err error) {
|
||||
func migrateFrom1_domains(s *KVStorage, oldUserId int64, newUserId string) (err error) {
|
||||
oldIdStr := []byte(fmt.Sprintf("\"id_owner\":%d", oldUserId))
|
||||
newIdStr := []byte(fmt.Sprintf("\"id_owner\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("domain-")
|
||||
iter := s.db.Search("domain-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
domRaw := iter.Value()
|
||||
domstr, ok := domRaw.([]byte)
|
||||
if !ok {
|
||||
log.Printf("Migrating v1 -> v2: skip %s (not bytes)...", iter.Key())
|
||||
continue
|
||||
}
|
||||
|
||||
migstr := bytes.Replace(domstr, oldIdStr, newIdStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, domstr) {
|
||||
log.Printf("Migrating v1 -> v2: %s...", iter.Key())
|
||||
|
||||
err = s.db.Put(iter.Key(), migstr, nil)
|
||||
err = s.db.Put(iter.Key(), migstr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -157,22 +165,27 @@ func migrateFrom1_domains(s *LevelDBStorage, oldUserId int64, newUserId string)
|
|||
return
|
||||
}
|
||||
|
||||
func migrateFrom1_provider(s *LevelDBStorage, oldUserId int64, newUserId string) (err error) {
|
||||
func migrateFrom1_provider(s *KVStorage, oldUserId int64, newUserId string) (err error) {
|
||||
oldIdStr := []byte(fmt.Sprintf("\"_ownerid\":%d", oldUserId))
|
||||
newIdStr := []byte(fmt.Sprintf("\"_ownerid\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("provider-")
|
||||
iter := s.db.Search("provider-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
provRaw := iter.Value()
|
||||
provstr, ok := provRaw.([]byte)
|
||||
if !ok {
|
||||
log.Printf("Migrating v1 -> v2: skip %s (not bytes)...", iter.Key())
|
||||
continue
|
||||
}
|
||||
|
||||
migstr := bytes.Replace(domstr, oldIdStr, newIdStr, 1)
|
||||
migstr := bytes.Replace(provstr, oldIdStr, newIdStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, domstr) {
|
||||
if !bytes.Equal(migstr, provstr) {
|
||||
log.Printf("Migrating v1 -> v2: %s...", iter.Key())
|
||||
|
||||
err = s.db.Put(iter.Key(), migstr, nil)
|
||||
err = s.db.Put(iter.Key(), migstr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -182,22 +195,27 @@ func migrateFrom1_provider(s *LevelDBStorage, oldUserId int64, newUserId string)
|
|||
return
|
||||
}
|
||||
|
||||
func migrateFrom1_zone(s *LevelDBStorage, oldUserId int64, newUserId string) (err error) {
|
||||
func migrateFrom1_zone(s *KVStorage, oldUserId int64, newUserId string) (err error) {
|
||||
oldIdStr := []byte(fmt.Sprintf("\"id_author\":%d", oldUserId))
|
||||
newIdStr := []byte(fmt.Sprintf("\"id_author\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("domain.zone-")
|
||||
iter := s.db.Search("domain.zone-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
zoneRaw := iter.Value()
|
||||
zonestr, ok := zoneRaw.([]byte)
|
||||
if !ok {
|
||||
log.Printf("Migrating v1 -> v2: skip %s (not bytes)...", iter.Key())
|
||||
continue
|
||||
}
|
||||
|
||||
migstr := bytes.Replace(domstr, oldIdStr, newIdStr, 1)
|
||||
migstr := bytes.Replace(zonestr, oldIdStr, newIdStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, domstr) {
|
||||
if !bytes.Equal(migstr, zonestr) {
|
||||
log.Printf("Migrating v1 -> v2: %s...", iter.Key())
|
||||
|
||||
err = s.db.Put(iter.Key(), migstr, nil)
|
||||
err = s.db.Put(iter.Key(), migstr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -24,16 +24,16 @@ package database
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
)
|
||||
|
||||
func migrateFrom2(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom2(s *KVStorage) (err error) {
|
||||
err = migrateFrom2_users_tree(s)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -50,16 +50,12 @@ type userV2 struct {
|
|||
Settings happydns.UserSettings
|
||||
}
|
||||
|
||||
func migrateFrom2_users_tree(s *LevelDBStorage) (err error) {
|
||||
iter := s.search("user-")
|
||||
defer iter.Release()
|
||||
func migrateFrom2_users_tree(s *KVStorage) (err error) {
|
||||
iter := NewKVIterator[userV2](s.db, s.db.Search("user-"))
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
var user userV2
|
||||
err = decodeData(iter.Value(), &user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
user := iter.Item()
|
||||
|
||||
newId := happydns.Identifier(user.Id)
|
||||
if len(newId) < happydns.IDENTIFIER_LEN {
|
||||
|
|
@ -79,18 +75,18 @@ func migrateFrom2_users_tree(s *LevelDBStorage) (err error) {
|
|||
|
||||
log.Printf("Migrating v2 -> v3: %s to user-%s...", iter.Key(), newId.String())
|
||||
|
||||
err = s.put(fmt.Sprintf("user-%s", newId.String()), newUser)
|
||||
err = s.db.Put(fmt.Sprintf("user-%s", newId.String()), newUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write %s: %w", iter.Key(), err)
|
||||
}
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete migrated %s: %w", iter.Key(), err)
|
||||
}
|
||||
|
||||
// Migrate object of the user
|
||||
err = migrateFrom2_auth(s, user.Id, newId, user)
|
||||
err = migrateFrom2_auth(s, user.Id, newId, *user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to migrate auth user for user-%s (%s): %w", newId.String(), user.Email, err)
|
||||
}
|
||||
|
|
@ -109,15 +105,16 @@ func migrateFrom2_users_tree(s *LevelDBStorage) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func migrateFrom2_auth(s *LevelDBStorage, oldUserId happydns.HexaString, newId happydns.Identifier, user userV2) (err error) {
|
||||
func migrateFrom2_auth(s *KVStorage, oldUserId happydns.HexaString, newId happydns.Identifier, user userV2) (err error) {
|
||||
oldIdStr := []byte(fmt.Sprintf("\"Id\":\"%s\"", base64.StdEncoding.EncodeToString(oldUserId)))
|
||||
newIdStr := []byte(fmt.Sprintf("\"Id\":\"%s\"", newId.String()))
|
||||
|
||||
oldAuthKey := fmt.Sprintf("auth-%x", oldUserId)
|
||||
|
||||
usrstr, err := s.db.Get([]byte(oldAuthKey), nil)
|
||||
var usrstr json.RawMessage
|
||||
err = s.db.Get(oldAuthKey, &usrstr)
|
||||
if err != nil {
|
||||
if err == errors.ErrNotFound {
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
user4auth := &happydns.UserAuth{
|
||||
Id: newId,
|
||||
Email: user.Email,
|
||||
|
|
@ -129,28 +126,28 @@ func migrateFrom2_auth(s *LevelDBStorage, oldUserId happydns.HexaString, newId h
|
|||
|
||||
log.Printf("Migrating v2 -> v3: auth-%s: %s not found, creating it", newId.String(), oldAuthKey)
|
||||
|
||||
return s.put(fmt.Sprintf("auth-%s", newId.String()), user4auth)
|
||||
return s.db.Put(fmt.Sprintf("auth-%s", newId.String()), user4auth)
|
||||
}
|
||||
return fmt.Errorf("unable to find/decode %s: %w", oldAuthKey, err)
|
||||
}
|
||||
|
||||
migstr := bytes.Replace(usrstr, oldIdStr, newIdStr, 1)
|
||||
migstr := bytes.Replace([]byte(usrstr), oldIdStr, newIdStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, usrstr) {
|
||||
if !bytes.Equal(migstr, []byte(usrstr)) {
|
||||
var newauth happydns.UserAuth
|
||||
err = decodeData(migstr, &newauth)
|
||||
err = s.db.DecodeData(migstr, &newauth)
|
||||
if err != nil {
|
||||
log.Printf("From %s to %s", usrstr, migstr)
|
||||
return fmt.Errorf("unable to reconstruct a valid auth user: %w", err)
|
||||
}
|
||||
|
||||
err = s.db.Put([]byte(fmt.Sprintf("auth-%s", newId.String())), migstr, nil)
|
||||
err = s.db.Put(fmt.Sprintf("auth-%s", newId.String()), json.RawMessage(migstr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write auth-%s (from %s): %w", newId.String(), oldAuthKey, err)
|
||||
}
|
||||
log.Printf("Migrating v2 -> v3: %s to auth-%s...", oldAuthKey, newId.String())
|
||||
|
||||
err = s.delete(oldAuthKey)
|
||||
err = s.db.Delete(oldAuthKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete migrated %s: %w", oldAuthKey, err)
|
||||
}
|
||||
|
|
@ -163,21 +160,22 @@ type sessionV2 struct {
|
|||
Id []byte `json:"id"`
|
||||
}
|
||||
|
||||
func migrateFrom2_session(s *LevelDBStorage, oldUserId happydns.HexaString, newUserId string) (err error) {
|
||||
func migrateFrom2_session(s *KVStorage, oldUserId happydns.HexaString, newUserId string) (err error) {
|
||||
oldOwnerIdStr := []byte(fmt.Sprintf("\"login\":\"%x\"", oldUserId))
|
||||
newOwnerIdStr := []byte(fmt.Sprintf("\"login\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("user.session-")
|
||||
defer iter.Release()
|
||||
iter := s.db.Search("user.session-")
|
||||
kvIter := NewKVIterator[json.RawMessage](s.db, iter)
|
||||
defer kvIter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
usrstr := iter.Value()
|
||||
for kvIter.Next() {
|
||||
usrstr := []byte(*kvIter.Item())
|
||||
|
||||
if bytes.Contains(usrstr, oldOwnerIdStr) {
|
||||
var session sessionV2
|
||||
err = decodeData(usrstr, &session)
|
||||
err = s.db.DecodeData(usrstr, &session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to decode %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
newId := happydns.Identifier(session.Id)
|
||||
|
|
@ -189,15 +187,15 @@ func migrateFrom2_session(s *LevelDBStorage, oldUserId happydns.HexaString, newU
|
|||
migstr = bytes.Replace(migstr, oldOwnerIdStr, newOwnerIdStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, usrstr) {
|
||||
err = s.db.Put([]byte(fmt.Sprintf("user.session-%s", newUserId)), migstr, nil)
|
||||
err = s.db.Put(fmt.Sprintf("user.session-%s", newUserId), json.RawMessage(migstr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write user.session-%s (from %s): %w", newId.String(), iter.Key(), err)
|
||||
return fmt.Errorf("unable to write user.session-%s (from %s): %w", newId.String(), kvIter.Key(), err)
|
||||
}
|
||||
log.Printf("Migrating v2 -> v3: %s to user.session-%s...", iter.Key(), newId.String())
|
||||
log.Printf("Migrating v2 -> v3: %s to user.session-%s...", kvIter.Key(), newId.String())
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(kvIter.Key())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete migrated %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to delete migrated %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -211,27 +209,28 @@ type providerV2 struct {
|
|||
OwnerId happydns.HexaString `json:"_ownerid"`
|
||||
}
|
||||
|
||||
func migrateFrom2_provider(s *LevelDBStorage, oldUserId happydns.HexaString, newUserId string) (err error) {
|
||||
func migrateFrom2_provider(s *KVStorage, oldUserId happydns.HexaString, newUserId string) (err error) {
|
||||
oldOwnerIdStr := []byte(fmt.Sprintf("\"_ownerid\":\"%x\"", oldUserId))
|
||||
newOwnerIdStr := []byte(fmt.Sprintf("\"_ownerid\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("provider-")
|
||||
defer iter.Release()
|
||||
iter := s.db.Search("provider-")
|
||||
kvIter := NewKVIterator[json.RawMessage](s.db, iter)
|
||||
defer kvIter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
for kvIter.Next() {
|
||||
domstr := []byte(*kvIter.Item())
|
||||
|
||||
if bytes.Contains(domstr, oldOwnerIdStr) {
|
||||
var provider providerV2
|
||||
err = decodeData(domstr, &provider)
|
||||
err = s.db.DecodeData(domstr, &provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to decode %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
var newId happydns.Identifier
|
||||
newId, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate a new identifier for %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to generate a new identifier for %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
oldIdStr := []byte(fmt.Sprintf("\"_id\":%d", provider.Id))
|
||||
|
|
@ -242,20 +241,20 @@ func migrateFrom2_provider(s *LevelDBStorage, oldUserId happydns.HexaString, new
|
|||
|
||||
if !bytes.Equal(migstr, domstr) {
|
||||
var newprv happydns.ProviderMeta
|
||||
err = decodeData(migstr, &newprv)
|
||||
err = s.db.DecodeData(migstr, &newprv)
|
||||
if err != nil {
|
||||
log.Printf("From %s to %s", domstr, migstr)
|
||||
return fmt.Errorf("unable to reconstruct a valid provider: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Migrating v2 -> v3: %s...", iter.Key())
|
||||
log.Printf("Migrating v2 -> v3: %s...", kvIter.Key())
|
||||
|
||||
err = s.db.Put([]byte(fmt.Sprintf("provider-%s", newId.String())), migstr, nil)
|
||||
err = s.db.Put(fmt.Sprintf("provider-%s", newId.String()), json.RawMessage(migstr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(kvIter.Key())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -277,29 +276,30 @@ type domainV2 struct {
|
|||
ZoneHistory []int64 `json:"zone_history"`
|
||||
}
|
||||
|
||||
func migrateFrom2_domains(s *LevelDBStorage, oldUserId happydns.HexaString, newUserId string, oldProviderId int64, newProviderId string) (err error) {
|
||||
func migrateFrom2_domains(s *KVStorage, oldUserId happydns.HexaString, newUserId string, oldProviderId int64, newProviderId string) (err error) {
|
||||
oldProviderIdStr := []byte(fmt.Sprintf("\"id_provider\":%d", oldProviderId))
|
||||
newProviderIdStr := []byte(fmt.Sprintf("\"id_provider\":\"%s\"", newProviderId))
|
||||
oldOwnerIdStr := []byte(fmt.Sprintf("\"id_owner\":\"%x\"", oldUserId))
|
||||
newOwnerIdStr := []byte(fmt.Sprintf("\"id_owner\":\"%s\"", newUserId))
|
||||
|
||||
iter := s.search("domain-")
|
||||
defer iter.Release()
|
||||
iter := s.db.Search("domain-")
|
||||
kvIter := NewKVIterator[json.RawMessage](s.db, iter)
|
||||
defer kvIter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
domstr := iter.Value()
|
||||
for kvIter.Next() {
|
||||
domstr := []byte(*kvIter.Item())
|
||||
|
||||
if bytes.Contains(domstr, oldProviderIdStr) && bytes.Contains(domstr, oldOwnerIdStr) {
|
||||
var domain domainV2
|
||||
err = decodeData(domstr, &domain)
|
||||
err = s.db.DecodeData(domstr, &domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to decode %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
var newId happydns.Identifier
|
||||
newId, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate a new identifier for %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to generate a new identifier for %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
oldIdStr := []byte(fmt.Sprintf("\"id\":%d", domain.Id))
|
||||
|
|
@ -312,7 +312,7 @@ func migrateFrom2_domains(s *LevelDBStorage, oldUserId happydns.HexaString, newU
|
|||
var newZoneId happydns.Identifier
|
||||
newZoneId, err = happydns.NewRandomIdentifier()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate a new identifier for a zone of %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to generate a new identifier for a zone of %s: %w", kvIter.Key(), err)
|
||||
}
|
||||
|
||||
err = migrateFrom2_zone(s, oldUserId, newUserId, zoneid, newZoneId.String())
|
||||
|
|
@ -333,20 +333,20 @@ func migrateFrom2_domains(s *LevelDBStorage, oldUserId happydns.HexaString, newU
|
|||
|
||||
if !bytes.Equal(migstr, domstr) {
|
||||
var newdn happydns.Domain
|
||||
err = decodeData(migstr, &newdn)
|
||||
err = s.db.DecodeData(migstr, &newdn)
|
||||
if err != nil {
|
||||
log.Printf("From %s to %s", domstr, migstr)
|
||||
return fmt.Errorf("unable to reconstruct a valid domain: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Migrating v2 -> v3: %s...", iter.Key())
|
||||
log.Printf("Migrating v2 -> v3: %s...", kvIter.Key())
|
||||
|
||||
err = s.db.Put([]byte(fmt.Sprintf("domain-%s", newId.String())), migstr, nil)
|
||||
err = s.db.Put(fmt.Sprintf("domain-%s", newId.String()), json.RawMessage(migstr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.delete(string(iter.Key()))
|
||||
err = s.db.Delete(kvIter.Key())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -357,7 +357,7 @@ func migrateFrom2_domains(s *LevelDBStorage, oldUserId happydns.HexaString, newU
|
|||
return
|
||||
}
|
||||
|
||||
func migrateFrom2_zone(s *LevelDBStorage, oldUserId happydns.HexaString, newUserId string, oldZoneId int64, newZoneId string) (err error) {
|
||||
func migrateFrom2_zone(s *KVStorage, oldUserId happydns.HexaString, newUserId string, oldZoneId int64, newZoneId string) (err error) {
|
||||
oldIdStr := []byte(fmt.Sprintf("\"id\":%d", oldZoneId))
|
||||
newIdStr := []byte(fmt.Sprintf("\"id\":\"%s\"", newZoneId))
|
||||
oldIdOwnerStr := []byte(fmt.Sprintf("\"id_author\":%d", oldUserId))
|
||||
|
|
@ -365,22 +365,23 @@ func migrateFrom2_zone(s *LevelDBStorage, oldUserId happydns.HexaString, newUser
|
|||
|
||||
oldZoneKey := fmt.Sprintf("domain.zone-%d", oldZoneId)
|
||||
|
||||
zonestr, err := s.db.Get([]byte(oldZoneKey), nil)
|
||||
var zonestr json.RawMessage
|
||||
err = s.db.Get(oldZoneKey, &zonestr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find/decode %s: %w", oldZoneKey, err)
|
||||
}
|
||||
|
||||
migstr := bytes.Replace(zonestr, oldIdStr, newIdStr, 1)
|
||||
migstr := bytes.Replace([]byte(zonestr), oldIdStr, newIdStr, 1)
|
||||
migstr = bytes.Replace(migstr, oldIdOwnerStr, newIdOwnerStr, 1)
|
||||
|
||||
if !bytes.Equal(migstr, zonestr) {
|
||||
err = s.db.Put([]byte(fmt.Sprintf("domain.zone-%s", newZoneId)), migstr, nil)
|
||||
if !bytes.Equal(migstr, []byte(zonestr)) {
|
||||
err = s.db.Put(fmt.Sprintf("domain.zone-%s", newZoneId), json.RawMessage(migstr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write domain.zone-%s (from %s): %w", newZoneId, oldZoneKey, err)
|
||||
}
|
||||
log.Printf("Migrating v2 -> v3: %s to domain.zone-%s...", oldZoneKey, newZoneId)
|
||||
|
||||
err = s.delete(oldZoneKey)
|
||||
err = s.db.Delete(oldZoneKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete migrated %s: %w", oldZoneKey, err)
|
||||
}
|
||||
|
|
@ -27,41 +27,41 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func migrateFrom3(s *LevelDBStorage) (err error) {
|
||||
err = migrateFrom3_records(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
func migrateFrom3(s *KVStorage) error {
|
||||
return migrateFrom3_records(s)
|
||||
}
|
||||
|
||||
func migrateFrom3_records(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom3_records(s *KVStorage) error {
|
||||
TypeStr := []byte("\"_svctype\":\"abstract.Origin\"")
|
||||
|
||||
iter := s.search("domain.zone-")
|
||||
iter := NewKVIterator[[]byte](s.db, s.db.Search("domain.zone-"))
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
zonestr, err := s.db.Get(iter.Key(), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find/decode %s: %w", iter.Key(), err)
|
||||
zonestr, ok := iter.Raw().([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key := iter.Key()
|
||||
|
||||
if bytes.Contains(zonestr, TypeStr) {
|
||||
migstr := zonestr
|
||||
migstr := make([]byte, len(zonestr))
|
||||
copy(migstr, zonestr)
|
||||
|
||||
migstr = bytes.Replace(migstr, []byte("000000000,\"retry\":"), []byte(",\"retry\":"), 1)
|
||||
migstr = bytes.Replace(migstr, []byte("000000000,\"expire\":"), []byte(",\"expire\":"), 1)
|
||||
migstr = bytes.Replace(migstr, []byte("000000000,\"nxttl\":"), []byte(",\"nxttl\":"), 1)
|
||||
migstr = bytes.Replace(migstr, []byte("000000000,\"ns\":"), []byte(",\"ns\":"), 1)
|
||||
|
||||
if !bytes.Equal(migstr, zonestr) {
|
||||
err = s.db.Put(iter.Key(), migstr, nil)
|
||||
err := s.db.Put(key, migstr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write %s: %w", iter.Key(), err)
|
||||
return fmt.Errorf("unable to write %s: %w", key, err)
|
||||
}
|
||||
log.Printf("Migrating v2 -> v3: %s (contains Origin)...", iter.Key())
|
||||
log.Printf("Migrating v3 -> v4: %s (contains Origin)...", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
|
@ -25,6 +25,6 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
func migrateFrom4(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom4(s *KVStorage) (err error) {
|
||||
return fmt.Errorf("Unable to migrate from DB version 4. Please use a previous happyDomain release to perform this migration")
|
||||
}
|
||||
|
|
@ -25,6 +25,6 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
func migrateFrom5(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom5(s *KVStorage) (err error) {
|
||||
return fmt.Errorf("Unable to migrate from DB version 4. Please use a previous happyDomain release to perform this migration")
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func migrateFrom6(s *LevelDBStorage) error {
|
||||
func migrateFrom6(s *KVStorage) error {
|
||||
log.Println("Drop all sessions to use new format")
|
||||
return s.ClearSessions()
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ func explodeAbstractEMail(dn happydns.Subdomain, in *happydns.ServiceMessage) ([
|
|||
|
||||
var migrateFrom7SvcType map[string]func(json.RawMessage) (json.RawMessage, error)
|
||||
|
||||
func migrateFrom7(s *LevelDBStorage) (err error) {
|
||||
func migrateFrom7(s *KVStorage) (err error) {
|
||||
migrateFrom7SvcType = make(map[string]func(json.RawMessage) (json.RawMessage, error))
|
||||
|
||||
// abstract.ACMEChallenge
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
|
|
@ -26,9 +26,9 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
type LevelDBMigrationFunc func(s *LevelDBStorage) error
|
||||
type KVMigrationFunc func(s *KVStorage) error
|
||||
|
||||
var migrations []LevelDBMigrationFunc = []LevelDBMigrationFunc{
|
||||
var migrations []KVMigrationFunc = []KVMigrationFunc{
|
||||
migrateFrom0,
|
||||
migrateFrom1,
|
||||
migrateFrom2,
|
||||
|
|
@ -39,49 +39,53 @@ var migrations []LevelDBMigrationFunc = []LevelDBMigrationFunc{
|
|||
migrateFrom7,
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) SchemaVersion() int {
|
||||
type Version struct {
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
func (s *KVStorage) SchemaVersion() int {
|
||||
return len(migrations)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) MigrateSchema() (err error) {
|
||||
func (s *KVStorage) MigrateSchema() (err error) {
|
||||
found := false
|
||||
|
||||
found, err = s.db.Has([]byte("version"), nil)
|
||||
found, err = s.db.Has("version")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var version int
|
||||
var version Version
|
||||
|
||||
if !found {
|
||||
version = len(migrations)
|
||||
err = s.put("version", version)
|
||||
version.Version = len(migrations)
|
||||
err = s.db.Put("version", version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = s.get("version", &version)
|
||||
err = s.db.Get("version", &version.Version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if version > len(migrations) {
|
||||
return fmt.Errorf("Your database has revision %d, which is newer than the revision this happyDomain version can handle (max DB revision %d). Please update happyDomain", version, len(migrations))
|
||||
if version.Version > len(migrations) {
|
||||
return fmt.Errorf("Your database has revision %d, which is newer than the revision this happyDomain version can handle (max DB revision %d). Please update happyDomain", version.Version, len(migrations))
|
||||
}
|
||||
|
||||
for v, migration := range migrations[version:] {
|
||||
log.Printf("Doing migration from %d to %d", version+v, version+v+1)
|
||||
for v, migration := range migrations[version.Version:] {
|
||||
log.Printf("Doing migration from %d to %d", version.Version+v, version.Version+v+1)
|
||||
// Do the migration
|
||||
if err = migration(s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Save the step
|
||||
if err = s.put("version", version+v+1); err != nil {
|
||||
if err = s.db.Put("version", version.Version+v+1); err != nil {
|
||||
return
|
||||
}
|
||||
log.Printf("Migration from %d to %d DONE!", version+v, version+v+1)
|
||||
log.Printf("Migration from %d to %d DONE!", version.Version+v, version.Version+v+1)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
|
|
@ -25,35 +25,33 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllUsers() (happydns.Iterator[happydns.User], error) {
|
||||
iter := s.search("user-")
|
||||
return NewLevelDBIterator[happydns.User](s.db, iter), nil
|
||||
func (s *KVStorage) ListAllUsers() (happydns.Iterator[happydns.User], error) {
|
||||
iter := s.db.Search("user-")
|
||||
return NewKVIterator[happydns.User](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getUser(key string) (*happydns.User, error) {
|
||||
func (s *KVStorage) getUser(key string) (*happydns.User, error) {
|
||||
u := &happydns.User{}
|
||||
err := s.get(key, &u)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
err := s.db.Get(key, &u)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrUserNotFound
|
||||
}
|
||||
return u, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetUser(id happydns.Identifier) (u *happydns.User, err error) {
|
||||
func (s *KVStorage) GetUser(id happydns.Identifier) (u *happydns.User, err error) {
|
||||
return s.getUser(fmt.Sprintf("user-%s", id.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetUserByEmail(email string) (*happydns.User, error) {
|
||||
func (s *KVStorage) GetUserByEmail(email string) (*happydns.User, error) {
|
||||
users, err := s.ListAllUsers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer users.Close()
|
||||
|
||||
for users.Next() {
|
||||
user := users.Item()
|
||||
|
|
@ -65,11 +63,12 @@ func (s *LevelDBStorage) GetUserByEmail(email string) (*happydns.User, error) {
|
|||
return nil, happydns.ErrUserNotFound
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UserExists(email string) bool {
|
||||
func (s *KVStorage) UserExists(email string) bool {
|
||||
users, err := s.ListAllUsers()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer users.Close()
|
||||
|
||||
for users.Next() {
|
||||
if users.Item().Email == email {
|
||||
|
|
@ -80,9 +79,9 @@ func (s *LevelDBStorage) UserExists(email string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateOrUpdateUser(u *happydns.User) error {
|
||||
func (s *KVStorage) CreateOrUpdateUser(u *happydns.User) error {
|
||||
if u.Id.IsEmpty() {
|
||||
_, id, err := s.findIdentifierKey("user-")
|
||||
_, id, err := s.db.FindIdentifierKey("user-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -90,39 +89,30 @@ func (s *LevelDBStorage) CreateOrUpdateUser(u *happydns.User) error {
|
|||
u.Id = id
|
||||
}
|
||||
|
||||
return s.put(fmt.Sprintf("user-%s", u.Id.String()), u)
|
||||
return s.db.Put(fmt.Sprintf("user-%s", u.Id.String()), u)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteUser(uId happydns.Identifier) error {
|
||||
return s.delete(fmt.Sprintf("user-%s", uId.String()))
|
||||
func (s *KVStorage) DeleteUser(uId happydns.Identifier) error {
|
||||
return s.db.Delete(fmt.Sprintf("user-%s", uId.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearUsers() error {
|
||||
func (s *KVStorage) ClearUsers() error {
|
||||
if err := s.ClearSessions(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := s.db.OpenTransaction()
|
||||
iter, err := s.ListAllUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("user-")), nil)
|
||||
defer iter.Release()
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
96
internal/storage/kvtpl/zone.go
Normal file
96
internal/storage/kvtpl/zone.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *KVStorage) ListAllZones() (happydns.Iterator[happydns.ZoneMessage], error) {
|
||||
iter := s.db.Search("domain.zone-")
|
||||
return NewKVIterator[happydns.ZoneMessage](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) GetZone(id happydns.Identifier) (*happydns.ZoneMessage, error) {
|
||||
z := &happydns.ZoneMessage{}
|
||||
err := s.db.Get(fmt.Sprintf("domain.zone-%s", id.String()), &z)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
return z, err
|
||||
}
|
||||
|
||||
func (s *KVStorage) getZoneMeta(id string) (z *happydns.ZoneMeta, err error) {
|
||||
z = &happydns.ZoneMeta{}
|
||||
err = s.db.Get(id, z)
|
||||
if errors.Is(err, happydns.ErrNotFound) {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) GetZoneMeta(id happydns.Identifier) (z *happydns.ZoneMeta, err error) {
|
||||
z, err = s.getZoneMeta(fmt.Sprintf("domain.zone-%s", id.String()))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *KVStorage) CreateZone(z *happydns.Zone) error {
|
||||
key, id, err := s.db.FindIdentifierKey("domain.zone-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
z.Id = id
|
||||
return s.db.Put(key, z)
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateZone(z *happydns.Zone) error {
|
||||
return s.db.Put(fmt.Sprintf("domain.zone-%s", z.Id.String()), z)
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateZoneMessage(z *happydns.ZoneMessage) error {
|
||||
return s.db.Put(fmt.Sprintf("domain.zone-%s", z.Id.String()), z)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteZone(id happydns.Identifier) error {
|
||||
return s.db.Delete(fmt.Sprintf("domain.zone-%s", id.String()))
|
||||
}
|
||||
|
||||
func (s *KVStorage) ClearZones() error {
|
||||
iter, err := s.ListAllZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for iter.Next() {
|
||||
err = s.db.Delete(iter.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"flag"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
)
|
||||
|
||||
var path string
|
||||
|
|
@ -36,5 +37,10 @@ func init() {
|
|||
}
|
||||
|
||||
func Instantiate() (storage.Storage, error) {
|
||||
return NewLevelDBStorage(path)
|
||||
db, err := NewLevelDBStorage(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kv.NewKVDatabase(db)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,15 @@ package database // import "git.happydns.org/happyDomain/internal/storage/leveld
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
|
|
@ -68,16 +69,31 @@ func decodeData(data []byte, v interface{}) error {
|
|||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) get(key string, v interface{}) error {
|
||||
func (s *LevelDBStorage) DecodeData(data interface{}, v interface{}) error {
|
||||
b, ok := data.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("data to decode are not in []byte format (%T)", data)
|
||||
}
|
||||
return decodeData(b, v)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) Has(key string) (bool, error) {
|
||||
return s.db.Has([]byte(key), nil)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) Get(key string, v interface{}) error {
|
||||
data, err := s.db.Get([]byte(key), nil)
|
||||
if err != nil {
|
||||
if goerrors.Is(err, leveldb.ErrNotFound) {
|
||||
return happydns.ErrNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return decodeData(data, v)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) put(key string, v interface{}) error {
|
||||
func (s *LevelDBStorage) Put(key string, v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -86,7 +102,7 @@ func (s *LevelDBStorage) put(key string, v interface{}) error {
|
|||
return s.db.Put([]byte(key), data, nil)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) findIdentifierKey(prefix string) (key string, id happydns.Identifier, err error) {
|
||||
func (s *LevelDBStorage) FindIdentifierKey(prefix string) (key string, id happydns.Identifier, err error) {
|
||||
found := true
|
||||
for found {
|
||||
id, err = happydns.NewRandomIdentifier()
|
||||
|
|
@ -103,10 +119,10 @@ func (s *LevelDBStorage) findIdentifierKey(prefix string) (key string, id happyd
|
|||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) delete(key string) error {
|
||||
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)
|
||||
func (s *LevelDBStorage) Search(prefix string) storage.Iterator {
|
||||
return NewIterator(s.db.NewIterator(util.BytesPrefix([]byte(prefix)), nil))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllDomainLogs() (happydns.Iterator[happydns.DomainLogWithDomainId], error) {
|
||||
iter := s.search("domain.log|")
|
||||
return NewLevelDBIteratorCustomDecode[happydns.DomainLogWithDomainId](s.db, iter, func(data []byte, v interface{}) error {
|
||||
err := decodeData(data, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st := strings.Split(string(iter.Key()), "|")
|
||||
if len(st) < 3 {
|
||||
return fmt.Errorf("invalid domain log key: %s", string(iter.Key()))
|
||||
}
|
||||
|
||||
v.(*happydns.DomainLogWithDomainId).DomainId, err = happydns.NewIdentifierFromString(st[1])
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ListDomainLogs(domain *happydns.Domain) (logs []*happydns.DomainLog, err error) {
|
||||
iter := s.search(fmt.Sprintf("domain.log|%s|", domain.Id.String()))
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var z happydns.DomainLog
|
||||
|
||||
err = decodeData(iter.Value(), &z)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logs = append(logs, &z)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getDomainLog(id string) (l *happydns.DomainLog, d *happydns.Domain, err error) {
|
||||
l = &happydns.DomainLog{}
|
||||
err = s.get(id, l)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, nil, happydns.ErrDomainLogNotFound
|
||||
}
|
||||
|
||||
st := strings.Split(id, "|")
|
||||
if len(st) < 3 {
|
||||
return
|
||||
}
|
||||
|
||||
d = &happydns.Domain{}
|
||||
err = s.get(id, fmt.Sprintf("domain-%s", st[1]))
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, nil, happydns.ErrDomainNotFound
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
key, id, err := s.findIdentifierKey(fmt.Sprintf("domain.log|%s|", d.Id.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Id = id
|
||||
return s.put(key, l)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
return s.put(fmt.Sprintf("domain.log|%s|%s", d.Id.String(), l.Id.String()), l)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteDomainLog(d *happydns.Domain, l *happydns.DomainLog) error {
|
||||
return s.delete(fmt.Sprintf("domain.log|%s|%s", d.Id.String(), l.Id.String()))
|
||||
}
|
||||
|
|
@ -22,104 +22,35 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
)
|
||||
|
||||
// LevelDBIterator is a generic implementation of Iterator for LevelDB.
|
||||
type LevelDBIterator[T any] struct {
|
||||
db *leveldb.DB
|
||||
iter iterator.Iterator
|
||||
err error
|
||||
item *T
|
||||
decode func([]byte, interface{}) error
|
||||
type LevelDBIterator struct {
|
||||
iter iterator.Iterator
|
||||
}
|
||||
|
||||
// NewLevelDBIterator creates a new LevelDBIterator instance for the given LevelDB iterator and decode function.
|
||||
func NewLevelDBIterator[T any](db *leveldb.DB, iter iterator.Iterator) *LevelDBIterator[T] {
|
||||
return &LevelDBIterator[T]{
|
||||
db: db,
|
||||
iter: iter,
|
||||
decode: decodeData,
|
||||
func NewIterator(iter iterator.Iterator) *LevelDBIterator {
|
||||
return &LevelDBIterator{
|
||||
iter: iter,
|
||||
}
|
||||
}
|
||||
|
||||
// NewLevelDBIterator creates a new LevelDBIterator instance for the given LevelDB iterator and decode function.
|
||||
func NewLevelDBIteratorCustomDecode[T any](db *leveldb.DB, iter iterator.Iterator, decodeFunc func([]byte, interface{}) error) *LevelDBIterator[T] {
|
||||
return &LevelDBIterator[T]{
|
||||
db: db,
|
||||
iter: iter,
|
||||
decode: decodeFunc,
|
||||
}
|
||||
func (it *LevelDBIterator) Next() bool {
|
||||
return it.iter.Next()
|
||||
}
|
||||
|
||||
// Next moves the iterator to the next valid item.
|
||||
// Skips items that fail to decode and logs the error.
|
||||
func (it *LevelDBIterator[T]) Next() bool {
|
||||
for it.iter.Next() {
|
||||
var value T
|
||||
err := it.decode(it.iter.Value(), &value)
|
||||
if err != nil {
|
||||
log.Printf("LevelDBIterator: error decoding item at key %q: %s", it.iter.Key(), err)
|
||||
it.err = err
|
||||
continue
|
||||
}
|
||||
it.item = &value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func (it *LevelDBIterator) Key() string {
|
||||
return string(it.iter.Key())
|
||||
}
|
||||
|
||||
// NextWithError advances the iterator to the next item, on decode error it doesn't continue to the next item.
|
||||
// Returns true if there is a next item, false otherwise.
|
||||
func (it *LevelDBIterator[T]) NextWithError() bool {
|
||||
if it.iter.Next() {
|
||||
var value T
|
||||
err := it.decode(it.iter.Value(), &value)
|
||||
if err != nil {
|
||||
it.err = err
|
||||
it.item = nil
|
||||
} else {
|
||||
it.err = nil
|
||||
it.item = &value
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Item returns the current item from the iterator.
|
||||
// Only valid after a successful call to Next().
|
||||
func (it *LevelDBIterator[T]) Item() *T {
|
||||
return it.item
|
||||
}
|
||||
|
||||
// DropItem deletes the key currently pointed to by the iterator.
|
||||
func (it *LevelDBIterator[T]) DropItem() error {
|
||||
if it.iter == nil || !it.iter.Valid() {
|
||||
return fmt.Errorf("DropItem: iterator is not valid")
|
||||
}
|
||||
return it.db.Delete(it.iter.Key(), nil)
|
||||
}
|
||||
|
||||
// Raw returns the raw (non-decoded) value at the current iterator position.
|
||||
// Should only be called after a successful call to Next().
|
||||
func (it *LevelDBIterator[T]) Raw() []byte {
|
||||
if it.iter == nil || !it.iter.Valid() {
|
||||
return []byte{}
|
||||
}
|
||||
func (it *LevelDBIterator) Value() interface{} {
|
||||
return it.iter.Value()
|
||||
}
|
||||
|
||||
// Err returns the first error encountered during iteration, if any.
|
||||
func (it *LevelDBIterator[T]) Err() error {
|
||||
return it.err
|
||||
func (it *LevelDBIterator) Valid() bool {
|
||||
return it.iter.Valid()
|
||||
}
|
||||
|
||||
// Close releases resources held by the underlying LevelDB iterator.
|
||||
func (it *LevelDBIterator[T]) Close() {
|
||||
func (it *LevelDBIterator) Release() {
|
||||
it.iter.Release()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllProviders() (happydns.Iterator[happydns.ProviderMessage], error) {
|
||||
iter := s.search("provider-")
|
||||
return NewLevelDBIterator[happydns.ProviderMessage](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getProviderMeta(id happydns.Identifier) (*happydns.ProviderMessage, error) {
|
||||
v, err := s.db.Get(id, nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, happydns.ErrProviderNotFound
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
srcMsg := &happydns.ProviderMessage{}
|
||||
err = decodeData(v, srcMsg)
|
||||
return srcMsg, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ListProviders(u *happydns.User) (srcs happydns.ProviderMessages, err error) {
|
||||
iter := s.search("provider-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var srcMsg happydns.ProviderMessage
|
||||
err = decodeData(iter.Value(), &srcMsg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(srcMsg.Owner, u.Id) {
|
||||
continue
|
||||
}
|
||||
|
||||
srcs = append(srcs, &srcMsg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetProvider(id happydns.Identifier) (*happydns.ProviderMessage, error) {
|
||||
v, err := s.db.Get([]byte(fmt.Sprintf("provider-%s", id.String())), nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, happydns.ErrProviderNotFound
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var prvdMsg happydns.ProviderMessage
|
||||
err = decodeData(v, &prvdMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &prvdMsg, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateProvider(prvd *happydns.Provider) error {
|
||||
key, id, err := s.findIdentifierKey("provider-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prvd.Id = id
|
||||
|
||||
return s.put(key, prvd)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateProvider(prvd *happydns.Provider) error {
|
||||
return s.put(fmt.Sprintf("provider-%s", prvd.Id.String()), prvd)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteProvider(prvdId happydns.Identifier) error {
|
||||
return s.delete(fmt.Sprintf("provider-%s", prvdId.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearProviders() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("provider-")), 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
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllSessions() (happydns.Iterator[happydns.Session], error) {
|
||||
iter := s.search("user.session-")
|
||||
return NewLevelDBIterator[happydns.Session](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getSession(id string) (*happydns.Session, error) {
|
||||
session := &happydns.Session{}
|
||||
err := s.get(id, &session)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, happydns.ErrSessionNotFound
|
||||
}
|
||||
return session, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetSession(id string) (session *happydns.Session, err error) {
|
||||
return s.getSession(fmt.Sprintf("user.session-%s", id))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ListAuthUserSessions(user *happydns.UserAuth) (sessions []*happydns.Session, err error) {
|
||||
iter := s.search("user.session-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var s happydns.Session
|
||||
|
||||
err = decodeData(iter.Value(), &s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s.IdUser.Equals(user.Id) {
|
||||
sessions = append(sessions, &s)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ListUserSessions(userid happydns.Identifier) (sessions []*happydns.Session, err error) {
|
||||
iter := s.search("user.session-")
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
var s happydns.Session
|
||||
|
||||
err = decodeData(iter.Value(), &s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s.IdUser.Equals(userid) {
|
||||
sessions = append(sessions, &s)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateSession(session *happydns.Session) error {
|
||||
return s.put(fmt.Sprintf("user.session-%s", session.Id), session)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteSession(id string) error {
|
||||
return s.delete(fmt.Sprintf("user.session-%s", id))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearSessions() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("user.session-")), 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
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) ListAllZones() (happydns.Iterator[happydns.ZoneMessage], error) {
|
||||
iter := s.search("domain.zone-")
|
||||
return NewLevelDBIterator[happydns.ZoneMessage](s.db, iter), nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetZone(id happydns.Identifier) (*happydns.ZoneMessage, error) {
|
||||
z := &happydns.ZoneMessage{}
|
||||
err := s.get(fmt.Sprintf("domain.zone-%s", id.String()), &z)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
return z, err
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getZoneMeta(id string) (z *happydns.ZoneMeta, err error) {
|
||||
z = &happydns.ZoneMeta{}
|
||||
err = s.get(id, z)
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
return nil, happydns.ErrZoneNotFound
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetZoneMeta(id happydns.Identifier) (z *happydns.ZoneMeta, err error) {
|
||||
z, err = s.getZoneMeta(fmt.Sprintf("domain.zone-%s", id.String()))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateZone(z *happydns.Zone) error {
|
||||
key, id, err := s.findIdentifierKey("domain.zone-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
z.Id = id
|
||||
return s.put(key, z)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateZone(z *happydns.Zone) error {
|
||||
return s.put(fmt.Sprintf("domain.zone-%s", z.Id.String()), z)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) UpdateZoneMessage(z *happydns.ZoneMessage) error {
|
||||
return s.put(fmt.Sprintf("domain.zone-%s", z.Id.String()), z)
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) DeleteZone(id happydns.Identifier) error {
|
||||
return s.delete(fmt.Sprintf("domain.zone-%s", id.String()))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) ClearZones() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("domain.zone-")), 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
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase"
|
||||
userUC "git.happydns.org/happyDomain/internal/usecase/user"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
|
|
@ -22,8 +23,9 @@ func (u testUserInfo) JoinNewsletter() bool { return u.newsletter }
|
|||
|
||||
func Test_CompleteAuthentication(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
uinfo := testUserInfo{
|
||||
id: happydns.Identifier([]byte("user-123")),
|
||||
|
|
@ -40,7 +42,7 @@ func Test_CompleteAuthentication(t *testing.T) {
|
|||
}
|
||||
|
||||
// Check the user is correctly stored in db
|
||||
stored, err := mem.GetUser(happydns.Identifier([]byte("user-123")))
|
||||
stored, err := db.GetUser(happydns.Identifier([]byte("user-123")))
|
||||
if err != nil {
|
||||
t.Fatalf("expected stored user, got error: %v", err)
|
||||
}
|
||||
|
|
@ -61,9 +63,10 @@ func (ds *testNewsletterSubscription) SubscribeToNewsletter(u happydns.UserInfo)
|
|||
|
||||
func Test_CompleteAuthentication_WithNewsletter(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
mockNewsletterSubscription := &testNewsletterSubscription{}
|
||||
userUsecase := userUC.NewUserUsecases(mem, mockNewsletterSubscription, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, mockNewsletterSubscription, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
uinfo := testUserInfo{
|
||||
id: happydns.Identifier([]byte("user-123")),
|
||||
|
|
@ -97,6 +100,7 @@ func Test_CompleteAuthentication_WithNewsletter(t *testing.T) {
|
|||
|
||||
func Test_AuthenticateUserWithPassword_WrongPassword(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
authUser := &happydns.UserAuth{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -106,13 +110,13 @@ func Test_AuthenticateUserWithPassword_WrongPassword(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = mem.CreateAuthUser(authUser)
|
||||
err = db.CreateAuthUser(authUser)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -125,6 +129,7 @@ func Test_AuthenticateUserWithPassword_WrongPassword(t *testing.T) {
|
|||
|
||||
func Test_AuthenticateUserWithPassword_WeakPassword(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
authUser := &happydns.UserAuth{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -134,13 +139,13 @@ func Test_AuthenticateUserWithPassword_WeakPassword(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = mem.CreateAuthUser(authUser)
|
||||
err = db.CreateAuthUser(authUser)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -153,6 +158,7 @@ func Test_AuthenticateUserWithPassword_WeakPassword(t *testing.T) {
|
|||
|
||||
func Test_AuthenticateUserWithPassword_UnverifiedEmail(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
authUser := &happydns.UserAuth{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -162,13 +168,13 @@ func Test_AuthenticateUserWithPassword_UnverifiedEmail(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = mem.CreateAuthUser(authUser)
|
||||
err = db.CreateAuthUser(authUser)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -181,6 +187,7 @@ func Test_AuthenticateUserWithPassword_UnverifiedEmail(t *testing.T) {
|
|||
|
||||
func Test_AuthenticateUserWithPassword_NoEmail(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
authUser := &happydns.UserAuth{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -190,13 +197,13 @@ func Test_AuthenticateUserWithPassword_NoEmail(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = mem.CreateAuthUser(authUser)
|
||||
err = db.CreateAuthUser(authUser)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{NoMail: true}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{NoMail: true}, db, userUsecase)
|
||||
|
||||
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
|
||||
Email: "a@b.c",
|
||||
|
|
@ -209,6 +216,7 @@ func Test_AuthenticateUserWithPassword_NoEmail(t *testing.T) {
|
|||
|
||||
func Test_AuthenticateUserWithPassword(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
now := time.Now()
|
||||
authUser := &happydns.UserAuth{
|
||||
|
|
@ -220,13 +228,13 @@ func Test_AuthenticateUserWithPassword(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = mem.CreateAuthUser(authUser)
|
||||
err = db.CreateAuthUser(authUser)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
|
||||
userUsecase := userUC.NewUserUsecases(db, nil, nil, nil)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, db, userUsecase)
|
||||
|
||||
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
|
||||
Email: "a@b.c",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase/authuser"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
|
@ -47,8 +49,9 @@ func (m *MockCloseUserSessionsUsecase) ByID(userID happydns.Identifier) error {
|
|||
return m.CloseAll(&happydns.UserAuth{Id: userID})
|
||||
}
|
||||
|
||||
func setupTestService() (*authuser.Service, *inmemory.InMemoryStorage) {
|
||||
store, _ := inmemory.NewInMemoryStorage()
|
||||
func setupTestService() (*authuser.Service, storage.Storage) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
store, _ := kv.NewKVDatabase(mem)
|
||||
cfg := &happydns.Options{
|
||||
DisableRegistration: false,
|
||||
}
|
||||
|
|
@ -75,7 +78,8 @@ func TestCanRegister_Success(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCanRegister_Closed(t *testing.T) {
|
||||
store, _ := inmemory.NewInMemoryStorage()
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
store, _ := kv.NewKVDatabase(mem)
|
||||
cfg := &happydns.Options{
|
||||
DisableRegistration: true, // Registration closed
|
||||
}
|
||||
|
|
@ -344,7 +348,8 @@ func TestCheckNewPassword(t *testing.T) {
|
|||
// ========== DeleteAuthUser Tests ==========
|
||||
|
||||
func TestDeleteAuthUser(t *testing.T) {
|
||||
store, _ := inmemory.NewInMemoryStorage()
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
store, _ := kv.NewKVDatabase(mem)
|
||||
cfg := &happydns.Options{
|
||||
DisableRegistration: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase/domain"
|
||||
providerUC "git.happydns.org/happyDomain/internal/usecase/provider"
|
||||
zoneUC "git.happydns.org/happyDomain/internal/usecase/zone"
|
||||
|
|
@ -90,7 +92,7 @@ func (m *mockDomainLogAppender) AppendDomainLog(d *happydns.Domain, log *happydn
|
|||
|
||||
// Helper functions
|
||||
|
||||
func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string) *happydns.User {
|
||||
func createTestUser(t *testing.T, store storage.Storage, email string) *happydns.User {
|
||||
user := &happydns.User{
|
||||
Id: happydns.Identifier([]byte("user-" + email)),
|
||||
Email: email,
|
||||
|
|
@ -101,7 +103,7 @@ func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string)
|
|||
return user
|
||||
}
|
||||
|
||||
func createTestProvider(t *testing.T, store *inmemory.InMemoryStorage, user *happydns.User, name string) happydns.Identifier {
|
||||
func createTestProvider(t *testing.T, store storage.Storage, user *happydns.User, name string) happydns.Identifier {
|
||||
provider := &happydns.Provider{
|
||||
ProviderMeta: happydns.ProviderMeta{
|
||||
Type: "mockProviderBody",
|
||||
|
|
@ -118,7 +120,7 @@ func createTestProvider(t *testing.T, store *inmemory.InMemoryStorage, user *hap
|
|||
return provider.Id
|
||||
}
|
||||
|
||||
func setupTestService(store *inmemory.InMemoryStorage) (*domain.Service, *mockDomainLogAppender) {
|
||||
func setupTestService(store storage.Storage) (*domain.Service, *mockDomainLogAppender) {
|
||||
// Create the provider service
|
||||
providerService := providerUC.NewService(store)
|
||||
|
||||
|
|
@ -146,10 +148,11 @@ func setupTestService(store *inmemory.InMemoryStorage) (*domain.Service, *mockDo
|
|||
|
||||
func Test_CreateDomain(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, logAppender := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, logAppender := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
domainToCreate := &happydns.Domain{
|
||||
DomainName: "example.com",
|
||||
|
|
@ -191,9 +194,10 @@ func Test_CreateDomain(t *testing.T) {
|
|||
|
||||
func Test_CreateDomain_InvalidProvider(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
invalidProviderId := happydns.Identifier([]byte("invalid-provider"))
|
||||
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -209,10 +213,11 @@ func Test_CreateDomain_InvalidProvider(t *testing.T) {
|
|||
|
||||
func Test_GetUserDomain(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -248,11 +253,12 @@ func Test_GetUserDomain(t *testing.T) {
|
|||
|
||||
func Test_GetUserDomain_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
providerId := createTestProvider(t, mem, user1, "Test Provider")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
providerId := createTestProvider(t, db, user1, "Test Provider")
|
||||
|
||||
// Create a domain for user1
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -283,9 +289,10 @@ func Test_GetUserDomain_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_GetUserDomain_NotFound(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
nonexistentId := happydns.Identifier([]byte("nonexistent-domain"))
|
||||
|
||||
_, err := service.GetUserDomain(user, nonexistentId)
|
||||
|
|
@ -299,10 +306,11 @@ func Test_GetUserDomain_NotFound(t *testing.T) {
|
|||
|
||||
func Test_GetUserDomainByFQDN(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -331,10 +339,11 @@ func Test_GetUserDomainByFQDN(t *testing.T) {
|
|||
|
||||
func Test_ListUserDomains(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create multiple domains
|
||||
domainNames := []string{"example1.com", "example2.com", "example3.com"}
|
||||
|
|
@ -362,12 +371,13 @@ func Test_ListUserDomains(t *testing.T) {
|
|||
|
||||
func Test_ListUserDomains_MultipleUsers(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
providerId1 := createTestProvider(t, mem, user1, "Provider 1")
|
||||
providerId2 := createTestProvider(t, mem, user2, "Provider 2")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
providerId1 := createTestProvider(t, db, user1, "Provider 1")
|
||||
providerId2 := createTestProvider(t, db, user2, "Provider 2")
|
||||
|
||||
// Create domains for user1
|
||||
for i := 1; i <= 2; i++ {
|
||||
|
|
@ -412,9 +422,10 @@ func Test_ListUserDomains_MultipleUsers(t *testing.T) {
|
|||
|
||||
func Test_ListUserDomains_Empty(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// List domains (should be empty)
|
||||
domains, err := service.ListUserDomains(user)
|
||||
|
|
@ -429,10 +440,11 @@ func Test_ListUserDomains_Empty(t *testing.T) {
|
|||
|
||||
func Test_UpdateDomain(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, logAppender := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, logAppender := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -480,10 +492,11 @@ func Test_UpdateDomain(t *testing.T) {
|
|||
|
||||
func Test_UpdateDomain_PreventIdChange(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -521,11 +534,12 @@ func Test_UpdateDomain_PreventIdChange(t *testing.T) {
|
|||
|
||||
func Test_UpdateDomain_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
providerId := createTestProvider(t, mem, user1, "Test Provider")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
providerId := createTestProvider(t, db, user1, "Test Provider")
|
||||
|
||||
// Create a domain for user1
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -555,10 +569,11 @@ func Test_UpdateDomain_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_DeleteDomain(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
@ -595,10 +610,11 @@ func Test_DeleteDomain(t *testing.T) {
|
|||
|
||||
func Test_UpdateDomain_Alias(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
service, _ := setupTestService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
service, _ := setupTestService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
providerId := createTestProvider(t, mem, user, "Test Provider")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase/domain_log"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string) *happydns.User {
|
||||
func createTestUser(t *testing.T, store storage.Storage, email string) *happydns.User {
|
||||
user := &happydns.User{
|
||||
Id: happydns.Identifier([]byte("user-" + email)),
|
||||
Email: email,
|
||||
|
|
@ -41,7 +43,7 @@ func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string)
|
|||
return user
|
||||
}
|
||||
|
||||
func createTestDomain(t *testing.T, store *inmemory.InMemoryStorage, user *happydns.User, domainName string) *happydns.Domain {
|
||||
func createTestDomain(t *testing.T, store storage.Storage, user *happydns.User, domainName string) *happydns.Domain {
|
||||
domain := &happydns.Domain{
|
||||
Owner: user.Id,
|
||||
DomainName: domainName,
|
||||
|
|
@ -54,10 +56,11 @@ func createTestDomain(t *testing.T, store *inmemory.InMemoryStorage, user *happy
|
|||
|
||||
func Test_AppendDomainLog(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
log := happydns.NewDomainLog(user, happydns.LOG_INFO, "Test log entry")
|
||||
|
||||
|
|
@ -83,7 +86,7 @@ func Test_AppendDomainLog(t *testing.T) {
|
|||
}
|
||||
|
||||
// Verify log is stored in database
|
||||
logs, err := mem.ListDomainLogs(domain)
|
||||
logs, err := db.ListDomainLogs(domain)
|
||||
if err != nil {
|
||||
t.Fatalf("expected stored logs, got error: %v", err)
|
||||
}
|
||||
|
|
@ -97,10 +100,11 @@ func Test_AppendDomainLog(t *testing.T) {
|
|||
|
||||
func Test_ListDomainLogs(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
// Create multiple logs with different timestamps
|
||||
log1 := happydns.NewDomainLog(user, happydns.LOG_INFO, "First log")
|
||||
|
|
@ -150,11 +154,12 @@ func Test_ListDomainLogs(t *testing.T) {
|
|||
|
||||
func Test_ListDomainLogs_MultipleDomains(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain1 := createTestDomain(t, mem, user, "example.com")
|
||||
domain2 := createTestDomain(t, mem, user, "test.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain1 := createTestDomain(t, db, user, "example.com")
|
||||
domain2 := createTestDomain(t, db, user, "test.com")
|
||||
|
||||
// Create logs for domain1
|
||||
log1 := happydns.NewDomainLog(user, happydns.LOG_INFO, "Domain1 Log 1")
|
||||
|
|
@ -197,10 +202,11 @@ func Test_ListDomainLogs_MultipleDomains(t *testing.T) {
|
|||
|
||||
func Test_UpdateDomainLog(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
// Create a log
|
||||
log := happydns.NewDomainLog(user, happydns.LOG_INFO, "Original content")
|
||||
|
|
@ -235,10 +241,11 @@ func Test_UpdateDomainLog(t *testing.T) {
|
|||
|
||||
func Test_DeleteDomainLog(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
// Create multiple logs
|
||||
log1 := happydns.NewDomainLog(user, happydns.LOG_INFO, "Log 1")
|
||||
|
|
@ -274,10 +281,11 @@ func Test_DeleteDomainLog(t *testing.T) {
|
|||
|
||||
func Test_AppendDomainLog_DifferentLogLevels(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
levels := []int8{
|
||||
happydns.LOG_CRIT,
|
||||
|
|
@ -311,10 +319,11 @@ func Test_AppendDomainLog_DifferentLogLevels(t *testing.T) {
|
|||
|
||||
func Test_ListDomainLogs_EmptyDomain(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
logService := domainlog.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
logService := domainlog.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
domain := createTestDomain(t, mem, user, "example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
domain := createTestDomain(t, db, user, "example.com")
|
||||
|
||||
// List logs for a domain with no logs
|
||||
logs, err := logService.ListDomainLogs(domain)
|
||||
|
|
|
|||
|
|
@ -25,13 +25,15 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase/provider"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
"git.happydns.org/happyDomain/providers"
|
||||
)
|
||||
|
||||
func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string) *happydns.User {
|
||||
func createTestUser(t *testing.T, store storage.Storage, email string) *happydns.User {
|
||||
user := &happydns.User{
|
||||
Id: happydns.Identifier([]byte("user-" + email)),
|
||||
Email: email,
|
||||
|
|
@ -74,11 +76,12 @@ func (v *mockValidator) Validate(p *happydns.Provider) error {
|
|||
|
||||
func Test_CreateProvider(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
// Replace validator with mock to avoid actual DNS validation
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test DDNS Provider")
|
||||
|
||||
p, err := providerService.CreateProvider(user, msg)
|
||||
|
|
@ -97,7 +100,7 @@ func Test_CreateProvider(t *testing.T) {
|
|||
}
|
||||
|
||||
// Verify provider is stored in database
|
||||
stored, err := mem.GetProvider(p.Id)
|
||||
stored, err := db.GetProvider(p.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("expected stored provider, got error: %v", err)
|
||||
}
|
||||
|
|
@ -108,10 +111,11 @@ func Test_CreateProvider(t *testing.T) {
|
|||
|
||||
func Test_GetUserProvider(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a provider
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
|
|
@ -136,11 +140,12 @@ func Test_GetUserProvider(t *testing.T) {
|
|||
|
||||
func Test_GetUserProvider_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create a provider for user1
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "User1 Provider")
|
||||
|
|
@ -161,9 +166,10 @@ func Test_GetUserProvider_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_GetUserProvider_NotFound(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
nonexistentID := happydns.Identifier([]byte("nonexistent-id"))
|
||||
_, err := providerService.GetUserProvider(user, nonexistentID)
|
||||
|
|
@ -177,10 +183,11 @@ func Test_GetUserProvider_NotFound(t *testing.T) {
|
|||
|
||||
func Test_GetUserProviderMeta(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a provider
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider Meta")
|
||||
|
|
@ -205,10 +212,11 @@ func Test_GetUserProviderMeta(t *testing.T) {
|
|||
|
||||
func Test_ListUserProviders(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create multiple providers
|
||||
_, err := providerService.CreateProvider(user, createTestProviderMessage(t, "DDNSServer", "Provider 1"))
|
||||
|
|
@ -237,11 +245,12 @@ func Test_ListUserProviders(t *testing.T) {
|
|||
|
||||
func Test_ListUserProviders_MultipleUsers(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create providers for user1
|
||||
_, err := providerService.CreateProvider(user1, createTestProviderMessage(t, "DDNSServer", "User1 Provider 1"))
|
||||
|
|
@ -280,10 +289,11 @@ func Test_ListUserProviders_MultipleUsers(t *testing.T) {
|
|||
|
||||
func Test_UpdateProvider(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a provider
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Original comment")
|
||||
|
|
@ -312,10 +322,11 @@ func Test_UpdateProvider(t *testing.T) {
|
|||
|
||||
func Test_UpdateProvider_PreventIdChange(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a provider
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
|
|
@ -339,11 +350,12 @@ func Test_UpdateProvider_PreventIdChange(t *testing.T) {
|
|||
|
||||
func Test_UpdateProvider_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create a provider for user1
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "User1 Provider")
|
||||
|
|
@ -363,10 +375,11 @@ func Test_UpdateProvider_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_DeleteProvider(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
providerService := provider.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
providerService := provider.NewService(db)
|
||||
providerService.SetValidator(&mockValidator{})
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a provider
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
|
|
@ -426,12 +439,13 @@ func Test_ParseProvider_InvalidType(t *testing.T) {
|
|||
|
||||
func Test_RestrictedService_CreateProvider_Disabled(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
config := &happydns.Options{
|
||||
DisableProviders: true,
|
||||
}
|
||||
providerService := provider.NewRestrictedService(config, mem)
|
||||
providerService := provider.NewRestrictedService(config, db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
|
||||
_, err := providerService.CreateProvider(user, msg)
|
||||
|
|
@ -445,11 +459,12 @@ func Test_RestrictedService_CreateProvider_Disabled(t *testing.T) {
|
|||
|
||||
func Test_RestrictedService_UpdateProvider_Disabled(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
// First create a provider without restrictions
|
||||
unrestricted := provider.NewService(mem)
|
||||
unrestricted := provider.NewService(db)
|
||||
unrestricted.SetValidator(&mockValidator{})
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
createdProvider, err := unrestricted.CreateProvider(user, msg)
|
||||
if err != nil {
|
||||
|
|
@ -460,7 +475,7 @@ func Test_RestrictedService_UpdateProvider_Disabled(t *testing.T) {
|
|||
config := &happydns.Options{
|
||||
DisableProviders: true,
|
||||
}
|
||||
restrictedService := provider.NewRestrictedService(config, mem)
|
||||
restrictedService := provider.NewRestrictedService(config, db)
|
||||
|
||||
err = restrictedService.UpdateProvider(createdProvider.Id, user, func(p *happydns.Provider) {
|
||||
p.Comment = "Updated"
|
||||
|
|
@ -475,11 +490,12 @@ func Test_RestrictedService_UpdateProvider_Disabled(t *testing.T) {
|
|||
|
||||
func Test_RestrictedService_DeleteProvider_Disabled(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
|
||||
// First create a provider without restrictions
|
||||
unrestricted := provider.NewService(mem)
|
||||
unrestricted := provider.NewService(db)
|
||||
unrestricted.SetValidator(&mockValidator{})
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
msg := createTestProviderMessage(t, "DDNSServer", "Test Provider")
|
||||
createdProvider, err := unrestricted.CreateProvider(user, msg)
|
||||
if err != nil {
|
||||
|
|
@ -490,7 +506,7 @@ func Test_RestrictedService_DeleteProvider_Disabled(t *testing.T) {
|
|||
config := &happydns.Options{
|
||||
DisableProviders: true,
|
||||
}
|
||||
restrictedService := provider.NewRestrictedService(config, mem)
|
||||
restrictedService := provider.NewRestrictedService(config, db)
|
||||
|
||||
err = restrictedService.DeleteProvider(user, createdProvider.Id)
|
||||
if err == nil {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
"git.happydns.org/happyDomain/internal/usecase/session"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string) *happydns.User {
|
||||
func createTestUser(t *testing.T, store storage.Storage, email string) *happydns.User {
|
||||
user := &happydns.User{
|
||||
Id: happydns.Identifier([]byte("user-" + email)),
|
||||
Email: email,
|
||||
|
|
@ -43,9 +45,10 @@ func createTestUser(t *testing.T, store *inmemory.InMemoryStorage, email string)
|
|||
|
||||
func Test_CreateUserSession(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
sess, err := sessionService.CreateUserSession(user, "Test session")
|
||||
if err != nil {
|
||||
|
|
@ -69,7 +72,7 @@ func Test_CreateUserSession(t *testing.T) {
|
|||
}
|
||||
|
||||
// Verify session is stored in database
|
||||
stored, err := mem.GetSession(sess.Id)
|
||||
stored, err := db.GetSession(sess.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("expected stored session, got error: %v", err)
|
||||
}
|
||||
|
|
@ -80,9 +83,10 @@ func Test_CreateUserSession(t *testing.T) {
|
|||
|
||||
func Test_GetUserSession(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a session
|
||||
createdSession, err := sessionService.CreateUserSession(user, "Test session")
|
||||
|
|
@ -106,10 +110,11 @@ func Test_GetUserSession(t *testing.T) {
|
|||
|
||||
func Test_GetUserSession_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create a session for user1
|
||||
createdSession, err := sessionService.CreateUserSession(user1, "User1 session")
|
||||
|
|
@ -129,9 +134,10 @@ func Test_GetUserSession_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_GetUserSession_NotFound(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
_, err := sessionService.GetUserSession(user, "nonexistent-session-id")
|
||||
if err == nil {
|
||||
|
|
@ -144,9 +150,10 @@ func Test_GetUserSession_NotFound(t *testing.T) {
|
|||
|
||||
func Test_ListUserSessions(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create multiple sessions
|
||||
_, err := sessionService.CreateUserSession(user, "Session 1")
|
||||
|
|
@ -175,10 +182,11 @@ func Test_ListUserSessions(t *testing.T) {
|
|||
|
||||
func Test_ListUserSessions_MultipleUsers(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create sessions for user1
|
||||
_, err := sessionService.CreateUserSession(user1, "User1 Session 1")
|
||||
|
|
@ -217,9 +225,10 @@ func Test_ListUserSessions_MultipleUsers(t *testing.T) {
|
|||
|
||||
func Test_UpdateUserSession(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a session
|
||||
createdSession, err := sessionService.CreateUserSession(user, "Original description")
|
||||
|
|
@ -250,9 +259,10 @@ func Test_UpdateUserSession(t *testing.T) {
|
|||
|
||||
func Test_UpdateUserSession_PreventIdChange(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a session
|
||||
createdSession, err := sessionService.CreateUserSession(user, "Test session")
|
||||
|
|
@ -274,10 +284,11 @@ func Test_UpdateUserSession_PreventIdChange(t *testing.T) {
|
|||
|
||||
func Test_UpdateUserSession_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create a session for user1
|
||||
createdSession, err := sessionService.CreateUserSession(user1, "User1 session")
|
||||
|
|
@ -296,9 +307,10 @@ func Test_UpdateUserSession_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_DeleteUserSession(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a session
|
||||
createdSession, err := sessionService.CreateUserSession(user, "Test session")
|
||||
|
|
@ -324,10 +336,11 @@ func Test_DeleteUserSession(t *testing.T) {
|
|||
|
||||
func Test_DeleteUserSession_WrongUser(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create a session for user1
|
||||
createdSession, err := sessionService.CreateUserSession(user1, "User1 session")
|
||||
|
|
@ -350,9 +363,10 @@ func Test_DeleteUserSession_WrongUser(t *testing.T) {
|
|||
|
||||
func Test_CloseUserSessions(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create multiple sessions
|
||||
_, err := sessionService.CreateUserSession(user, "Session 1")
|
||||
|
|
@ -386,10 +400,11 @@ func Test_CloseUserSessions(t *testing.T) {
|
|||
|
||||
func Test_CloseUserSessions_MultipleUsers(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user1 := createTestUser(t, mem, "user1@example.com")
|
||||
user2 := createTestUser(t, mem, "user2@example.com")
|
||||
user1 := createTestUser(t, db, "user1@example.com")
|
||||
user2 := createTestUser(t, db, "user2@example.com")
|
||||
|
||||
// Create sessions for both users
|
||||
_, err := sessionService.CreateUserSession(user1, "User1 Session")
|
||||
|
|
@ -439,14 +454,15 @@ func (u testUserInfo) JoinNewsletter() bool { return false }
|
|||
|
||||
func Test_CloseAll_UserInfoInterface(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
userID := happydns.Identifier([]byte("user-123"))
|
||||
user := &happydns.User{
|
||||
Id: userID,
|
||||
Email: "test@example.com",
|
||||
}
|
||||
if err := mem.CreateOrUpdateUser(user); err != nil {
|
||||
if err := db.CreateOrUpdateUser(user); err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -479,14 +495,15 @@ func Test_CloseAll_UserInfoInterface(t *testing.T) {
|
|||
|
||||
func Test_ByID(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
userID := happydns.Identifier([]byte("user-123"))
|
||||
user := &happydns.User{
|
||||
Id: userID,
|
||||
Email: "test@example.com",
|
||||
}
|
||||
if err := mem.CreateOrUpdateUser(user); err != nil {
|
||||
if err := db.CreateOrUpdateUser(user); err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -534,9 +551,10 @@ func Test_NewSessionID(t *testing.T) {
|
|||
|
||||
func Test_SessionExpiration(t *testing.T) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
sessionService := session.NewService(mem)
|
||||
db, _ := kv.NewKVDatabase(mem)
|
||||
sessionService := session.NewService(db)
|
||||
|
||||
user := createTestUser(t, mem, "test@example.com")
|
||||
user := createTestUser(t, db, "test@example.com")
|
||||
|
||||
// Create a session
|
||||
sess, err := sessionService.CreateUserSession(user, "Test session")
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import (
|
|||
"bytes"
|
||||
"testing"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/storage/inmemory"
|
||||
kv "git.happydns.org/happyDomain/internal/storage/kvtpl"
|
||||
authuserUC "git.happydns.org/happyDomain/internal/usecase/authuser"
|
||||
sessionUC "git.happydns.org/happyDomain/internal/usecase/session"
|
||||
"git.happydns.org/happyDomain/internal/usecase/user"
|
||||
|
|
@ -70,8 +72,9 @@ func (u testUserInfo) GetUserId() happydns.Identifier { return u.id }
|
|||
func (u testUserInfo) GetEmail() string { return u.email }
|
||||
func (u testUserInfo) JoinNewsletter() bool { return u.joinNewsletter }
|
||||
|
||||
func createTestService(t *testing.T) (*user.Service, *inmemory.InMemoryStorage, *mockNewsletterSubscriptor, *mockSessionCloser) {
|
||||
mem, err := inmemory.NewInMemoryStorage()
|
||||
func createTestService(t *testing.T) (*user.Service, storage.Storage, *mockNewsletterSubscriptor, *mockSessionCloser) {
|
||||
mem, _ := inmemory.NewInMemoryStorage()
|
||||
db, err := kv.NewKVDatabase(mem)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create in-memory storage: %v", err)
|
||||
}
|
||||
|
|
@ -79,15 +82,15 @@ func createTestService(t *testing.T) (*user.Service, *inmemory.InMemoryStorage,
|
|||
cfg := &happydns.Options{
|
||||
DisableRegistration: false,
|
||||
}
|
||||
sessionService := sessionUC.NewService(mem)
|
||||
authUserService := authuserUC.NewAuthUserUsecases(cfg, nil, mem, sessionService)
|
||||
sessionService := sessionUC.NewService(db)
|
||||
authUserService := authuserUC.NewAuthUserUsecases(cfg, nil, db, sessionService)
|
||||
|
||||
newsletter := &mockNewsletterSubscriptor{}
|
||||
sessionCloser := &mockSessionCloser{}
|
||||
|
||||
service := user.NewUserUsecases(mem, newsletter, authUserService, sessionCloser)
|
||||
service := user.NewUserUsecases(db, newsletter, authUserService, sessionCloser)
|
||||
|
||||
return service, mem, newsletter, sessionCloser
|
||||
return service, db, newsletter, sessionCloser
|
||||
}
|
||||
|
||||
func Test_CreateUser(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ var (
|
|||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrUserAlreadyExist = errors.New("user already exists")
|
||||
ErrZoneNotFound = errors.New("zone not found")
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
const TryAgainErr = "Sorry, we are currently unable to sent email validation link. Please try again later."
|
||||
|
|
|
|||
|
|
@ -39,9 +39,13 @@ type Iterator[T any] interface {
|
|||
// Must be called only after a successful call to Next().
|
||||
DropItem() error
|
||||
|
||||
// Key returns the storage key of the current item.
|
||||
// Should be called only after a successful call to Next().
|
||||
Key() string
|
||||
|
||||
// Raw returns the raw (non-decoded) value at the current iterator position.
|
||||
// Should only be called after a successful call to Next().
|
||||
Raw() []byte
|
||||
Raw() interface{}
|
||||
|
||||
// Err returns the first error encountered during iteration, if any.
|
||||
Err() error
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue