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)"