diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate
index 45ce814..4934839 100644
Binary files a/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index 1d67c13..bcd2102 100644
--- a/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -8,15 +8,15 @@
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
@@ -24,15 +24,15 @@
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
@@ -46,9 +46,9 @@
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
- startingLineNumber = "200"
- endingLineNumber = "200"
- landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:downloaded:tabletserial:)"
+ 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">
@@ -174,8 +174,8 @@
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
- startingLineNumber = "285"
- endingLineNumber = "285"
+ startingLineNumber = "291"
+ endingLineNumber = "291"
landmarkName = "init(from:)"
landmarkType = "7">
@@ -222,76 +222,12 @@
filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
- startingLineNumber = "291"
- endingLineNumber = "291"
+ startingLineNumber = "297"
+ endingLineNumber = "297"
landmarkName = "init(from:)"
landmarkType = "7">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -318,54 +254,10 @@
filePath = "Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
- startingLineNumber = "670"
- endingLineNumber = "670"
+ startingLineNumber = "674"
+ endingLineNumber = "674"
landmarkName = "create(req:)"
landmarkType = "7">
-
-
-
-
-
-
-
-
diff --git a/Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift b/Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift
index 36ba6ae..67d0ed5 100644
--- a/Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift
+++ b/Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift
@@ -649,21 +649,25 @@ struct FuelEntryV2ControllerV2_1: RouteCollection {
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
+
+
+
+ let data = entry.image ?? nil
+ let length = data?.count
+ if(data == nil || length == 0){
+ input.image = nil
}
-
- }
- try await input.save(on: req.db)
- results.append(input)
+ 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)
diff --git a/Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift b/Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift
index e69de29..9c8569c 100644
--- a/Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift
+++ b/Sources/App/Controllers/V2.2/FuelEntryControllerV2_2.swift
@@ -0,0 +1,819 @@
+//
+// 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 = try req.query.decode(Dictionary.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 = try req.query.decode(Dictionary.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 = try req.query.decode(Dictionary.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 input = FuelEntry.init(from: entry)
+ let exists = try await FuelEntry.query(on: req.db).filter(\.$uniqueID, .equal, input.uniqueID).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_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
+// }
+//}
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/App/DTO/V2.2/v2.2.swift b/Sources/App/DTO/V2.2/v2.2.swift
index e69de29..e86c155 100644
--- a/Sources/App/DTO/V2.2/v2.2.swift
+++ b/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?.uuidString
+ }
+ }
+
+}
+
diff --git a/Sources/App/Migrations/CreateFuelEntry.swift b/Sources/App/Migrations/CreateFuelEntry.swift
index a579dfa..0c9181e 100755
--- a/Sources/App/Migrations/CreateFuelEntry.swift
+++ b/Sources/App/Migrations/CreateFuelEntry.swift
@@ -96,6 +96,20 @@ struct AddDownloadedBool: AsyncMigration {
}
}
+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 AddAppVersion: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
diff --git a/Sources/App/Models/FuelEntry.swift b/Sources/App/Models/FuelEntry.swift
index 108fba5..7a031ae 100755
--- a/Sources/App/Models/FuelEntry.swift
+++ b/Sources/App/Models/FuelEntry.swift
@@ -115,46 +115,46 @@ final class FuelEntry: Model, Content {
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: 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
@@ -170,11 +170,13 @@ final class FuelEntry: Model, Content {
@Timestamp(key: "loadDate", on: .create) var loaddate: Date?
@Timestamp(key: "pullDate", on: .none) var pulldate: Date?
-// var imageData: Data?
-
+ @Field(key: "unique_id") var uniqueID: UUID?
+
+ // var imageData: Data?
+
init() {}
- 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) { //imageData: Data?
+ 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: UUID? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
@@ -193,10 +195,11 @@ final class FuelEntry: Model, Content {
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
self.appVersion = appversion ?? nil
-// self.imageData = imageData
+ 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) { //imageData: Data?
+
+ 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: UUID? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
@@ -214,9 +217,10 @@ final class FuelEntry: Model, Content {
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
-// self.imageData = imageData
+ 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) { //imageData: Data?
+ 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: UUID? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
@@ -234,9 +238,10 @@ final class FuelEntry: Model, Content {
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
-// self.imageData = imageData
+ 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) { //imageData: Data?
+ 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: UUID? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
@@ -252,26 +257,27 @@ final class FuelEntry: Model, Content {
self.fuelType = fuelType
self.isOverride = isOverride
self.tabletserial = tabletserial ?? nil
-// self.imageData = imageData
+ 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?
-// }
+ // 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
@@ -292,4 +298,25 @@ final class FuelEntry: Model, Content {
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 == nil ? nil : UUID(uuidString: decoder.uniqueID!)
+ }
}
diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift
index 92901ff..ce8affc 100755
--- a/Sources/App/configure.swift
+++ b/Sources/App/configure.swift
@@ -52,6 +52,7 @@ public func configure(_ app: Application) async throws {
app.migrations.add(AddLoadDateAndPullDate())
app.migrations.add(AddDeviceSerial())
app.migrations.add(AddAppVersion())
+ app.migrations.add(AddUniqueIDFiled())
try await app.autoMigrate().get()
// register routes