mirror of
https://github.com/dnote/dnote
synced 2026-03-17 07:55:50 +01:00
API login/logout
This commit is contained in:
parent
3d96f93d1f
commit
5fd65da9c0
3 changed files with 99 additions and 18 deletions
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/dnote/dnote/pkg/server/app"
|
||||
"github.com/dnote/dnote/pkg/server/database"
|
||||
"github.com/dnote/dnote/pkg/server/log"
|
||||
"github.com/dnote/dnote/pkg/server/views"
|
||||
"github.com/gorilla/schema"
|
||||
|
|
@ -210,3 +211,38 @@ func handleHTMLError(w http.ResponseWriter, r *http.Request, err error, msg stri
|
|||
|
||||
v.Render(w, r, d)
|
||||
}
|
||||
|
||||
// handleJSONError logs the error and responds with the given status code with a generic status text
|
||||
func handleJSONError(w http.ResponseWriter, err error, msg string) {
|
||||
statusCode := getStatusCode(err)
|
||||
|
||||
rootErr := errors.Cause(err)
|
||||
|
||||
var respText string
|
||||
if pErr, ok := rootErr.(views.PublicError); ok {
|
||||
respText = pErr.Public()
|
||||
} else {
|
||||
respText = http.StatusText(statusCode)
|
||||
}
|
||||
|
||||
logError(err, msg)
|
||||
http.Error(w, respText, statusCode)
|
||||
}
|
||||
|
||||
// respondWithSession makes a HTTP response with the session from the user with the given userID.
|
||||
// It sets the HTTP-Only cookie for browser clients and also sends a JSON response for non-browser clients.
|
||||
func respondWithSession(w http.ResponseWriter, statusCode int, session *database.Session) {
|
||||
setSessionCookie(w, session.Key, session.ExpiresAt)
|
||||
|
||||
response := SessionResponse{
|
||||
Key: session.Key,
|
||||
ExpiresAt: session.ExpiresAt.Unix(),
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
handleJSONError(w, err, "encoding payload")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import (
|
|||
|
||||
"github.com/dnote/dnote/pkg/server/app"
|
||||
"github.com/dnote/dnote/pkg/server/config"
|
||||
"github.com/dnote/dnote/pkg/server/database"
|
||||
"github.com/dnote/dnote/pkg/server/log"
|
||||
"github.com/dnote/dnote/pkg/server/views"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewUsers creates a new Users controller.
|
||||
|
|
@ -75,25 +77,37 @@ type LoginForm struct {
|
|||
Password string `schema:"password" json:"password"`
|
||||
}
|
||||
|
||||
// Login handles login
|
||||
func (u *Users) Login(w http.ResponseWriter, r *http.Request) {
|
||||
vd := views.Data{}
|
||||
|
||||
func (u *Users) login(r *http.Request) (*database.Session, error) {
|
||||
var form LoginForm
|
||||
if err := parseRequestData(r, &form); err != nil {
|
||||
handleHTMLError(w, r, err, "parsing request data", u.LoginView, &vd)
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := u.app.Authenticate(form.Email, form.Password)
|
||||
if err != nil {
|
||||
handleHTMLError(w, r, err, "authenticating user", u.LoginView, &vd)
|
||||
return
|
||||
// If the user is not found, treat it as invalid login
|
||||
if err == app.ErrNotFound {
|
||||
return nil, app.ErrLoginInvalid
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session, err := u.app.SignIn(user)
|
||||
s, err := u.app.SignIn(user)
|
||||
if err != nil {
|
||||
handleHTMLError(w, r, err, "signing in a user", u.LoginView, &vd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Login handles login
|
||||
func (u *Users) Login(w http.ResponseWriter, r *http.Request) {
|
||||
vd := views.Data{}
|
||||
|
||||
session, err := u.login(r)
|
||||
if err != nil {
|
||||
handleHTMLError(w, r, err, "logging in user", u.LoginView, &vd)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -101,22 +115,50 @@ func (u *Users) Login(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
|
||||
// V3Login handles login
|
||||
func (u *Users) V3Login(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := u.login(r)
|
||||
if err != nil {
|
||||
handleJSONError(w, err, "logging in user")
|
||||
return
|
||||
}
|
||||
|
||||
respondWithSession(w, http.StatusOK, session)
|
||||
}
|
||||
|
||||
func (u *Users) logout(r *http.Request) error {
|
||||
key, err := GetCredential(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting credentials")
|
||||
}
|
||||
|
||||
if err = u.app.DeleteSession(key); err != nil {
|
||||
return errors.Wrap(err, "deleting session")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout handles logout
|
||||
func (u *Users) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
var vd views.Data
|
||||
|
||||
key, err := GetCredential(r)
|
||||
if err != nil {
|
||||
handleHTMLError(w, r, err, "getting credentials", u.LoginView, &vd)
|
||||
u.LoginView.Render(w, r, vd)
|
||||
return
|
||||
}
|
||||
|
||||
if err = u.app.DeleteSession(key); err != nil {
|
||||
handleHTMLError(w, r, err, "deleting session", u.LoginView, &vd)
|
||||
if err := u.logout(r); err != nil {
|
||||
handleHTMLError(w, r, err, "logging out", u.LoginView, &vd)
|
||||
return
|
||||
}
|
||||
|
||||
unsetSessionCookie(w)
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
}
|
||||
|
||||
// V3Logout handles logout via API
|
||||
func (u *Users) V3Logout(w http.ResponseWriter, r *http.Request) {
|
||||
if err := u.logout(r); err != nil {
|
||||
handleJSONError(w, err, "logging out")
|
||||
return
|
||||
}
|
||||
|
||||
unsetSessionCookie(w)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ func NewWebRoutes(app *app.App, c *controllers.Controllers) []Route {
|
|||
|
||||
// NewAPIRoutes returns a new api routes
|
||||
func NewAPIRoutes(c *controllers.Controllers) []Route {
|
||||
return []Route{}
|
||||
return []Route{
|
||||
{"POST", "/v1/login", Cors(c.Users.V3Login), true},
|
||||
{"POST", "/v1/logout", Cors(c.Users.V3Logout), true},
|
||||
}
|
||||
}
|
||||
|
||||
// Config is the configuration for routes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue