feat: Fixes merge conflicts

This commit is contained in:
2025-01-27 10:42:06 -05:00
11 changed files with 135 additions and 3 deletions

View File

@@ -47,6 +47,7 @@ jobs:
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
type=raw,value=prod
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If
# the build succeeds, it pushes the image to GitHub Packages. It uses the `context` parameter to define the build's context
# as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)"

67
README.md Normal file
View File

@@ -0,0 +1,67 @@
# vapor-po
The website for generating purchase orders.
## Usage
Generally the app should be ran through a docker container or docker-compose file. Examples are in
the `./docker` folder.
Images get built in the `CI` environment when a tag is pushed to the repository.
### Getting Started
When the application is first launched an admin user should be created in the running container.
Attach to the container using `docker exec` or `docker compose exec`, then run:
```
./App generate-admin --username "admin" --password "super-secret --confirmPassword "super-secret"
```
You can then login and generate user, employees, vendors, etc.
After the setup has been completed, then you should generate a mock purchase order and set the `id`
to the value you would like new purchase orders to start from. This should be done through calling
the api, as the web interface does not allow users to enter an id value.
#### Example
These examples use `httpie`, note the port is used for local development, in a production
environment you would just use the FQDN of where the application is running.
**Login**
```
http :8080/api/v1/login username="admin" password="super-secret" \
| jq '.["token"]' \
| pbcopy
```
**Set the token as environment variable**
```
export API_TOKEN=<clipboard contents>
```
**Get the employees to copy an id to use for the purchase order**
```
http -A bearer -a "$API_TOKEN" :8080/api/v1/employees
```
**Get the vendor branches to copy an id to use for the purchase order**
```
http -A bearer -a "$API_TOKEN" :8080/api/v1/vendors/branches
```
**Generate first po**
```
http -A bearer -a "$API_TOKEN" :8080/api/v1/purchase-orders \
id:="60000" \
materials="Test" \
customer="Testy McTestface" \
createdForID="<employee-id>"
vendorBranchID="<vendor-branch-id>"
```

View File

