Commits vergleichen

...

56 Commits

Autor SHA1 Nachricht Datum
  Mike Carman 5a22a084a3 Fixed unique filed vor 2 Wochen
  Mike Carman 5d4bdbdf42 trapping error vor 2 Wochen
  Mike Carman 7ebd78bbdc preconcurrency vor 2 Wochen
  Mike Carman 834ffecde0 went to 6.3.1 vor 2 Wochen
  Mike Carman ba30a29e65 @nonsendable Application Extension added vor 2 Wochen
  Mike Carman 7859e2490f ?? vor 2 Wochen
  Mike Carman 5ce3f35db6 db port vor 2 Wochen
  Mike Carman 2fb399a024 upgraderd to swift 6.4.1 vor 2 Wochen
  Mike Carman 2d9b0b99a7 Configured Database connection vor 2 Wochen
  Mike Carman 70597e7abf V2.2 vor 2 Wochen
  Mike Carman eb2ba91a21 V2.2 vor 2 Wochen
  Mike Carman 3824389ec3 V2.2 vor 2 Wochen
  Mike Carman fbce601623 v2.1 vor 2 Wochen
  Mike Carman a2515ae396 V2.1 vor 2 Wochen
  Mike Carman 1ac2e40102 V2.1 Controller vor 2 Wochen
  Mike Carman bfce5ee48b Minor fixes to post function vor 1 Monat
  Mike Carman a3e9110dc1 Fixed Post function vor 1 Monat
  Mike Carman 5633bb9b72 Added V2.1 vor 1 Monat
  Mike Carman 58e35c8a2e Adds vor 1 Monat
  Mike Carman 96ca775eb2 Fix for useremail vor 6 Monaten
  Mike Carman 745b9971f0 Fueling change to allow for update and create. vor 9 Monaten
  Mike Carman 75284b1afe Changed tableserial to useremail vor 1 Jahr
  Mike Carman 87eac42ba9 Added tabletserial to database and responses vor 1 Jahr
  Mike Carman d651f365a4 db connection vor 1 Jahr
  Mike Carman 4216bbf96c Maxim qstrings and other data vor 1 Jahr
  Mike Carman ce895f2712 Fixed image always null vor 1 Jahr
  Mike Carman aafba8cb9b Connection String Port Number update vor 1 Jahr
  Mike Carman a3e809a74b All fueling changes requested for apis vor 1 Jahr
  Mike Carman cade0e4f23 useriterface vor 1 Jahr
  Mike Carman 5f0204d89c database port vor 1 Jahr
  Mike Carman 7a66dc7ed3 Changes for Andy vor 1 Jahr
  Mike Carman 976f328f37 small modification to rpm query vor 1 Jahr
  Mike Carman ad33c6ce24 Fixed db connection string vor 1 Jahr
  Mike Carman e1d679bf03 Fueling service changes per Andy added startdate and enddate perameter as well as previous parameter vor 1 Jahr
  Mike Carman c586708c1c Reset port vor 1 Jahr
  Mike Carman ee94988698 fix for null downloaded vor 1 Jahr
  Mike Carman 5ecff22dbf Changed port on connection string vor 1 Jahr
  Mike Carman 8539c28d4b latest vor 1 Jahr
  rpm-mcarman 5fd27fa971 Update for image field vor 2 Jahren
  rpm-mcarman e1c683d582 Token auth implementation vor 2 Jahren
  rpm-mcarman 3a257e2b4d Added to selected fields vor 2 Jahren
  rpm-mcarman da4e9a7324 Added downloaded field to data vor 2 Jahren
  rpm-mcarman f202dff478 Pushing edits vor 2 Jahren
  rpm-mcarman e6b23df180 w/o token auth on api calls vor 2 Jahren
  rpm-mcarman 7e31c3c36b Removed Pagination vor 2 Jahren
  Bill Ryckman 46d69a83dc Adding pagination to GET request vor 2 Jahren
  admin 3976bac4aa app.routes.defaultMaxBodySize = 128mb vor 2 Jahren
  admin 678340efac Remove processing of imageData to a file. Store imageData in image field in database. vor 2 Jahren
  admin 5e562aa8d8 Update routes to use api/v2 and include with-images vor 2 Jahren
  admin 64f9c4aac9 Second attempt at uploading images. This time the imageData will come with the JSON data and get processed during the Create. vor 2 Jahren
  admin d6a38d0e35 Remove Field imageData as it's not going to the database, just JSON. vor 2 Jahren
  admin 607135f873 Move back to Swift Tools 5.8 and move to port 3002. vor 2 Jahren
  admin 86d9ef808e Downgrade Swift Tools Package to 5.7.3 to work on Vapor server. vor 2 Jahren
  admin 934d4d9c25 Added getAllEntriesWithImages to bring down pictures with data if need be. Left default route intact without images. vor 2 Jahren
  admin b6402df273 Update to create new controller for V2 routes. vor 2 Jahren
  admin 9ccee49e92 First attempt at image upload. Need to set image = to WorkingDirectory+Images+UUID.png. Then upload id and image to complete process. vor 2 Jahren
27 geänderte Dateien mit 4013 neuen und 127 gelöschten Zeilen
  1. BIN
      .swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate
  2. BIN
      .swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate
  3. +92
    -0
      .swiftpm/xcode/xcshareddata/xcschemes/Fueling.xcscheme
  4. +424
    -0
      .swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  5. +27
    -0
      .swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcschemes/xcschememanagement.plist
  6. +24
    -0
      .swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  7. +4
    -3
      Sources/App/Controllers/FuelEntryController.swift
  8. +797
    -0
      Sources/App/Controllers/FuelEntryV2Controller.swift
  9. +0
    -31
      Sources/App/Controllers/TodoController.swift
  10. +42
    -0
      Sources/App/Controllers/UserController.swift
  11. +808
    -0
      Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift
  12. +822
    -0
      Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift
  13. +68
    -0
      Sources/App/DTO/V2.1/FuelEntryDTO.swift
  14. +71
    -0
      Sources/App/DTO/V2.2/v2.2.swift
  15. +31
    -0
      Sources/App/Errors/UserErrors.swift
  16. +53
    -0
      Sources/App/Helpers/PageHelper.swift
  17. +142
    -19
      Sources/App/Migrations/CreateFuelEntry.swift
  18. +0
    -14
      Sources/App/Migrations/CreateTodo.swift
  19. +51
    -0
      Sources/App/Migrations/CreateToken.swift
  20. +69
    -0
      Sources/App/Migrations/CreateUser.swift
  21. +269
    -19
      Sources/App/Models/FuelEntry.swift
  22. +0
    -19
      Sources/App/Models/Todo.swift
  23. +54
    -0
      Sources/App/Models/Token.swift
  24. +58
    -0
      Sources/App/Models/User.swift
  25. +50
    -5
      Sources/App/configure.swift
  26. +13
    -2
      Sources/App/entrypoint.swift
  27. +44
    -15
      Sources/App/routes.swift

BIN
.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate Datei anzeigen


BIN
.swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate Datei anzeigen


+ 92
- 0
.swiftpm/xcode/xcshareddata/xcschemes/Fueling.xcscheme Datei anzeigen

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2640"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "App"
BuildableName = "App"
BlueprintName = "App"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AppTests"
BuildableName = "AppTests"
BlueprintName = "AppTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "/Users/mcarman/Projects/Vapor/fueling_v1"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
queueDebuggingEnableBacktraceRecording = "Yes">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "App"
BuildableName = "App"
BlueprintName = "App"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "App"
BuildableName = "App"
BlueprintName = "App"
ReferencedContainer = "container:">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

+ 424
- 0
.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist Datei anzeigen

@@ -0,0 +1,424 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "83CCDCAB-5627-45C3-BC68-1A59A1D654E0"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1165C094-7F83-4580-9E1E-36ACC4A42F04"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "247"
endingLineNumber = "247"
landmarkName = "init(id:timestamp:userID:latitude:longitude:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:uniqueID:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "F812306B-8399-447C-84BE-4AC7A776D420"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "225"
endingLineNumber = "225"
landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:uniqueID:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "71BCB6D1-5BA1-44A4-B4BB-3CF6616E1B45"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "203"
endingLineNumber = "203"
landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:downloaded:tabletserial:uniqueID:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "D9ECF7FA-C31F-4E3E-A5E0-09B15672960B"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "469"
endingLineNumber = "469"
landmarkName = "filtered(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "FCC6E6E2-0876-4286-93D8-266C58EAE0FE"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "491"
endingLineNumber = "491"
landmarkName = "filtered(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "0BD134C2-3EF8-4F87-A31F-13B3E66DE82C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "511"
endingLineNumber = "511"
landmarkName = "filtered(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "44A1C379-F3B1-438B-9427-C87E053877C5"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "529"
endingLineNumber = "529"
landmarkName = "filtered(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "9959EDC1-617C-4BD3-A47D-FA6774BA18A9"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "547"
endingLineNumber = "547"
landmarkName = "filtered(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "C4228ECF-D80C-49F9-BBD8-A33956D74C07"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/FuelEntryV2Controller.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "291"
endingLineNumber = "291"
landmarkName = "filterLoadDate(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "EDB87104-3630-4689-B6E2-9D8D6579F668"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/configure.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "56"
endingLineNumber = "56"
landmarkName = "configure(_:)"
landmarkType = "9">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "4D7F14D0-ED08-423F-B099-38D61A323334"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "293"
endingLineNumber = "293"
landmarkName = "init(from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "F780E27A-6B44-4CA1-B584-E095BC15C57D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "649"
endingLineNumber = "649"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "69F0984D-1601-4AF5-BE09-C67CC4143CA8"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "648"
endingLineNumber = "648"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "E872CE7E-FDFD-4BAF-9E4D-6BC2AE7ED040"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "299"
endingLineNumber = "299"
landmarkName = "init(from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "4BA9FF0F-7885-427C-BFB1-D1967AA88E83"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "677"
endingLineNumber = "677"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "96C20337-81C9-49B5-9456-AE00B5B367EA"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "674"
endingLineNumber = "674"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "41995477-EE00-40B0-82C8-77CE86AB02D9"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "655"
endingLineNumber = "655"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "453CDD8B-F708-48F9-A903-483CC4F2C2F6"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "663"
endingLineNumber = "663"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "8E150450-9BCE-4BF7-A6EB-404074E2B086"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "680"
endingLineNumber = "680"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "9C0D83E8-7F5C-4097-B58F-6EC149EABA42"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/DTO/V2.2/v2.2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "66"
endingLineNumber = "66"
landmarkName = "init(from:img:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "AC2EE12C-8447-43D6-8C1E-489E4925645E"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "657"
endingLineNumber = "657"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "597F55FD-D311-466E-B34B-0DDB9CE84EF7"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "662"
endingLineNumber = "662"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "E380BBC5-32F7-4E93-95AD-15814A7DFB10"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/DTO/V2.2/v2.2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "67"
endingLineNumber = "67"
landmarkName = "init(from:img:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "642CF5F2-CE21-45F9-A85F-9A3481C10567"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "661"
endingLineNumber = "661"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "6BAA281D-5395-4922-B90D-9833111DD7AF"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "660"
endingLineNumber = "660"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1DCE9617-D797-4D10-A6B6-2CB87605432F"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Migrations/CreateFuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "121"
endingLineNumber = "121"
landmarkName = "prepare(on:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

+ 27
- 0
.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcschemes/xcschememanagement.plist Datei anzeigen

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Fueling.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>App</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>AppTests</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

+ 24
- 0
.swiftpm/xcode/xcuserdata/michielcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist Datei anzeigen

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "D3C706B5-9CBD-4F91-8A68-CF08562866B5"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "65DB5B80-779D-462A-A47F-A0A75B1C75F8"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/Controllers/UserController.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "35"
endingLineNumber = "35"
landmarkName = "create(req:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

+ 4
- 3
Sources/App/Controllers/FuelEntryController.swift Datei anzeigen

@@ -1,6 +1,6 @@
//
// FuelEntryController.swift
//
//
//
// Created by Bill Ryckman on 4/24/23.
//
@@ -20,13 +20,13 @@ struct FuelEntryController: RouteCollection {
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.create(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)
@@ -39,3 +39,4 @@ struct FuelEntryController: RouteCollection {





+ 797
- 0
Sources/App/Controllers/FuelEntryV2Controller.swift Datei anzeigen

@@ -0,0 +1,797 @@
//
// FuelEntryController.swift
//
//
// Created by Bill Ryckman on 4/24/23.
//

import Vapor
import FluentKit
import FluentSQL

struct FuelEntryV2Controller: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let fuelEntries = routes.grouped("fuel_entries")
let tokenAuthMiddleware = Token.authenticator()
let guardMiddleware = User.guardMiddleware()
let tokenAuthGroup = fuelEntries.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: filterLoadDate)
tokenAuthGroup.get("main", use: filterLoadDate)
tokenAuthGroup.get("timestamp", use: filterTimeStamp)
tokenAuthGroup.get("images", use: filterImages)
tokenAuthGroup.get("test", use: filtered)
tokenAuthGroup.get("rpm", use: rpm)
tokenAuthGroup.get("maxim", use: filtered)
tokenAuthGroup.post(use: create)
tokenAuthGroup.put( use: softdelete)
tokenAuthGroup.group(":fuelEntryID") { fuelEntry in
fuelEntry.delete(use: delete)
}
}
func index(req: Request) async throws -> [FuelEntry] {
return try await FuelEntry.query(on: req.db).all()
}
struct filteredreturn: Content{
var fuelentries: [FuelEntry]
var pages: Int
}
struct dload: Content{
var id: UUID
}
@Sendable
func softdelete(req: Request) async throws -> Response {
let ids = try req.content.decode([dload].self)
var msg: [String] = []
for id in ids {
guard let fe = try await FuelEntry.find(id.id, on: req.db) else {
msg.append("create error message for item")
continue
}
fe.downloaded = true
try await fe.save(on: req.db)
}
let response = Response(status: .ok, body: "good")
return response
}
fileprivate func queryParamExists(_ req: Request, _ param: String) async throws -> Bool {
let x = try req.query.get(Bool.self, at: param) ? true : false
print ("x: \(x)")
return x
}
fileprivate func parseDate2(_ dateString: String, _ start: Bool = true) throws -> Date? {
let currentDate = Calendar.current.startOfDay(for: Date())
if(dateString.count == 0 && start){
if(start) {
return Calendar.current.date(byAdding: .day, value: -2, to: currentDate)
} else {
return currentDate
}
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
return nil
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
return nil
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
return nil
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
return nil
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func parseDate(_ dateString: String, _ start: Bool = true) throws -> Date? {
if(dateString.count == 0){
return nil
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted."))
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Year component must be in the format yyyy and must be an integer"))
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Month component must be in the format MM and must be an integer"))
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Day component must be in the format dd and must be an integer"))
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func generateSQLNoImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\";")
}
if(enddate != nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) ;")
} else {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate);")
}
}
fileprivate func generateSQLImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
var sql: String = ""
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"image\" is not null;")
}
if(enddate != nil) {
sql = "select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(String(describing: startdate)) and \"loadDate\" <= \(String(describing: enddate)) and \"image\" is not null;"
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) and \"image\" is not null;")
} else {
sql = "select count(*) as total from \"fuel_entries\" where \"image\" is not null and \"loadDate\" >= '\(startdate ?? Date())';"
return SQLQueryString(sql)
}
}
struct CountRecords: Content {
let total: Int
}
fileprivate func getCount(_ req: Request, _ sql: SQLQueryString) async throws -> Int {
print(sql)
if let db = req.db as? SQLDatabase {
let answer = try await db
.raw(sql)
.first(decoding: CountRecords.self)
return answer?.total ?? 0
} else {
return 0
}
}
fileprivate func getPaging(_ page: Int, _ per: Int, _ records: Int) async throws -> [String: Any] {
var vals: [String: Any] = [:]
vals["page"] = page
vals["per"] = per
vals["records"] = records
let lastpage = Int(ceil(Double(records) / Double(per)))
vals["lastpage"] = (page == lastpage) ? true : false
vals["offset"] = ( page * per) - per
return vals
}
fileprivate func getParams(_ req: Request) async throws -> [qparam] {
let urlComponents = URLComponents(string: req.url.description)!
var params: [qparam] = []
for queryItem in urlComponents.queryItems ?? [] {
params.append(qparam(name: queryItem.name, value: queryItem.value ?? ""))
}
return params
}
fileprivate func getMaximError(_ req: Request, _ message: String) async throws -> returnObj {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let data : [FuelEntry.Output] = []
let meta = Meta(page: 0, per: 0, total: 0, lastpage: true, error: true, message: message, method: method, baseurl: baseurl, route: rt, querystring: "" )
return returnObj(data: data, meta: meta)
}
@Sendable
func filterLoadDate(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let qsStr = qs.joined(separator: "&")
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: emptyData, loaddate: $0.loaddate, useremail: $0.tabletserial ?? "")
}
let response = returnObj(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterImages(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 5
var records : Int = 0
if(enddate == nil) {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
.all()
.count
} else {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate)
}
.all()
.count
}
let qsStr = qs.joined(separator: "&")
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$image)
.field(\.$tabletserial)
.group(.and) {
grp in if(enddate != nil) {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate!)
} else {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: $0.image, loaddate: $0.loaddate, useremail: $0.tabletserial ?? "")
}
let response = returnObj(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterTimeStamp(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qs.joined(separator: "&"))")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$timestamp >= startdate ?? Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: -2, to: Date())!))
if( enddate != nil) {
grp.filter(\.$timestamp <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: emptyData, loaddate: $0.loaddate, useremail: $0.tabletserial ?? "")
}
let response = returnObj(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filtered(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
//.filter(\.$image != nil)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$tabletserial)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date())
}
if(!previous){
wimage.items.forEach{
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date(), useremail: img.tabletserial)
}
if(!previous) {
woimage.items.forEach {
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await woimage.encodeResponse(for: req)
}
}
@Sendable
func rpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}

func create(req: Request) async throws -> [FuelEntry] {
let fuelEntry = try req.content.decode([FuelEntry].self)
for entry in fuelEntry {
let data = entry.image ?? nil
let length = data?.count
if(data == nil || length == 0){
entry.image = nil
}
if(entry.id != nil){
let dbentry = try await FuelEntry.find(entry.id!, on: req.db)
if(dbentry != nil){
entry._$idExists = true
}
}
entry.save(on: req.db).whenFailure({ error in
})
}
// try await fuelEntry.create(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
}
@Sendable
func getrpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}
}

struct custMeta: Content {
let per: Int
let page: Int
let total: Int
let status: String?
}

struct PageRequest: Content {
var page: Int
var size: Int
var status: String?
// Add any other properties needed for pagination
init(page: Int, size: Int, status: String? = nil) {
self.page = page
self.size = size
self.status = status
}
}











+ 0
- 31
Sources/App/Controllers/TodoController.swift Datei anzeigen

@@ -1,31 +0,0 @@
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
}
}

+ 42
- 0
Sources/App/Controllers/UserController.swift Datei anzeigen

@@ -0,0 +1,42 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/26/24.
//

import Vapor
import Fluent

struct UserSignup: Content {
let username: String
let password: String
}

struct NewSession: Content {
let token: String
let user: User.Public
}

extension UserSignup: Validatable {
static func validations(_ validations: inout Validations) {
validations.add("username", as: String.self, is: !.empty)
validations.add("password", as: String.self, is: .count(6...))
}
}

struct UserController: RouteCollection {
func boot(routes: any RoutesBuilder) throws {
let users = routes.grouped("users")
users.post(use: create)
}
@Sendable
func create(req: Request) async throws -> Response{
let user = try req.content.decode(User.self)
user.passwordHash = try Bcrypt.hash(user.passwordHash)
try await user.save(on: req.db)
let token = try Token.generate(for: user)
try await token.save(on: req.db)
return try await token.encodeResponse(for: req)
}
}

+ 808
- 0
Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift Datei anzeigen

@@ -0,0 +1,808 @@
//
// FuelEntryControllerV2_1.swift
// Fueling
//
// Created by Michiel Carman on 9/15/26.
//

import Vapor
import FluentKit
import FluentSQL

