github.com/readium/readium-lcp-server@v0.0.0-20240509124024-799e77a0bbd6/frontend/webuser/webuser.go (about)

     1  // Copyright 2020 Readium Foundation. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license
     3  // that can be found in the LICENSE file exposed on Github (readium) in the project repository.
     4  
     5  package webuser
     6  
     7  import (
     8  	"database/sql"
     9  	"errors"
    10  	"log"
    11  
    12  	"github.com/readium/readium-lcp-server/config"
    13  	"github.com/readium/readium-lcp-server/dbutils"
    14  	uuid "github.com/satori/go.uuid"
    15  )
    16  
    17  // ErrNotFound error trown when user is not found
    18  var ErrNotFound = errors.New("User not found")
    19  
    20  // WebUser interface for user db interaction
    21  type WebUser interface {
    22  	Get(id int64) (User, error)
    23  	GetByEmail(email string) (User, error)
    24  	Add(c User) error
    25  	Update(c User) error
    26  	DeleteUser(UserID int64) error
    27  	ListUsers(page int, pageNum int) func() (User, error)
    28  }
    29  
    30  // User struct defines a user
    31  type User struct {
    32  	ID       int64  `json:"id"`
    33  	UUID     string `json:"uuid"`
    34  	Name     string `json:"name,omitempty"`
    35  	Email    string `json:"email,omitempty"`
    36  	Password string `json:"password,omitempty"`
    37  	Hint     string `json:"hint"`
    38  }
    39  
    40  type dbUser struct {
    41  	db           *sql.DB
    42  	dbGetUser    *sql.Stmt
    43  	dbGetByEmail *sql.Stmt
    44  	dbList       *sql.Stmt
    45  }
    46  
    47  // Get returns a user
    48  func (user dbUser) Get(id int64) (User, error) {
    49  
    50  	row := user.dbGetUser.QueryRow(id)
    51  	var c User
    52  	err := row.Scan(&c.ID, &c.UUID, &c.Name, &c.Email, &c.Password, &c.Hint)
    53  	if err != nil {
    54  		return User{}, ErrNotFound
    55  	}
    56  	return c, err
    57  }
    58  
    59  // GetByEmail returns a user
    60  func (user dbUser) GetByEmail(email string) (User, error) {
    61  
    62  	row := user.dbGetByEmail.QueryRow(email)
    63  	var c User
    64  	err := row.Scan(&c.ID, &c.UUID, &c.Name, &c.Email, &c.Password, &c.Hint)
    65  	return c, err
    66  }
    67  
    68  // Add inserts a user
    69  func (user dbUser) Add(newUser User) error {
    70  
    71  	// Create uuid
    72  	uid, err_u := uuid.NewV4()
    73  	if err_u != nil {
    74  		return err_u
    75  	}
    76  	newUser.UUID = uid.String()
    77  
    78  	_, err := user.db.Exec(dbutils.GetParamQuery(config.Config.FrontendServer.Database,
    79  		"INSERT INTO \"user\" (uuid, name, email, password, hint) VALUES (?, ?, ?, ?, ?)"),
    80  		newUser.UUID, newUser.Name, newUser.Email, newUser.Password, newUser.Hint)
    81  	return err
    82  }
    83  
    84  // Update updates a user
    85  func (user dbUser) Update(changedUser User) error {
    86  
    87  	_, err := user.db.Exec(dbutils.GetParamQuery(config.Config.FrontendServer.Database,
    88  		"UPDATE \"user\" SET name=? , email=?, password=?, hint=? WHERE id=?"),
    89  		changedUser.Name, changedUser.Email, changedUser.Password, changedUser.Hint, changedUser.ID)
    90  	return err
    91  }
    92  
    93  // DeleteUser deletes a user
    94  func (user dbUser) DeleteUser(userID int64) error {
    95  
    96  	// delete user purchases
    97  	_, err := user.db.Exec(dbutils.GetParamQuery(config.Config.FrontendServer.Database, "DELETE FROM purchase WHERE user_id=?"), userID)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// delete user
   103  	_, err = user.db.Exec(dbutils.GetParamQuery(config.Config.FrontendServer.Database, "DELETE FROM \"user\" WHERE id=?"), userID)
   104  	return err
   105  }
   106  
   107  // ListUsers lists users
   108  func (user dbUser) ListUsers(page int, pageNum int) func() (User, error) {
   109  
   110  	var rows *sql.Rows
   111  	var err error
   112  	driver, _ := config.GetDatabase(config.Config.FrontendServer.Database)
   113  	if driver == "mssql" {
   114  		rows, err = user.dbList.Query(pageNum*page, page)
   115  	} else {
   116  		rows, err = user.dbList.Query(page, pageNum*page)
   117  	}
   118  	if err != nil {
   119  		return func() (User, error) { return User{}, err }
   120  	}
   121  
   122  	return func() (User, error) {
   123  		var u User
   124  		var err error
   125  		if rows.Next() {
   126  			err = rows.Scan(&u.ID, &u.UUID, &u.Name, &u.Email, &u.Password, &u.Hint)
   127  		} else {
   128  			rows.Close()
   129  			err = ErrNotFound
   130  		}
   131  		return u, err
   132  	}
   133  }
   134  
   135  // Open  returns a WebUser interface (db interaction)
   136  func Open(db *sql.DB) (i WebUser, err error) {
   137  
   138  	driver, _ := config.GetDatabase(config.Config.FrontendServer.Database)
   139  	// if sqlite, create the content table in the frontend db if it does not exist
   140  	if driver == "sqlite3" {
   141  		_, err = db.Exec(tableDef)
   142  		if err != nil {
   143  			log.Println("Error creating user table")
   144  			return
   145  		}
   146  	}
   147  
   148  	var dbGetUser *sql.Stmt
   149  	dbGetUser, err = db.Prepare(dbutils.GetParamQuery(config.Config.FrontendServer.Database,
   150  		"SELECT id, uuid, name, email, password, hint FROM \"user\" WHERE id = ?"))
   151  	if err != nil {
   152  		return
   153  	}
   154  
   155  	var dbGetByEmail *sql.Stmt
   156  	if driver == "mssql" {
   157  		dbGetByEmail, err = db.Prepare("SELECT TOP 1 id, uuid, name, email, password, hint FROM \"user\" WHERE email = ?")
   158  	} else {
   159  		dbGetByEmail, err = db.Prepare(dbutils.GetParamQuery(config.Config.FrontendServer.Database,
   160  			"SELECT id, uuid, name, email, password, hint FROM \"user\" WHERE email = ? LIMIT 1"))
   161  	}
   162  	if err != nil {
   163  		return
   164  	}
   165  
   166  	var dbList *sql.Stmt
   167  	if driver == "mssql" {
   168  		dbList, err = db.Prepare("SELECT id, uuid, name, email, password, hint	FROM \"user\" ORDER BY email desc OFFSET ? ROWS FETCH NEXT ? ROWS ONLY")
   169  	} else {
   170  		dbList, err = db.Prepare(dbutils.GetParamQuery(config.Config.FrontendServer.Database,
   171  			"SELECT id, uuid, name, email, password, hint	FROM \"user\" ORDER BY email desc LIMIT ? OFFSET ?"))
   172  	}
   173  	if err != nil {
   174  		return
   175  	}
   176  
   177  	i = dbUser{db, dbGetUser, dbGetByEmail, dbList}
   178  	return
   179  }
   180  
   181  const tableDef = "CREATE TABLE IF NOT EXISTS \"user\" (" +
   182  	"id integer NOT NULL PRIMARY KEY," +
   183  	"uuid varchar(255) NOT NULL," +
   184  	"name varchar(64) NOT NULL," +
   185  	"email varchar(64) NOT NULL," +
   186  	"password varchar(64) NOT NULL," +
   187  	"hint varchar(64) NOT NULL)"