@@ -1,6 +1,9 @@
import Dependencies
import Foundation
/// Represents an employee database model.
///
/// Employee's are who purchase orders can be generated for.
public struct Employee: Codable, Equatable, Identifiable, Sendable {
public var id: UUID
public var active: Bool
@@ -31,6 +34,8 @@ public struct Employee: Codable, Equatable, Identifiable, Sendable {
}
public extension Employee {
/// Represents the required fields for generating a new employee in the
/// database.
struct Create: Codable, Sendable, Equatable {
public let firstName: String
public let lastName: String
@@ -47,6 +52,8 @@ public extension Employee {
}
}
/// Represents the required fields for updating an existing employee in the
/// database.
struct Update: Codable, Sendable, Equatable {
public let firstName: String?
public let lastName: String?

View File

@@ -1,6 +1,11 @@
import Dependencies
import Foundation
/// Represents a purchase order database model.
///
/// A purchase order is generated on behalf of an `Employee` and issued to
/// a `VendorBranch`. It includes information about the customer / job it was created
/// for, the materials that were purchased, etc.
public struct PurchaseOrder: Codable, Equatable, Identifiable, Sendable {
public let id: Int
@@ -41,6 +46,7 @@ public struct PurchaseOrder: Codable, Equatable, Identifiable, Sendable {
public extension PurchaseOrder {
/// Represents the required fields for generating a new purchase order in the database.
struct Create: Codable, Sendable, Equatable {
public let id: Int?
@@ -73,6 +79,9 @@ public extension PurchaseOrder {
}
}
/// Represents the required fields for generating a new purchase order in the database,
/// without the user information who is issuing the request, which get's parsed from the
/// currently authenticated user's session and is used to generate the full `Create` request.
struct CreateIntermediate: Codable, Sendable, Equatable {
public let id: Int?
@@ -115,6 +124,8 @@ public extension PurchaseOrder {
}
}
/// Represents the context to search or filter purchase orders based on the
/// given parameters.
enum SearchContext: Sendable, Equatable {
case customer(String)
case vendor(VendorBranch.ID)

View File

@@ -4,6 +4,9 @@ import Foundation
public extension SiteRoute {
/// Represents api routes that can be interacted with.
///
/// These routes return json information, as opposed to html like the view routes.
enum Api: Sendable, Equatable {
case employee(EmployeeRoute)

View File

@@ -2,6 +2,7 @@ import CasePathsCore
import Foundation
@preconcurrency import URLRouting
/// Represents all the routes that our server can handle.
public enum SiteRoute: Sendable {
case api(SiteRoute.Api)
case health

View File

@@ -4,6 +4,10 @@ import Foundation
public extension SiteRoute {
// swiftlint:disable type_body_length
/// Represents view routes that can be interacted with.
///
/// These routes return html and are used to generate the web interface.
enum View: Sendable, Equatable {
case employee(SiteRoute.View.EmployeeRoute)

View File

@@ -1,6 +1,11 @@
import Dependencies
import Foundation
/// Represents a user database model.
///
/// User's are who can login to the system and generate purchase orders, manage
/// employees, vendors, etc.
///
public struct User: Codable, Equatable, Identifiable, Sendable {
public var id: UUID
@@ -26,6 +31,7 @@ public struct User: Codable, Equatable, Identifiable, Sendable {
public extension User {
/// Represents the fields needed to generate a new user in the database.
struct Create: Codable, Sendable, Equatable {
public let username: String
public let email: String
@@ -45,6 +51,7 @@ public extension User {
}
}
/// Represents the fields needed for new user to login.
struct Login: Codable, Sendable, Equatable {
public let username: String?
public let email: String?
@@ -61,6 +68,7 @@ public extension User {
}
}
/// Represents the fields needed to reset the password of a user.
struct ResetPassword: Codable, Equatable, Sendable {
public let password: String
public let confirmPassword: String
@@ -74,6 +82,8 @@ public extension User {
}
}
/// Represents a user token that can be used to authenticate a user, typically
/// used for interacting with api routes remotely.
struct Token: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let userID: User.ID
@@ -90,6 +100,7 @@ public extension User {
}
}
/// Represents the fields needed to update a user's attributes in the database.
struct Update: Codable, Equatable, Sendable {
public let username: String?
public let email: String?

View File

@@ -1,6 +1,10 @@
import Dependencies
import Foundation
/// Represents a vendor item in the database.
///
/// A vendor is parent item that contains one or more branches where purchase orders
/// can be issued to. It is primarily a name space to group related branches together.
public struct Vendor: Codable, Equatable, Identifiable, Sendable {
public var id: UUID
public var name: String
@@ -25,6 +29,7 @@ public struct Vendor: Codable, Equatable, Identifiable, Sendable {
public extension Vendor {
/// Represents the fields required to generate a new vendor in the database.
struct Create: Codable, Sendable, Equatable {
public let name: String
@@ -33,6 +38,7 @@ public extension Vendor {
}
}
/// Represents the fields required to update a vendor in the database.
struct Update: Codable, Sendable, Equatable {
public let name: String

View File

@@ -1,6 +1,10 @@
import Dependencies
import Foundation
/// Represents a vendor branch database model.
///
/// A vendor branch is who purchase orders can be issued to on behalf an `Employee`.
/// They are associated with a particular `Vendor`.
public struct VendorBranch: Codable, Equatable, Identifiable, Sendable {
public var id: UUID
public var name: String
@@ -24,6 +28,8 @@ public struct VendorBranch: Codable, Equatable, Identifiable, Sendable {
}
public extension VendorBranch {
/// Represents the fields required to generate a new vendor branch in the database.
struct Create: Codable, Sendable, Equatable {
public let name: String
public let vendorID: Vendor.ID
@@ -34,6 +40,10 @@ public extension VendorBranch {
}
}
/// Represents the details of a vendor branch, which includes the parent vendor item.
///
/// This is used in several of the views / api routes that require information about both the
/// vendor branch and it's associated parent vendor item.
struct Detail: Codable, Equatable, Identifiable, Sendable {
public var id: UUID
public var name: String
@@ -56,6 +66,7 @@ public extension VendorBranch {
}
}
/// Represents the fields that are used to update attributes of a vendor branch in the database.
struct Update: Codable, Sendable, Equatable {
public let name: String?

View File

@@ -21,7 +21,8 @@ services:
app:
image: hhe-po:latest
build:
context: .
context: ..
dockerfile: ./docker/Dockerfile
environment:
<<: *shared_environment
volumes:
@@ -30,12 +31,20 @@ services:
- '8080:8080'
labels:
- dev.orbstack.domains=po.local
healthcheck:
test: curl --fail -s http://0.0.0.0:8080/health || exit 1
interval: 1m30s
timeout: 10s
retries: 3
deploy:
replicas: 3
# user: '0' # uncomment to run as root for testing purposes even though Dockerfile defines 'vapor' user.
command: ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]
migrate:
image: hhe-po:latest
build:
context: .
context: ..
dockerfile: ./docker/Dockerfile
environment:
<<: *shared_environment
command: ["migrate", "--yes"]
@@ -46,7 +55,8 @@ services:
revert:
image: hhe-po:latest
build:
context: .
context: ..
dockerfile: ./docker/Dockerfile
environment:
<<: *shared_environment
command: ["migrate", "--revert", "--yes"]