feat: Begins creating an auth client and integrates into view controller routes.
This commit is contained in:
@@ -7,6 +7,7 @@ let package = Package(
|
|||||||
products: [
|
products: [
|
||||||
.executable(name: "App", targets: ["App"]),
|
.executable(name: "App", targets: ["App"]),
|
||||||
.library(name: "ApiController", targets: ["ApiController"]),
|
.library(name: "ApiController", targets: ["ApiController"]),
|
||||||
|
.library(name: "AuthClient", targets: ["AuthClient"]),
|
||||||
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
||||||
.library(name: "ProjectClient", targets: ["ProjectClient"]),
|
.library(name: "ProjectClient", targets: ["ProjectClient"]),
|
||||||
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
.library(name: "ManualDCore", targets: ["ManualDCore"]),
|
||||||
@@ -32,6 +33,7 @@ let package = Package(
|
|||||||
name: "App",
|
name: "App",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.target(name: "ApiController"),
|
.target(name: "ApiController"),
|
||||||
|
.target(name: "AuthClient"),
|
||||||
.target(name: "DatabaseClient"),
|
.target(name: "DatabaseClient"),
|
||||||
.target(name: "ViewController"),
|
.target(name: "ViewController"),
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
@@ -54,6 +56,15 @@ let package = Package(
|
|||||||
.product(name: "Vapor", package: "vapor"),
|
.product(name: "Vapor", package: "vapor"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "AuthClient",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "DatabaseClient"),
|
||||||
|
.target(name: "ManualDCore"),
|
||||||
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
|
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||||
|
]
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "DatabaseClient",
|
name: "DatabaseClient",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
@@ -112,6 +123,7 @@ let package = Package(
|
|||||||
.target(
|
.target(
|
||||||
name: "ViewController",
|
name: "ViewController",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
.target(name: "AuthClient"),
|
||||||
.target(name: "DatabaseClient"),
|
.target(name: "DatabaseClient"),
|
||||||
.target(name: "ProjectClient"),
|
.target(name: "ProjectClient"),
|
||||||
.target(name: "ManualDClient"),
|
.target(name: "ManualDClient"),
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ extension ViewController {
|
|||||||
.init(
|
.init(
|
||||||
route: route,
|
route: route,
|
||||||
isHtmxRequest: request.isHtmxRequest,
|
isHtmxRequest: request.isHtmxRequest,
|
||||||
logger: request.logger,
|
logger: request.logger
|
||||||
authenticateUser: { request.session.authenticate($0) },
|
|
||||||
currentUser: {
|
|
||||||
try request.auth.require(User.self)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return AnyHTMLResponse(value: html)
|
return AnyHTMLResponse(value: html)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import ApiController
|
import ApiController
|
||||||
|
import AuthClient
|
||||||
import DatabaseClient
|
import DatabaseClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
|
import ManualDCore
|
||||||
import Vapor
|
import Vapor
|
||||||
import ViewController
|
import ViewController
|
||||||
|
|
||||||
// Taken from discussions page on `swift-dependencies`.
|
// Taken from discussions page on `swift-dependencies`.
|
||||||
|
|
||||||
// FIX: Use live view controller.
|
|
||||||
struct DependenciesMiddleware: AsyncMiddleware {
|
struct DependenciesMiddleware: AsyncMiddleware {
|
||||||
|
|
||||||
private let values: DependencyValues.Continuation
|
private let values: DependencyValues.Continuation
|
||||||
@@ -29,6 +30,7 @@ struct DependenciesMiddleware: AsyncMiddleware {
|
|||||||
try await values.yield {
|
try await values.yield {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
$0.apiController = apiController
|
$0.apiController = apiController
|
||||||
|
$0.authClient = .live(on: request)
|
||||||
$0.database = database
|
$0.database = database
|
||||||
// $0.dateFormatter = .liveValue
|
// $0.dateFormatter = .liveValue
|
||||||
$0.viewController = viewController
|
$0.viewController = viewController
|
||||||
|
|||||||
51
Sources/AuthClient/Interface.swift
Normal file
51
Sources/AuthClient/Interface.swift
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import DatabaseClient
|
||||||
|
import Dependencies
|
||||||
|
import DependenciesMacros
|
||||||
|
import ManualDCore
|
||||||
|
import Vapor
|
||||||
|
|
||||||
|
extension DependencyValues {
|
||||||
|
public var authClient: AuthClient {
|
||||||
|
get { self[AuthClient.self] }
|
||||||
|
set { self[AuthClient.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DependencyClient
|
||||||
|
public struct AuthClient: Sendable {
|
||||||
|
public var createAndLogin: @Sendable (User.Create) async throws -> User
|
||||||
|
public var currentUser: @Sendable () throws -> User
|
||||||
|
public var login: @Sendable (User.Login) async throws -> User
|
||||||
|
public var logout: @Sendable () throws -> Void
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AuthClient: TestDependencyKey {
|
||||||
|
public static let testValue = Self()
|
||||||
|
|
||||||
|
public static func live(on request: Request) -> Self {
|
||||||
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
createAndLogin: { createForm in
|
||||||
|
let user = try await database.users.create(createForm)
|
||||||
|
_ = try await database.users.login(
|
||||||
|
.init(email: createForm.email, password: createForm.password)
|
||||||
|
)
|
||||||
|
request.auth.login(user)
|
||||||
|
request.logger.debug("LOGGED IN: \(user.id)")
|
||||||
|
return user
|
||||||
|
},
|
||||||
|
currentUser: {
|
||||||
|
try request.auth.require(User.self)
|
||||||
|
},
|
||||||
|
login: { loginForm in
|
||||||
|
let token = try await database.users.login(loginForm)
|
||||||
|
let user = try await database.users.get(token.userID)!
|
||||||
|
request.session.authenticate(user)
|
||||||
|
request.logger.debug("LOGGED IN: \(user.id)")
|
||||||
|
return user
|
||||||
|
},
|
||||||
|
logout: { request.auth.logout(User.self) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import AuthClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
import DependenciesMacros
|
import DependenciesMacros
|
||||||
import Elementary
|
import Elementary
|
||||||
@@ -15,10 +16,6 @@ public typealias AnySendableHTML = (any HTML & Sendable)
|
|||||||
|
|
||||||
@DependencyClient
|
@DependencyClient
|
||||||
public struct ViewController: Sendable {
|
public struct ViewController: Sendable {
|
||||||
|
|
||||||
public typealias AuthenticateHandler = @Sendable (User) -> Void
|
|
||||||
public typealias CurrentUserHandler = @Sendable () throws -> User
|
|
||||||
|
|
||||||
public var view: @Sendable (Request) async throws -> AnySendableHTML
|
public var view: @Sendable (Request) async throws -> AnySendableHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,21 +26,15 @@ extension ViewController {
|
|||||||
public let route: SiteRoute.View
|
public let route: SiteRoute.View
|
||||||
public let isHtmxRequest: Bool
|
public let isHtmxRequest: Bool
|
||||||
public let logger: Logger
|
public let logger: Logger
|
||||||
public let authenticateUser: AuthenticateHandler
|
|
||||||
public let currentUser: CurrentUserHandler
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
route: SiteRoute.View,
|
route: SiteRoute.View,
|
||||||
isHtmxRequest: Bool,
|
isHtmxRequest: Bool,
|
||||||
logger: Logger,
|
logger: Logger
|
||||||
authenticateUser: @escaping AuthenticateHandler,
|
|
||||||
currentUser: @escaping CurrentUserHandler
|
|
||||||
) {
|
) {
|
||||||
self.route = route
|
self.route = route
|
||||||
self.isHtmxRequest = isHtmxRequest
|
self.isHtmxRequest = isHtmxRequest
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.authenticateUser = authenticateUser
|
|
||||||
self.currentUser = currentUser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -62,28 +53,23 @@ extension ViewController: DependencyKey {
|
|||||||
|
|
||||||
extension ViewController.Request {
|
extension ViewController.Request {
|
||||||
|
|
||||||
|
func currentUser() throws -> User {
|
||||||
|
@Dependency(\.authClient.currentUser) var currentUser
|
||||||
|
return try currentUser()
|
||||||
|
}
|
||||||
|
|
||||||
func authenticate(
|
func authenticate(
|
||||||
_ login: User.Login
|
_ login: User.Login
|
||||||
) async throws -> User {
|
) async throws -> User {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.authClient) var auth
|
||||||
let token = try await users.login(login)
|
return try await auth.login(login)
|
||||||
let user = try await users.get(token.userID)!
|
|
||||||
authenticateUser(user)
|
|
||||||
logger.debug("Logged in user: \(user.id)")
|
|
||||||
return user
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func createAndAuthenticate(
|
func createAndAuthenticate(
|
||||||
_ signup: User.Create
|
_ signup: User.Create
|
||||||
) async throws -> User {
|
) async throws -> User {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.authClient) var auth
|
||||||
let user = try await users.create(signup)
|
return try await auth.createAndLogin(signup)
|
||||||
let _ = try await users.login(
|
|
||||||
.init(email: signup.email, password: signup.password)
|
|
||||||
)
|
|
||||||
authenticateUser(user)
|
|
||||||
logger.debug("Created and logged in user: \(user.id)")
|
|
||||||
return user
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user