From 3601d8b769554508b41d7db7d788c143896bbe5a Mon Sep 17 00:00:00 2001 From: rpm-mcarman <65228808+rpm-mcarman@users.noreply.github.com> Date: Fri, 26 May 2023 11:28:38 -0400 Subject: [PATCH] First Commit --- .DS_Store | Bin 0 -> 6148 bytes .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 7921 bytes .../xcschemes/xcschememanagement.plist | 14 + Dockerfile | 77 ++++++ Package.resolved | 239 ++++++++++++++++++ Package.swift | 35 +++ Public/.gitkeep | 0 Sources/.DS_Store | Bin 0 -> 6148 bytes Sources/App/.DS_Store | Bin 0 -> 6148 bytes Sources/App/Controllers/.gitkeep | 0 .../App/Controllers/FuelEntryController.swift | 41 +++ Sources/App/Controllers/TodoController.swift | 31 +++ Sources/App/Migrations/CreateFuelEntry.swift | 34 +++ Sources/App/Migrations/CreateTodo.swift | 14 + Sources/App/Models/FuelEntry.swift | 105 ++++++++ Sources/App/Models/Todo.swift | 19 ++ Sources/App/configure.swift | 29 +++ Sources/App/entrypoint.swift | 35 +++ Sources/App/routes.swift | 34 +++ Tests/AppTests/AppTests.swift | 15 ++ docker-compose.yml | 74 ++++++ 22 files changed, 803 insertions(+) create mode 100644 .DS_Store create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 .swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 .swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100755 Dockerfile create mode 100755 Package.resolved create mode 100755 Package.swift create mode 100755 Public/.gitkeep create mode 100755 Sources/.DS_Store create mode 100755 Sources/App/.DS_Store create mode 100755 Sources/App/Controllers/.gitkeep create mode 100755 Sources/App/Controllers/FuelEntryController.swift create mode 100755 Sources/App/Controllers/TodoController.swift create mode 100755 Sources/App/Migrations/CreateFuelEntry.swift create mode 100755 Sources/App/Migrations/CreateTodo.swift create mode 100755 Sources/App/Models/FuelEntry.swift create mode 100755 Sources/App/Models/Todo.swift create mode 100755 Sources/App/configure.swift create mode 100755 Sources/App/entrypoint.swift create mode 100755 Sources/App/routes.swift create mode 100755 Tests/AppTests/AppTests.swift create mode 100755 docker-compose.yml diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..eae830c5351ca8e6047a0998ccc09aeeca92361b GIT binary patch literal 6148 zcmeHLJ5Iwu5Pb^u*4PMAlw0Z&Ol2`gJ^iO zyMWgwln6yL((YT&XJ`B*?_>kO3>TvT&@{tZDoyjU%Bkz!zC9n@^dApH`{-Bu^t^F4m?wr} z6USH}N6Fkpm|M)Dy_p}VU+&)@B!95Sr24L5nNLuMO@l3Ds94Dr3Y=j!niXYURizkP zrm)EDwd>I;^YYr!)Y_+2=n}86R7=?gPNYQ4F$-%kcdVshZPoEfSK#p@Sq+aczqAOI zRbqfWTco$+P-kU88Bhkk7?ATJ<`7Ig)(-8_!O30$h!NFRxVDXg7>k0Kc&r^#LoqIt z=t7OZVi*@reeC@bkF`S=4r4QOAD`Ly8;bF5%EZ + + + + diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..f5ed61856e7cd8f5d496efcb18414c59945572f6 GIT binary patch literal 7921 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 + + + + SchemeUserState + + Fueling.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..e3bde89 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# ================================ +# Build image +# ================================ +FROM swift:5.8-jammy as build + +# Install OS updates and, if needed, sqlite3 +RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ + && apt-get -q update \ + && apt-get -q dist-upgrade -y\ + && rm -rf /var/lib/apt/lists/* + +# Set up a build area +WORKDIR /build + +# First just resolve dependencies. +# This creates a cached layer that can be reused +# as long as your Package.swift/Package.resolved +# files do not change. +COPY ./Package.* ./ +RUN swift package resolve + +# Copy entire repo into container +COPY . . + +# Build everything, with optimizations +RUN swift build -c release --static-swift-stdlib + +# Switch to the staging area +WORKDIR /staging + +# Copy main executable to staging area +RUN cp "$(swift build --package-path /build -c release --show-bin-path)/App" ./ + +# Copy resources bundled by SPM to staging area +RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \; + +# Copy any resources from the public directory and views directory if the directories exist +# Ensure that by default, neither the directory nor any of its contents are writable. +RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true +RUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true + +# ================================ +# Run image +# ================================ +FROM ubuntu:jammy + +# Make sure all system packages are up to date, and install only essential packages. +RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ + && apt-get -q update \ + && apt-get -q dist-upgrade -y \ + && apt-get -q install -y \ + ca-certificates \ + tzdata \ +# If your app or its dependencies import FoundationNetworking, also install `libcurl4`. + # libcurl4 \ +# If your app or its dependencies import FoundationXML, also install `libxml2`. + # libxml2 \ + && rm -r /var/lib/apt/lists/* + +# Create a vapor user and group with /app as its home directory +RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app vapor + +# Switch to the new home directory +WORKDIR /app + +# Copy built executable and any staged resources from builder +COPY --from=build --chown=vapor:vapor /staging /app + +# Ensure all further commands run as the vapor user +USER vapor:vapor + +# Let Docker bind to port 8080 +EXPOSE 8080 + +# Start the Vapor service when the image is run, default to listening on 8080 in production environment +ENTRYPOINT ["./App"] +CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"] diff --git a/Package.resolved b/Package.resolved new file mode 100755 index 0000000..8b97df4 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,239 @@ +{ + "pins" : [ + { + "identity" : "async-http-client", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/async-http-client.git", + "state" : { + "revision" : "333e60cc90f52973f7ee29cd8e3a7f6adfe79f4e", + "version" : "1.17.0" + } + }, + { + "identity" : "async-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/async-kit.git", + "state" : { + "revision" : "a61da00d404ec91d12766f1b9aac7d90777b484d", + "version" : "1.17.0" + } + }, + { + "identity" : "console-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/console-kit.git", + "state" : { + "revision" : "447f1046fb4e9df40973fe426ecb24a6f0e8d3b4", + "version" : "4.6.0" + } + }, + { + "identity" : "fluent", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent.git", + "state" : { + "revision" : "4b4d8bf15a06fd60137e9c543e5503c4b842654e", + "version" : "4.8.0" + } + }, + { + "identity" : "fluent-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent-kit.git", + "state" : { + "revision" : "559dc9c730cde77cc9dac76fba73d6e0855f0220", + "version" : "1.42.0" + } + }, + { + "identity" : "fluent-postgres-driver", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent-postgres-driver.git", + "state" : { + "revision" : "afc07463fc5589f5bf95f98ae2719d13ad9fbbcf", + "version" : "2.6.0" + } + }, + { + "identity" : "multipart-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/multipart-kit.git", + "state" : { + "revision" : "1adfd69df2da08f7931d4281b257475e32c96734", + "version" : "4.5.4" + } + }, + { + "identity" : "postgres-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/postgres-kit.git", + "state" : { + "revision" : "3ad08265aade48fff5f2ec772e96ecf51b7900a2", + "version" : "2.10.0" + } + }, + { + "identity" : "postgres-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/postgres-nio.git", + "state" : { + "revision" : "98b8e1b1488c706f8bff9eb07560745630a64679", + "version" : "1.14.0" + } + }, + { + "identity" : "routing-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/routing-kit.git", + "state" : { + "revision" : "611bc45c5dfb1f54b84d99b89d1f72191fb6b71b", + "version" : "4.7.2" + } + }, + { + "identity" : "sql-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/sql-kit.git", + "state" : { + "revision" : "d547122756534d8224d4599739da0e82e4695623", + "version" : "3.25.0" + } + }, + { + "identity" : "swift-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-algorithms.git", + "state" : { + "revision" : "b14b7f4c528c942f121c8b860b9410b2bf57825e", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10", + "version" : "1.1.0" + } + }, + { + "identity" : "swift-backtrace", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/swift-backtrace.git", + "state" : { + "revision" : "f25620d5d05e2f1ba27154b40cafea2b67566956", + "version" : "1.3.3" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", + "version" : "1.0.4" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "33a20e650c33f6d72d822d558333f2085effa3dc", + "version" : "2.5.0" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", + "version" : "1.5.2" + } + }, + { + "identity" : "swift-metrics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-metrics.git", + "state" : { + "revision" : "e8bced74bc6d747745935e469f45d03f048d6cbd", + "version" : "2.3.4" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "f7c46552983b06b0958a1a4c8bc5199406ae4c8a", + "version" : "2.51.0" + } + }, + { + "identity" : "swift-nio-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-extras.git", + "state" : { + "revision" : "0e0d0aab665ff1a0659ce75ac003081f2b1c8997", + "version" : "1.19.0" + } + }, + { + "identity" : "swift-nio-http2", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-http2.git", + "state" : { + "revision" : "6d021a48483dbb273a9be43f65234bdc9185b364", + "version" : "1.26.0" + } + }, + { + "identity" : "swift-nio-ssl", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-ssl.git", + "state" : { + "revision" : "9d0d5d8798a576fbf674a823734e65e15ca5f2ec", + "version" : "2.23.1" + } + }, + { + "identity" : "swift-nio-transport-services", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-transport-services.git", + "state" : { + "revision" : "59b966415dd336db6f388bbfe3fb43cfa549b981", + "version" : "1.16.0" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics", + "state" : { + "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", + "version" : "1.0.2" + } + }, + { + "identity" : "vapor", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/vapor.git", + "state" : { + "revision" : "f4b00a5350238fe896d865d96d64f12fcbbeda95", + "version" : "4.76.0" + } + }, + { + "identity" : "websocket-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/websocket-kit.git", + "state" : { + "revision" : "2166cbe932b29b6419f9cd751e8b27c647e1238e", + "version" : "2.8.0" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift new file mode 100755 index 0000000..732222e --- /dev/null +++ b/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.8 +import PackageDescription + +let package = Package( + name: "Fueling", + platforms: [ + .macOS(.v12) + ], + dependencies: [ + // 💧 A server-side Swift web framework. + .package(url: "https://github.com/vapor/vapor.git", from: "4.76.0"), + .package(url: "https://github.com/vapor/fluent.git", from: "4.8.0"), + .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"), + ], + targets: [ + .executableTarget( + name: "App", + dependencies: [ + .product(name: "Fluent", package: "fluent"), + .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"), + .product(name: "Vapor", package: "vapor") + ], + swiftSettings: [ + // Enable better optimizations when building in Release configuration. Despite the use of + // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release + // builds. See for details. + .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)) + ] + ), + .testTarget(name: "AppTests", dependencies: [ + .target(name: "App"), + .product(name: "XCTVapor", package: "vapor"), + ]) + ] +) diff --git a/Public/.gitkeep b/Public/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/Sources/.DS_Store b/Sources/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..d424cbc6907e942c0b184c17c8604b6262c4c656 GIT binary patch literal 6148 zcmeHKOG?B*5Up}X7;uxN%f7;_-SiA`FU$ob6VOG54v6~(FXSb>k4Ny;M@=xe97L+1 z`&H_f^n-MF5fSg7`z=vJL>ml27G*)iyy&{{;7cG&jpNXjGC-x3yZ$`Lx36eNBaQvj z^!wE`JZulgavE(JH}BuUr_#3FP&!z^O}%?*-gZ-O|BH8goK}6c;xPvU!9Xw&3s9OgnZ2^F5m{p)lFCjU}F*{a6cpz-4KucwB zG1$^!Pac;YtD&V6d-K8m=FjGZ_3fyiG@Lja#vBX;19JwvNpIx*zr`=J*yPVs;wcyi z2L2fXylxNe9*gp`^~dt$tW6mA7$Oo^MS(z{T>|hR`^dFA&7VX^Tz0I6Qbp{V4vdR{ M5)xA|a0UiG0N#Ws)Bpeg literal 0 HcmV?d00001 diff --git a/Sources/App/.DS_Store b/Sources/App/.DS_Store new file mode 100755 index 0000000000000000000000000000000000000000..a1c20a60a38c11c459bf68812661f96339c62979 GIT binary patch literal 6148 zcmeHKOHRWu5FM8Y3Sp5YOJAWk2qi38^#V{@wUrW571(>n6*v%_g%t~SEI{Io$13?& zVuKKxsqE+2Z=9FsHm-@tjPGY7qJ)TsD2#(cR2{+NTv{;*J;y-f=h#q5IX-jRwAkA5 z7aicWi|GvOc0+eqweR)2%@?D|by=)M_Q69xvnaD{nwL{B@I`USJ3XK7V$XiSUN5UE zET*L+x}X&;p!N!yS6+KVZS>N|t81nCn`rHvvnZ=dcXK;*?n7ruGyEEIYUqnUp|3Vx z*X15xKc3jb2B)g0o715K(`cvz>VP^>a{zlb$1oPuS{+aa)Pb!7{C=<~jA=k8=wBVE z{1E^cq1y-Ae3sxG8!!#%1Tlgzk_wbm6OI^0($Q}NFAeAfC7nz-e3)=&6HX|`-yQQ? zPbZTKYOM~a19b=Xt<8 literal 0 HcmV?d00001 diff --git a/Sources/App/Controllers/.gitkeep b/Sources/App/Controllers/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/Sources/App/Controllers/FuelEntryController.swift b/Sources/App/Controllers/FuelEntryController.swift new file mode 100755 index 0000000..fa8e6b9 --- /dev/null +++ b/Sources/App/Controllers/FuelEntryController.swift @@ -0,0 +1,41 @@ +// +// FuelEntryController.swift +// +// +// Created by Bill Ryckman on 4/24/23. +// + +import Vapor + +struct FuelEntryController: RouteCollection { + func boot(routes: RoutesBuilder) throws { + let fuelEntries = routes.grouped("fuel_entries") + fuelEntries.get(use: index) + fuelEntries.post(use: create) + fuelEntries.group(":fuelEntryID") { fuelEntry in + fuelEntry.delete(use: delete) + } + } + + func index(req: Request) async throws -> [FuelEntry] { + try await FuelEntry.query(on: req.db).all() + } + + func create(req: Request) async throws -> FuelEntry { + let fuelEntry = try req.content.decode(FuelEntry.self) + try await fuelEntry.save(on: req.db) + return fuelEntry + } + + func delete(req: Request) async throws -> HTTPStatus { + guard let fuelEntry = try await FuelEntry.find(req.parameters.get("fuelEntryID"), on: req.db) else { + throw Abort(.notFound) + } + try await fuelEntry.delete(on: req.db) + return .noContent + } +} + + + + diff --git a/Sources/App/Controllers/TodoController.swift b/Sources/App/Controllers/TodoController.swift new file mode 100755 index 0000000..265228d --- /dev/null +++ b/Sources/App/Controllers/TodoController.swift @@ -0,0 +1,31 @@ +import Fluent +import Vapor + +struct TodoController: RouteCollection { + func boot(routes: RoutesBuilder) throws { + let todos = routes.grouped("todos") + todos.get(use: index) + todos.post(use: create) + todos.group(":todoID") { todo in + todo.delete(use: delete) + } + } + + func index(req: Request) async throws -> [Todo] { + try await Todo.query(on: req.db).all() + } + + func create(req: Request) async throws -> Todo { + let todo = try req.content.decode(Todo.self) + try await todo.save(on: req.db) + return todo + } + + func delete(req: Request) async throws -> HTTPStatus { + guard let todo = try await Todo.find(req.parameters.get("todoID"), on: req.db) else { + throw Abort(.notFound) + } + try await todo.delete(on: req.db) + return .noContent + } +} diff --git a/Sources/App/Migrations/CreateFuelEntry.swift b/Sources/App/Migrations/CreateFuelEntry.swift new file mode 100755 index 0000000..1b61d55 --- /dev/null +++ b/Sources/App/Migrations/CreateFuelEntry.swift @@ -0,0 +1,34 @@ +// +// File.swift +// +// +// Created by Bill Ryckman on 4/26/23. +// + +import Vapor +import Fluent + +struct CreateFuelEntry: AsyncMigration { + func prepare(on database: Database) async throws { + try await database.schema("fuel_entries") + .id() + .field("timestamp", .datetime) + .field("user_id", .string) + .field("gps_location", .string) + .field("image", .string) + .field("product", .string) + .field("make", .string) + .field("model", .string) + .field("serial_number", .string) + .field("tank_id", .string) + .field("fuel_amount", .double) + .field("fuel_type", .string) + .field("is_override", .bool) + .create() + } + + func revert(on database: Database) async throws { + try await database.schema("fuel_entries").delete() + } +} + diff --git a/Sources/App/Migrations/CreateTodo.swift b/Sources/App/Migrations/CreateTodo.swift new file mode 100755 index 0000000..bf6d945 --- /dev/null +++ b/Sources/App/Migrations/CreateTodo.swift @@ -0,0 +1,14 @@ +import Fluent + +struct CreateTodo: AsyncMigration { + func prepare(on database: Database) async throws { + try await database.schema("todos") + .id() + .field("title", .string, .required) + .create() + } + + func revert(on database: Database) async throws { + try await database.schema("todos").delete() + } +} diff --git a/Sources/App/Models/FuelEntry.swift b/Sources/App/Models/FuelEntry.swift new file mode 100755 index 0000000..073d0ef --- /dev/null +++ b/Sources/App/Models/FuelEntry.swift @@ -0,0 +1,105 @@ +// +// FuelEntry.swift +// +// +// Created by Bill Ryckman on 4/24/23. +// + +import Fluent +import Vapor + +final class FuelEntry: Model, Content { + static let schema = "fuel_entries" + + @ID(key: .id) + var id: UUID? + + @Field(key: "timestamp") + var timestamp: Date + + @Field(key: "user_id") + var userID: String + + @Field(key: "gps_location") + var gpsLocation: String + + @Field(key: "image") + var image: String + + @Field(key: "product") + var product: String + + @Field(key: "make") + var make: String + + @Field(key: "model") + var model: String + + @Field(key: "serial_number") + var serialNumber: String + + @Field(key: "tank_id") + var tankID: String + + @Field(key: "fuel_amount") + var fuelAmount: Double + + @Field(key: "fuel_type") + var fuelType: String + + @Field(key: "is_override") + var isOverride: Bool + + init() {} + + init(id: UUID? = nil, timestamp: Date, userID: String, gpsLocation: String, image: String, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool) { + self.id = id + self.timestamp = timestamp + self.userID = userID + self.gpsLocation = gpsLocation + self.image = image + self.product = product + self.make = make + self.model = model + self.serialNumber = serialNumber + self.tankID = tankID + self.fuelAmount = fuelAmount + self.fuelType = fuelType + self.isOverride = isOverride + } +} + + +//extension FuelEntry: Content {} +//extension FuelEntry: Migration { +// func prepare(on database: FluentKit.Database) -> NIOCore.EventLoopFuture { +// <#code#> +// } +// +// func revert(on database: FluentKit.Database) -> NIOCore.EventLoopFuture { +// <#code#> +// } +// func prepare(on database: Database) -> EventLoopFuture { +// return database.schema("fuel_entries") +// .id() +// .field("timestamp", .datetime) +// .field("user_id", .string) +// .field("gps_location", .string) +// .field("image", .string) +// .field("product", .string) +// .field("make", .string) +// .field("model", .string) +// .field("serial_number", .string) +// .field("tank_id", .string) +// .field("fuel_amount", .double) +// .field("fuel_type", .string) +// .field("is_override", .bool) +// .create() +// } +// +// func revert(on database: Database) -> EventLoopFuture { +// return database.schema("fuel_entries").delete() +// } +//} +//extension FuelEntry: Parameter {} + diff --git a/Sources/App/Models/Todo.swift b/Sources/App/Models/Todo.swift new file mode 100755 index 0000000..c3c9eac --- /dev/null +++ b/Sources/App/Models/Todo.swift @@ -0,0 +1,19 @@ +import Fluent +import Vapor + +final class Todo: Model, Content { + static let schema = "todos" + + @ID(key: .id) + var id: UUID? + + @Field(key: "title") + var title: String + + init() { } + + init(id: UUID? = nil, title: String) { + self.id = id + self.title = title + } +} diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift new file mode 100755 index 0000000..63e3a31 --- /dev/null +++ b/Sources/App/configure.swift @@ -0,0 +1,29 @@ +import Fluent +import FluentPostgresDriver +import Vapor + +// configures your application +public func configure(_ app: Application) async throws { + // uncomment to serve files from /Public folder + // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) + + app.databases.use(.postgres(hostname: "localhost", username: "mcarman", password: "@ng31F@rm0823262", database: "fueling"), as: .psql) + +// app.databases.use(.postgres( +// hostname: Environment.get("localhost") ?? "localhost", +// port: Environment.get("5432").flatMap(Int.init(_:)) ?? PostgresConfiguration.ianaPortNumber, +// username: Environment.get("mcarman") ?? "vapor_username", +// //username: Environment.get("bryckman") ?? "vapor_username", +// password: Environment.get("@ng31F@rm0823262" ?? "vapor_password"), +// //password: Environment.get("55H1ck0ry1669") ?? "vapor_password", +// database: Environment.get("fueling") ?? "vapor_database" +// ), as: .psql) + app.http.server.configuration.port = 3001 + +// app.migrations.add(CreateTodo()) + app.migrations.add(CreateFuelEntry()) + try await app.autoMigrate().get() + + // register routes + try routes(app) +} diff --git a/Sources/App/entrypoint.swift b/Sources/App/entrypoint.swift new file mode 100755 index 0000000..cae6165 --- /dev/null +++ b/Sources/App/entrypoint.swift @@ -0,0 +1,35 @@ +import Vapor +import Dispatch +import Logging + +/// This extension is temporary and can be removed once Vapor gets this support. +private extension Vapor.Application { + static let baseExecutionQueue = DispatchQueue(label: "vapor.codes.entrypoint") + + func runFromAsyncMainEntrypoint() async throws { + try await withCheckedThrowingContinuation { continuation in + Vapor.Application.baseExecutionQueue.async { [self] in + do { + try self.run() + continuation.resume() + } catch { + continuation.resume(throwing: error) + } + } + } + } +} + +@main +enum Entrypoint { + static func main() async throws { + var env = try Environment.detect() + try LoggingSystem.bootstrap(from: &env) + + let app = Application(env) + defer { app.shutdown() } + + try await configure(app) + try await app.runFromAsyncMainEntrypoint() + } +} diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift new file mode 100755 index 0000000..be06662 --- /dev/null +++ b/Sources/App/routes.swift @@ -0,0 +1,34 @@ +//import Fluent +//import Vapor +// +//func routes(_ app: Application) throws { +// app.get { req async in +// "It works!" +// } +// +// app.get("hello") { req async -> String in +// "Hello, world!" +// } +// +// try app.register(collection: TodoController()) +//} + +import Vapor + +func routes(_ app: Application) throws { + let fuelEntryController = FuelEntryController() + + app.get("fuel-entries") { req in + try await fuelEntryController.index(req: req) + } + + app.post("fuel-entries") { req in + try await fuelEntryController.create(req: req) + } + + app.delete("fuel-entries", ":fuelEntryID") { req in + try await fuelEntryController.delete(req: req) + } +} + + diff --git a/Tests/AppTests/AppTests.swift b/Tests/AppTests/AppTests.swift new file mode 100755 index 0000000..8cfd310 --- /dev/null +++ b/Tests/AppTests/AppTests.swift @@ -0,0 +1,15 @@ +@testable import App +import XCTVapor + +final class AppTests: XCTestCase { + func testHelloWorld() async throws { + let app = Application(.testing) + defer { app.shutdown() } + try await configure(app) + + try app.test(.GET, "hello", afterResponse: { res in + XCTAssertEqual(res.status, .ok) + XCTAssertEqual(res.body.string, "Hello, world!") + }) + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..ab5ac60 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,74 @@ +# Docker Compose file for Vapor +# +# Install Docker on your system to run and test +# your Vapor app in a production-like environment. +# +# Note: This file is intended for testing and does not +# implement best practices for a production deployment. +# +# Learn more: https://docs.docker.com/compose/reference/ +# +# Build images: docker-compose build +# Start app: docker-compose up app +# Start database: docker-compose up db +# Run migrations: docker-compose run migrate +# Stop all: docker-compose down (add -v to wipe db) +# +version: '3.7' + +volumes: + db_data: + +x-shared_environment: &shared_environment + LOG_LEVEL: ${LOG_LEVEL:-debug} + DATABASE_HOST: db + DATABASE_NAME: vapor_database + DATABASE_USERNAME: vapor_username + DATABASE_PASSWORD: vapor_password + +services: + app: + image: fueling:latest + build: + context: . + environment: + <<: *shared_environment + depends_on: + - db + ports: + - '8080:8080' + # 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: fueling:latest + build: + context: . + environment: + <<: *shared_environment + depends_on: + - db + command: ["migrate", "--yes"] + deploy: + replicas: 0 + revert: + image: fueling:latest + build: + context: . + environment: + <<: *shared_environment + depends_on: + - db + command: ["migrate", "--revert", "--yes"] + deploy: + replicas: 0 + db: + image: postgres:15-alpine + volumes: + - db_data:/var/lib/postgresql/data/pgdata + environment: + PGDATA: /var/lib/postgresql/data/pgdata + POSTGRES_USER: vapor_username + POSTGRES_PASSWORD: vapor_password + POSTGRES_DB: vapor_database + ports: + - '5432:5432'