struct FuelEntryV2ControllerV2_1: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let fuelEntries = routes.grouped("fuel_entries")
let tokenAuthMiddleware = Token.authenticator()
let guardMiddleware = User.guardMiddleware()
let tokenAuthGroup = fuelEntries.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: filterLoadDate)
tokenAuthGroup.get("main", use: filterLoadDate)
tokenAuthGroup.get("timestamp", use: filterTimeStamp)
tokenAuthGroup.get("images", use: filterImages)
tokenAuthGroup.get("test", use: filtered)
tokenAuthGroup.get("rpm", use: rpm)
tokenAuthGroup.get("maxim", use: filtered)
tokenAuthGroup.post(use: create)
tokenAuthGroup.put( use: softdelete)
tokenAuthGroup.group(":fuelEntryID") { fuelEntry in
fuelEntry.delete(use: delete)
}
}
@Sendable
func index(req: Request) async throws -> Response {
return try await FuelEntry.query(on: req.db).all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}.encodeResponse(for: req)
}
struct filteredreturn: Content{
var fuelentries: [FuelEntry]
var pages: Int
}
struct dload: Content{
var id: UUID
}
@Sendable
func softdelete(req: Request) async throws -> Response {
let ids = try req.content.decode([dload].self)
var msg: [String] = []
for id in ids {
guard let fe = try await FuelEntry.find(id.id, on: req.db) else {
msg.append("create error message for item")
continue
}
fe.downloaded = true
try await fe.save(on: req.db)
}
let response = Response(status: .ok, body: "good")
return response
}
fileprivate func queryParamExists(_ req: Request, _ param: String) async throws -> Bool {
let x = try req.query.get(Bool.self, at: param) ? true : false
print ("x: \(x)")
return x
}
fileprivate func parseDate2(_ dateString: String, _ start: Bool = true) throws -> Date? {
let currentDate = Calendar.current.startOfDay(for: Date())
if(dateString.count == 0 && start){
if(start) {
return Calendar.current.date(byAdding: .day, value: -2, to: currentDate)
} else {
return currentDate
}
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
return nil
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
return nil
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
return nil
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
return nil
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func parseDate(_ dateString: String, _ start: Bool = true) throws -> Date? {
if(dateString.count == 0){
return nil
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted."))
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Year component must be in the format yyyy and must be an integer"))
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Month component must be in the format MM and must be an integer"))
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Day component must be in the format dd and must be an integer"))
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func generateSQLNoImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\";")
}
if(enddate != nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) ;")
} else {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate);")
}
}
fileprivate func generateSQLImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
var sql: String = ""
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"image\" is not null;")
}
if(enddate != nil) {
sql = "select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(String(describing: startdate)) and \"loadDate\" <= \(String(describing: enddate)) and \"image\" is not null;"
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) and \"image\" is not null;")
} else {
sql = "select count(*) as total from \"fuel_entries\" where \"image\" is not null and \"loadDate\" >= '\(startdate ?? Date())';"
return SQLQueryString(sql)
}
}
struct CountRecords: Content {
let total: Int
}
fileprivate func getCount(_ req: Request, _ sql: SQLQueryString) async throws -> Int {
print(sql)
if let db = req.db as? SQLDatabase {
let answer = try await db
.raw(sql)
.first(decoding: CountRecords.self)
return answer?.total ?? 0
} else {
return 0
}
}
fileprivate func getPaging(_ page: Int, _ per: Int, _ records: Int) async throws -> [String: Any] {
var vals: [String: Any] = [:]
vals["page"] = page
vals["per"] = per
vals["records"] = records
let lastpage = Int(ceil(Double(records) / Double(per)))
vals["lastpage"] = (page == lastpage) ? true : false
vals["offset"] = ( page * per) - per
return vals
}
fileprivate func getParams(_ req: Request) async throws -> [qparam] {
let urlComponents = URLComponents(string: req.url.description)!
var params: [qparam] = []
for queryItem in urlComponents.queryItems ?? [] {
params.append(qparam(name: queryItem.name, value: queryItem.value ?? ""))
}
return params
}
fileprivate func getMaximError(_ req: Request, _ message: String) async throws -> returnObjV2_1 {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let data : [FuelEntry.V2_1_Output] = []
let meta = Meta(page: 0, per: 0, total: 0, lastpage: true, error: true, message: message, method: method, baseurl: baseurl, route: rt, querystring: "" )
return returnObjV2_1(data: data, meta: meta)
}
@Sendable
func filterLoadDate(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let qsStr = qs.joined(separator: "&")
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterImages(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 5
var records : Int = 0
if(enddate == nil) {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
.all()
.count
} else {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate)
}
.all()
.count
}
let qsStr = qs.joined(separator: "&")
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$image)
.field(\.$tabletserial)
.group(.and) {
grp in if(enddate != nil) {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate!)
} else {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterTimeStamp(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qs.joined(separator: "&"))")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$timestamp >= startdate ?? Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: -2, to: Date())!))
if( enddate != nil) {
grp.filter(\.$timestamp <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filtered(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
//.filter(\.$image != nil)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$tabletserial)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date())
}
if(!previous){
wimage.items.forEach{
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date(), useremail: img.tabletserial)
}
if(!previous) {
woimage.items.forEach {
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await woimage.encodeResponse(for: req)
}
}
@Sendable
func rpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}

func create(req: Request) async throws -> Response {
let fuelEntry = try req.content.decode([FuelEntry.V2_1_Input].self)
var results: [FuelEntry] = []
for entry in fuelEntry {
let input = try FuelEntry.init(from: entry)
let data = entry.image ?? nil
let length = data?.count
if(data == nil || length == 0){
input.image = nil
}
if(entry.id != nil){
if (try await FuelEntry.find(entry.id!, on: req.db)) != nil {
input._$id.exists = true
} else {
input._$idExists = false
}
}
try await input.save(on: req.db)
results.append(input)
}
// try await fuelEntry.create(on: req.db)
let response = try await results.map{
try FuelEntry.V2_1_Output.init(from: $0)
}.encodeResponse(for: req)
return response
}

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
}
@Sendable
func getrpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}
}

//struct custMeta: Content {
// let per: Int
// let page: Int
// let total: Int
// let status: String?
//}
//
//struct PageRequest: Content {
// var page: Int
// var size: Int
// var status: String?
// // Add any other properties needed for pagination
//
// init(page: Int, size: Int, status: String? = nil) {
// self.page = page
// self.size = size
// self.status = status
// }
//}











+ 822
- 0
Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift Datei anzeigen

@@ -0,0 +1,822 @@
//
// FuelEntryControllerV2_2.swift
// Fueling
//
// Created by Michiel Carman on 10/26/26.
//

//
// FuelEntryControllerV2_1.swift
// Fueling
//
// Created by Michiel Carman on 9/15/26.
//

import Vapor
import FluentKit
import FluentSQL

