Stian Aune
5 years ago
8 changed files with 242 additions and 10 deletions
-
20Gopkg.lock
-
12cmd/lucifer-server/main.go
-
70controllers/user-controller.go
-
94database/sqlite/user-repository.go
-
4internal/config/config.go
-
18internal/respond/error.go
-
14internal/respond/json.go
-
20models/user.go
@ -0,0 +1,70 @@ |
|||||
|
package controllers |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"net/http" |
||||
|
|
||||
|
"git.aiterp.net/lucifer/lucifer/internal/respond" |
||||
|
"git.aiterp.net/lucifer/lucifer/models" |
||||
|
"github.com/gorilla/mux" |
||||
|
) |
||||
|
|
||||
|
// The UserController is a controller for all user inports.
|
||||
|
type UserController struct { |
||||
|
users models.UserRepository |
||||
|
} |
||||
|
|
||||
|
// getUsers (`GET /`): List users
|
||||
|
func (c *UserController) getUsers(w http.ResponseWriter, r *http.Request) { |
||||
|
// TODO: Check session
|
||||
|
|
||||
|
users, err := c.users.List(r.Context()) |
||||
|
if err != nil { |
||||
|
respond.Error(w, 500, "db_error", err.Error()) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
respond.JSON(w, 200, users) |
||||
|
} |
||||
|
|
||||
|
// login (`POST /login`): Log in as user
|
||||
|
func (c *UserController) login(w http.ResponseWriter, r *http.Request) { |
||||
|
loginData := struct { |
||||
|
Username string `json:"username"` |
||||
|
Password string `json:"password"` |
||||
|
}{} |
||||
|
|
||||
|
err := json.NewDecoder(r.Body).Decode(&loginData) |
||||
|
if err != nil { |
||||
|
respond.Error(w, 400, "invalid_json", "Input is not valid JSON.") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
user, err := c.users.FindByName(r.Context(), loginData.Username) |
||||
|
if err != nil { |
||||
|
respond.Error(w, http.StatusUnauthorized, "login_failed", "Login failed.") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if err := user.CheckPassword(loginData.Password); err != nil { |
||||
|
respond.Error(w, http.StatusUnauthorized, "login_failed", "Login failed.") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// TODO: Open session
|
||||
|
|
||||
|
respond.JSON(w, 200, user) |
||||
|
} |
||||
|
|
||||
|
// Mount mounts the controller
|
||||
|
func (c *UserController) Mount(router *mux.Router, prefix string) { |
||||
|
sub := router.PathPrefix(prefix).Subrouter() |
||||
|
|
||||
|
sub.Handle("/", http.HandlerFunc(c.getUsers)).Methods("GET") |
||||
|
sub.Handle("/login", http.HandlerFunc(c.login)).Methods("POST") |
||||
|
} |
||||
|
|
||||
|
// NewUserController creates a new UserController.
|
||||
|
func NewUserController(users models.UserRepository) *UserController { |
||||
|
return &UserController{users: users} |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
package sqlite |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"git.aiterp.net/lucifer/lucifer/models" |
||||
|
) |
||||
|
|
||||
|
// UserRepository is a sqlite database.
|
||||
|
var UserRepository = &userRepository{} |
||||
|
|
||||
|
type userRepository struct{} |
||||
|
|
||||
|
func (repo *userRepository) FindByID(ctx context.Context, id int) (models.User, error) { |
||||
|
row := db.QueryRowxContext(ctx, "SELECT * FROM user WHERE id=?", id) |
||||
|
if err := row.Err(); err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
user := models.User{} |
||||
|
if err := row.StructScan(&user); err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
return user, nil |
||||
|
} |
||||
|
|
||||
|
func (repo *userRepository) FindByName(ctx context.Context, name string) (models.User, error) { |
||||
|
row := db.QueryRowxContext(ctx, "SELECT * FROM user WHERE name=?", name) |
||||
|
if err := row.Err(); err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
user := models.User{} |
||||
|
if err := row.StructScan(&user); err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
return user, nil |
||||
|
} |
||||
|
|
||||
|
func (repo *userRepository) List(ctx context.Context) ([]models.User, error) { |
||||
|
res, err := db.QueryxContext(ctx, "SELECT * FROM user") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} else if err := res.Err(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
users := make([]models.User, 0, 64) |
||||
|
for res.Next() { |
||||
|
user := models.User{} |
||||
|
if err := res.StructScan(&user); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
users = append(users, user) |
||||
|
} |
||||
|
|
||||
|
return users, nil |
||||
|
} |
||||
|
|
||||
|
func (repo *userRepository) Insert(ctx context.Context, user models.User) (models.User, error) { |
||||
|
res, err := db.NamedExecContext(ctx, "INSERT INTO user (name, hash) VALUES(:name, :hash)", user) |
||||
|
if err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
id, err := res.LastInsertId() |
||||
|
if err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
|
||||
|
user.ID = int(id) |
||||
|
return user, nil |
||||
|
} |
||||
|
|
||||
|
func (repo *userRepository) Update(ctx context.Context, user models.User) error { |
||||
|
_, err := db.NamedExecContext(ctx, "UPDATE user SET name=:name AND hash=:hash WHERE id=:id", user) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (repo *userRepository) Remove(ctx context.Context, user models.User) error { |
||||
|
_, err := db.ExecContext(ctx, "REMOVE FROM user WHERE id=?", user.ID) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
return err |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package respond |
||||
|
|
||||
|
import "net/http" |
||||
|
|
||||
|
// Error responds with a standardized error object.
|
||||
|
func Error(w http.ResponseWriter, code int, kind string, message string) { |
||||
|
type errorContent struct { |
||||
|
Code int `json:"code"` |
||||
|
Kind string `json:"kind"` |
||||
|
Message string `json:"message"` |
||||
|
} |
||||
|
|
||||
|
JSON(w, code, &errorContent{ |
||||
|
Code: code, |
||||
|
Kind: kind, |
||||
|
Message: message, |
||||
|
}) |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package respond |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"net/http" |
||||
|
) |
||||
|
|
||||
|
// JSON gets the json value.
|
||||
|
func JSON(w http.ResponseWriter, code int, data interface{}) { |
||||
|
w.Header().Set("Content-Type", "application/json") |
||||
|
w.WriteHeader(code) |
||||
|
|
||||
|
json.NewEncoder(w).Encode(data) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue