1
0
Bifurcation 0

feat: implement user model + migrations

Cette révision appartient à :
Hayden Young 2021-03-21 19:32:31 +00:00
Parent 4b518d43ba
révision a2cf369d11
6 fichiers modifiés avec 109 ajouts et 0 suppressions

Voir le fichier

@ -28,3 +28,10 @@ URI from normal.
```env
DATABASE_URI="<username>:<password>@tcp(<host>:<port>)/<database>"
```
## Migrations
This repository uses the `migrate` CLI to run its migrations.
See https://github.com/golang-migrate/migrate/tree/master/cmd/migrate for an
installation guide.

1
go.mod
Voir le fichier

@ -6,4 +6,5 @@ require (
github.com/getsentry/sentry-go v0.10.0 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 // indirect
)

6
go.sum
Voir le fichier

@ -137,6 +137,8 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 h1:gzMM0EjIYiRmJI3+jBdFuoynZlpxa2JQZsolKu09BXo=
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -146,6 +148,7 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -155,8 +158,11 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

Voir le fichier

@ -0,0 +1 @@
DROP TABLE `users`;

Voir le fichier

@ -0,0 +1,28 @@
CREATE TABLE `users` (
id BINARY(16) NOT NULL UNIQUE DEFAULT UNHEX(REPLACE(UUID(), '-', '')),
username VARCHAR(32) NOT NULL UNIQUE,
email VARCHAR(320) NOT NULL UNIQUE,
password VARCHAR(60) NOT NULL,
level_id INT NOT NULL DEFAULT 0,
last_seen DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
website VARCHAR(2048),
biography TEXT,
views INT NOT NULL DEFAULT 0,
uploads INT NOT NULL DEFAULT 0,
premium BOOL NOT NULL DEFAULT false,
md_at_home BOOL NOT NULL DEFAULT false,
avatar_url VARCHAR(2048) NOT NULL,
joined_at DATETIME DEFAULT NOW(),
update_at DATETIME DEFAULT NOW() ON UPDATE NOW()
);
INSERT INTO `users` (username, email, password, website, biography, avatar_url)
VALUES (
'root',
'root@example.com',
'$2a$10$EMCB89WuPHdGMMY.3Xy2yuxHsjVW36sDuq01y4lyU2zcH1JHJSen6',
'https://example.com',
'The root account used on initial installation.',
'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50.jpg'
);

Voir le fichier

@ -2,15 +2,24 @@ package models
import (
"encoding/json"
"errors"
"log"
"time"
"github.com/go-sql-driver/mysql"
"github.com/hbjydev/mangadex-next/database"
"golang.org/x/crypto/bcrypt"
)
type password string
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Password password `json:"password"`
Email string `json:"email"`
LevelID string `json:"level_id"`
JoinedAt time.Time `json:"joined_at"`
UpdateAt time.Time `json:"update_at"`
LastSeen time.Time `json:"last_seen"`
Website string `json:"website"`
Biography string `json:"biography"`
@ -21,6 +30,55 @@ type User struct {
AvatarURL string `json:"avatar"`
}
func (password) MarshalJSON() ([]byte, error) {
return []byte(`""`), nil
}
func UserByUsername(username string) (*User, error) {
row := database.DB.QueryRow(`
SELECT
hex(id), username, email, password, level_id, last_seen, website,
biography, views, uploads, premium, md_at_home, avatar_url,
joined_at, update_at FROM users
WHERE username = ?
`, username)
var u User
var ls mysql.NullTime
var ja mysql.NullTime
var ua mysql.NullTime
if err := row.Scan(&u.ID, &u.Username, &u.Email, &u.Password, &u.LevelID,
&ls, &u.Website, &u.Biography, &u.Views, &u.Uploads, &u.Premium,
&u.MDAtHome, &u.AvatarURL, &ja, &ua); err != nil {
log.Printf("UserByUsername: error at SQL query: %v\n", err)
return nil, err
}
if ls.Valid {
u.LastSeen = ls.Time
} else {
log.Printf("UserByUsername: invalid SQL datetime value (id %v)\n", u.ID)
return nil, errors.New("invalid sql datetime value")
}
if ja.Valid {
u.JoinedAt = ja.Time
} else {
log.Printf("UserByUsername: invalid SQL datetime value (id %v)\n", u.ID)
return nil, errors.New("invalid sql datetime value")
}
if ua.Valid {
u.UpdateAt = ua.Time
} else {
log.Printf("UserByUsername: invalid SQL datetime value (id %v)\n", u.ID)
return nil, errors.New("invalid sql datetime value")
}
return &u, nil
}
func UserFromJSON(jsonString string) (*User, error) {
var user User
@ -32,6 +90,14 @@ func UserFromJSON(jsonString string) (*User, error) {
return &user, nil
}
func (u *User) CheckPassword(candidate string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(candidate))
if err != nil {
return false
}
return true
}
func (u *User) Normalize() (*string, error) {
bytes, err := json.Marshal(u)
if err != nil {