mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
refactor: use cookie-based login
feat: add login page
This commit is contained in:
43
routes/common.go
Normal file
43
routes/common.go
Normal file
@ -0,0 +1,43 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
)
|
||||
|
||||
func init() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
var templates map[string]*template.Template
|
||||
|
||||
func loadTemplates() {
|
||||
tplPath := "views"
|
||||
tpls := template.New("").Funcs(template.FuncMap{
|
||||
"json": utils.Json,
|
||||
"date": utils.FormatDateHuman,
|
||||
})
|
||||
templates = make(map[string]*template.Template)
|
||||
|
||||
files, err := ioutil.ReadDir(tplPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
tplName := file.Name()
|
||||
if file.IsDir() || path.Ext(tplName) != ".html" {
|
||||
continue
|
||||
}
|
||||
|
||||
tpl, err := tpls.New(tplName).ParseFiles(fmt.Sprintf("%s/%s", tplPath, tplName))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
templates[tplName] = tpl
|
||||
}
|
||||
}
|
@ -7,12 +7,16 @@ import (
|
||||
)
|
||||
|
||||
type HealthHandler struct {
|
||||
Db *gorm.DB
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewHealthHandler(db *gorm.DB) *HealthHandler {
|
||||
return &HealthHandler{db: db}
|
||||
}
|
||||
|
||||
func (h *HealthHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
var dbStatus int
|
||||
if err := h.Db.DB().Ping(); err == nil {
|
||||
if err := h.db.DB().Ping(); err == nil {
|
||||
dbStatus = 1
|
||||
}
|
||||
|
||||
|
@ -12,15 +12,18 @@ import (
|
||||
)
|
||||
|
||||
type HeartbeatHandler struct {
|
||||
HeartbeatSrvc *services.HeartbeatService
|
||||
config *models.Config
|
||||
heartbeatSrvc *services.HeartbeatService
|
||||
}
|
||||
|
||||
func NewHeartbeatHandler(config *models.Config, heartbearService *services.HeartbeatService) *HeartbeatHandler {
|
||||
return &HeartbeatHandler{
|
||||
config: config,
|
||||
heartbeatSrvc: heartbearService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var heartbeats []*models.Heartbeat
|
||||
user := r.Context().Value(models.UserKey).(*models.User)
|
||||
opSys, editor, _ := utils.ParseUserAgent(r.Header.Get("User-Agent"))
|
||||
@ -37,7 +40,7 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
hb.Editor = editor
|
||||
hb.User = user
|
||||
hb.UserID = user.ID
|
||||
hb.Augment(h.HeartbeatSrvc.Config.CustomLanguages)
|
||||
hb.Augment(h.config.CustomLanguages)
|
||||
|
||||
if !hb.Valid() {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@ -46,7 +49,7 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.HeartbeatSrvc.InsertBatch(heartbeats); err != nil {
|
||||
if err := h.heartbeatSrvc.InsertBatch(heartbeats); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
os.Stderr.WriteString(err.Error())
|
||||
return
|
||||
|
104
routes/index.go
Normal file
104
routes/index.go
Normal file
@ -0,0 +1,104 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/services"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type IndexHandler struct {
|
||||
config *models.Config
|
||||
userSrvc *services.UserService
|
||||
}
|
||||
|
||||
var loginDecoder = schema.NewDecoder()
|
||||
|
||||
func NewIndexHandler(config *models.Config, userService *services.UserService) *IndexHandler {
|
||||
return &IndexHandler{
|
||||
config: config,
|
||||
userSrvc: userService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *IndexHandler) Index(w http.ResponseWriter, r *http.Request) {
|
||||
if h.config.IsDev() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
if cookie, err := r.Cookie(models.AuthCookieKey); err == nil && cookie.Value != "" {
|
||||
http.Redirect(w, r, "/summary", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.URL.Query().Get("error"); err != "" {
|
||||
if err == "unauthorized" {
|
||||
respondError(w, err, http.StatusUnauthorized)
|
||||
} else {
|
||||
respondError(w, err, http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
templates["index.tpl.html"].Execute(w, nil)
|
||||
}
|
||||
|
||||
func (h *IndexHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
if h.config.IsDev() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
var login models.Login
|
||||
if err := r.ParseForm(); err != nil {
|
||||
respondError(w, "missing parameters", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := loginDecoder.Decode(&login, r.PostForm); err != nil {
|
||||
respondError(w, "missing parameters", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userSrvc.GetUserById(login.Username)
|
||||
if err != nil {
|
||||
respondError(w, "resource not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !utils.CheckPassword(user, login.Password) {
|
||||
respondError(w, "invalid credentials", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
encoded, err := h.config.SecureCookie.Encode(models.AuthCookieKey, login)
|
||||
if err != nil {
|
||||
respondError(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: models.AuthCookieKey,
|
||||
Value: encoded,
|
||||
Path: "/",
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
http.Redirect(w, r, "/summary", http.StatusFound)
|
||||
}
|
||||
|
||||
func (h *IndexHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
if h.config.IsDev() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
utils.ClearCookie(w, models.AuthCookieKey)
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
|
||||
func respondError(w http.ResponseWriter, error string, status int) {
|
||||
w.WriteHeader(status)
|
||||
templates["index.tpl.html"].Execute(w, struct {
|
||||
Error string
|
||||
}{Error: error})
|
||||
}
|
@ -2,11 +2,7 @@ package routes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
@ -24,55 +20,19 @@ const (
|
||||
)
|
||||
|
||||
type SummaryHandler struct {
|
||||
SummarySrvc *services.SummaryService
|
||||
Initialized bool
|
||||
templates map[string]*template.Template
|
||||
cummarySrvc *services.SummaryService
|
||||
config *models.Config
|
||||
}
|
||||
|
||||
func (m *SummaryHandler) Init() {
|
||||
m.loadTemplates()
|
||||
m.Initialized = true
|
||||
}
|
||||
|
||||
func (m *SummaryHandler) loadTemplates() {
|
||||
tplPath := "views"
|
||||
templates := template.New("").Funcs(template.FuncMap{
|
||||
"json": utils.Json,
|
||||
"date": utils.FormatDateHuman,
|
||||
})
|
||||
m.templates = make(map[string]*template.Template)
|
||||
|
||||
files, err := ioutil.ReadDir(tplPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
tplName := file.Name()
|
||||
if file.IsDir() || path.Ext(tplName) != ".html" {
|
||||
continue
|
||||
}
|
||||
|
||||
tpl, err := templates.New(tplName).ParseFiles(fmt.Sprintf("%s/%s", tplPath, tplName))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
m.templates[tplName] = tpl
|
||||
func NewSummaryHandler(config *models.Config, summaryService *services.SummaryService) *SummaryHandler {
|
||||
return &SummaryHandler{
|
||||
cummarySrvc: summaryService,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SummaryHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if !h.Initialized {
|
||||
h.Init()
|
||||
}
|
||||
|
||||
summary, err, status := loadUserSummary(r, h.SummarySrvc)
|
||||
summary, err, status := loadUserSummary(r, h.cummarySrvc)
|
||||
if err != nil {
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(err.Error()))
|
||||
@ -83,17 +43,8 @@ func (h *SummaryHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *SummaryHandler) Index(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if !h.Initialized {
|
||||
h.Init()
|
||||
}
|
||||
|
||||
if h.SummarySrvc.Config.IsDev() {
|
||||
h.loadTemplates()
|
||||
if h.config.IsDev() {
|
||||
loadTemplates()
|
||||
}
|
||||
|
||||
q := r.URL.Query()
|
||||
@ -102,7 +53,7 @@ func (h *SummaryHandler) Index(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
summary, err, status := loadUserSummary(r, h.SummarySrvc)
|
||||
summary, err, status := loadUserSummary(r, h.cummarySrvc)
|
||||
if err != nil {
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(err.Error()))
|
||||
@ -111,10 +62,10 @@ func (h *SummaryHandler) Index(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
vm := models.SummaryViewModel{
|
||||
Summary: summary,
|
||||
LanguageColors: utils.FilterLanguageColors(h.SummarySrvc.Config.LanguageColors, summary),
|
||||
LanguageColors: utils.FilterLanguageColors(h.config.LanguageColors, summary),
|
||||
}
|
||||
|
||||
h.templates["index.tpl.html"].Execute(w, vm)
|
||||
templates["summary.tpl.html"].Execute(w, vm)
|
||||
}
|
||||
|
||||
func loadUserSummary(r *http.Request, summaryService *services.SummaryService) (*models.Summary, error, int) {
|
||||
|
Reference in New Issue
Block a user