Init
This commit is contained in:
commit
4a4917beed
|
@ -0,0 +1,52 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApiError struct {
|
||||||
|
Err string
|
||||||
|
Status int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ApiError) Error() string {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiFunc func(http.ResponseWriter, *http.Request) error
|
||||||
|
|
||||||
|
func MakeHTTPHandler(f apiFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := f(w, r); err != nil {
|
||||||
|
if e, ok := err.(ApiError); ok {
|
||||||
|
WriteJSON(w, e.Status, e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
WriteJSON(w, 500, ApiError{Err: err.Error(), Status: http.StatusInternalServerError})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatusAndContent(w http.ResponseWriter, status int, contentType string) {
|
||||||
|
w.Header().Add("Content-Type", contentType)
|
||||||
|
w.WriteHeader(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteJSON(w http.ResponseWriter, status int, v any) error {
|
||||||
|
StatusAndContent(w, status, "application/json")
|
||||||
|
return json.NewEncoder(w).Encode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WritePlain(w http.ResponseWriter, status int, text string) error {
|
||||||
|
StatusAndContent(w, status, "text/plain")
|
||||||
|
_, err := w.Write([]byte(text))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteBlob(w http.ResponseWriter, status int, contentType string, blob []byte) error {
|
||||||
|
StatusAndContent(w, status, contentType)
|
||||||
|
_, err := w.Write(blob)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func run() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
db, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://root:example@localhost:27017"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Can not connect to db: ", err)
|
||||||
|
}
|
||||||
|
if err := db.Ping(ctx, nil); err != nil {
|
||||||
|
log.Fatal("Can not connect to db: ", err)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
//
|
||||||
|
defer func() {
|
||||||
|
if err := db.Disconnect(context.TODO()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
storage := MongodbStorage{db: db}
|
||||||
|
|
||||||
|
router := mux.NewRouter()
|
||||||
|
server := Server{
|
||||||
|
host: ":4000",
|
||||||
|
mux: router,
|
||||||
|
db: storage,
|
||||||
|
}
|
||||||
|
server.InitHandlers()
|
||||||
|
log.Println("Server starting at ", server.host)
|
||||||
|
log.Fatal(http.ListenAndServe(server.host, router))
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
module git.nefrace.ru/nefrace/nashboard
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require go.mongodb.org/mongo-driver v1.11.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.1 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
|
golang.org/x/crypto v0.4.0 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
golang.org/x/text v0.5.0 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,50 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||||
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
|
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||||
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE=
|
||||||
|
go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,126 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Println("Not enough args")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "run":
|
||||||
|
log.Println("Running")
|
||||||
|
run()
|
||||||
|
default:
|
||||||
|
fmt.Println("invalid command")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CtxUser string
|
||||||
|
|
||||||
|
func (s Server) AuthorizedOnly(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionHeader := r.Header.Get("SessionID")
|
||||||
|
session, err := s.db.GetSessionByToken(sessionHeader)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := s.db.GetUserByID(session.UserID)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(ctx, CtxUser("user"), user)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MongodbStorage struct {
|
||||||
|
db *mongo.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) CreateUser(user *User) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
_, err := m.Collection("users").InsertOne(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := err.(mongo.WriteError); ok {
|
||||||
|
log.Println("Error creating new user: ", e)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) GetUsers() ([]User, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
cur, err := m.Collection("users").Find(ctx, bson.D{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error fetching users: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var res []User
|
||||||
|
err = cur.All(ctx, &res)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error collection users to slice: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) GetUserByName(username string) (*User, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
res := m.Collection("users").FindOne(ctx, bson.D{{Key: "username", Value: username}})
|
||||||
|
if res.Err() != nil {
|
||||||
|
return nil, res.Err()
|
||||||
|
}
|
||||||
|
var user User
|
||||||
|
res.Decode(&user)
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) GetUserByID(id primitive.ObjectID) (*User, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
res := m.Collection("users").FindOne(ctx, bson.D{{Key: "_id", Value: id}})
|
||||||
|
if res.Err() != nil {
|
||||||
|
return nil, res.Err()
|
||||||
|
}
|
||||||
|
var user User
|
||||||
|
res.Decode(&user)
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) UpdateUser(user *User) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
_, err := m.Collection("users").ReplaceOne(ctx, bson.D{{Key: "_id", Value: user.Id}}, user)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) DeleteUser(id primitive.ObjectID) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
_, err := m.Collection("users").DeleteOne(ctx, bson.D{{Key: "_id", Value: id}})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) CreateSession(s *Session) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
_, err := m.Collection("sessions").InsertOne(ctx, s)
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := err.(mongo.WriteError); ok {
|
||||||
|
log.Println("Error creating new session: ", e)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) GetSessionByToken(token string) (*Session, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
res := m.Collection("sessions").FindOne(ctx, bson.D{{Key: "token", Value: token}})
|
||||||
|
if res.Err() != nil {
|
||||||
|
return nil, res.Err()
|
||||||
|
}
|
||||||
|
var session Session
|
||||||
|
res.Decode(&session)
|
||||||
|
return &session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) DeleteSession(id primitive.ObjectID) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
_, err := m.Collection("users").DeleteOne(ctx, bson.D{{Key: "_id", Value: id}})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MongodbStorage) Collection(col string) *mongo.Collection {
|
||||||
|
return m.db.Database("nash").Collection(col)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>NashBoard</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Hello world!
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
CreateUser(*User) error
|
||||||
|
GetUsers() ([]User, error)
|
||||||
|
GetUserByName(string) (*User, error)
|
||||||
|
GetUserByID(primitive.ObjectID) (*User, error)
|
||||||
|
UpdateUser(*User) error
|
||||||
|
DeleteUser(primitive.ObjectID) error
|
||||||
|
|
||||||
|
CreateSession(*Session) error
|
||||||
|
GetSessionByToken(string) (*Session, error)
|
||||||
|
DeleteSession(primitive.ObjectID) error
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
Username string
|
||||||
|
Password []byte `json:"-"`
|
||||||
|
IsAdmin bool
|
||||||
|
LastLogin time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
Token string
|
||||||
|
UserID primitive.ObjectID
|
||||||
|
IP string
|
||||||
|
LoggedAt time.Time
|
||||||
|
ExpiresAt time.Time
|
||||||
|
IsAdmin bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiKey struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
UserID primitive.ObjectID
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
Name string
|
||||||
|
Icon []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bookmark struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
FolderID primitive.ObjectID
|
||||||
|
Icon []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Note struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"id,omitempty"`
|
||||||
|
Name string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenSessionToken() string {
|
||||||
|
b := make([]byte, 128)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(b)
|
||||||
|
}
|
Loading…
Reference in New Issue