feat: Working login form with htmx.

This commit is contained in:
2025-01-06 19:28:14 -05:00
parent 35ca73e1b4
commit f9b58676bb
5 changed files with 56 additions and 33 deletions

View File

@@ -9,10 +9,10 @@
<body> <body>
<h1>#(title)</h1> <h1>#(title)</h1>
<div id="body" <div id="content"
hx-get="/body" hx-get="/home"
hx-trigger="load" hx-trigger="load"
hx-swap="outerHTML"> >
</div> </div>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,7 @@
<div id="body"> <p>We're in!</p>
<p>We're in!</p> <button hx-post="/logout"
</div> hx-target="#content"
hx-trigger="click"
>
Log Out.
</button>

View File

@@ -1,15 +1,9 @@
<div id="body" class="container"> <form class="login-form" hx-post="/login" hx-target="#content">
<form class="login-form"> <label for="username">Username</label>
<label for="email">Email</label> <input type="text" id="username" placeholder="Username" name="username" autocomplete="username" required autofocus>
<input type="text" id="email" placeholder="Email" name="email" autocomplete="email" required autofocus> <br>
<br> <label for="password">Password</label>
<label for="password">Password</label> <input type="password" id="password" placeholder="Password" name="password" autocomplete="current-password" required>
<input type="password" id="password" placeholder="Password" name="password" autocomplete="current-password" required> <br>
<br> <input type="submit" value="Sign In">
<button hx-post="/login" </form>
hx-target="#body"
hx-trigger="click">
Sign In
</button>
</form>
</div>

View File

@@ -80,7 +80,7 @@ extension User {
} }
extension User: ModelAuthenticatable { extension User: ModelAuthenticatable {
static let usernameKey = \User.$email static let usernameKey = \User.$username
static let passwordHashKey = \User.$passwordHash static let passwordHashKey = \User.$passwordHash
func verify(password: String) throws -> Bool { func verify(password: String) throws -> Bool {

View File

@@ -2,32 +2,52 @@ import Fluent
import Vapor import Vapor
func routes(_ app: Application) throws { func routes(_ app: Application) throws {
let redirectMiddleware = User.redirectMiddleware { req in let redirectMiddleware = User.redirectMiddleware(path: "login")
"login?next=\(req.url.path)" // let protected = app.grouped(redirectMiddleware)
} let credentialsProtected = app.grouped(User.credentialsAuthenticator(), redirectMiddleware)
let protected = app.grouped(User.sessionAuthenticator(), redirectMiddleware, User.guardMiddleware())
let credentialsProtected = protected.grouped(User.credentialsAuthenticator())
app.get { req async throws in app.get { req async throws in
try await req.view.render("index", ["title": "HHE - Purchase Orders"]) try await req.view.render("index", ["title": "HHE - Purchase Orders"])
} }
app.get("login") { req async throws in app.get("login") { req async throws -> View in
req.logger.info("login") req.logger.info("login")
return try await req.view.render("login") return try await req.view.render("login")
} }
credentialsProtected.post("login") { req async throws -> View in app.post("logout") { req async throws -> View in
req.logger.info("login POST") req.auth.logout(User.self)
return try await req.view.render("login")
}
app.post("login") { req async throws -> View in
let content = try req.content.decode(UserForm.self)
guard let user = try await User.query(on: req.db)
.filter(\.$username == content.username)
.first()
else {
throw Abort(.badRequest, reason: "User not found.")
}
guard try user.verify(password: content.password) else {
throw Abort(.unauthorized, reason: "Invalid password.")
}
req.auth.login(user)
req.logger.debug("User: \(user.toDTO())")
return try await req.view.render("logged-in") return try await req.view.render("logged-in")
} }
credentialsProtected.get("body") { req async throws in credentialsProtected.get("home") { req async throws in
req.logger.info("body") req.logger.info("home")
return try await req.view.render("logged-in") return try await req.view.render("logged-in")
} }
// TODO: Remove.
credentialsProtected.get("logged-in") { _ in
"Hello, logged-in!"
}
// app.get("index") { req async throws -> View in // app.get("index") { req async throws -> View in
// //
// } // }
@@ -38,3 +58,8 @@ func routes(_ app: Application) throws {
try app.register(collection: ApiController()) try app.register(collection: ApiController())
} }
struct UserForm: Content {
let username: String
let password: String
}