feat: Begins integrating database client into vapor app.

This commit is contained in:
2025-01-14 11:50:06 -05:00
parent c8bcffa0b5
commit ccf80f05a7
42 changed files with 2378 additions and 2540 deletions

View File

@@ -1,4 +1,4 @@
import DatabaseClient
@_exported import DatabaseClient
import FluentKit
import SharedModels

View File

@@ -1,13 +1,15 @@
import DatabaseClient
import FluentKit
import Foundation
import HummingbirdBcrypt
import SharedModels
import Vapor
public extension DatabaseClient.Users {
static func live(database: any Database) -> Self {
.init { create in
.init {
try await UserModel.query(on: database).count()
} create: { create in
let model = try create.toModel()
try await model.save(on: database)
return try model.toDTO()
@@ -98,7 +100,7 @@ extension User.Create {
func toModel() throws -> UserModel {
try validate()
return .init(username: username, email: email, passwordHash: Bcrypt.hash(password, cost: 12))
return try .init(username: username, email: email, passwordHash: Bcrypt.hash(password, cost: 12))
}
func validate() throws {
@@ -209,3 +211,62 @@ final class UserTokenModel: Model, Codable, @unchecked Sendable {
}
}
// MARK: - Authentication
extension User: Authenticatable {}
extension User: SessionAuthenticatable {
public var sessionID: String { username }
}
extension User: Content {}
public struct UserPasswordAuthenticator: AsyncBasicAuthenticator {
public typealias User = SharedModels.User
public init() {}
public func authenticate(basic: BasicAuthorization, for request: Request) async throws {
guard let user = try await UserModel.query(on: request.db)
.filter(\.$username == basic.username)
.first(),
try Bcrypt.verify(basic.password, created: user.passwordHash)
else {
throw Abort(.unauthorized)
}
try request.auth.login(user.toDTO())
}
}
public struct UserTokenAuthenticator: AsyncBearerAuthenticator {
public typealias User = SharedModels.User
public init() {}
public func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
guard let token = try await UserTokenModel.query(on: request.db)
.filter(\.$value == bearer.token)
.with(\.$user)
.first()
else {
throw Abort(.unauthorized)
}
try request.auth.login(token.user.toDTO())
}
}
public struct UserSessionAuthenticator: AsyncSessionAuthenticator {
public typealias User = SharedModels.User
public init() {}
public func authenticate(sessionID: User.SessionID, for request: Request) async throws {
guard let user = try await UserModel.query(on: request.db)
.filter(\.$username == sessionID)
.first()
else {
throw Abort(.unauthorized)
}
try request.auth.login(user.toDTO())
}
}