struct FuelEntryV2ControllerV2_2: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let fuelEntries = routes.grouped("fuel_entries")
let tokenAuthMiddleware = Token.authenticator()
let guardMiddleware = User.guardMiddleware()
let tokenAuthGroup = fuelEntries.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: filterLoadDate)
tokenAuthGroup.get("main", use: filterLoadDate)
tokenAuthGroup.get("timestamp", use: filterTimeStamp)
tokenAuthGroup.get("images", use: filterImages)
tokenAuthGroup.get("test", use: filtered)
tokenAuthGroup.get("rpm", use: rpm)
tokenAuthGroup.get("maxim", use: filtered)
tokenAuthGroup.post(use: create)
tokenAuthGroup.put( use: softdelete)
tokenAuthGroup.group(":fuelEntryID") { fuelEntry in
fuelEntry.delete(use: delete)
}
}
@Sendable
func index(req: Request) async throws -> Response {
return try await FuelEntry.query(on: req.db).all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}.encodeResponse(for: req)
}
struct filteredreturn: Content{
var fuelentries: [FuelEntry]
var pages: Int
}
struct dload: Content{
var id: UUID
}
@Sendable
func softdelete(req: Request) async throws -> Response {
let ids = try req.content.decode([dload].self)
var msg: [String] = []
for id in ids {
guard let fe = try await FuelEntry.find(id.id, on: req.db) else {
msg.append("create error message for item")
continue
}
fe.downloaded = true
try await fe.save(on: req.db)
}
let response = Response(status: .ok, body: "good")
return response
}
fileprivate func queryParamExists(_ req: Request, _ param: String) async throws -> Bool {
let x = try req.query.get(Bool.self, at: param) ? true : false
print ("x: \(x)")
return x
}
fileprivate func parseDate2(_ dateString: String, _ start: Bool = true) throws -> Date? {
let currentDate = Calendar.current.startOfDay(for: Date())
if(dateString.count == 0 && start){
if(start) {
return Calendar.current.date(byAdding: .day, value: -2, to: currentDate)
} else {
return currentDate
}
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
return nil
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
return nil
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
return nil
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
return nil
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func parseDate(_ dateString: String, _ start: Bool = true) throws -> Date? {
if(dateString.count == 0){
return nil
} else {
let parts = dateString.split(separator: "-")
if(parts.count != 3) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted."))
}
let year = Int(parts[0]) ?? nil
if(year == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Year component must be in the format yyyy and must be an integer"))
}
let month = Int(parts[1]) ?? nil
if(month == nil) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Month component must be in the format MM and must be an integer"))
}
let day = Int(parts[2]) ?? nil
if( day == nil ) {
throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Day component must be in the format dd and must be an integer"))
}
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
if(start) {
dateComponents.hour = 0
dateComponents.minute = 0
dateComponents.second = 0
} else {
dateComponents.hour = 23
dateComponents.minute = 59
dateComponents.second = 59
}
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: dateComponents)
}
}
fileprivate func generateSQLNoImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\";")
}
if(enddate != nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) ;")
} else {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate);")
}
}
fileprivate func generateSQLImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString {
var sql: String = ""
if(startdate == nil) {
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"image\" is not null;")
}
if(enddate != nil) {
sql = "select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(String(describing: startdate)) and \"loadDate\" <= \(String(describing: enddate)) and \"image\" is not null;"
return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) and \"image\" is not null;")
} else {
sql = "select count(*) as total from \"fuel_entries\" where \"image\" is not null and \"loadDate\" >= '\(startdate ?? Date())';"
return SQLQueryString(sql)
}
}
struct CountRecords: Content {
let total: Int
}
fileprivate func getCount(_ req: Request, _ sql: SQLQueryString) async throws -> Int {
print(sql)
if let db = req.db as? SQLDatabase {
let answer = try await db
.raw(sql)
.first(decoding: CountRecords.self)
return answer?.total ?? 0
} else {
return 0
}
}
fileprivate func getPaging(_ page: Int, _ per: Int, _ records: Int) async throws -> [String: Any] {
var vals: [String: Any] = [:]
vals["page"] = page
vals["per"] = per
vals["records"] = records
let lastpage = Int(ceil(Double(records) / Double(per)))
vals["lastpage"] = (page == lastpage) ? true : false
vals["offset"] = ( page * per) - per
return vals
}
fileprivate func getParams(_ req: Request) async throws -> [qparam] {
let urlComponents = URLComponents(string: req.url.description)!
var params: [qparam] = []
for queryItem in urlComponents.queryItems ?? [] {
params.append(qparam(name: queryItem.name, value: queryItem.value ?? ""))
}
return params
}
fileprivate func getMaximError(_ req: Request, _ message: String) async throws -> returnObjV2_1 {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let data : [FuelEntry.V2_1_Output] = []
let meta = Meta(page: 0, per: 0, total: 0, lastpage: true, error: true, message: message, method: method, baseurl: baseurl, route: rt, querystring: "" )
return returnObjV2_1(data: data, meta: meta)
}
@Sendable
func filterLoadDate(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let qsStr = qs.joined(separator: "&")
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterImages(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 5
var records : Int = 0
if(enddate == nil) {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
.all()
.count
} else {
records = try await FuelEntry.query(on: req.db)
.field(\.$id)
.group(.and) {
grp in
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate)
}
.all()
.count
}
let qsStr = qs.joined(separator: "&")
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$image)
.field(\.$tabletserial)
.group(.and) {
grp in if(enddate != nil) {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
.filter(\.$loaddate <= enddate!)
} else {
grp.filter(\.$loaddate >= startdate)
.filter(\.$image != nil)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filterTimeStamp(req: Request) async throws -> Response {
let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "")
let method = req.method.rawValue
let baseurl = "https://fueling.prelub.com"
let qstring = req.query
let components: Dictionary<String, String?> = try req.query.decode(Dictionary<String, String?>.self)
var startdate : Date?
var qs: [String] = []
for component in components {
qs.append( "\(component.key)=\(component.value ?? "nil")")
}
if(try qstring.get(String?.self, at: "startdate") != nil) {
startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "")
if(startdate == nil) {
return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req)
}
} else {
//throw error
return try await getMaximError(req, "startdate is required").encodeResponse(for: req)
}
var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
if(try qstring.get(String?.self, at: "enddate") != nil) {
enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false)
if(enddate == nil) {
return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req)
}
}
let emptyData: Data? = Data()
let page = try qstring.get(Int?.self, at: "page") ?? 1
let per = try qstring.get(Int?.self, at: "per") ?? 10
let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) )
let paging = try await getPaging(page, per, records)
let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qs.joined(separator: "&"))")
let query = try await FuelEntry.query(on: req.db)
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
.group(.and) {
grp in
grp.filter(\.$timestamp >= startdate ?? Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: -2, to: Date())!))
if( enddate != nil) {
grp.filter(\.$timestamp <= enddate!)
}
}
.sort(\.$loaddate, .ascending)
.range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int))
.all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(data: query, meta: meta)
return try await response.encodeResponse(for: req)
}
@Sendable
func filtered(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
.field(\.$tabletserial)
//.filter(\.$image != nil)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$tabletserial)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date())
}
if(!previous){
wimage.items.forEach{
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date(), useremail: img.tabletserial)
}
if(!previous) {
woimage.items.forEach {
_ = $0.toFuelEntry().save(on: req.db)
}
}
return try await woimage.encodeResponse(for: req)
}
}
@Sendable
func rpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}

func create(req: Request) async throws -> Response {
let fuelEntry = try req.content.decode([FuelEntry.V2_2_Input].self)
var results: [FuelEntry] = []
for entry in fuelEntry {
let uuid = entry.uniqueID
let input = FuelEntry.init(from: entry)
let exists = try await FuelEntry.query(on: req.db).filter(\.$uniqueID, .equal, uuid).first()
if(exists == nil) {
let data = entry.image ?? nil
let length = data?.count
if(data == nil || length == 0){
input.image = nil
}
if(entry.id != nil){
if (try await FuelEntry.find(entry.id!, on: req.db)) != nil {
input._$id.exists = true
} else {
input._$idExists = false
}
}
try await input.save(on: req.db)
results.append(input)
} else {
results.append(exists!)
}
}
// try await fuelEntry.create(on: req.db)
let response = try await results.map{
try FuelEntry.V2_2_Output.init(from: $0)
}.encodeResponse(for: req)
return response
}

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
}
@Sendable
func getrpm(req: Request) async throws -> Response {
let qstring = req.query
let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "")
let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false)
let image = try qstring.get(Bool?.self, at: "image") ?? false
let serial = try qstring.get(String?.self, at: "serial") ?? nil
let tank = try qstring.get(String?.self, at: "tank") ?? nil
let previous = try qstring.get(Bool?.self, at: "previous") ?? false
var query = FuelEntry.query(on: req.db)
if(image){
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$image)
.field(\.$downloaded)
.field(\.$loaddate)
} else {
query = query
.field(\.$id)
.field(\.$timestamp)
.field(\.$userID)
.field(\.$product)
.field(\.$make)
.field(\.$model)
.field(\.$serialNumber)
.field(\.$tankID)
.field(\.$fuelAmount)
.field(\.$fuelType)
.field(\.$isOverride)
.field(\.$latitude)
.field(\.$longitude)
.field(\.$downloaded)
.field(\.$loaddate)
}
query = query.group(.and){
grp in
if( startdate != nil) {
grp.filter(\.$loaddate >= startdate!)
}
if( enddate != nil) {
grp.filter(\.$loaddate <= enddate!)
}
if( serial != nil) {
grp.filter(\.$serialNumber == serial!)
}
if( tank != nil) {
grp.filter(\.$tankID == tank!)
}
if(!previous){
grp.filter(\.$pulldate == nil)
}
}
query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber)
if(image){
//query = query.filter(\.$image)
let wimage = try await query.paginate(for: req).map{
img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date())
}
return try await wimage.encodeResponse(for: req)
} else {
let woimage = try await query.paginate(for: req).map{
img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date(), useremail: img.tabletserial)
}
return try await woimage.encodeResponse(for: req)
}
}
}

//struct custMeta: Content {
// let per: Int
// let page: Int
// let total: Int
// let status: String?
//}
//
//struct PageRequest: Content {
// var page: Int
// var size: Int
// var status: String?
// // Add any other properties needed for pagination
//
// init(page: Int, size: Int, status: String? = nil) {
// self.page = page
// self.size = size
// self.status = status
// }
//}











+ 68
- 0
Sources/App/DTO/V2.1/FuelEntryDTO.swift Datei anzeigen

@@ -0,0 +1,68 @@
//
// FuelEntryDTO.swift
// Fueling
//
// Created by Michiel Carman on 9/15/26.
//

