Skip to content
Snippets Groups Projects
Select Git revision
  • ba8f7f28f9becd76db3994923d6d4ab8951b2401
  • master default protected
  • a
  • bug-reporter
  • animation-change
  • challengedialog
  • ophase2019
7 results

orm.go

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    orm.go 7.59 KiB
    package wtfd
    
    import (
    	"errors"
    	"fmt"
    	"github.com/go-xorm/xorm"
    	_ "github.com/mattn/go-sqlite3" // needed for xorm
    	"golang.org/x/crypto/bcrypt"
    	"os"
    	"xorm.io/core"
    )
    
    const (
    	_ORMUserName            = "User"            // Table name for db
    	_ORMChallengeByUserName = "ChallengeByUser" // Table name for db
    )
    
    var (
    	engine *xorm.Engine
    )
    
    ////////////////////////////////////////////////////////////////////////////////
    // ORM definitions /////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
    type _ORMUser struct {
    	Name        string `xorm:"unique"`
    	DisplayName string `xorm:"unique"`
    	Hash        []byte
    	Points      int
    }
    
    type _ORMChallengesByUser struct {
    	UserName      string // Foregin keys don't exist
    	ChallengeName string
    }
    
    func (u _ORMUser) TableName() string {
    	return _ORMUserName
    }
    
    func (c _ORMChallengesByUser) TableName() string {
    	return _ORMChallengeByUserName
    }
    
    func ormSync() {
    	_ = engine.Sync(_ORMUser{})
    	_ = engine.Sync(_ORMChallengesByUser{})
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    
    // Login checks if password is right for username and returns the User object of it
    func Login(username, passwd string) error {
    	user, err := Get(username)
    	if err != nil {
    		return err
    	}
    	if pwdRight := user.ComparePassword(passwd); !pwdRight {
    		return errWrongPassword
    	}
    	fmt.Printf("User login: %s\n", username)
    	return nil
    
    }
    
    // NewUser creates a new user object
    func NewUser(name, password, displayname string) (User, error) {
    	if Contains(name, displayname) {
    		return User{}, errUserExisting
    	}
    	hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    	if err != nil {
    		return User{}, err
    	}
    
    	fmt.Printf("New User added: %s\n", name)
    	return User{Name: name, Hash: hash, DisplayName: displayname}, nil
    
    }
    
    // Contains looks if a username is in the datenbank
    func Contains(username, displayname string) bool {
    	count, _ := ormUserExists(User{Name: username, DisplayName: displayname})
    	return count
    }
    
    // Get gets username based on username
    func Get(username string) (User, error) {
    	user, err := ormLoadUser(username)
    	if err != nil {
    		fmt.Printf("Get Error: username: %v, user: %v, err: %v\n", username, user, err)
    		return User{}, err
    	}
    	return user, err
    
    }
    
    func ormStart(logFile string) error {
    	var err error
    	engine, err = xorm.NewEngine("sqlite3", "./state.db")
    
    	if err != nil {
    		return err
    	}
    
    	if logFile != "" {
    		f, err := os.Create(logFile)
    		if err != nil {
    			return err
    		}
    		engine.SetLogger(xorm.NewSimpleLogger(f))
    	}
    
    	engine.SetMapper(core.SameMapper{})
    
    	ormSync()
    	return nil
    }
    
    //noinspection GoUnusedFunction
    func _ORMGenericError(desc string) error {
    	return fmt.Errorf("ORM Error %s", desc)
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // DB Operations ///////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
    
    // Create new User DB record from a User struct
    func ormNewUser(user User) error {
    	exists, err := ormUserExists(user)
    	if err != nil {
    		return err
    	}
    
    	if exists {
    		return errUserExisting
    	}
    
    	_, err = engine.Insert(_ORMUser{
    		Name:        user.Name,
    		Hash:        user.Hash,
    		DisplayName: user.DisplayName,
    		Points:      0,
    	})
    
    	return err
    }
    
    // ormGetSolveCount returns the number of solves for the Challenge chall
    func ormGetSolveCount(chall Challenge) int64 {
    
    	count, err := engine.Where("ChallengeName = ?", chall.Name).Count(_ORMChallengesByUser{})
    	if err != nil {
    		return 0
    	}
    	return count
    
    }
    
    // Update existing user record (user.Name) with other values from user
    // Solved challenges WON'T be updated (refer to ormChallengeSolved)
    func ormUpdateUser(user User) error {
    	var exists bool
    	var err error
    	var u _ORMUser
    
    	if exists, err = ormUserExists(user); err != nil {
    		return err
    	}
    
    	if !exists {
    		return errUserNotExisting
    	}
    
    	u = _ORMUser{
    		Name:        user.Name,
    		Hash:        user.Hash,
    		DisplayName: user.DisplayName,
    		Points:      user.Points,
    	}
    
    	if _, err = engine.Where("Name = ?", user.Name).Update(&u); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // remove user from db (matches user.Name)
    func ormDeleteUser(user User) error {
    	var u _ORMUser
    	var exists bool
    	var err error
    
    	if exists, err = ormUserExists(user); err != nil {
    		return err
    	}
    
    	if !exists {
    		return errUserNotExisting
    	}
    
    	if _, err = engine.Where("Name = ?", user.Name).Get(&u); err != nil {
    		return err
    	}
    
    	if _, err = engine.Where("Name = ?", user.Name).Delete(&u); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // check if user exists in db
    func ormUserExists(user User) (bool, error) {
    	count, err := engine.Where("Name = ?", user.Name).Count(_ORMUser{})
    	//fmt.Printf("ormUserExists: user: %v, count: %v, err: %v\n", user, count, err)
    	if err != nil {
    		return false, err
    	}
    
    	if count == 0 {
    		return false, nil
    	}
    	if count == 1 {
    		return true, nil
    	}
    	return false, errors.New("DB User-table is in an invalid state")
    
    }
    
    func ormDisplayNameExists(name string) (bool, error) {
    	count, err := engine.Where("DisplayName = ?", name).Count(_ORMUser{})
    	//fmt.Printf("ormUserExists: user: %v, count: %v, err: %v\n", user, count, err)
    	if err != nil {
    		return false, err
    	}
    
    	if count == 0 {
    		return false, nil
    	}
    	if count == 1 {
    		return true, nil
    	}
    	return false, errors.New("DB User-table is in an invalid state")
    
    }
    
    // get Challenges{} solved by user (db-state)
    func ormChallengesSolved(user User) ([]*Challenge, error) {
    	var challenges []*Challenge
    
    	_ = engine.Where("UserName = ?", user.Name).Iterate(_ORMChallengesByUser{}, func(i int, bean interface{}) error {
    		relation := bean.(*_ORMChallengesByUser)
    
    		for _, c := range challs {
    			if c.Name == relation.ChallengeName {
    				challenges = append(challenges, c)
    			}
    		}
    
    		return nil
    	})
    
    	return challenges, nil
    }
    
    // Write solved state (user solved chall) in db
    func ormSolvedChallenge(user User, chall *Challenge) error {
    	var exists bool
    	var err error
    	var relation _ORMChallengesByUser
    	var count int64
    
    	if exists, err = ormUserExists(user); err != nil {
    		return err
    	}
    
    	if !exists {
    		return errUserNotExisting
    	}
    
    	count, err = engine.Where("UserName = ?", user.Name).And("ChallengeName = ?", chall.Name).Count(_ORMChallengesByUser{})
    	if err != nil {
    		return nil
    	}
    
    	if count == 1 {
    		return errors.New("user already solved this challenge")
    	} else if count > 1 {
    		return errors.New("DB-State Error: User solved this challenge multiple times")
    	}
    
    	relation = _ORMChallengesByUser{
    		ChallengeName: chall.Name,
    		UserName:      user.Name,
    	}
    
    	_, err = engine.Insert(relation)
    
    	return err
    }
    
    func ormAllUsersSortedByPoints() ([]_ORMUser, error) {
    	var a []_ORMUser
    	err := engine.Desc("Points").Find(&a)
    	if err != nil {
    		return a, err
    
    	}
    	return a, nil
    
    }
    
    // load a single user from db (search by name)
    // The remaining fields of u will be filled by this function
    func ormLoadUser(name string) (User, error) {
    	var user _ORMUser
    	var u User
    
    	exists, err := ormUserExists(User{Name: name})
    	// fmt.Printf("ormLoadUser: name: %v, exists: %v, err: %v\n",name, exists,err)
    	if err != nil {
    		return User{}, err
    	}
    
    	if !exists {
    		return User{}, errUserNotExisting
    	}
    
    	if _, err := engine.Where("Name = ?", name).Get(&user); err != nil {
    		// fmt.Printf("User %s seems to not exist", name)
    		return User{}, err
    	}
    
    	u = User{
    		Name:        user.Name,
    		Hash:        user.Hash,
    		DisplayName: user.DisplayName,
    		Points:      user.Points,
    	}
    
    	if u.Completed, err = ormChallengesSolved(u); err != nil {
    		return u, err
    	}
    
    	return u, nil
    }
    
    ////////////////////////////////////////////////////////////////////////////////