diff options
-rw-r--r-- | account/addtoken.go | 56 | ||||
-rw-r--r-- | account/adduser.go | 43 | ||||
-rw-r--r-- | account/getid.go | 33 | ||||
-rw-r--r-- | account/login.go | 50 | ||||
-rw-r--r-- | account/register.go | 49 | ||||
-rw-r--r-- | account/user.go (renamed from user/user.go) | 3 | ||||
-rw-r--r-- | auth/genid.go | 14 | ||||
-rw-r--r-- | auth/hashpass.go | 13 | ||||
-rw-r--r-- | build/ci/drone.yml | 6 | ||||
-rw-r--r-- | cmd/perseus/main.go | 13 | ||||
-rw-r--r-- | core/version.go | 6 | ||||
-rw-r--r-- | handler/web/login.go | 81 | ||||
-rw-r--r-- | handler/web/page.go | 7 | ||||
-rw-r--r-- | handler/web/register.go | 80 | ||||
-rw-r--r-- | password/check.go (renamed from auth/checkpass.go) | 11 | ||||
-rw-r--r-- | password/check_test.go (renamed from auth/checkpass_test.go) | 26 | ||||
-rw-r--r-- | password/hash.go | 11 | ||||
-rw-r--r-- | password/hash_test.go (renamed from auth/hashpass_test.go) | 18 | ||||
-rw-r--r-- | password/randstr.go | 13 | ||||
-rw-r--r-- | storage/init.go (renamed from storage/sqlite3/init.go) | 15 | ||||
-rw-r--r-- | storage/sqlite3/db.go | 13 | ||||
-rw-r--r-- | storage/storage.go | 16 | ||||
-rw-r--r-- | user/adduser.go | 42 | ||||
-rw-r--r-- | user/getid.go | 29 | ||||
-rw-r--r-- | web/templates/login.html (renamed from web/login.html) | 2 | ||||
-rw-r--r-- | web/templates/register.html (renamed from web/register.html) | 2 |
26 files changed, 475 insertions, 177 deletions
diff --git a/account/addtoken.go b/account/addtoken.go new file mode 100644 index 0000000..1c36ad8 --- /dev/null +++ b/account/addtoken.go @@ -0,0 +1,56 @@ +package account + +import ( + "log" + "time" + + "tildegit.org/andinus/perseus/password" + "tildegit.org/andinus/perseus/storage" +) + +// addToken will generate a random token, add it to database and +// return the token. +func (u *User) addToken(db *storage.DB) error { + u.Token = password.RandStr(64) + + // Set user id from username. + err := u.GetID(db) + if err != nil { + log.Printf("account/addtoken.go: %s\n", + "failed to get id from username") + return err + } + + // Acquire write lock on the database. + db.Mu.Lock() + defer db.Mu.Unlock() + + // Start the transaction + tx, err := db.Conn.Begin() + defer tx.Rollback() + if err != nil { + log.Printf("account/addtoken.go: %s\n", + "failed to begin transaction") + return err + } + + stmt, err := db.Conn.Prepare(` +INSERT INTO access(id, token, genTime) values(?, ?, ?)`) + if err != nil { + log.Printf("account/addtoken.go: %s\n", + "failed to prepare statement") + return err + } + defer stmt.Close() + + _, err = stmt.Exec(u.ID, u.Token, time.Now().UTC()) + if err != nil { + log.Printf("account/addtoken.go: %s\n", + "failed to execute statement") + return err + } + + tx.Commit() + return err + +} diff --git a/account/adduser.go b/account/adduser.go new file mode 100644 index 0000000..8a8734f --- /dev/null +++ b/account/adduser.go @@ -0,0 +1,43 @@ +package account + +import ( + "log" + "time" + + "tildegit.org/andinus/perseus/storage" +) + +// addUser adds the user to record. +func (u *User) addUser(db *storage.DB) error { + // Acquire write lock on the database. + db.Mu.Lock() + defer db.Mu.Unlock() + + // Start the transaction + tx, err := db.Conn.Begin() + defer tx.Rollback() + if err != nil { + log.Printf("account/adduser.go: %s\n", + "failed to begin transaction") + return err + } + + stmt, err := db.Conn.Prepare(` +INSERT INTO accounts(id, username, hash, regTime) values(?, ?, ?, ?)`) + if err != nil { + log.Printf("account/adduser.go: %s\n", + "failed to prepare statement") + return err + } + defer stmt.Close() + + _, err = stmt.Exec(u.ID, u.Username, u.Hash, time.Now().UTC()) + if err != nil { + log.Printf("account/adduser.go: %s\n", + "failed to execute statement") + return err + } + + tx.Commit() + return err +} diff --git a/account/getid.go b/account/getid.go new file mode 100644 index 0000000..4f6da69 --- /dev/null +++ b/account/getid.go @@ -0,0 +1,33 @@ +package account + +import ( + "log" + + "tildegit.org/andinus/perseus/storage" +) + +// GetID returns id from username. +func (u *User) GetID(db *storage.DB) error { + // Acquire read lock on database. + db.Mu.RLock() + defer db.Mu.RUnlock() + + // Get password for this user from the database. + stmt, err := db.Conn.Prepare("SELECT id FROM accounts WHERE username = ?") + if err != nil { + log.Printf("account/getid.go: %s\n", + "failed to prepare statement") + return err + } + defer stmt.Close() + + var id string + err = stmt.QueryRow(u.Username).Scan(&id) + if err != nil { + log.Printf("account/getid.go: %s\n", + "query failed") + } + u.ID = id + + return err +} diff --git a/account/login.go b/account/login.go new file mode 100644 index 0000000..c81fcbd --- /dev/null +++ b/account/login.go @@ -0,0 +1,50 @@ +package account + +import ( + "log" + + "tildegit.org/andinus/perseus/password" + "tildegit.org/andinus/perseus/storage" +) + +// Login takes in login details and returns an error. If error doesn't +// equal nil then consider login failed. It will also set the u.Token +// field. +func (u *User) Login(db *storage.DB) error { + // Acquire read lock on the database. + db.Mu.RLock() + + // Get password for this user from the database. + stmt, err := db.Conn.Prepare("SELECT hash FROM accounts WHERE username = ?") + if err != nil { + log.Printf("account/login.go: %s\n", + "failed to prepare statement") + return err + } + defer stmt.Close() + + var hash string + err = stmt.QueryRow(u.Username).Scan(&hash) + if err != nil { + log.Printf("account/login.go: %s\n", + "query failed") + return err + } + u.Hash = hash + + // Check user's password. + err = password.Check(u.Password, u.Hash) + if err != nil { + log.Printf("account/login.go: %s%s\n", + "user login failed, username: ", u.Username) + return err + } + db.Mu.RUnlock() + + err = u.addToken(db) + if err != nil { + log.Printf("account/login.go: %s\n", + "addtoken failed") + } + return err +} diff --git a/account/register.go b/account/register.go new file mode 100644 index 0000000..e008370 --- /dev/null +++ b/account/register.go @@ -0,0 +1,49 @@ +package account + +import ( + "errors" + "log" + "regexp" + "strings" + + "tildegit.org/andinus/perseus/password" + "tildegit.org/andinus/perseus/storage" +) + +// Register takes in registration details and returns an error. If +// error doesn't equal nil then the registration was unsuccessful. +func (u User) Register(db *storage.DB) error { + var err error + u.ID = password.RandStr(64) + u.Username = strings.ToLower(u.Username) + + // Validate username. It must be alphanumeric and less than + // 128 characters. + re := regexp.MustCompile("^[a-zA-Z0-9]*$") + if !re.MatchString(u.Username) { + return errors.New("account/register.go: invalid username") + } + if len(u.Username) > 128 { + return errors.New("account/register.go: username too long") + } + + // Validate password + if len(u.Password) < 8 { + return errors.New("account/register.go: password too short") + } + + u.Hash, err = password.Hash(u.Password) + if err != nil { + log.Printf("account/register.go: %s\n", + "password.Hash func failed") + return err + } + + err = u.addUser(db) + if err != nil { + log.Printf("account/register.go: %s\n", + "addUser func failed") + } + return err + +} diff --git a/user/user.go b/account/user.go index 078215e..769e8fc 100644 --- a/user/user.go +++ b/account/user.go @@ -1,4 +1,4 @@ -package user +package account // User holds information about the user. type User struct { @@ -6,4 +6,5 @@ type User struct { Username string Password string Hash string + Token string } diff --git a/auth/genid.go b/auth/genid.go deleted file mode 100644 index fea7ac9..0000000 --- a/auth/genid.go +++ /dev/null @@ -1,14 +0,0 @@ -package auth - -import ( - "crypto/rand" - "encoding/base64" -) - -// genID generates a random id string of length n. Don't forget to -// seed the random number generator otherwise it won't be random. -func genID(n int) string { - b := make([]byte, n/2) - rand.Read(b) - return base64.StdEncoding.EncodeToString(b) -} diff --git a/auth/hashpass.go b/auth/hashpass.go deleted file mode 100644 index 4e5041a..0000000 --- a/auth/hashpass.go +++ /dev/null @@ -1,13 +0,0 @@ -package auth - -import ( - "golang.org/x/crypto/bcrypt" -) - -// hashPass takes a string as input and returns the hash of the -// password. -func hashPass(password string) (string, error) { - // 10 is the default cost. - bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10) - return string(bytes), err -} diff --git a/build/ci/drone.yml b/build/ci/drone.yml index 6d68a3f..37590ac 100644 --- a/build/ci/drone.yml +++ b/build/ci/drone.yml @@ -6,12 +6,12 @@ steps: - name: vet image: golang:1.13 commands: - - go vet ./... + - go vet ./... - name: test image: golang:1.13 commands: - - go test -v ./auth + - go test -v ./password --- kind: pipeline @@ -24,4 +24,4 @@ steps: GOARCH: amd64 GOOS: openbsd commands: - - go build ./cmd/perseus + - go build ./cmd/perseus diff --git a/cmd/perseus/main.go b/cmd/perseus/main.go index 6abd3a4..ca1a50d 100644 --- a/cmd/perseus/main.go +++ b/cmd/perseus/main.go @@ -15,25 +15,24 @@ func main() { db := storage.Init() defer db.Conn.Close() - envPort, exists := os.LookupEnv("PERSEUS_PORT") - if !exists { + envPort := os.Getenv("PERSEUS_PORT") + if envPort == "" { envPort = "8080" } - addr := fmt.Sprintf("127.0.0.1:%s", envPort) srv := &http.Server{ - Addr: addr, + Addr: fmt.Sprintf("127.0.0.1:%s", envPort), WriteTimeout: 8 * time.Second, ReadTimeout: 8 * time.Second, } http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) { - web.HandleRegister(w, r, db) + web.RegisterHandler(w, r, db) }) http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { - web.HandleLogin(w, r, db) + web.LoginHandler(w, r, db) }) - log.Printf("main/main.go: listening on port %s...", envPort) + log.Printf("perseus: listening on port %s...", envPort) log.Fatal(srv.ListenAndServe()) } diff --git a/core/version.go b/core/version.go deleted file mode 100644 index 3e45c6f..0000000 --- a/core/version.go +++ /dev/null @@ -1,6 +0,0 @@ -package core - -// Version will return the current version. -func Version() string { - return "v0.1.0" -} diff --git a/handler/web/login.go b/handler/web/login.go new file mode 100644 index 0000000..3ad15a1 --- /dev/null +++ b/handler/web/login.go @@ -0,0 +1,81 @@ +package web + +import ( + "fmt" + "html/template" + "log" + "net/http" + "time" + + "tildegit.org/andinus/perseus/account" + "tildegit.org/andinus/perseus/storage" +) + +// LoginHandler handles login. +func LoginHandler(w http.ResponseWriter, r *http.Request, db *storage.DB) { + p := Page{} + var err error + + t, err := template.ParseFiles("web/templates/login.html") + if err != nil { + log.Printf("web/login.go: 500 Internal Server Error :: %s", err.Error()) + http.Error(w, "500 Internal Server Error", http.StatusInternalServerError) + return + } + + switch r.Method { + case http.MethodGet: + t.Execute(w, p) + + case http.MethodPost: + if err = r.ParseForm(); err != nil { + log.Printf("web/login.go: 400 Bad Request :: %s", err.Error()) + http.Error(w, "400 Bad Request", http.StatusBadRequest) + return + } + + // Get form values + u := account.User{} + u.Username = r.FormValue("username") + u.Password = r.FormValue("password") + + // Perform login + err = u.Login(db) + + if err != nil { + log.Printf("web/login.go: %s :: %s", + "login failed", + err.Error()) + + error := []string{} + error = append(error, + fmt.Sprintf("Login failed")) + + p.Error = error + t.Execute(w, p) + return + } + + // Login successful, set token + cookie := http.Cookie{ + Name: "token", + Value: u.Token, + // Expire the cookie after 16 days from + // current UTC time. + Expires: time.Now().UTC().Add(16 * 24 * time.Hour), + SameSite: http.SameSiteLaxMode, + HttpOnly: true, + } + http.SetCookie(w, &cookie) + success := []string{} + success = append(success, + fmt.Sprintf("Login successful")) + p.Success = success + t.Execute(w, p) + + default: + w.WriteHeader(http.StatusMethodNotAllowed) + log.Printf("web/login.go: %v not allowed on %v", r.Method, r.URL) + } + +} diff --git a/handler/web/page.go b/handler/web/page.go index 1f457de..91e8e56 100644 --- a/handler/web/page.go +++ b/handler/web/page.go @@ -1,11 +1,8 @@ package web -import ( - "html/template" -) +import "html/template" -// Page holds page information that is sent to all webpages rendered -// by perseus. +// Page holds page information. type Page struct { SafeList []template.HTML List []string diff --git a/handler/web/register.go b/handler/web/register.go new file mode 100644 index 0000000..1a80651 --- /dev/null +++ b/handler/web/register.go @@ -0,0 +1,80 @@ +package web + +import ( + "fmt" + "html/template" + "log" + "net/http" + "strings" + + "tildegit.org/andinus/perseus/account" + "tildegit.org/andinus/perseus/storage" +) + +// RegisterHandler handles registration. +func RegisterHandler(w http.ResponseWriter, r *http.Request, db *storage.DB) { + p := Page{} + var err error + + t, err := template.ParseFiles("web/templates/register.html") + if err != nil { + log.Printf("web/register.go: 500 Internal Server Error :: %s", err.Error()) + http.Error(w, "500 Internal Server Error", http.StatusInternalServerError) + return + } + + p.Notice = []string{ + "Only [a-z] & [0-9] allowed for username", + "Password length must be greater than 8 characters", + } + + switch r.Method { + case http.MethodGet: + t.Execute(w, p) + + case http.MethodPost: + if err = r.ParseForm(); err != nil { + log.Printf("web/register.go: 400 Bad Request :: %s", err.Error()) + http.Error(w, "400 Bad Request", http.StatusBadRequest) + return + } + + // Get form values + u := account.User{} + u.Username = r.FormValue("username") + u.Password = r.FormValue("password") + + // Perform registration + err = u.Register(db) + + if err != nil { + log.Printf("web/register.go: %s :: %s", + "registration failed", + err.Error()) + + error := []string{} + error = append(error, + fmt.Sprintf("Registration failed")) + + // Check if the error was because of username + // not being unique. + if strings.HasPrefix(err.Error(), "UNIQUE constraint failed") { + error = append(error, + fmt.Sprintf("Username not unique")) + } + p.Error = error + } else { + success := []string{} + success = append(success, + fmt.Sprintf("Registration successful")) + p.Success = success + } + + t.Execute(w, p) + + default: + w.WriteHeader(http.StatusMethodNotAllowed) + log.Printf("web/register.go: %v not allowed on %v", r.Method, r.URL) + } + +} diff --git a/auth/checkpass.go b/password/check.go index 64e3c9f..b84e9fe 100644 --- a/auth/checkpass.go +++ b/password/check.go @@ -1,14 +1,13 @@ -package auth +// Password package contains functions related to passwords. +package password -import ( - "golang.org/x/crypto/bcrypt" -) +import "golang.org/x/crypto/bcrypt" -// checkPass takes a string and hash as input and returns an error. If +// Check takes a string and hash as input and returns an error. If // the error is not nil then the consider the password wrong. We're // returning error instead of a bool so that we can print failed // logins to log and logging shouldn't happen here. -func checkPass(password, hash string) error { +func Check(password, hash string) error { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err } diff --git a/auth/checkpass_test.go b/password/check_test.go index c8afaee..e55c169 100644 --- a/auth/checkpass_test.go +++ b/password/check_test.go @@ -1,9 +1,9 @@ -package auth +package password import "testing" -// TestCheckPass tests the checkPass function. -func TestCheckPass(t *testing.T) { +// TestCheck tests the Check function. +func TestCheck(t *testing.T) { var err error passhash := make(map[string]string) @@ -13,24 +13,22 @@ func TestCheckPass(t *testing.T) { passhash["Z1S/kQ=="] = "$2a$10$fZ05kKmb7bh4vBLebpK1u.3bUNQ6eeX5ghT/GZaekgS.5bx4.Ru1e" passhash["J861dQ=="] = "$2a$10$nXb6Btn6n3AWMAUkDh9bFObvQw5V9FLKhfX.E1EzRWgVDuqIp99u2" - // We also check with values generated with hashPass, this may - // fail if hashPass itself fails in that case it's not - // checkPass error so the test shouldn't fail but warning - // should be sent. We use genID func to generate random inputs - // for this test. + // We also check with values generated with Hash, this may + // fail if Hash itself fails in that case it's not Check error + // so the test shouldn't fail but warning should be sent. We + // use genID func to generate random inputs for this test. for i := 1; i <= 4; i++ { - p := genID(8) - passhash[p], err = hashPass(p) + p := RandStr(8) + passhash[p], err = Hash(p) if err != nil { t.Log("hashPass func failed") } } - // We test the checkPass func by ranging over all values of - // passhash. We assume that hashPass func returns correct - // hashes. + // We test the Check func by ranging over all values of + // passhash. We assume that Hash func returns correct hashes. for p, h := range passhash { - err = checkPass(p, h) + err = Check(p, h) if err != nil { t.Errorf("password: %s, hash: %s didn't match.", p, h) diff --git a/password/hash.go b/password/hash.go new file mode 100644 index 0000000..3dd949b --- /dev/null +++ b/password/hash.go @@ -0,0 +1,11 @@ +package password + +import "golang.org/x/crypto/bcrypt" + +// Hash takes a string as input and returns the hash of the +// password. +func Hash(password string) (string, error) { + // 10 is the default cost. + out, err := bcrypt.GenerateFromPassword([]byte(password), 10) + return string(out), err +} diff --git a/auth/hashpass_test.go b/password/hash_test.go index 7ea9480..4f37393 100644 --- a/auth/hashpass_test.go +++ b/password/hash_test.go @@ -1,21 +1,21 @@ -package auth +package password import "testing" -// TestHashPass tests the checkPass function. -func TestHashPass(t *testing.T) { +// TestHash tests the Hash function. +func TestHash(t *testing.T) { var err error passhash := make(map[string]string) - // We generate random hashes with hashPass, random string is - // generate by genID func. + // We generate random hashes with Hash, random string is + // generate by RandStr func. for i := 1; i <= 8; i++ { - p := genID(8) - passhash[p], err = hashPass(p) + p := RandStr(8) + passhash[p], err = Hash(p) // Here we test if the hashPass func runs sucessfully. if err != nil { - t.Errorf("hashPass func failed for password: %s", + t.Errorf("Hash func failed for password: %s", p) } } @@ -24,7 +24,7 @@ func TestHashPass(t *testing.T) { // hashes. We assume that checkPass func returns correct // values. for p, h := range passhash { - err = checkPass(p, h) + err = Check(p, h) if err != nil { t.Errorf("password: %s, hash: %s didn't match.", p, h) diff --git a/password/randstr.go b/password/randstr.go new file mode 100644 index 0000000..86ae9a1 --- /dev/null +++ b/password/randstr.go @@ -0,0 +1,13 @@ +package password + +import ( + "crypto/rand" + "encoding/base64" +) + +// RandStr will return a random base64 encoded string of length n. +func RandStr(n int) string { + b := make([]byte, n/2) + rand.Read(b) + return base64.StdEncoding.EncodeToString(b) +} diff --git a/storage/sqlite3/init.go b/storage/init.go index e79d3ff..2103498 100644 --- a/storage/sqlite3/init.go +++ b/storage/init.go @@ -1,4 +1,4 @@ -package sqlite3 +package storage import ( "database/sql" @@ -17,8 +17,7 @@ func initErr(db *DB, err error) { log.Fatalf("Initialization Error :: %s", err.Error()) } -// Init initializes a sqlite3 database. -func Init(db *DB) { +func initDB(db *DB) { var err error // We set the database path, first the environment variable @@ -36,7 +35,7 @@ func Init(db *DB) { db.Conn, err = sql.Open("sqlite3", db.Path) if err != nil { log.Printf("sqlite3/init.go: %s\n", - "Failed to open database connection") + "failed to open database connection") initErr(db, err) } @@ -50,11 +49,11 @@ func Init(db *DB) { token TEXT NOT NULL, genTime TEXT NOT NULL);`, - `CREATE TABLE IF NOT EXISTS users ( + `CREATE TABLE IF NOT EXISTS accounts ( id TEXT PRIMARY KEY, type TEXT NOT NULL DEFAULT user, username VARCHAR(128) NOT NULL UNIQUE, - password TEXT NOT NULL, + hash TEXT NOT NULL, regTime TEXT NOT NULL);`, } @@ -67,7 +66,7 @@ func Init(db *DB) { if err != nil { log.Printf("sqlite3/init.go: %s\n", - "Failed to prepare statement") + "failed to prepare statement") log.Println(s) initErr(db, err) } @@ -76,7 +75,7 @@ func Init(db *DB) { stmt.Close() if err != nil { log.Printf("sqlite3/init.go: %s\n", - "Failed to execute statement") + "failed to execute statement") log.Println(s) initErr(db, err) } diff --git a/storage/sqlite3/db.go b/storage/sqlite3/db.go deleted file mode 100644 index e949bba..0000000 --- a/storage/sqlite3/db.go +++ /dev/null @@ -1,13 +0,0 @@ -package sqlite3 - -import ( - "database/sql" - "sync" -) - -// DB holds the database connection, mutex & path. -type DB struct { - Path string - Mu *sync.RWMutex - Conn *sql.DB -} diff --git a/storage/storage.go b/storage/storage.go index 24f770d..33dd29f 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,17 +1,23 @@ package storage import ( + "database/sql" "sync" - - "tildegit.org/andinus/perseus/storage/sqlite3" ) +// DB holds the database connection, mutex & path. +type DB struct { + Path string + Mu *sync.RWMutex + Conn *sql.DB +} + // Init initializes the database. -func Init() *sqlite3.DB { - var db sqlite3.DB = sqlite3.DB{ +func Init() *DB { + db := DB{ Mu: new(sync.RWMutex), } - sqlite3.Init(&db) + initDB(&db) return &db } diff --git a/user/adduser.go b/user/adduser.go deleted file mode 100644 index d1dbcde..0000000 --- a/user/adduser.go +++ /dev/null @@ -1,42 +0,0 @@ -package user - -import ( - "log" - "time" - - "tildegit.org/andinus/perseus/storage/sqlite3" -) - -// AddUser adds the user to record. -func (u User) AddUser(db *sqlite3.DB) error { - // Acquire write lock on the database. - db.Mu.Lock() - defer db.Mu.Unlock() - - // Start the transaction - tx, err := db.Conn.Begin() - if err != nil { - log.Printf("user/adduser.go: %s\n", - "failed to begin transaction") - return err - } - - usrStmt, err := db.Conn.Prepare(` -INSERT INTO users(id, username, password, regTime) values(?, ?, ?, ?)`) - if err != nil { - log.Printf("user/adduser.go: %s\n", - "failed to prepare statement") - return err - } - defer usrStmt.Close() - - _, err = usrStmt.Exec(u.ID, u.Username, u.Password, time.Now().UTC()) - if err != nil { - log.Printf("user/adduser.go: %s\n", - "failed to execute statement") - return err - } - - tx.Commit() - return err -} diff --git a/user/getid.go b/user/getid.go deleted file mode 100644 index 9cf8870..0000000 --- a/user/getid.go +++ /dev/null @@ -1,29 +0,0 @@ -package user - -import ( - "log" - - "tildegit.org/andinus/perseus/storage/sqlite3" -) - -// GetID returns id from username. -func (u *User) GetID(db *sqlite3.DB) error { - // Get password for this user from the database. - stmt, err := db.Conn.Prepare("SELECT id FROM users WHERE username = ?") - if err != nil { - log.Printf("user/getid.go: %s\n", - "failed to prepare statement") - return err - } - defer stmt.Close() - - var id string - err = stmt.QueryRow(u.Username).Scan(&id) - if err != nil { - log.Printf("user/getid.go: %s\n", - "query failed") - } - u.ID = id - - return err -} diff --git a/web/login.html b/web/templates/login.html index cd0d6a7..88024e3 100644 --- a/web/login.html +++ b/web/templates/login.html @@ -40,7 +40,7 @@ / <a href="https://andinus.nand.sh/perseus">Perseus</a> <span style="float:right"> - Perseus {{ .Version }} + Perseus {{ if .Version}} {{ . }} {{ end }} / <a href="https://tildegit.org/andinus/perseus"> Source Code diff --git a/web/register.html b/web/templates/register.html index c351ae1..998e012 100644 --- a/web/register.html +++ b/web/templates/register.html @@ -40,7 +40,7 @@ / <a href="https://andinus.nand.sh/perseus">Perseus</a> <span style="float:right"> - Perseus {{ .Version }} + Perseus {{ if .Version}} {{ . }} {{ end }} / <a href="https://tildegit.org/andinus/perseus"> Source Code |