feat: Begins integrating database client into vapor app.
This commit is contained in:
@@ -1,64 +1,64 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct EmployeeApiController: RouteCollection {
|
||||
|
||||
@Dependency(\.employees) var employees
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let protected = routes.apiProtected(route: "employees")
|
||||
protected.get(use: index(req:))
|
||||
protected.post(use: create(req:))
|
||||
protected.group(":employeeID") {
|
||||
$0.get(use: get(req:))
|
||||
$0.put(use: update(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> [Employee.DTO] {
|
||||
let params = try req.query.decode(EmployeesIndexQuery.self)
|
||||
return try await employees.fetchAll(params.active == true ? .active : .default)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> Employee.DTO {
|
||||
try await employees.create(
|
||||
req.ensureValidContent(Employee.Create.self)
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func get(req: Request) async throws -> Employee.DTO {
|
||||
guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self),
|
||||
let employee = try await employees.get(id)
|
||||
else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
return employee
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func update(req: Request) async throws -> Employee.DTO {
|
||||
guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Employee id value not provided")
|
||||
}
|
||||
let updates = try req.ensureValidContent(Employee.Update.self)
|
||||
return try await employees.update(employeeID, updates)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Employee id value not provided")
|
||||
}
|
||||
try await employees.delete(employeeID)
|
||||
return .ok
|
||||
}
|
||||
}
|
||||
|
||||
struct EmployeesIndexQuery: Content {
|
||||
let active: Bool?
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct EmployeeApiController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.employees) var employees
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let protected = routes.apiProtected(route: "employees")
|
||||
// protected.get(use: index(req:))
|
||||
// protected.post(use: create(req:))
|
||||
// protected.group(":employeeID") {
|
||||
// $0.get(use: get(req:))
|
||||
// $0.put(use: update(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> [Employee.DTO] {
|
||||
// let params = try req.query.decode(EmployeesIndexQuery.self)
|
||||
// return try await employees.fetchAll(params.active == true ? .active : .default)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> Employee.DTO {
|
||||
// try await employees.create(
|
||||
// req.ensureValidContent(Employee.Create.self)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func get(req: Request) async throws -> Employee.DTO {
|
||||
// guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self),
|
||||
// let employee = try await employees.get(id)
|
||||
// else {
|
||||
// throw Abort(.notFound)
|
||||
// }
|
||||
// return employee
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> Employee.DTO {
|
||||
// guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Employee id value not provided")
|
||||
// }
|
||||
// let updates = try req.ensureValidContent(Employee.Update.self)
|
||||
// return try await employees.update(employeeID, updates)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> HTTPStatus {
|
||||
// guard let employeeID = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Employee id value not provided")
|
||||
// }
|
||||
// try await employees.delete(employeeID)
|
||||
// return .ok
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// struct EmployeesIndexQuery: Content {
|
||||
// let active: Bool?
|
||||
// }
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
// TODO: Add update route.
|
||||
|
||||
struct PurchaseOrderApiController: RouteCollection {
|
||||
|
||||
@Dependency(\.purchaseOrders) var purchaseOrders
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let protected = routes.apiProtected(route: "purchase-orders")
|
||||
protected.get(use: index(req:))
|
||||
protected.post(use: create(req:))
|
||||
protected.group(":id") {
|
||||
$0.get(use: get(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> [PurchaseOrder.DTO] {
|
||||
try await purchaseOrders.fetchAll()
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
try await purchaseOrders.create(
|
||||
req.ensureValidContent(PurchaseOrder.Create.self),
|
||||
req.auth.require(User.self).requireID()
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func get(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self),
|
||||
let purchaseOrder = try await purchaseOrders.get(id)
|
||||
else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
return purchaseOrder
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Purchase order id not provided.")
|
||||
}
|
||||
try await purchaseOrders.delete(id)
|
||||
return .ok
|
||||
}
|
||||
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Purchase order id not provided.")
|
||||
// }
|
||||
// try await purchaseOrders.delete(id: id, on: req.db)
|
||||
// return .ok
|
||||
// }
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// // TODO: Add update route.
|
||||
//
|
||||
// struct PurchaseOrderApiController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.purchaseOrders) var purchaseOrders
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let protected = routes.apiProtected(route: "purchase-orders")
|
||||
// protected.get(use: index(req:))
|
||||
// protected.post(use: create(req:))
|
||||
// protected.group(":id") {
|
||||
// $0.get(use: get(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> [PurchaseOrder.DTO] {
|
||||
// try await purchaseOrders.fetchAll()
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
// try await purchaseOrders.create(
|
||||
// req.ensureValidContent(PurchaseOrder.Create.self),
|
||||
// req.auth.require(User.self).requireID()
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func get(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self),
|
||||
// let purchaseOrder = try await purchaseOrders.get(id)
|
||||
// else {
|
||||
// throw Abort(.notFound)
|
||||
// }
|
||||
// return purchaseOrder
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> HTTPStatus {
|
||||
// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Purchase order id not provided.")
|
||||
// }
|
||||
// try await purchaseOrders.delete(id)
|
||||
// return .ok
|
||||
// }
|
||||
//
|
||||
// // @Sendable
|
||||
// // func update(req: Request) async throws -> PurchaseOrder.DTO {
|
||||
// // guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
// // throw Abort(.badRequest, reason: "Purchase order id not provided.")
|
||||
// // }
|
||||
// // try await purchaseOrders.delete(id: id, on: req.db)
|
||||
// // return .ok
|
||||
// // }
|
||||
// }
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import DatabaseClient
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import SharedModels
|
||||
import Vapor
|
||||
|
||||
// TODO: Add update and get by id.
|
||||
struct UserApiController: RouteCollection {
|
||||
|
||||
@Dependency(\.users) var users
|
||||
@Dependency(\.database.users) var users
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let unProtected = routes.apiUnprotected(route: "users")
|
||||
@@ -20,26 +22,28 @@ struct UserApiController: RouteCollection {
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> [User.DTO] {
|
||||
func index(req: Request) async throws -> [User] {
|
||||
try await users.fetchAll()
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> User.DTO {
|
||||
func create(req: Request) async throws -> User {
|
||||
// Allow the first user to be created without authentication.
|
||||
let count = try await User.query(on: req.db).count()
|
||||
// let count = try await User.query(on: req.db).count()
|
||||
let count = try await users.count()
|
||||
if count > 0 {
|
||||
guard req.auth.get(User.self) != nil else {
|
||||
throw Abort(.unauthorized)
|
||||
}
|
||||
}
|
||||
return try await users.create(req.ensureValidContent(User.Create.self))
|
||||
return try await users.create(req.content.decode(User.Create.self))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func login(req: Request) async throws -> UserToken {
|
||||
func login(req: Request) async throws -> User {
|
||||
let user = try req.auth.require(User.self)
|
||||
return try await users.login(user)
|
||||
return user
|
||||
// return try await users.login(user)
|
||||
}
|
||||
|
||||
// @Sendable
|
||||
@@ -50,9 +54,10 @@ struct UserApiController: RouteCollection {
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
guard let id = req.parameters.get("id", as: User.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "User id not provided")
|
||||
}
|
||||
// guard let id = req.parameters.get("id", as: User.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "User id not provided")
|
||||
// }
|
||||
let id = try req.ensureIDPathComponent()
|
||||
try await users.delete(id)
|
||||
return .ok
|
||||
}
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct VendorApiController: RouteCollection {
|
||||
|
||||
@Dependency(\.vendors) var vendors
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let protected = routes.apiProtected(route: "vendors")
|
||||
protected.get(use: index(req:))
|
||||
protected.post(use: create(req:))
|
||||
protected.group(":id") {
|
||||
$0.put(use: update(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> [Vendor.DTO] {
|
||||
let params = try req.query.decode(VendorsIndexQuery.self)
|
||||
return try await vendors.fetchAll(params.fetchRequest)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> Vendor.DTO {
|
||||
try await vendors.create(req.ensureValidContent(Vendor.Create.self))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func update(req: Request) async throws -> Vendor.DTO {
|
||||
guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
}
|
||||
try Vendor.Update.validate(content: req)
|
||||
let updates = try req.content.decode(Vendor.Update.self)
|
||||
return try await vendors.update(id, updates)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
}
|
||||
try await vendors.delete(id)
|
||||
return .ok
|
||||
}
|
||||
}
|
||||
|
||||
struct VendorsIndexQuery: Content {
|
||||
let branches: Bool?
|
||||
|
||||
var fetchRequest: VendorDB.FetchRequest {
|
||||
if branches == true { return .withBranches }
|
||||
return .default
|
||||
}
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct VendorApiController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.vendors) var vendors
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let protected = routes.apiProtected(route: "vendors")
|
||||
// protected.get(use: index(req:))
|
||||
// protected.post(use: create(req:))
|
||||
// protected.group(":id") {
|
||||
// $0.put(use: update(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> [Vendor.DTO] {
|
||||
// let params = try req.query.decode(VendorsIndexQuery.self)
|
||||
// return try await vendors.fetchAll(params.fetchRequest)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> Vendor.DTO {
|
||||
// try await vendors.create(req.ensureValidContent(Vendor.Create.self))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> Vendor.DTO {
|
||||
// guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
// }
|
||||
// try Vendor.Update.validate(content: req)
|
||||
// let updates = try req.content.decode(Vendor.Update.self)
|
||||
// return try await vendors.update(id, updates)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> HTTPStatus {
|
||||
// guard let id = req.parameters.get("id", as: Vendor.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
// }
|
||||
// try await vendors.delete(id)
|
||||
// return .ok
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// struct VendorsIndexQuery: Content {
|
||||
// let branches: Bool?
|
||||
//
|
||||
// var fetchRequest: VendorDB.FetchRequest {
|
||||
// if branches == true { return .withBranches }
|
||||
// return .default
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct VendorBranchApiController: RouteCollection {
|
||||
|
||||
@Dependency(\.vendorBranches) var vendorBranches
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let prefix = routes.apiProtected(route: "vendors")
|
||||
let root = prefix.grouped("branches")
|
||||
root.get(use: index(req:))
|
||||
root.group(":id") {
|
||||
$0.put(use: update(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
}
|
||||
|
||||
prefix.group(":vendorID", "branches") {
|
||||
$0.get(use: indexForVendor(req:))
|
||||
$0.post(use: create(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> [VendorBranch.DTO] {
|
||||
try await vendorBranches.fetchAll()
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func indexForVendor(req: Request) async throws -> [VendorBranch.DTO] {
|
||||
guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
}
|
||||
return try await vendorBranches.fetchAll(.for(vendorID: id))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> VendorBranch.DTO {
|
||||
guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
}
|
||||
return try await vendorBranches.create(
|
||||
req.ensureValidContent(VendorBranch.Create.self),
|
||||
id
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func update(req: Request) async throws -> VendorBranch.DTO {
|
||||
guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor branch id not provided.")
|
||||
}
|
||||
try VendorBranch.Update.validate(content: req)
|
||||
let updates = try req.content.decode(VendorBranch.Update.self)
|
||||
return try await vendorBranches.update(id, updates)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Vendor branch id not provided.")
|
||||
}
|
||||
try await vendorBranches.delete(id)
|
||||
return .ok
|
||||
}
|
||||
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct VendorBranchApiController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.vendorBranches) var vendorBranches
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let prefix = routes.apiProtected(route: "vendors")
|
||||
// let root = prefix.grouped("branches")
|
||||
// root.get(use: index(req:))
|
||||
// root.group(":id") {
|
||||
// $0.put(use: update(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// }
|
||||
//
|
||||
// prefix.group(":vendorID", "branches") {
|
||||
// $0.get(use: indexForVendor(req:))
|
||||
// $0.post(use: create(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> [VendorBranch.DTO] {
|
||||
// try await vendorBranches.fetchAll()
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func indexForVendor(req: Request) async throws -> [VendorBranch.DTO] {
|
||||
// guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
// }
|
||||
// return try await vendorBranches.fetchAll(.for(vendorID: id))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> VendorBranch.DTO {
|
||||
// guard let id = req.parameters.get("vendorID", as: Vendor.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor id not provided.")
|
||||
// }
|
||||
// return try await vendorBranches.create(
|
||||
// req.ensureValidContent(VendorBranch.Create.self),
|
||||
// id
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> VendorBranch.DTO {
|
||||
// guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor branch id not provided.")
|
||||
// }
|
||||
// try VendorBranch.Update.validate(content: req)
|
||||
// let updates = try req.content.decode(VendorBranch.Update.self)
|
||||
// return try await vendorBranches.update(id, updates)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> HTTPStatus {
|
||||
// guard let id = req.parameters.get("id", as: VendorBranch.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Vendor branch id not provided.")
|
||||
// }
|
||||
// try await vendorBranches.delete(id)
|
||||
// return .ok
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
@@ -3,10 +3,10 @@ import Vapor
|
||||
|
||||
struct ApiController: RouteCollection {
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
try routes.register(collection: EmployeeApiController())
|
||||
try routes.register(collection: PurchaseOrderApiController())
|
||||
// try routes.register(collection: EmployeeApiController())
|
||||
// try routes.register(collection: PurchaseOrderApiController())
|
||||
try routes.register(collection: UserApiController())
|
||||
try routes.register(collection: VendorApiController())
|
||||
try routes.register(collection: VendorBranchApiController())
|
||||
// try routes.register(collection: VendorApiController())
|
||||
// try routes.register(collection: VendorBranchApiController())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +1,158 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Leaf
|
||||
import Vapor
|
||||
|
||||
struct EmployeeViewController: RouteCollection {
|
||||
|
||||
@Dependency(\.employees) var employees
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let protected = routes.protected.grouped("employees")
|
||||
protected.get(use: index(req:))
|
||||
protected.get("form", use: employeeForm(req:))
|
||||
protected.post(use: create(req:))
|
||||
protected.group(":employeeID") {
|
||||
$0.get(use: get(req:))
|
||||
$0.get("edit", use: edit(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
$0.put(use: update(req:))
|
||||
$0.patch("toggle-active", use: toggleActive(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> View {
|
||||
return try await renderIndex(req)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
private func renderIndex(
|
||||
_ req: Request,
|
||||
_ employee: Employee.DTO? = nil,
|
||||
_ form: EmployeeFormCTX? = nil
|
||||
) async throws -> View {
|
||||
return try await req.view.render(
|
||||
"employees/index",
|
||||
EmployeesCTX(employee: employee, employees: employees.fetchAll(), form: form ?? .init())
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> View {
|
||||
try Employee.Create.validate(content: req)
|
||||
let employee = try await employees.create(req.content.decode(Employee.Create.self))
|
||||
return try await req.view.render("employees/table-row", employee)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func get(req: Request) async throws -> View {
|
||||
let employee = try await employees.get(req.ensureIDPathComponent(key: "employeeID"))
|
||||
// Check if we've rendered the page yet.
|
||||
guard req.isHtmxRequest else {
|
||||
return try await renderIndex(req, employee)
|
||||
}
|
||||
return try await req.view.render("employees/detail", ["employee": employee])
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func toggleActive(req: Request) async throws -> View {
|
||||
guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Employee id not supplied.")
|
||||
}
|
||||
let employee = try await employees.toggleActive(id)
|
||||
return try await req.view.render("employees/table-row", employee)
|
||||
}
|
||||
|
||||
// TODO: I think we can just return a response and remove the table-row, here.
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> View {
|
||||
let id = try req.requireEmployeeID()
|
||||
_ = try await employees.delete(id)
|
||||
let employees = try await employees.fetchAll()
|
||||
return try await req.view.render("employees/table", ["employees": employees])
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func edit(req: Request) async throws -> View {
|
||||
guard let employee = try await employees.get(req.parameters.get("employeeID")) else {
|
||||
throw Abort(.notFound)
|
||||
}
|
||||
return try await req.view.render("employees/detail", EmployeeDetailCTX(editing: true, employee: employee))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func update(req: Request) async throws -> View {
|
||||
let id = try req.requireEmployeeID()
|
||||
try Employee.Update.validate(content: req)
|
||||
let updates = try req.content.decode(Employee.Update.self)
|
||||
req.logger.info("Employee updates: \(updates)")
|
||||
let employee = try await employees.update(id, updates)
|
||||
req.logger.info("Done updating employee: \(employee)")
|
||||
return try await req.view.render("employees/table-row", employee)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func employeeForm(req: Request) async throws -> View {
|
||||
try await req.view.render("employees/form", EmployeeFormCTX())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension Request {
|
||||
func requireEmployeeID() throws -> Employee.IDValue {
|
||||
guard let id = parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Employee id not supplied")
|
||||
}
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
private struct EmployeeDetailCTX: Content {
|
||||
let editing: Bool
|
||||
let employee: Employee.DTO?
|
||||
|
||||
init(editing: Bool = false, employee: Employee.DTO? = nil) {
|
||||
self.editing = editing
|
||||
self.employee = employee
|
||||
}
|
||||
}
|
||||
|
||||
private struct EmployeesCTX: Content {
|
||||
let employee: Employee.DTO?
|
||||
let employees: [Employee.DTO]
|
||||
let form: EmployeeFormCTX
|
||||
|
||||
init(
|
||||
employee: Employee.DTO? = nil,
|
||||
employees: [Employee.DTO],
|
||||
form: EmployeeFormCTX? = nil
|
||||
) {
|
||||
self.employee = employee
|
||||
self.employees = employees
|
||||
self.form = form ?? .init()
|
||||
}
|
||||
}
|
||||
|
||||
private struct EmployeeFormCTX: Content {
|
||||
|
||||
let htmxForm: HtmxFormCTX<Context>
|
||||
|
||||
init(employee: Employee.DTO? = nil) {
|
||||
self.htmxForm = .init(
|
||||
formClass: "employee-form",
|
||||
formId: "employee-form",
|
||||
htmxTargetUrl: employee?.id == nil ? .post("/employees") : .put("/employees/\(employee!.id!)"),
|
||||
htmxTarget: "#employee-table",
|
||||
htmxPushUrl: false,
|
||||
htmxResetAfterRequest: true,
|
||||
htmxSwapOob: nil,
|
||||
htmxSwap: employee == nil ? .outerHTML : nil,
|
||||
context: .init(employee: employee)
|
||||
)
|
||||
}
|
||||
|
||||
struct Context: Content {
|
||||
let employee: Employee.DTO?
|
||||
}
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Leaf
|
||||
// import Vapor
|
||||
//
|
||||
// struct EmployeeViewController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.employees) var employees
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let protected = routes.protected.grouped("employees")
|
||||
// protected.get(use: index(req:))
|
||||
// protected.get("form", use: employeeForm(req:))
|
||||
// protected.post(use: create(req:))
|
||||
// protected.group(":employeeID") {
|
||||
// $0.get(use: get(req:))
|
||||
// $0.get("edit", use: edit(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// $0.put(use: update(req:))
|
||||
// $0.patch("toggle-active", use: toggleActive(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> View {
|
||||
// return try await renderIndex(req)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// private func renderIndex(
|
||||
// _ req: Request,
|
||||
// _ employee: Employee.DTO? = nil,
|
||||
// _ form: EmployeeFormCTX? = nil
|
||||
// ) async throws -> View {
|
||||
// return try await req.view.render(
|
||||
// "employees/index",
|
||||
// EmployeesCTX(employee: employee, employees: employees.fetchAll(), form: form ?? .init())
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> View {
|
||||
// try Employee.Create.validate(content: req)
|
||||
// let employee = try await employees.create(req.content.decode(Employee.Create.self))
|
||||
// return try await req.view.render("employees/table-row", employee)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func get(req: Request) async throws -> View {
|
||||
// let employee = try await employees.get(req.ensureIDPathComponent(key: "employeeID"))
|
||||
// // Check if we've rendered the page yet.
|
||||
// guard req.isHtmxRequest else {
|
||||
// return try await renderIndex(req, employee)
|
||||
// }
|
||||
// return try await req.view.render("employees/detail", ["employee": employee])
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func toggleActive(req: Request) async throws -> View {
|
||||
// guard let id = req.parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Employee id not supplied.")
|
||||
// }
|
||||
// let employee = try await employees.toggleActive(id)
|
||||
// return try await req.view.render("employees/table-row", employee)
|
||||
// }
|
||||
//
|
||||
// // TODO: I think we can just return a response and remove the table-row, here.
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> View {
|
||||
// let id = try req.requireEmployeeID()
|
||||
// _ = try await employees.delete(id)
|
||||
// let employees = try await employees.fetchAll()
|
||||
// return try await req.view.render("employees/table", ["employees": employees])
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func edit(req: Request) async throws -> View {
|
||||
// guard let employee = try await employees.get(req.parameters.get("employeeID")) else {
|
||||
// throw Abort(.notFound)
|
||||
// }
|
||||
// return try await req.view.render("employees/detail", EmployeeDetailCTX(editing: true, employee: employee))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> View {
|
||||
// let id = try req.requireEmployeeID()
|
||||
// try Employee.Update.validate(content: req)
|
||||
// let updates = try req.content.decode(Employee.Update.self)
|
||||
// req.logger.info("Employee updates: \(updates)")
|
||||
// let employee = try await employees.update(id, updates)
|
||||
// req.logger.info("Done updating employee: \(employee)")
|
||||
// return try await req.view.render("employees/table-row", employee)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func employeeForm(req: Request) async throws -> View {
|
||||
// try await req.view.render("employees/form", EmployeeFormCTX())
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private extension Request {
|
||||
// func requireEmployeeID() throws -> Employee.IDValue {
|
||||
// guard let id = parameters.get("employeeID", as: Employee.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Employee id not supplied")
|
||||
// }
|
||||
// return id
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct EmployeeDetailCTX: Content {
|
||||
// let editing: Bool
|
||||
// let employee: Employee.DTO?
|
||||
//
|
||||
// init(editing: Bool = false, employee: Employee.DTO? = nil) {
|
||||
// self.editing = editing
|
||||
// self.employee = employee
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct EmployeesCTX: Content {
|
||||
// let employee: Employee.DTO?
|
||||
// let employees: [Employee.DTO]
|
||||
// let form: EmployeeFormCTX
|
||||
//
|
||||
// init(
|
||||
// employee: Employee.DTO? = nil,
|
||||
// employees: [Employee.DTO],
|
||||
// form: EmployeeFormCTX? = nil
|
||||
// ) {
|
||||
// self.employee = employee
|
||||
// self.employees = employees
|
||||
// self.form = form ?? .init()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct EmployeeFormCTX: Content {
|
||||
//
|
||||
// let htmxForm: HtmxFormCTX<Context>
|
||||
//
|
||||
// init(employee: Employee.DTO? = nil) {
|
||||
// self.htmxForm = .init(
|
||||
// formClass: "employee-form",
|
||||
// formId: "employee-form",
|
||||
// htmxTargetUrl: employee?.id == nil ? .post("/employees") : .put("/employees/\(employee!.id!)"),
|
||||
// htmxTarget: "#employee-table",
|
||||
// htmxPushUrl: false,
|
||||
// htmxResetAfterRequest: true,
|
||||
// htmxSwapOob: nil,
|
||||
// htmxSwap: employee == nil ? .outerHTML : nil,
|
||||
// context: .init(employee: employee)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// struct Context: Content {
|
||||
// let employee: Employee.DTO?
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,196 +1,196 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct PurchaseOrderViewController: RouteCollection {
|
||||
@Dependency(\.employees) var employees
|
||||
@Dependency(\.purchaseOrders) var purchaseOrders
|
||||
@Dependency(\.vendorBranches) var vendorBranches
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let pos = routes.protected.grouped("purchase-orders")
|
||||
|
||||
pos.get(use: index(req:))
|
||||
pos.group("details", "close") {
|
||||
$0.get(use: detailClose(req:))
|
||||
}
|
||||
pos.post(use: create(req:))
|
||||
pos.group(":id") {
|
||||
$0.get(use: detail(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> View {
|
||||
let params = try? req.query.decode(PurchaseOrderIndex.self)
|
||||
let purchaseOrdersPage = try await purchaseOrders.fetchPage(
|
||||
.init(page: params?.page ?? 1, per: params?.limit ?? 50)
|
||||
)
|
||||
let branches = try await vendorBranches.getBranches(req: req)
|
||||
let employees = try await employees.fetchAll()
|
||||
req.logger.debug("Branches: \(branches)")
|
||||
return try await req.view.render(
|
||||
"purchaseOrders/index",
|
||||
PurchaseOrderCTX(
|
||||
page: purchaseOrdersPage,
|
||||
form: .create(branches: branches, employees: employees)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func detail(req: Request) async throws -> View {
|
||||
guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
throw Abort(.badRequest, reason: "Id not supplied.")
|
||||
}
|
||||
let purchaseOrder = try await purchaseOrders.get(id)
|
||||
return try await req.view.render("purchaseOrders/detail", ["purchaseOrderDetail": purchaseOrder])
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func detailClose(req: Request) async throws -> View {
|
||||
return try await req.view.render("purchaseOrders/detail")
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> View {
|
||||
try PurchaseOrder.FormCreate.validate(content: req)
|
||||
let createdById = try req.auth.require(User.self).requireID()
|
||||
let create = try req.content.decode(PurchaseOrder.FormCreate.self).toCreate()
|
||||
let purchaseOrder = try await purchaseOrders.create(create, createdById)
|
||||
return try await req.view.render("purchaseOrders/table-row", purchaseOrder)
|
||||
}
|
||||
}
|
||||
|
||||
private struct PurchaseOrderIndex: Content {
|
||||
let page: Int?
|
||||
let limit: Int?
|
||||
}
|
||||
|
||||
private struct PurchaseOrderCTX: Content {
|
||||
let purchaseOrderDetail: PurchaseOrder.DTO?
|
||||
let purchaseOrders: [PurchaseOrder.DTO]
|
||||
let page: Int
|
||||
let limit: Int
|
||||
let hasNext: Bool
|
||||
let hasPrevious: Bool
|
||||
let form: PurchaseOrderFormCTX?
|
||||
|
||||
init(
|
||||
detail: PurchaseOrder.DTO? = nil,
|
||||
page: Page<PurchaseOrder.DTO>,
|
||||
form: PurchaseOrderFormCTX?
|
||||
) {
|
||||
self.purchaseOrderDetail = detail
|
||||
self.purchaseOrders = page.items
|
||||
self.page = page.metadata.page
|
||||
self.limit = page.metadata.per
|
||||
self.hasNext = page.metadata.hasNext
|
||||
self.hasPrevious = page.metadata.page > 1
|
||||
self.form = form
|
||||
}
|
||||
}
|
||||
|
||||
private extension PageMetadata {
|
||||
var hasNext: Bool {
|
||||
total > (page * per)
|
||||
}
|
||||
}
|
||||
|
||||
private struct PurchaseOrderFormCTX: Content {
|
||||
|
||||
let htmxForm: HtmxFormCTX<Context>
|
||||
|
||||
struct Context: Content {
|
||||
let branches: [VendorBranch.FormDTO]
|
||||
let employees: [Employee.DTO]
|
||||
}
|
||||
|
||||
static func create(branches: [VendorBranch.FormDTO], employees: [Employee.DTO]) -> Self {
|
||||
.init(htmxForm: .init(
|
||||
formClass: "po-form",
|
||||
formId: "po-form",
|
||||
htmxTargetUrl: .post("/purchase-orders"),
|
||||
htmxTarget: "#po-table-body",
|
||||
htmxPushUrl: false,
|
||||
htmxResetAfterRequest: true,
|
||||
htmxSwapOob: nil,
|
||||
htmxSwap: .afterbegin,
|
||||
context: .init(branches: branches, employees: employees)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension VendorBranch {
|
||||
struct FormDTO: Content {
|
||||
let id: UUID
|
||||
let name: String
|
||||
let vendor: Vendor.DTO
|
||||
}
|
||||
|
||||
func toFormDTO() throws -> VendorBranch.FormDTO {
|
||||
try .init(
|
||||
id: requireID(),
|
||||
name: name,
|
||||
vendor: vendor.toDTO()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PurchaseOrder {
|
||||
struct FormCreate: Content {
|
||||
let id: Int?
|
||||
let workOrder: String?
|
||||
let materials: String
|
||||
let customer: String
|
||||
let truckStock: Bool?
|
||||
let createdForID: Employee.IDValue
|
||||
let vendorBranchID: VendorBranch.IDValue
|
||||
|
||||
// TODO: Remove.
|
||||
func toModel(createdByID: User.IDValue) -> PurchaseOrder {
|
||||
.init(
|
||||
id: id,
|
||||
workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
||||
materials: materials,
|
||||
customer: customer,
|
||||
truckStock: truckStock ?? false,
|
||||
createdByID: createdByID,
|
||||
createdForID: createdForID,
|
||||
vendorBranchID: vendorBranchID,
|
||||
createdAt: nil,
|
||||
updatedAt: nil
|
||||
)
|
||||
}
|
||||
|
||||
func toCreate() -> PurchaseOrder.Create {
|
||||
.init(
|
||||
id: id,
|
||||
workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
||||
materials: materials,
|
||||
customer: customer,
|
||||
truckStock: truckStock,
|
||||
createdForID: createdForID,
|
||||
vendorBranchID: vendorBranchID
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension VendorBranchDB {
|
||||
|
||||
func getBranches(req: Request) async throws -> [VendorBranch.FormDTO] {
|
||||
try await VendorBranch.query(on: req.db)
|
||||
.with(\.$vendor)
|
||||
.all()
|
||||
.map { try $0.toFormDTO() }
|
||||
}
|
||||
}
|
||||
|
||||
extension PurchaseOrder.FormCreate: Validatable {
|
||||
|
||||
static func validations(_ validations: inout Validations) {
|
||||
validations.add("materials", as: String.self, is: !.empty)
|
||||
validations.add("customer", as: String.self, is: !.empty)
|
||||
}
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct PurchaseOrderViewController: RouteCollection {
|
||||
// @Dependency(\.employees) var employees
|
||||
// @Dependency(\.purchaseOrders) var purchaseOrders
|
||||
// @Dependency(\.vendorBranches) var vendorBranches
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let pos = routes.protected.grouped("purchase-orders")
|
||||
//
|
||||
// pos.get(use: index(req:))
|
||||
// pos.group("details", "close") {
|
||||
// $0.get(use: detailClose(req:))
|
||||
// }
|
||||
// pos.post(use: create(req:))
|
||||
// pos.group(":id") {
|
||||
// $0.get(use: detail(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> View {
|
||||
// let params = try? req.query.decode(PurchaseOrderIndex.self)
|
||||
// let purchaseOrdersPage = try await purchaseOrders.fetchPage(
|
||||
// .init(page: params?.page ?? 1, per: params?.limit ?? 50)
|
||||
// )
|
||||
// let branches = try await vendorBranches.getBranches(req: req)
|
||||
// let employees = try await employees.fetchAll()
|
||||
// req.logger.debug("Branches: \(branches)")
|
||||
// return try await req.view.render(
|
||||
// "purchaseOrders/index",
|
||||
// PurchaseOrderCTX(
|
||||
// page: purchaseOrdersPage,
|
||||
// form: .create(branches: branches, employees: employees)
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func detail(req: Request) async throws -> View {
|
||||
// guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else {
|
||||
// throw Abort(.badRequest, reason: "Id not supplied.")
|
||||
// }
|
||||
// let purchaseOrder = try await purchaseOrders.get(id)
|
||||
// return try await req.view.render("purchaseOrders/detail", ["purchaseOrderDetail": purchaseOrder])
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func detailClose(req: Request) async throws -> View {
|
||||
// return try await req.view.render("purchaseOrders/detail")
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> View {
|
||||
// try PurchaseOrder.FormCreate.validate(content: req)
|
||||
// let createdById = try req.auth.require(User.self).requireID()
|
||||
// let create = try req.content.decode(PurchaseOrder.FormCreate.self).toCreate()
|
||||
// let purchaseOrder = try await purchaseOrders.create(create, createdById)
|
||||
// return try await req.view.render("purchaseOrders/table-row", purchaseOrder)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct PurchaseOrderIndex: Content {
|
||||
// let page: Int?
|
||||
// let limit: Int?
|
||||
// }
|
||||
//
|
||||
// private struct PurchaseOrderCTX: Content {
|
||||
// let purchaseOrderDetail: PurchaseOrder.DTO?
|
||||
// let purchaseOrders: [PurchaseOrder.DTO]
|
||||
// let page: Int
|
||||
// let limit: Int
|
||||
// let hasNext: Bool
|
||||
// let hasPrevious: Bool
|
||||
// let form: PurchaseOrderFormCTX?
|
||||
//
|
||||
// init(
|
||||
// detail: PurchaseOrder.DTO? = nil,
|
||||
// page: Page<PurchaseOrder.DTO>,
|
||||
// form: PurchaseOrderFormCTX?
|
||||
// ) {
|
||||
// self.purchaseOrderDetail = detail
|
||||
// self.purchaseOrders = page.items
|
||||
// self.page = page.metadata.page
|
||||
// self.limit = page.metadata.per
|
||||
// self.hasNext = page.metadata.hasNext
|
||||
// self.hasPrevious = page.metadata.page > 1
|
||||
// self.form = form
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private extension PageMetadata {
|
||||
// var hasNext: Bool {
|
||||
// total > (page * per)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct PurchaseOrderFormCTX: Content {
|
||||
//
|
||||
// let htmxForm: HtmxFormCTX<Context>
|
||||
//
|
||||
// struct Context: Content {
|
||||
// let branches: [VendorBranch.FormDTO]
|
||||
// let employees: [Employee.DTO]
|
||||
// }
|
||||
//
|
||||
// static func create(branches: [VendorBranch.FormDTO], employees: [Employee.DTO]) -> Self {
|
||||
// .init(htmxForm: .init(
|
||||
// formClass: "po-form",
|
||||
// formId: "po-form",
|
||||
// htmxTargetUrl: .post("/purchase-orders"),
|
||||
// htmxTarget: "#po-table-body",
|
||||
// htmxPushUrl: false,
|
||||
// htmxResetAfterRequest: true,
|
||||
// htmxSwapOob: nil,
|
||||
// htmxSwap: .afterbegin,
|
||||
// context: .init(branches: branches, employees: employees)
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// extension VendorBranch {
|
||||
// struct FormDTO: Content {
|
||||
// let id: UUID
|
||||
// let name: String
|
||||
// let vendor: Vendor.DTO
|
||||
// }
|
||||
//
|
||||
// func toFormDTO() throws -> VendorBranch.FormDTO {
|
||||
// try .init(
|
||||
// id: requireID(),
|
||||
// name: name,
|
||||
// vendor: vendor.toDTO()
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private extension PurchaseOrder {
|
||||
// struct FormCreate: Content {
|
||||
// let id: Int?
|
||||
// let workOrder: String?
|
||||
// let materials: String
|
||||
// let customer: String
|
||||
// let truckStock: Bool?
|
||||
// let createdForID: Employee.IDValue
|
||||
// let vendorBranchID: VendorBranch.IDValue
|
||||
//
|
||||
// // TODO: Remove.
|
||||
// func toModel(createdByID: User.IDValue) -> PurchaseOrder {
|
||||
// .init(
|
||||
// id: id,
|
||||
// workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
||||
// materials: materials,
|
||||
// customer: customer,
|
||||
// truckStock: truckStock ?? false,
|
||||
// createdByID: createdByID,
|
||||
// createdForID: createdForID,
|
||||
// vendorBranchID: vendorBranchID,
|
||||
// createdAt: nil,
|
||||
// updatedAt: nil
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// func toCreate() -> PurchaseOrder.Create {
|
||||
// .init(
|
||||
// id: id,
|
||||
// workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
|
||||
// materials: materials,
|
||||
// customer: customer,
|
||||
// truckStock: truckStock,
|
||||
// createdForID: createdForID,
|
||||
// vendorBranchID: vendorBranchID
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private extension VendorBranchDB {
|
||||
//
|
||||
// func getBranches(req: Request) async throws -> [VendorBranch.FormDTO] {
|
||||
// try await VendorBranch.query(on: req.db)
|
||||
// .with(\.$vendor)
|
||||
// .all()
|
||||
// .map { try $0.toFormDTO() }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// extension PurchaseOrder.FormCreate: Validatable {
|
||||
//
|
||||
// static func validations(_ validations: inout Validations) {
|
||||
// validations.add("materials", as: String.self, is: !.empty)
|
||||
// validations.add("customer", as: String.self, is: !.empty)
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,121 +1,121 @@
|
||||
import Dependencies
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct UserViewController: RouteCollection {
|
||||
|
||||
@Dependency(\.users) var users
|
||||
|
||||
private let api = UserApiController()
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let users = routes.protected.grouped("users")
|
||||
users.get(use: index(req:))
|
||||
users.post(use: create(req:))
|
||||
users.group(":id") {
|
||||
$0.get(use: details(req:))
|
||||
$0.delete(use: delete(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> View {
|
||||
try await renderIndex(req)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
private func renderIndex(_ req: Request, _ user: User.DTO? = nil) async throws -> View {
|
||||
let users = try await api.getSortedUsers(req: req)
|
||||
return try await req.view.render("users/index", UsersCTX(user: user, users: users))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> View {
|
||||
let user = try await api.create(req: req)
|
||||
return try await req.view.render("users/table-row", user)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func details(req: Request) async throws -> View {
|
||||
let user = try await users.get(req.ensureIDPathComponent())
|
||||
// Check if the page has been rendered before.
|
||||
guard req.isHtmxRequest else {
|
||||
// Not an htmx-request, so render the whole page with the details.
|
||||
return try await renderIndex(req, user)
|
||||
}
|
||||
// An htmx-request header was present, so just return the details,
|
||||
return try await req.view.render("users/detail", ["user": user])
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> View {
|
||||
_ = try await api.delete(req: req)
|
||||
return try await req.view.render("users/table", ["users": api.getSortedUsers(req: req)])
|
||||
}
|
||||
}
|
||||
|
||||
struct UserFormCTX: Content {
|
||||
let htmxForm: HtmxFormCTX<Context>
|
||||
|
||||
struct Context: Content {
|
||||
let showConfirmPassword: Bool
|
||||
let showEmailInput: Bool
|
||||
let buttonLabel: String
|
||||
}
|
||||
|
||||
static func signIn(next: String?) -> Self {
|
||||
.init(
|
||||
htmxForm: .init(
|
||||
formClass: "user-form",
|
||||
formId: "user-form",
|
||||
htmxTargetUrl: .post("/login\((next != nil && next != "/") ? "?next=\(next!)" : "")"),
|
||||
htmxTarget: "user-table",
|
||||
htmxPushUrl: true,
|
||||
htmxResetAfterRequest: true,
|
||||
htmxSwapOob: nil,
|
||||
htmxSwap: .afterbegin,
|
||||
context: .init(showConfirmPassword: false, showEmailInput: false, buttonLabel: "Sign In")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
static func create() -> Self {
|
||||
.init(
|
||||
htmxForm: .init(
|
||||
formClass: "user-form",
|
||||
formId: "user-form",
|
||||
htmxTargetUrl: .post("/users"),
|
||||
htmxTarget: "#user-table",
|
||||
htmxPushUrl: false,
|
||||
htmxResetAfterRequest: true,
|
||||
htmxSwapOob: nil,
|
||||
htmxSwap: nil,
|
||||
context: .init(showConfirmPassword: true, showEmailInput: true, buttonLabel: "Create")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private struct UsersCTX: Content {
|
||||
let user: User.DTO?
|
||||
let users: [User.DTO]
|
||||
let form: UserFormCTX
|
||||
|
||||
init(
|
||||
user: User.DTO? = nil,
|
||||
users: [User.DTO],
|
||||
form: UserFormCTX? = nil
|
||||
) {
|
||||
self.user = user
|
||||
self.users = users
|
||||
self.form = form ?? .create()
|
||||
}
|
||||
}
|
||||
|
||||
private extension UserApiController {
|
||||
|
||||
func getSortedUsers(req: Request) async throws -> [User.DTO] {
|
||||
try await index(req: req)
|
||||
.sorted { ($0.username ?? "") < ($1.username ?? "") }
|
||||
}
|
||||
}
|
||||
// import Dependencies
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct UserViewController: RouteCollection {
|
||||
//
|
||||
// @Dependency(\.users) var users
|
||||
//
|
||||
// private let api = UserApiController()
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let users = routes.protected.grouped("users")
|
||||
// users.get(use: index(req:))
|
||||
// users.post(use: create(req:))
|
||||
// users.group(":id") {
|
||||
// $0.get(use: details(req:))
|
||||
// $0.delete(use: delete(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> View {
|
||||
// try await renderIndex(req)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// private func renderIndex(_ req: Request, _ user: User.DTO? = nil) async throws -> View {
|
||||
// let users = try await api.getSortedUsers(req: req)
|
||||
// return try await req.view.render("users/index", UsersCTX(user: user, users: users))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> View {
|
||||
// let user = try await api.create(req: req)
|
||||
// return try await req.view.render("users/table-row", user)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func details(req: Request) async throws -> View {
|
||||
// let user = try await users.get(req.ensureIDPathComponent())
|
||||
// // Check if the page has been rendered before.
|
||||
// guard req.isHtmxRequest else {
|
||||
// // Not an htmx-request, so render the whole page with the details.
|
||||
// return try await renderIndex(req, user)
|
||||
// }
|
||||
// // An htmx-request header was present, so just return the details,
|
||||
// return try await req.view.render("users/detail", ["user": user])
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> View {
|
||||
// _ = try await api.delete(req: req)
|
||||
// return try await req.view.render("users/table", ["users": api.getSortedUsers(req: req)])
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// struct UserFormCTX: Content {
|
||||
// let htmxForm: HtmxFormCTX<Context>
|
||||
//
|
||||
// struct Context: Content {
|
||||
// let showConfirmPassword: Bool
|
||||
// let showEmailInput: Bool
|
||||
// let buttonLabel: String
|
||||
// }
|
||||
//
|
||||
// static func signIn(next: String?) -> Self {
|
||||
// .init(
|
||||
// htmxForm: .init(
|
||||
// formClass: "user-form",
|
||||
// formId: "user-form",
|
||||
// htmxTargetUrl: .post("/login\((next != nil && next != "/") ? "?next=\(next!)" : "")"),
|
||||
// htmxTarget: "user-table",
|
||||
// htmxPushUrl: true,
|
||||
// htmxResetAfterRequest: true,
|
||||
// htmxSwapOob: nil,
|
||||
// htmxSwap: .afterbegin,
|
||||
// context: .init(showConfirmPassword: false, showEmailInput: false, buttonLabel: "Sign In")
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// static func create() -> Self {
|
||||
// .init(
|
||||
// htmxForm: .init(
|
||||
// formClass: "user-form",
|
||||
// formId: "user-form",
|
||||
// htmxTargetUrl: .post("/users"),
|
||||
// htmxTarget: "#user-table",
|
||||
// htmxPushUrl: false,
|
||||
// htmxResetAfterRequest: true,
|
||||
// htmxSwapOob: nil,
|
||||
// htmxSwap: nil,
|
||||
// context: .init(showConfirmPassword: true, showEmailInput: true, buttonLabel: "Create")
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct UsersCTX: Content {
|
||||
// let user: User.DTO?
|
||||
// let users: [User.DTO]
|
||||
// let form: UserFormCTX
|
||||
//
|
||||
// init(
|
||||
// user: User.DTO? = nil,
|
||||
// users: [User.DTO],
|
||||
// form: UserFormCTX? = nil
|
||||
// ) {
|
||||
// self.user = user
|
||||
// self.users = users
|
||||
// self.form = form ?? .create()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private extension UserApiController {
|
||||
//
|
||||
// func getSortedUsers(req: Request) async throws -> [User.DTO] {
|
||||
// try await index(req: req)
|
||||
// .sorted { ($0.username ?? "") < ($1.username ?? "") }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,108 +1,108 @@
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
struct VendorViewController: RouteCollection {
|
||||
private let api = VendorApiController()
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let vendors = routes.protected.grouped("vendors")
|
||||
|
||||
vendors.get(use: index(req:))
|
||||
vendors.post(use: create(req:))
|
||||
vendors.group(":vendorID") {
|
||||
$0.delete(use: delete(req:))
|
||||
$0.put(use: update(req:))
|
||||
}
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func index(req: Request) async throws -> View {
|
||||
return try await req.view.render("vendors/index", makeCtx(req: req))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func create(req: Request) async throws -> View {
|
||||
let ctx = try req.content.decode(CreateVendorCTX.self)
|
||||
req.logger.debug("CTX: \(ctx)")
|
||||
let vendor = Vendor.Create(name: ctx.name).toModel()
|
||||
try await vendor.save(on: req.db)
|
||||
|
||||
if let branchString = ctx.branches {
|
||||
let branches = branchString.split(separator: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
|
||||
for branch in branches {
|
||||
try await vendor.$branches.create(
|
||||
VendorBranch(name: String(branch), vendorId: vendor.requireID()),
|
||||
on: req.db
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return try await req.view.render("vendors/table", makeCtx(req: req))
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func delete(req: Request) async throws -> HTTPStatus {
|
||||
try await api.delete(req: req)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func update(req: Request) async throws -> View {
|
||||
_ = try await api.update(req: req)
|
||||
return try await req.view.render("vendors/table", makeCtx(req: req, oob: true))
|
||||
}
|
||||
|
||||
private func makeCtx(req: Request, vendor: Vendor? = nil, oob: Bool = false) async throws -> VendorsCTX {
|
||||
let vendors = try await Vendor.query(on: req.db)
|
||||
.with(\.$branches)
|
||||
.sort(\.$name, .ascending)
|
||||
.all()
|
||||
.map { $0.toDTO() }
|
||||
|
||||
return .init(
|
||||
vendors: vendors,
|
||||
form: .init(vendor: vendor, oob: oob)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct VendorFormCTX: Content {
|
||||
let htmxForm: HtmxFormCTX<Context>
|
||||
|
||||
init(vendor: Vendor? = nil, oob: Bool = false) {
|
||||
self.htmxForm = .init(
|
||||
formClass: "vendor-form",
|
||||
formId: "vendor-form",
|
||||
htmxTargetUrl: vendor == nil ? .post("/vendors") : .put("/vendors"),
|
||||
htmxTarget: "#vendor-table",
|
||||
htmxPushUrl: false,
|
||||
htmxResetAfterRequest: true,
|
||||
htmxSwapOob: oob ? .outerHTML : nil,
|
||||
htmxSwap: oob ? nil : .outerHTML,
|
||||
context: .init(vendor: vendor)
|
||||
)
|
||||
}
|
||||
|
||||
struct Context: Content {
|
||||
let vendor: Vendor?
|
||||
let branches: String?
|
||||
let buttonLabel: String
|
||||
|
||||
init(vendor: Vendor? = nil) {
|
||||
self.vendor = vendor
|
||||
self.branches = vendor?.branches.map(\.name).joined(separator: ", ")
|
||||
self.buttonLabel = vendor == nil ? "Create" : "Update"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct VendorsCTX: Content {
|
||||
let vendors: [Vendor.DTO]
|
||||
let form: VendorFormCTX
|
||||
}
|
||||
|
||||
private struct CreateVendorCTX: Content {
|
||||
let name: String
|
||||
let branches: String?
|
||||
}
|
||||
// import Fluent
|
||||
// import Vapor
|
||||
//
|
||||
// struct VendorViewController: RouteCollection {
|
||||
// private let api = VendorApiController()
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let vendors = routes.protected.grouped("vendors")
|
||||
//
|
||||
// vendors.get(use: index(req:))
|
||||
// vendors.post(use: create(req:))
|
||||
// vendors.group(":vendorID") {
|
||||
// $0.delete(use: delete(req:))
|
||||
// $0.put(use: update(req:))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func index(req: Request) async throws -> View {
|
||||
// return try await req.view.render("vendors/index", makeCtx(req: req))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func create(req: Request) async throws -> View {
|
||||
// let ctx = try req.content.decode(CreateVendorCTX.self)
|
||||
// req.logger.debug("CTX: \(ctx)")
|
||||
// let vendor = Vendor.Create(name: ctx.name).toModel()
|
||||
// try await vendor.save(on: req.db)
|
||||
//
|
||||
// if let branchString = ctx.branches {
|
||||
// let branches = branchString.split(separator: ",")
|
||||
// .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
//
|
||||
// for branch in branches {
|
||||
// try await vendor.$branches.create(
|
||||
// VendorBranch(name: String(branch), vendorId: vendor.requireID()),
|
||||
// on: req.db
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return try await req.view.render("vendors/table", makeCtx(req: req))
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func delete(req: Request) async throws -> HTTPStatus {
|
||||
// try await api.delete(req: req)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func update(req: Request) async throws -> View {
|
||||
// _ = try await api.update(req: req)
|
||||
// return try await req.view.render("vendors/table", makeCtx(req: req, oob: true))
|
||||
// }
|
||||
//
|
||||
// private func makeCtx(req: Request, vendor: Vendor? = nil, oob: Bool = false) async throws -> VendorsCTX {
|
||||
// let vendors = try await Vendor.query(on: req.db)
|
||||
// .with(\.$branches)
|
||||
// .sort(\.$name, .ascending)
|
||||
// .all()
|
||||
// .map { $0.toDTO() }
|
||||
//
|
||||
// return .init(
|
||||
// vendors: vendors,
|
||||
// form: .init(vendor: vendor, oob: oob)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// struct VendorFormCTX: Content {
|
||||
// let htmxForm: HtmxFormCTX<Context>
|
||||
//
|
||||
// init(vendor: Vendor? = nil, oob: Bool = false) {
|
||||
// self.htmxForm = .init(
|
||||
// formClass: "vendor-form",
|
||||
// formId: "vendor-form",
|
||||
// htmxTargetUrl: vendor == nil ? .post("/vendors") : .put("/vendors"),
|
||||
// htmxTarget: "#vendor-table",
|
||||
// htmxPushUrl: false,
|
||||
// htmxResetAfterRequest: true,
|
||||
// htmxSwapOob: oob ? .outerHTML : nil,
|
||||
// htmxSwap: oob ? nil : .outerHTML,
|
||||
// context: .init(vendor: vendor)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// struct Context: Content {
|
||||
// let vendor: Vendor?
|
||||
// let branches: String?
|
||||
// let buttonLabel: String
|
||||
//
|
||||
// init(vendor: Vendor? = nil) {
|
||||
// self.vendor = vendor
|
||||
// self.branches = vendor?.branches.map(\.name).joined(separator: ", ")
|
||||
// self.buttonLabel = vendor == nil ? "Create" : "Update"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private struct VendorsCTX: Content {
|
||||
// let vendors: [Vendor.DTO]
|
||||
// let form: VendorFormCTX
|
||||
// }
|
||||
//
|
||||
// private struct CreateVendorCTX: Content {
|
||||
// let name: String
|
||||
// let branches: String?
|
||||
// }
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
import Fluent
|
||||
import Leaf
|
||||
import Vapor
|
||||
|
||||
struct ViewController: RouteCollection {
|
||||
|
||||
private let api = ApiController()
|
||||
private let employees = EmployeeViewController()
|
||||
private let purchaseOrders = PurchaseOrderViewController()
|
||||
private let users = UserViewController()
|
||||
private let vendors = VendorViewController()
|
||||
|
||||
func boot(routes: any RoutesBuilder) throws {
|
||||
let protected = routes.protected
|
||||
|
||||
// MARK: - Non-protected routes.
|
||||
|
||||
// routes.get(use: index(req:))
|
||||
routes.get("login", use: getLogin(req:))
|
||||
routes.post("login", use: postLogin(req:))
|
||||
|
||||
// MARK: Protected routes.
|
||||
|
||||
protected.get(use: home(req:))
|
||||
protected.get("**", use: catchAll(req:))
|
||||
protected.post("logout", use: logout(req:))
|
||||
// protected.get("users", use: users(req:))
|
||||
try routes.register(collection: employees)
|
||||
try routes.register(collection: purchaseOrders)
|
||||
try routes.register(collection: users)
|
||||
try routes.register(collection: vendors)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func getLogin(req: Request) async throws -> View {
|
||||
req.logger.debug("Login Query: \(req.url.query ?? "n/a")")
|
||||
let params = try? req.query.decode(LoginParameter.self)
|
||||
return try await req.view.render(
|
||||
"login", UserFormCTX.signIn(next: params?.next)
|
||||
)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func postLogin(req: Request) async throws -> Response {
|
||||
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 logged in: \(user.toDTO())")
|
||||
return try await home(req: req)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func logout(req: Request) async throws -> View {
|
||||
req.auth.logout(User.self)
|
||||
return try await req.view.render("login")
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func home(req: Request) async throws -> Response {
|
||||
if let loginParams = try? req.query.decode(LoginParameter.self) {
|
||||
return req.redirect(to: loginParams.next)
|
||||
}
|
||||
return try await req.view.render("home").encodeResponse(for: req)
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func catchAll(req: Request) async throws -> View {
|
||||
var route: HomeRoute?
|
||||
|
||||
if let loginParams = try? req.query.decode(LoginParameter.self),
|
||||
let next = loginParams.next.split(separator: "/").last
|
||||
{
|
||||
route = HomeRoute(rawValue: String(next))
|
||||
} else if let routeString = req.parameters.getCatchall().last {
|
||||
route = HomeRoute(rawValue: routeString)
|
||||
}
|
||||
|
||||
return try await req.view.render("home", HomeCTX(route: route))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private struct UserForm: Content {
|
||||
let username: String
|
||||
let password: String
|
||||
}
|
||||
|
||||
enum HomeRoute: String, Content {
|
||||
case employees
|
||||
case purchaseOrders
|
||||
case users
|
||||
case vendors
|
||||
}
|
||||
|
||||
struct HomeCTX: Content {
|
||||
let route: HomeRoute?
|
||||
}
|
||||
|
||||
struct LoginParameter: Content {
|
||||
let next: String
|
||||
}
|
||||
// import Fluent
|
||||
// import Leaf
|
||||
// import Vapor
|
||||
//
|
||||
// struct ViewController: RouteCollection {
|
||||
//
|
||||
// private let api = ApiController()
|
||||
// private let employees = EmployeeViewController()
|
||||
// private let purchaseOrders = PurchaseOrderViewController()
|
||||
// private let users = UserViewController()
|
||||
// private let vendors = VendorViewController()
|
||||
//
|
||||
// func boot(routes: any RoutesBuilder) throws {
|
||||
// let protected = routes.protected
|
||||
//
|
||||
// // MARK: - Non-protected routes.
|
||||
//
|
||||
// // routes.get(use: index(req:))
|
||||
// routes.get("login", use: getLogin(req:))
|
||||
// routes.post("login", use: postLogin(req:))
|
||||
//
|
||||
// // MARK: Protected routes.
|
||||
//
|
||||
// protected.get(use: home(req:))
|
||||
// protected.get("**", use: catchAll(req:))
|
||||
// protected.post("logout", use: logout(req:))
|
||||
// // protected.get("users", use: users(req:))
|
||||
// try routes.register(collection: employees)
|
||||
// try routes.register(collection: purchaseOrders)
|
||||
// try routes.register(collection: users)
|
||||
// try routes.register(collection: vendors)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func getLogin(req: Request) async throws -> View {
|
||||
// req.logger.debug("Login Query: \(req.url.query ?? "n/a")")
|
||||
// let params = try? req.query.decode(LoginParameter.self)
|
||||
// return try await req.view.render(
|
||||
// "login", UserFormCTX.signIn(next: params?.next)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func postLogin(req: Request) async throws -> Response {
|
||||
// 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 logged in: \(user.toDTO())")
|
||||
// return try await home(req: req)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func logout(req: Request) async throws -> View {
|
||||
// req.auth.logout(User.self)
|
||||
// return try await req.view.render("login")
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func home(req: Request) async throws -> Response {
|
||||
// if let loginParams = try? req.query.decode(LoginParameter.self) {
|
||||
// return req.redirect(to: loginParams.next)
|
||||
// }
|
||||
// return try await req.view.render("home").encodeResponse(for: req)
|
||||
// }
|
||||
//
|
||||
// @Sendable
|
||||
// func catchAll(req: Request) async throws -> View {
|
||||
// var route: HomeRoute?
|
||||
//
|
||||
// if let loginParams = try? req.query.decode(LoginParameter.self),
|
||||
// let next = loginParams.next.split(separator: "/").last
|
||||
// {
|
||||
// route = HomeRoute(rawValue: String(next))
|
||||
// } else if let routeString = req.parameters.getCatchall().last {
|
||||
// route = HomeRoute(rawValue: routeString)
|
||||
// }
|
||||
//
|
||||
// return try await req.view.render("home", HomeCTX(route: route))
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private struct UserForm: Content {
|
||||
// let username: String
|
||||
// let password: String
|
||||
// }
|
||||
//
|
||||
// enum HomeRoute: String, Content {
|
||||
// case employees
|
||||
// case purchaseOrders
|
||||
// case users
|
||||
// case vendors
|
||||
// }
|
||||
//
|
||||
// struct HomeCTX: Content {
|
||||
// let route: HomeRoute?
|
||||
// }
|
||||
//
|
||||
// struct LoginParameter: Content {
|
||||
// let next: String
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user