import Vapor

extension FuelEntry {
struct V2_1_Input: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
var userId: String?
}
struct V2_1_Output: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
init(from decoder: FuelEntry, img: Bool = false) throws {
self.id = decoder.id
self.tabletSerial = decoder.tabletserial
self.fuelAmount = decoder.fuelAmount
self.fuelType = decoder.fuelType
self.timeStamp = decoder.timestamp
self.appVersion = decoder.appVersion
self.longitude = decoder.longitude
self.product = decoder.product
self.latitude = decoder.latitude
self.serialNumber = decoder.serialNumber
self.isOverride = decoder.isOverride
self.make = decoder.make
self.model = decoder.model
self.tankId = decoder.tankID
if img {
self.image = decoder.image
}
}
}
}

+ 71
- 0
Sources/App/DTO/V2.2/v2.2.swift Datei anzeigen

@@ -0,0 +1,71 @@
//
// v2.2.swift
// Fueling
//
// Created by Michiel Carman on 10/26/26.
//

import Vapor

extension FuelEntry {
struct V2_2_Input: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
var userId: String?
var uniqueID: String?
}
struct V2_2_Output: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
var uniqueID: String?
init(from decoder: FuelEntry, img: Bool = false) throws {
self.id = decoder.id
self.tabletSerial = decoder.tabletserial
self.fuelAmount = decoder.fuelAmount
self.fuelType = decoder.fuelType
self.timeStamp = decoder.timestamp
self.appVersion = decoder.appVersion
self.longitude = decoder.longitude
self.product = decoder.product
self.latitude = decoder.latitude
self.serialNumber = decoder.serialNumber
self.isOverride = decoder.isOverride
self.make = decoder.make
self.model = decoder.model
self.tankId = decoder.tankID
if img {
self.image = decoder.image
}
self.uniqueID = decoder.uniqueID
}
}
}


+ 31
- 0
Sources/App/Errors/UserErrors.swift Datei anzeigen

@@ -0,0 +1,31 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/26/24.
//

import Foundation
import Vapor

enum UserError {
case usernameTaken
}

extension UserError: AbortError {
var description: String {
reason
}

var status: HTTPResponseStatus {
switch self {
case .usernameTaken: return .conflict
}
}

var reason: String {
switch self {
case .usernameTaken: return "Username already taken"
}
}
}

+ 53
- 0
Sources/App/Helpers/PageHelper.swift Datei anzeigen

@@ -0,0 +1,53 @@
//
// PageHelper.swift
// Fueling
//
// Created by Michiel Carman on 5/5/25.
//

import Foundation
import Fluent
import Vapor

final class PageHelper {
// static func paginate<T: Paginate>(
// _ query: Query<T>,
// page: Int? = nil,
// perPage: Int? = nil
// ) -> EventLoopFuture<PagedCollection<T>> {
// let page = page ?? 1
// let perPage = perPage ?? 10
// return query
// .skip((page - 1) * perPage)
// }
}

public struct returnObjV2_1: Content{
let data: [FuelEntry.V2_1_Output]
let meta: Meta
}

public struct returnObj: Content{
let data: [FuelEntry.Output]
let meta: Meta
}

public struct Meta: Content{
let page: Int
let per: Int
let total: Int
let lastpage: Bool
let error: Bool
let message: String?
let method: String?
let baseurl: String?
let route: String?
let querystring: String?
}

public struct qparam: Content{
let name: String
let value: String
}

+ 142
- 19
Sources/App/Migrations/CreateFuelEntry.swift Datei anzeigen

@@ -1,6 +1,6 @@
//
// File.swift
//
//
//
// Created by Bill Ryckman on 4/26/23.
//
@@ -26,29 +26,152 @@ struct CreateFuelEntry: AsyncMigration {
.field("is_override", .bool)
.create()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries").delete()
}
}

struct AddLatitudeAndLongitudeAndRemoveGpsLocationAndImage: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("latitude", .double)
.field("longitude", .double)
.deleteField("gps_location")
.deleteField("image")
.update()
}

func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("latitude")
.deleteField("longitude")
.field("gps_location", .string)
.field("image", .string)
.update()
}
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("latitude", .double)
.field("longitude", .double)
.deleteField("gps_location")
.deleteField("image")
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("latitude")
.deleteField("longitude")
.field("gps_location", .string)
.field("image", .string)
.update()
}
}

struct AddImageBackIn: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("image", .string)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("image")
.update()
}
}

struct StoreImageDataInDatabase: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("image")
.field("image", .data)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("image")
.field("image", .string)
.update()
}
}

struct AddDownloadedBool: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("downloaded", .bool)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("downloaded")
.update()
}
}

struct AddUniqueIDFiled: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("unique_id", .uuid)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("unique_id")
.update()
}
}

struct ChangeUniqueIDFiled: AsyncMigration {
func prepare(on database: Database) async throws {
do {
try await database.schema("fuel_entries")
.deleteField("unique_id")
.field("unique_id", .string)
.update()
} catch {
throw error
}
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("unique_id")
.field("unique_id", .uuid)
.update()
}
}

struct AddAppVersion: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("app_version", .string)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("app_version")
.update()
}
}

struct AddLoadDateAndPullDate: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("loadDate", .datetime)
.field("pullDate", .datetime)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("loadDate")
.deleteField("pullDate")
.update()
}
}

struct AddDeviceSerial: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("tablet_serial", .string)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("tablet_serial")
.update()
}
}


+ 0
- 14
Sources/App/Migrations/CreateTodo.swift Datei anzeigen

@@ -1,14 +0,0 @@
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()
}
}

+ 51
- 0
Sources/App/Migrations/CreateToken.swift Datei anzeigen

@@ -0,0 +1,51 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/26/24.
//

import Foundation
import Vapor
import Fluent

struct CreateTokens: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
// 2
database.schema(Token.schema)
// 3
.id()
.field("user_id", .uuid, .references("user", "id"))
.field("value", .string, .required)
.unique(on: "value")
.field("source", .int, .required)
.field("created_at", .datetime, .required)
.field("expires_at", .datetime)
.create()
}

// 5
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema).delete()
}
}
struct UpdateTokens1: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
// 2
database.schema(Token.schema)
// 3
.deleteField("source")
.deleteField("created_at")
.deleteField("expires_at")
.update()
}

// 5
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema)
.field("source", .int, .required)
.field("created_at", .datetime, .required)
.field("expires_at", .datetime)
.update()
}
}

+ 69
- 0
Sources/App/Migrations/CreateUser.swift Datei anzeigen

@@ -0,0 +1,69 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/26/24.
//

import Foundation
import Vapor
import Fluent

struct CreateUser: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema(User.schema)
.id()
.field("username", .string, .required)
.unique(on: "username")
.field("password_hash", .string, .required)
.field("created_at", .datetime, .required)
.field("updated_at", .datetime, .required)
.create()
}
func revert(on database: Database) async throws {
try await database.schema(User.schema).delete()
}
}

struct UpdateUser1: AsyncMigration{
func prepare(on database: Database) async throws {
try await database.schema(User.schema)
.deleteField("created_at")
.deleteField("updated_at")
.update()
}
func revert(on database: Database) async throws {
try await database.schema(User.schema)
.field("created_at", .datetime, .required)
.field("updated_at", .datetime, .required)
.update()
}
}
struct UpdateUser2_AddTokenfield: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema(User.schema)
.field("token", .string)
.update()
}
func revert(on database: Database) async throws {
try await database.schema(User.schema)
.deleteField("token")
.update()
}
}
struct UpdateUser3_RemoveTokenfield: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema(User.schema)
.deleteField("token")
.update()
}
func revert(on database: Database) async throws {
try await database.schema(User.schema)
.field("token", .string)
.update()
}
}

