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