diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..eae830c Binary files /dev/null and b/.DS_Store differ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + 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 0000000..f5ed618 Binary files /dev/null and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/.swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..f5003f8 --- /dev/null +++ b/.swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + 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 0000000..d424cbc Binary files /dev/null and b/Sources/.DS_Store differ diff --git a/Sources/App/.DS_Store b/Sources/App/.DS_Store new file mode 100755 index 0000000..a1c20a6 Binary files /dev/null and b/Sources/App/.DS_Store differ 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'