feat: Begins view controller integration into app target.
This commit is contained in:
@@ -37,7 +37,8 @@ let package = Package(
|
|||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "App",
|
name: "App",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"DatabaseClientLive",
|
.target(name: "DatabaseClientLive"),
|
||||||
|
.target(name: "ViewControllerLive"),
|
||||||
.product(name: "Fluent", package: "fluent"),
|
.product(name: "Fluent", package: "fluent"),
|
||||||
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
|
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
|
||||||
.product(name: "Vapor", package: "vapor"),
|
.product(name: "Vapor", package: "vapor"),
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import DatabaseClientLive
|
import DatabaseClientLive
|
||||||
import Dependencies
|
import Dependencies
|
||||||
|
import Elementary
|
||||||
import Fluent
|
import Fluent
|
||||||
import FluentSQLiteDriver
|
import FluentSQLiteDriver
|
||||||
import NIOSSL
|
import NIOSSL
|
||||||
import SharedModels
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
|
import VaporElementary
|
||||||
@preconcurrency import VaporRouting
|
@preconcurrency import VaporRouting
|
||||||
|
import ViewControllerLive
|
||||||
|
|
||||||
// configures your application
|
// configures your application
|
||||||
public func configure(
|
public func configure(
|
||||||
@@ -95,3 +98,52 @@ func siteHandler(
|
|||||||
return try await route.handle(request: request)
|
return try await route.handle(request: request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ViewRoute {
|
||||||
|
func respond(request: Request) async throws -> any AsyncResponseEncodable {
|
||||||
|
if self == .index {
|
||||||
|
return request.redirect(to: ViewRoute.router.path(for: .purchaseOrder(.index)))
|
||||||
|
} else {
|
||||||
|
let html = try await view(isHtmxRequest: request.isHtmxRequest, authenticate: { request.auth.login($0) })
|
||||||
|
// Delete routes return nil, but are valid routes.
|
||||||
|
guard let html else {
|
||||||
|
return HTTPStatus.ok
|
||||||
|
}
|
||||||
|
return AnyHTMLResponse(value: html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnyHTMLResponse: AsyncResponseEncodable {
|
||||||
|
|
||||||
|
public var chunkSize: Int
|
||||||
|
public var headers: HTTPHeaders = ["Content-Type": "text/html; charset=utf-8"]
|
||||||
|
var value: _SendableAnyHTMLBox
|
||||||
|
|
||||||
|
init(chunkSize: Int = 1024, additionalHeaders: HTTPHeaders = [:], value: any HTML & Sendable) {
|
||||||
|
self.chunkSize = chunkSize
|
||||||
|
if additionalHeaders.contains(name: .contentType) {
|
||||||
|
self.headers = additionalHeaders
|
||||||
|
} else {
|
||||||
|
headers.add(contentsOf: additionalHeaders)
|
||||||
|
}
|
||||||
|
self.value = .init(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeResponse(for request: Request) async throws -> Response {
|
||||||
|
Response(
|
||||||
|
status: .ok,
|
||||||
|
headers: headers,
|
||||||
|
body: .init(asyncStream: { [value, chunkSize] writer in
|
||||||
|
guard let html = value.tryTake() else {
|
||||||
|
assertionFailure("Non-sendable HTML value consumed more than once")
|
||||||
|
request.logger.error("Non-sendable HTML value consumed more than once")
|
||||||
|
throw Abort(.internalServerError)
|
||||||
|
}
|
||||||
|
try await writer.writeHTML(html, chunkSize: chunkSize)
|
||||||
|
try await writer.write(.end)
|
||||||
|
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import Elementary
|
|||||||
import SharedModels
|
import SharedModels
|
||||||
import Vapor
|
import Vapor
|
||||||
|
|
||||||
extension SharedModels.ViewRoute {
|
public typealias AnySendableHTML = (any HTML & Sendable)
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool, authenticate: @escaping @Sendable (User) -> Void) async throws -> (any HTML)? {
|
public extension SharedModels.ViewRoute {
|
||||||
|
|
||||||
|
func view(isHtmxRequest: Bool, authenticate: @escaping @Sendable (User) -> Void) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.database.users) var users
|
||||||
switch self {
|
switch self {
|
||||||
case .index:
|
case .index:
|
||||||
@@ -60,7 +62,7 @@ extension SharedModels.ViewRoute.EmployeeRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database.employees) var employees
|
@Dependency(\.database.employees) var employees
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
@@ -110,7 +112,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
@Dependency(\.database.purchaseOrders) var purchaseOrders
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
@@ -163,7 +165,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> any HTML {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
switch self {
|
switch self {
|
||||||
case let .index(context: context, table: table):
|
case let .index(context: context, table: table):
|
||||||
@@ -193,7 +195,7 @@ extension SharedModels.ViewRoute.UserRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.database.users) var users
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
@@ -235,7 +237,7 @@ extension SharedModels.ViewRoute.VendorRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
@@ -277,7 +279,7 @@ extension SharedModels.ViewRoute.VendorRoute {
|
|||||||
|
|
||||||
extension SharedModels.ViewRoute.VendorBranchRoute {
|
extension SharedModels.ViewRoute.VendorBranchRoute {
|
||||||
|
|
||||||
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
|
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
@@ -350,7 +352,7 @@ private func render<C: HTML>(
|
|||||||
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
||||||
_ isHtmxRequest: Bool,
|
_ isHtmxRequest: Bool,
|
||||||
@HTMLBuilder html: () -> C
|
@HTMLBuilder html: () -> C
|
||||||
) async rethrows -> any HTML {
|
) async rethrows -> AnySendableHTML where C: Sendable {
|
||||||
guard isHtmxRequest else {
|
guard isHtmxRequest else {
|
||||||
return try await mainPage(html())
|
return try await mainPage(html())
|
||||||
}
|
}
|
||||||
@@ -361,6 +363,6 @@ private func render<C: HTML>(
|
|||||||
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
_ mainPage: (C) async throws -> any SendableHTMLDocument,
|
||||||
_ isHtmxRequest: Bool,
|
_ isHtmxRequest: Bool,
|
||||||
_ html: @autoclosure @escaping () -> C
|
_ html: @autoclosure @escaping () -> C
|
||||||
) async rethrows -> any HTML {
|
) async rethrows -> AnySendableHTML where C: Sendable {
|
||||||
try await render(mainPage, isHtmxRequest) { html() }
|
try await render(mainPage, isHtmxRequest) { html() }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user