// SPDX-License-Identifier: Apache-2.0 // Copyright © 2022 Roberto Hidalgo package server import ( "encoding/json" "fmt" "net/http" "git.rob.mx/nidito/puerta/internal/user" "github.com/SherClockHolmes/webpush-go" "github.com/julienschmidt/httprouter" "github.com/sirupsen/logrus" "github.com/upper/db/v4" "golang.org/x/crypto/bcrypt" ) func sendError(w http.ResponseWriter, err error) { logrus.Error(err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } func writeJSON(w http.ResponseWriter, data any) error { res, err := json.Marshal(data) if err != nil { return err } w.Header().Add("content-type", "application/json") w.WriteHeader(http.StatusOK) _, err = w.Write(res) return err } func listUsers(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { users := []*user.User{} if err := _db.Collection("user").Find().All(&users); err != nil { sendError(w, err) return } writeJSON(w, users) } func userFromRequest(r *http.Request, u *user.User) (*user.User, error) { dec := json.NewDecoder(r.Body) res := &user.User{} if err := dec.Decode(&res); err != nil { return nil, err } logrus.Debugf("Unserialized user data: %v", res) if u == nil { u = &user.User{ Handle: res.Handle, } } u.Name = res.Name u.Expires = res.Expires u.Greeting = res.Greeting u.IsAdmin = res.IsAdmin u.IsNotified = res.IsNotified u.Require2FA = res.Require2FA u.Schedule = res.Schedule u.TTL = res.TTL if res.Password != "" { password, err := bcrypt.GenerateFromPassword([]byte(res.Password), bcrypt.DefaultCost) if err != nil { return nil, err } u.Password = string(password) } return u, nil } func createUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { user, err := userFromRequest(r, nil) if err != nil { sendError(w, err) return } if _, err := _db.Collection("user").Insert(user); err != nil { sendError(w, err) return } w.WriteHeader(http.StatusCreated) } func getUser(w http.ResponseWriter, r *http.Request, params httprouter.Params) { this := user.User{} idString := params.ByName("id") if err := _db.Get(&this, db.Cond{"handle": idString}); err != nil { sendError(w, err) return } writeJSON(w, this) } func updateUser(w http.ResponseWriter, r *http.Request, params httprouter.Params) { logrus.Infof("updating user: %s", params.ByName("id")) user := user.User{} if err := _db.Get(&user, db.Cond{"handle": params.ByName("id")}); err != nil { logrus.Error(err) http.NotFound(w, r) return } modified, err := userFromRequest(r, &user) if err != nil { sendError(w, err) return } if err := _db.Collection("user").UpdateReturning(modified); err != nil { sendError(w, err) return } w.WriteHeader(http.StatusNoContent) } func deleteUser(w http.ResponseWriter, r *http.Request, params httprouter.Params) { err := _db.Collection("user").Find(db.Cond{"handle": params.ByName("id")}).Delete() if err != nil { sendError(w, err) return } w.WriteHeader(http.StatusNoContent) } func createSubscription(w http.ResponseWriter, r *http.Request, params httprouter.Params) { u := user.FromContext(r) dec := json.NewDecoder(r.Body) res := &webpush.Subscription{} if err := dec.Decode(&res); err != nil { sendError(w, err) return } logrus.Infof("Unserialized subscription data: %v", res) sub := &user.Subscription{ UserID: u.ID, Data: &user.WPS{Subscription: res}, } ins, err := _db.Collection("subscription").Insert(sub) if err != nil { sendError(w, err) return } logrus.Infof("Created subscription for: %s (%v)", u.Handle, ins) w.WriteHeader(http.StatusCreated) w.Write([]byte(`{"status": "ok"}`)) } func deleteSubscription(w http.ResponseWriter, r *http.Request, params httprouter.Params) { u := user.FromContext(r) dec := json.NewDecoder(r.Body) res := &webpush.Subscription{} if err := dec.Decode(&res); err != nil { sendError(w, err) return } encoded, err := json.Marshal(res) if err != nil { sendError(w, err) return } err = _db.Collection("subscription").Find(db.Cond{"user": u.ID, "data": db.Like(fmt.Sprintf("%%%s%%", encoded))}).Delete() if err != nil { sendError(w, fmt.Errorf("could not delete subscription: %s", err)) return } logrus.Infof("Deleted subscription for: %s (%s)", u.Handle, encoded) w.WriteHeader(http.StatusCreated) w.Write([]byte(`{"status": "ok"}`)) } func rexRecords(w http.ResponseWriter, r *http.Request, params httprouter.Params) { records := []*auditLog{} err := _db.Collection("log").Find().OrderBy("-timestamp").Limit(20).All(&records) if err != nil { sendError(w, err) return } writeJSON(w, records) }