feat: Adds file client.

This commit is contained in:
2026-01-27 13:50:42 -05:00
parent 6064b5267a
commit 273da46db2
4 changed files with 49 additions and 10 deletions

View File

@@ -9,6 +9,7 @@ let package = Package(
.library(name: "ApiController", targets: ["ApiController"]),
.library(name: "AuthClient", targets: ["AuthClient"]),
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
.library(name: "FileClient", targets: ["FileClient"]),
.library(name: "PdfClient", targets: ["PdfClient"]),
.library(name: "ProjectClient", targets: ["ProjectClient"]),
.library(name: "ManualDCore", targets: ["ManualDCore"]),
@@ -76,6 +77,13 @@ let package = Package(
.product(name: "Vapor", package: "vapor"),
]
),
.target(
name: "FileClient",
dependencies: [
.product(name: "Dependencies", package: "swift-dependencies"),
.product(name: "DependenciesMacros", package: "swift-dependencies"),
]
),
.target(
name: "PdfClient",
dependencies: [

View File

@@ -14,8 +14,7 @@ private let viewRouteMiddleware: [any Middleware] = [
extension SiteRoute.View {
var middleware: [any Middleware]? {
switch self {
// TODO: Should pdf require authentication, just here now for testing.
case .project(.detail(_, .pdf)), .login, .signup, .test:
case .login, .signup, .test:
return nil
case .project, .user:
return viewRouteMiddleware

View File

@@ -120,7 +120,7 @@ func handlePdf(_ projectID: Project.ID, on request: Request) async throws -> Res
let html = try await projectClient.toHTML(projectID)
let url = "/tmp/\(projectID)"
try await request.fileio.writeFile(.init(string: html.renderFormatted()), at: "\(url).html")
try await request.fileio.writeFile(.init(string: html.render()), at: "\(url).html")
let process = Process()
let standardInput = Pipe()
@@ -129,21 +129,24 @@ func handlePdf(_ projectID: Project.ID, on request: Request) async throws -> Res
process.standardOutput = standardOutput
process.executableURL = URL(fileURLWithPath: "/bin/pandoc")
process.arguments = [
"\(url).html", "--pdf-engine=weasyprint", "-f", "html",
"\(url).html",
"--pdf-engine=weasyprint",
"--from=html",
"--css=Public/css/pdf.css",
"-o", "\(url).pdf",
]
try process.run()
process.waitUntilExit()
var headers = HTTPHeaders()
headers.add(name: .contentType, value: "application/octet-stream")
headers.add(name: .contentDisposition, value: "attachment")
let response = try await request.fileio.asyncStreamFile(at: "\(url).pdf", mediaType: .pdf)
let response = try await request.fileio.asyncStreamFile(at: "\(url).pdf", mediaType: .pdf) { _ in
// Remove files here.
try FileManager.default.removeItem(atPath: "\(url).pdf")
try FileManager.default.removeItem(atPath: "\(url).html")
}
response.headers.replaceOrAdd(name: .contentType, value: "application/octet-stream")
response.headers.replaceOrAdd(
name: .contentDisposition, value: "attachment; filename=Duct-Calc.pdf")
name: .contentDisposition, value: "attachment; filename=Duct-Calc.pdf"
)
return response
}

View File

@@ -0,0 +1,29 @@
import Dependencies
import DependenciesMacros
import Foundation
extension DependencyValues {
public var fileClient: FileClient {
get { self[FileClient.self] }
set { self[FileClient.self] = newValue }
}
}
@DependencyClient
public struct FileClient: Sendable {
public var writeFile: @Sendable (String, String) async throws -> Void
public var removeFile: @Sendable (String) async throws -> Void
}
extension FileClient: DependencyKey {
public static let testValue = Self()
public static let liveValue = Self(
writeFile: { contents, path in
try contents.write(to: URL(fileURLWithPath: path), atomically: true, encoding: .utf8)
},
removeFile: { path in
try FileManager.default.removeItem(atPath: path)
}
)
}