+ 269
- 19
Sources/App/Models/FuelEntry.swift Datei anzeigen

@@ -10,58 +10,224 @@ import Vapor

final class FuelEntry: Model, Content {
static let schema = "fuel_entries"

struct woimage: Content{
let id: UUID?
let timestamp: Date
let userID: String
let latitude: Double
let longitude: Double
let product: String
let make: String
let model: String
let serialNumber: String
let tankID: String
let fuelAmount: Double
let fuelType: String
let image: Data?
let isOverride: Bool
let downloaded: Bool?
let loaddate: Date?
let pulldate: Date?
let useremail: String?
func toFuelEntry() -> FuelEntry {
let entry = FuelEntry()
entry.id = self.id
entry.timestamp = self.timestamp
entry.userID = self.userID
entry.latitude = self.latitude
entry.longitude = self.longitude
entry.product = self.product
entry.make = self.make
entry.model = self.model
entry.serialNumber = self.serialNumber
entry.tankID = self.tankID
entry.fuelAmount = self.fuelAmount
entry.fuelType = self.fuelType
entry.isOverride = self.isOverride
entry.image = nil
entry.downloaded = self.downloaded
entry.loaddate = self.loaddate
entry.pulldate = self.pulldate
entry._$idExists = self.id == nil ? false : true
entry.tabletserial = self.useremail
return entry
}
}
struct Output: Content {
let id: UUID?
let timestamp: Date
let ssid: String
let latitude: Double
let longitude: Double
let product: String
let make: String
let model: String
let serialNumber: String
let tankID: String
let fuelAmount: Double
let fuelType: String
let isOverride: Bool
let image: Data?
let loaddate: Date?
let useremail: String?
}
struct wimage: Content{
let id: UUID?
let timestamp: Date
let userID: String
let latitude: Double
let longitude: Double
let product: String
let make: String
let model: String
let serialNumber: String
let tankID: String
let fuelAmount: Double
let fuelType: String
let isOverride: Bool
let image: Data?
let downloaded: Bool?
let loaddate: Date?
let pulldate: Date?
func toFuelEntry() -> FuelEntry {
let entry = FuelEntry()
entry.id = self.id
entry.timestamp = self.timestamp
entry.userID = self.userID
entry.latitude = self.latitude
entry.longitude = self.longitude
entry.product = self.product
entry.make = self.make
entry.model = self.model
entry.serialNumber = self.serialNumber
entry.tankID = self.tankID
entry.fuelAmount = self.fuelAmount
entry.fuelType = self.fuelType
entry.isOverride = self.isOverride
entry.image = self.image
entry.downloaded = self.downloaded
entry.loaddate = self.loaddate
entry.pulldate = self.pulldate
entry._$idExists = self.id == nil ? false : true
return entry
}
}
@ID(key: .id)
var id: UUID?

@Field(key: "timestamp")
var timestamp: Date

@Field(key: "user_id")
var userID: String

@Field(key: "latitude")
var latitude: Double
@Field(key: "longitude")
var longitude: Double

// @Field(key: "image")
// var image: String

@Field(key: "image")
var image: Data?
@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

@Field(key: "downloaded")
var downloaded: Bool?
@Field(key: "tablet_serial")
var tabletserial: String?
@Field(key: "app_version")
var appVersion: String?
@Timestamp(key: "loadDate", on: .create) var loaddate: Date?
@Timestamp(key: "pullDate", on: .none) var pulldate: Date?
@Field(key: "unique_id") var uniqueID: String?
// var imageData: Data?
init() {}

init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool) {
init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, downloaded: Bool? = nil, tabletserial: String? = nil, appversion: String? = nil, uniqueID: String? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
self.latitude = latitude
self.longitude = longitude
self.image = image ?? nil
self.product = product
self.make = make
self.model = model
self.serialNumber = serialNumber
self.tankID = tankID
self.fuelAmount = fuelAmount
self.fuelType = fuelType
self.isOverride = isOverride
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
self.appVersion = appversion ?? nil
self.uniqueID = uniqueID
// self.imageData = imageData
}
init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, downloaded: Bool? = nil, tabletserial: String? = nil, uniqueID: String? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
self.latitude = latitude
self.longitude = longitude
self.image = image ?? nil
self.product = product
self.make = make
self.model = model
self.serialNumber = serialNumber
self.tankID = tankID
self.fuelAmount = fuelAmount
self.fuelType = fuelType
self.isOverride = isOverride
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
self.uniqueID = uniqueID
// self.imageData = imageData
}
init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, tabletserial: String? = nil, uniqueID: String? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
self.latitude = latitude
self.longitude = longitude
// self.image = image
self.image = image ?? nil
self.product = product
self.make = make
self.model = model
@@ -70,5 +236,89 @@ final class FuelEntry: Model, Content {
self.fuelAmount = fuelAmount
self.fuelType = fuelType
self.isOverride = isOverride
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
self.uniqueID = uniqueID
// self.imageData = imageData
}
init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, tabletserial: String? = nil, uniqueID: String? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
self.latitude = latitude
self.longitude = longitude
self.image = image ?? nil
self.product = product
self.make = make
self.model = model
self.serialNumber = serialNumber
self.tankID = tankID
self.fuelAmount = fuelAmount
self.fuelType = fuelType
self.isOverride = isOverride
self.tabletserial = tabletserial ?? nil
self.uniqueID = uniqueID
// self.imageData = imageData
}
// struct V2_1_Input: Content {
// var id: UUID?
// var tabletSerial: Date?
// var fuelAmount: Double?
// var fuelType: String?
// var timeStamp: Date?
// var appVersion: String?
// var longitude: String?
// var product: String?
// var latitude: String?
// var serialNumber: String?
// var isOverride: Bool?
// var make: String?
// var model: String?
// var tankId: String?
// var image: Data?
// }
init(from decoder: FuelEntry.V2_1_Input) throws {
self.id = decoder.id
self.timestamp = decoder.timeStamp ?? Date()
self.fuelAmount = decoder.fuelAmount ?? 0
self.fuelType = decoder.fuelType ?? ""
self.appVersion = decoder.appVersion ?? ""
self.longitude = decoder.longitude ?? 0
self.product = decoder.product ?? ""
self.latitude = decoder.latitude ?? 0
self.serialNumber = decoder.serialNumber ?? ""
self.isOverride = decoder.isOverride ?? false
self.make = decoder.make ?? ""
self.model = decoder.model ?? ""
self.tankID = decoder.tankId ?? ""
self.userID = decoder.userId ?? ""
self.image = decoder.image
self.tabletserial = decoder.tabletSerial ?? ""
self.downloaded = false
}
init(from decoder: FuelEntry.V2_2_Input) {
self.id = decoder.id
self.timestamp = decoder.timeStamp ?? Date()
self.fuelAmount = decoder.fuelAmount ?? 0
self.fuelType = decoder.fuelType ?? ""
self.appVersion = decoder.appVersion ?? ""
self.longitude = decoder.longitude ?? 0
self.product = decoder.product ?? ""
self.latitude = decoder.latitude ?? 0
self.serialNumber = decoder.serialNumber ?? ""
self.isOverride = decoder.isOverride ?? false
self.make = decoder.make ?? ""
self.model = decoder.model ?? ""
self.tankID = decoder.tankId ?? ""
self.userID = decoder.userId ?? ""
self.image = decoder.image
self.tabletserial = decoder.tabletSerial ?? ""
self.downloaded = false
self.uniqueID = decoder.uniqueID
}
}

