127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type Server struct {
|
|
host string
|
|
mux *mux.Router
|
|
db Storage
|
|
}
|
|
|
|
var ErrUnauthorized ApiError = ApiError{Status: http.StatusUnauthorized, Err: "Unauthorized"}
|
|
var ErrBadRequest ApiError = ApiError{Status: http.StatusBadRequest, Err: "bad request"}
|
|
|
|
func (s Server) InitHandlers() {
|
|
r := s.mux
|
|
apiRouter := r.PathPrefix("/api").Subrouter()
|
|
|
|
authRouter := apiRouter.PathPrefix("/user").Subrouter()
|
|
me := authRouter.Path("/me").Subrouter()
|
|
me.Use(s.AuthorizedOnly)
|
|
me.Path("").HandlerFunc(MakeHTTPHandler(s.handleGetUser))
|
|
authRouter.Path("/reg").Methods("POST").HandlerFunc(MakeHTTPHandler(s.handleRegister))
|
|
authRouter.Path("/login").Methods("POST").HandlerFunc(MakeHTTPHandler(s.handleLogin))
|
|
|
|
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
|
|
}
|
|
|
|
var anon User = User{
|
|
Id: primitive.ObjectID{},
|
|
Username: "Anonymous",
|
|
IsAdmin: false,
|
|
}
|
|
|
|
func (s Server) handleGetUser(w http.ResponseWriter, r *http.Request) error {
|
|
ctx := r.Context()
|
|
user := ctx.Value(CtxUser("user")).(*User)
|
|
|
|
return WriteJSON(w, 200, user)
|
|
}
|
|
|
|
type registerForm struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func (s Server) handleRegister(w http.ResponseWriter, r *http.Request) error {
|
|
var form registerForm
|
|
err := DecodeJSON(r.Body, &form)
|
|
if err != nil {
|
|
return ApiError{Status: http.StatusBadRequest, Err: "bad credentials"}
|
|
}
|
|
password, err := bcrypt.GenerateFromPassword([]byte(form.Password), 0)
|
|
if err != nil {
|
|
log.Println("Can not generate hash from password: ", err)
|
|
return ApiError{Status: http.StatusInternalServerError, Err: "cannot register"}
|
|
}
|
|
newUser := User{
|
|
Id: primitive.NewObjectID(),
|
|
Username: form.Username,
|
|
Password: password,
|
|
IsAdmin: true,
|
|
}
|
|
if err = s.db.CreateUser(&newUser); err != nil {
|
|
log.Println("Can not create user: ", err)
|
|
return ApiError{Status: http.StatusInternalServerError, Err: "cannot register"}
|
|
}
|
|
|
|
return WriteJSON(w, http.StatusCreated, &newUser)
|
|
}
|
|
|
|
type loginForm struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func (s Server) handleLogin(w http.ResponseWriter, r *http.Request) error {
|
|
var form loginForm
|
|
err := DecodeJSON(r.Body, &form)
|
|
if err != nil {
|
|
log.Println("Can not decode json from login: ", err)
|
|
return ErrBadRequest
|
|
}
|
|
user, err := s.db.GetUserByName(form.Username)
|
|
if err != nil {
|
|
log.Println("Can not find user: ", err)
|
|
return ErrBadRequest
|
|
}
|
|
err = bcrypt.CompareHashAndPassword(user.Password, []byte(form.Password))
|
|
if err != nil {
|
|
log.Println("Can not compare password and hash: ", err)
|
|
return ErrBadRequest
|
|
}
|
|
|
|
ses := Session{
|
|
Id: primitive.NewObjectID(),
|
|
Token: GenSessionToken(),
|
|
UserID: user.Id,
|
|
LoggedAt: time.Now(),
|
|
IsAdmin: true,
|
|
ExpiresAt: time.Now().Add(42 * time.Hour * 24),
|
|
IP: r.RemoteAddr,
|
|
}
|
|
user.LastLogin = time.Now()
|
|
if err := s.db.UpdateUser(user); err != nil {
|
|
log.Println("Can not update user: ", err)
|
|
}
|
|
if err = s.db.CreateSession(&ses); err != nil {
|
|
log.Println("Can not store session: ", err)
|
|
return ErrBadRequest
|
|
}
|
|
return WriteJSON(w, 200, ses)
|
|
}
|
|
|
|
func DecodeJSON(r io.Reader, v any) error {
|
|
return json.NewDecoder(r).Decode(v)
|
|
}
|