Have got logout working

This commit is contained in:
Leon Mika 2026-02-25 22:30:28 +11:00
parent 01c6e9de87
commit b7e0269e9d
7 changed files with 66 additions and 10 deletions

View file

@ -0,0 +1,9 @@
import { Controller } from "@hotwired/stimulus"
export default class LogoutController extends Controller {
async logout(ev) {
ev.preventDefault();
await fetch(`/logout`, { method: 'POST' });
window.location.href = '/login';
}
}

View file

@ -3,8 +3,10 @@ import { Application } from "@hotwired/stimulus";
import ToastController from "./controllers/toast"; import ToastController from "./controllers/toast";
import PostlistController from "./controllers/postlist"; import PostlistController from "./controllers/postlist";
import PosteditController from "./controllers/postedit"; import PosteditController from "./controllers/postedit";
import LogoutController from "./controllers/logout";
window.Stimulus = Application.start() window.Stimulus = Application.start()
Stimulus.register("toast", ToastController); Stimulus.register("toast", ToastController);
Stimulus.register("postlist", PostlistController); Stimulus.register("postlist", PostlistController);
Stimulus.register("postedit", PosteditController); Stimulus.register("postedit", PosteditController);
Stimulus.register("logout", LogoutController);

View file

@ -29,12 +29,21 @@ func (lh *LoginHandler) Login(c fiber.Ctx) error {
return nil return nil
} }
func (lh *LoginHandler) Logout(c fiber.Ctx) error {
sess := session.FromContext(c)
sess.Destroy()
return c.Redirect().To("/login")
}
func (lh *LoginHandler) DoLogin(c fiber.Ctx) error { func (lh *LoginHandler) DoLogin(c fiber.Ctx) error {
var req struct { var req struct {
Username string `form:"username"` Username string `form:"username"`
Password string `form:"password"` Password string `form:"password"`
LoginChallenge string `form:"_login_challenge"` LoginChallenge string `form:"_login_challenge"`
} }
if err := c.Bind().Body(&req); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Failed to parse request body")
}
if req.Username == "" || req.Password == "" { if req.Username == "" || req.Password == "" {
return c.Status(fiber.StatusBadRequest).SendString("Username and password are required") return c.Status(fiber.StatusBadRequest).SendString("Username and password are required")
@ -43,7 +52,7 @@ func (lh *LoginHandler) DoLogin(c fiber.Ctx) error {
sess := session.FromContext(c) sess := session.FromContext(c)
challenge, _ := sess.Get("_login_challenge").(string) challenge, _ := sess.Get("_login_challenge").(string)
if challenge == req.LoginChallenge { if challenge != req.LoginChallenge {
return c.Redirect().To("/login") return c.Redirect().To("/login")
} }

21
main.go
View file

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"flag"
"html" "html"
"html/template" "html/template"
"log" "log"
@ -29,6 +30,10 @@ import (
) )
func main() { func main() {
flagUser := flag.String("user", "", "select user to perform operation on")
flagPasswd := flag.String("passwd", "", "change password for user")
flag.Parse()
cfg, err := config.LoadConfig() cfg, err := config.LoadConfig()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -43,9 +48,20 @@ func main() {
authSvc := auth.New(dbp) authSvc := auth.New(dbp)
publisherSvc := publisher.New(dbp) publisherSvc := publisher.New(dbp)
publisherQueue := publisher.NewQueue(publisherSvc) publisherQueue := publisher.NewQueue(publisherSvc)
publisherQueue.Start(context.Background())
postService := posts.New(dbp, publisherQueue) postService := posts.New(dbp, publisherQueue)
// CLI tools
if *flagPasswd != "" && *flagUser != "" {
user, err := authSvc.SetPassword(context.Background(), *flagUser, *flagPasswd)
if err != nil {
log.Fatal(err)
}
log.Printf("Password changed for user %s\n", user.Username)
return
}
publisherQueue.Start(context.Background())
fiberTemplate := fiber_html.New("./views", ".html") fiberTemplate := fiber_html.New("./views", ".html")
fiberTemplate.Funcmap["sub"] = func(x, y int) int { return x - y } fiberTemplate.Funcmap["sub"] = func(x, y int) int { return x - y }
fiberTemplate.Funcmap["markdown"] = func() func(s string) template.HTML { fiberTemplate.Funcmap["markdown"] = func() func(s string) template.HTML {
@ -108,7 +124,8 @@ func main() {
ph := handlers.PostsHandler{PostService: postService} ph := handlers.PostsHandler{PostService: postService}
app.Get("/login", lh.Login) app.Get("/login", lh.Login)
app.Post("/login", lh.Login) app.Post("/login", lh.DoLogin)
app.Post("/logout", lh.Logout)
siteGroup := app.Group("/sites/:siteID", middleware.AuthUser(authSvc), middleware.RequiresSite(dbp)) siteGroup := app.Group("/sites/:siteID", middleware.AuthUser(authSvc), middleware.RequiresSite(dbp))

View file

@ -34,3 +34,13 @@ func (s *Service) Login(ctx context.Context, username, password string) (models.
func (s *Service) GetUser(ctx context.Context, userID int64) (models.User, error) { func (s *Service) GetUser(ctx context.Context, userID int64) (models.User, error) {
return s.db.SelectUserByID(ctx, userID) return s.db.SelectUserByID(ctx, userID)
} }
func (s *Service) SetPassword(ctx context.Context, username, password string) (models.User, error) {
user, err := s.db.SelectUserByUsername(ctx, username)
if err != nil {
return models.User{}, err
}
user.SetPassword(password)
return user, s.db.SaveUser(ctx, &user)
}

View file

@ -18,8 +18,17 @@
</div> </div>
--> -->
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"/> <div class="nav-item dropdown">
<button class="btn btn-outline-success" type="submit">Search</button> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ .user.Username }}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" data-controller="logout" data-action="logout#logout">Logout</a></li>
</ul>
</div>
</form> </form>
</div> </div>
</div> </div>

View file

@ -2,15 +2,15 @@
<div class="text-center mb-3"> <div class="text-center mb-3">
<h3>Weiro Login</h3> <h3>Weiro Login</h3>
</div> </div>
<input type="hidden" name="_login_challenge" value="{{ .challenge }}">
<form action="/login" method="post"> <form action="/login" method="post">
<input type="hidden" name="_login_challenge" value="{{ .challenge }}">
<div class="mb-2"> <div class="mb-2">
<label for="login_username" class="form-label">Login</label> <label for="username" class="form-label">Login</label>
<input type="email" class="form-control" id="login_username"> <input type="text" class="form-control" name="username" id="username">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="login_password" class="form-label">Password</label> <label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="login_password"> <input type="password" class="form-control" name="password" id="password">
</div> </div>
<div class="mb-3 text-end"> <div class="mb-3 text-end">
<input type="submit" class="btn btn-primary" value="Login"> <input type="submit" class="btn btn-primary" value="Login">