feat: Working login form with htmx.
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
<button hx-post="/login"
|
<input type="submit" value="Sign In">
|
||||||
hx-target="#body"
|
</form>
|
||||||
hx-trigger="click">
|
|
||||||
Sign In
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user