コミットを比較

...

56 コミット

作成者 SHA1 メッセージ 日付
  Mike Carman 5a22a084a3 Fixed unique filed 2週間前
  Mike Carman 5d4bdbdf42 trapping error 2週間前
  Mike Carman 7ebd78bbdc preconcurrency 2週間前
  Mike Carman 834ffecde0 went to 6.3.1 2週間前
  Mike Carman ba30a29e65 @nonsendable Application Extension added 2週間前
  Mike Carman 7859e2490f ?? 2週間前
  Mike Carman 5ce3f35db6 db port 2週間前
  Mike Carman 2fb399a024 upgraderd to swift 6.4.1 2週間前
  Mike Carman 2d9b0b99a7 Configured Database connection 2週間前
  Mike Carman 70597e7abf V2.2 2週間前
  Mike Carman eb2ba91a21 V2.2 2週間前
  Mike Carman 3824389ec3 V2.2 2週間前
  Mike Carman fbce601623 v2.1 2週間前
  Mike Carman a2515ae396 V2.1 2週間前
  Mike Carman 1ac2e40102 V2.1 Controller 2週間前
  Mike Carman bfce5ee48b Minor fixes to post function 1ヶ月前
  Mike Carman a3e9110dc1 Fixed Post function 1ヶ月前
  Mike Carman 5633bb9b72 Added V2.1 1ヶ月前
  Mike Carman 58e35c8a2e Adds 1ヶ月前
  Mike Carman 96ca775eb2 Fix for useremail 6ヶ月前
  Mike Carman 745b9971f0 Fueling change to allow for update and create. 9ヶ月前
  Mike Carman 75284b1afe Changed tableserial to useremail 1年前
  Mike Carman 87eac42ba9 Added tabletserial to database and responses 1年前
  Mike Carman d651f365a4 db connection 1年前
  Mike Carman 4216bbf96c Maxim qstrings and other data 1年前
  Mike Carman ce895f2712 Fixed image always null 1年前
  Mike Carman aafba8cb9b Connection String Port Number update 1年前
  Mike Carman a3e809a74b All fueling changes requested for apis 1年前
  Mike Carman cade0e4f23 useriterface 1年前
  Mike Carman 5f0204d89c database port 1年前
  Mike Carman 7a66dc7ed3 Changes for Andy 1年前
  Mike Carman 976f328f37 small modification to rpm query 1年前
  Mike Carman ad33c6ce24 Fixed db connection string 1年前
  Mike Carman e1d679bf03 Fueling service changes per Andy added startdate and enddate perameter as well as previous parameter 1年前
  Mike Carman c586708c1c Reset port 1年前
  Mike Carman ee94988698 fix for null downloaded 1年前
  Mike Carman 5ecff22dbf Changed port on connection string 1年前
  Mike Carman 8539c28d4b latest 1年前
  rpm-mcarman 5fd27fa971 Update for image field 2年前
  rpm-mcarman e1c683d582 Token auth implementation 2年前
  rpm-mcarman 3a257e2b4d Added to selected fields 2年前
  rpm-mcarman da4e9a7324 Added downloaded field to data 2年前
  rpm-mcarman f202dff478 Pushing edits 2年前
  rpm-mcarman e6b23df180 w/o token auth on api calls 2年前
  rpm-mcarman 7e31c3c36b Removed Pagination 2年前
  Bill Ryckman 46d69a83dc Adding pagination to GET request 2年前
  admin 3976bac4aa app.routes.defaultMaxBodySize = 128mb 2年前
  admin 678340efac Remove processing of imageData to a file. Store imageData in image field in database. 2年前
  admin 5e562aa8d8 Update routes to use api/v2 and include with-images 2年前
  admin 64f9c4aac9 Second attempt at uploading images. This time the imageData will come with the JSON data and get processed during the Create. 2年前
  admin d6a38d0e35 Remove Field imageData as it's not going to the database, just JSON. 2年前
  admin 607135f873 Move back to Swift Tools 5.8 and move to port 3002. 2年前
  admin 86d9ef808e Downgrade Swift Tools Package to 5.7.3 to work on Vapor server. 2年前
  admin 934d4d9c25 Added getAllEntriesWithImages to bring down pictures with data if need be. Left default route intact without images. 2年前
  admin b6402df273 Update to create new controller for V2 routes. 2年前
  admin 9ccee49e92 First attempt at image upload. Need to set image = to WorkingDirectory+Images+UUID.png. Then upload id and image to complete process. 2年前
27個のファイルの変更、4013行の追加、127行の削除
分割表示
  1. バイナリ
      .swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate
  2. バイナリ
      .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

バイナリ
.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate ファイルの表示


バイナリ
.swiftpm/xcode/package.xcworkspace/xcuserdata/michielcarman.xcuserdatad/UserInterfaceState.xcuserstate ファイルの表示


+ 92
- 0
.swiftpm/xcode/xcshareddata/xcschemes/Fueling.xcscheme ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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 ファイルの表示

@@ -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)
// }
// }
}



読み込み中…
キャンセル
保存