+ 0
- 19
Sources/App/Models/Todo.swift Datei anzeigen

@@ -1,19 +0,0 @@
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
}
}

+ 54
- 0
Sources/App/Models/Token.swift Datei anzeigen

@@ -0,0 +1,54 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/24/24.
//

import Vapor
import Fluent



final class Token: Model, Content, @unchecked Sendable {
static let schema = "token"
@ID()
var id: UUID?
@Parent(key: "user_id")
var user: User
@Field(key: "value")
var value: String
init() {}
init(id: UUID? = nil, userId: User.IDValue, token: String) {
self.id = id
self.$user.id = userId
self.value = token
}
}

extension Token {
static func generate(for user: User) throws -> Token {
let random = [UInt8].random(count: 32).base64
return try Token( userId: user.requireID(), token: random)
}
}

extension Token: ModelTokenAuthenticatable {
typealias User = App.User

static var valueKey: KeyPath<Token, Field<String>> {
return \Token.$value
}

static var userKey: KeyPath<Token, Parent<User>> {
return \Token.$user
}

var isValid: Bool { true }
}

+ 58
- 0
Sources/App/Models/User.swift Datei anzeigen

@@ -0,0 +1,58 @@
//
// File.swift
//
//
// Created by Michiel Carman on 9/24/24.
//

import Fluent
import Vapor

final class User: Model, Content {
static let schema = "user"
final class Public: Content {
var id: UUID?
var username: String
init(id: UUID?, username: String) {
self.id = id
self.username = username
}
}
@ID()
var id: UUID?
@Field(key: "username")
var username: String
@Field(key: "password_hash")
var passwordHash: String
// @Timestamp(key: "created_at", on: .create)
// var createdAt: Date?
//
// @Timestamp(key: "updated_at", on: .update)
// var updatedAt: Date?
init() {}
init(id: UUID? = nil, username: String, passwordHash: String) {
self.id = id
self.username = username
self.passwordHash = passwordHash
}
}
extension User {
func convertToPublic() -> User.Public {
return User.Public(id: id, username: username)
}
}
extension User: ModelAuthenticatable {
static var usernameKey: KeyPath<User, Field<String>> { \User.$username }
static var passwordHashKey: KeyPath<User, Field<String>> { \User.$passwordHash }

func verify(password: String) throws -> Bool {
try Bcrypt.verify(password, created: self.passwordHash)
}
}

+ 50
- 5
Sources/App/configure.swift Datei anzeigen

@@ -1,20 +1,65 @@
import Fluent
import FluentPostgresDriver
import NIOSSL

import Vapor

// configures your application
public func configure(_ app: Application) async throws {
app.middleware.use(ErrorMiddleware.default(environment: app.environment))
// uncomment to serve files from /Public folder
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
app.routes.defaultMaxBodySize = "1000kb"
// let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(300), read: .seconds(300))
// app.http.client.configuration.timeout = timeout
app.databases.use(.postgres(hostname: "localhost", username: "mcarman", password: "@ng31F@rm0823262", database: "fueling"), as: .psql)
app.http.server.configuration.port = 3001
app.routes.defaultMaxBodySize = "128mb"
let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(300), read: .seconds(300))
app.http.client.configuration.timeout = timeout
// let envHost = Environment.get("DATABASE_HOST") ?? "vapor.prelub.com"
var envHost: String // = Environment.get("DATABASE_HOST") ?? "127.0.0.1"
var envPort: Int
switch app.environment {
case .development:
envHost = "localhost"
envPort = 5432
default:
envHost = "vapor.prelub.com"
envPort = 5432
}
let envUsername = Environment.get("DATABASE_USERNAME") ?? "mcarman"
let envPassword = Environment.get("DATABASE_PASSWORD") ?? "@ng31F@rm0823262"
let envDB = Environment.get("DATABASE_NAME") ?? "fueling"
app.databases.use(DatabaseConfigurationFactory.postgres(configuration: .init(
hostname: envHost,
port: envPort,
username: envUsername,
password: envPassword,
database: envDB)
), as: .psql)
// app.databases.use(.postgres(hostname: "127.0.0.1", port: 5432, username: "mcarman", password: "@ng31F@rm0823262", database: "fueling"), as: .psql)

app.http.server.configuration.port = 3002
app.migrations.add(CreateFuelEntry())
app.migrations.add(AddLatitudeAndLongitudeAndRemoveGpsLocationAndImage())
app.migrations.add(AddImageBackIn())
app.migrations.add(StoreImageDataInDatabase())
app.migrations.add(AddDownloadedBool())
app.migrations.add(CreateUser())
app.migrations.add(CreateTokens())
app.migrations.add(UpdateUser1())
app.migrations.add(UpdateTokens1())
app.migrations.add(UpdateUser2_AddTokenfield())
app.migrations.add(UpdateUser3_RemoveTokenfield())
app.migrations.add(AddLoadDateAndPullDate())
app.migrations.add(AddDeviceSerial())
app.migrations.add(AddAppVersion())
app.migrations.add(AddUniqueIDFiled())
app.migrations.add(ChangeUniqueIDFiled())
try await app.autoMigrate().get()

// register routes
try routes(app)
}



+ 13
- 2
Sources/App/entrypoint.swift Datei anzeigen

@@ -1,7 +1,11 @@
import Vapor
@preconcurrency import Vapor
import Dispatch
import Logging

private struct UncheckedApplication: @unchecked Sendable {
let app: Application
}

/// 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")
@@ -30,6 +34,13 @@ enum Entrypoint {
defer { app.shutdown() }
try await configure(app)
try await app.runFromAsyncMainEntrypoint()
do {
try await app.runFromAsyncMainEntrypoint()
} catch {
print("STARTUP ERROR:", String(reflecting: error))
throw error
}
}
}



+ 44
- 15
Sources/App/routes.swift Datei anzeigen

@@ -1,23 +1,22 @@
//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 {
app.routes.caseInsensitive = true
app.routes.defaultMaxBodySize = "64mb";
let V2_1 = app.grouped("api").grouped("v2.1")
try V2_1.register(collection: FuelEntryV2ControllerV2_1())
let v2_2 = app.grouped("api").grouped("v2.2")
try v2_2.register(collection: FuelEntryV2ControllerV2_2())
let v2 = app.grouped("api").grouped("v2")
try v2.register(collection: FuelEntryV2Controller())
try v2.register(collection: UserController())
let fuelEntryController = FuelEntryController()
app.get {
req in return "Fueling API v2"
}
app.get("fuel-entries") { req in
try await fuelEntryController.index(req: req)
}
@@ -29,6 +28,36 @@ func routes(_ app: Application) throws {
app.delete("fuel-entries", ":fuelEntryID") { req in
try await fuelEntryController.delete(req: req)
}
// V2 API routes with image upload
// let v2Routes: () = app.group("api", "v2") { v2 in
// let fuelEntryV2Controller = FuelEntryV2Controller()
//
// v2.get("fuel-entries") {req in
// try await fuelEntryV2Controller.index(req: req)
// }
//
//// v2.get("fuel-entries", "with-images") {req in
//// try await fuelEntryV2Controller.getAllEntriesWithImages(req: req)
//// }
//
// v2.post("fuel-entries") {req in
// try await fuelEntryV2Controller.create(req: req)
// }
//
//// v2.post("fuel-entries", ":id", "upload-image") { req -> FuelEntry in
//// return try await fuelEntryV2Controller.uploadImage(req: req)
//// }
//
// v2.delete("fuel-entries", ":fuelEntryID") { req in
// try await fuelEntryV2Controller.delete(req: req)
// }
// }
}



Laden…
Abbrechen
Speichern