diff --git a/Package.resolved b/Package.resolved index 62ad7b5e..54d16a6c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,22 +1,22 @@ { - "originHash" : "8b4e65f6b024df461416d808e2d0a6b815c12cb521697f0bd70d60002fa01a1d", + "originHash" : "25e8201281b55a93e0dc2eb271b2b327d568944d28063efba00877c780e6fa71", "pins" : [ { "identity" : "apns", "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/apns.git", "state" : { - "revision" : "a8bc863a718eb23e05b2ae3bfc7de66407b6b85d", - "version" : "1.0.1" + "revision" : "be523e3dc2a054d1ab51cb610c015dbbffb1d1ac", + "version" : "5.0.0" } }, { "identity" : "apnswift", "kind" : "remoteSourceControl", - "location" : "https://github.com/kylebrowning/APNSwift.git", + "location" : "https://github.com/swift-server-community/APNSwift.git", "state" : { - "revision" : "ce8213a3e0ec0581c4c39a155e7778401ec5e24a", - "version" : "2.2.0" + "revision" : "f407b435d28456c2568cc2713a226cd06a5d5736", + "version" : "6.1.0" } }, { @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swift-server/async-http-client.git", "state" : { - "revision" : "0a9b72369b9d87ab155ef585ef50700a34abf070", - "version" : "1.23.1" + "revision" : "8430dd49d4e2b417f472141805c9691ec2923cb8", + "version" : "1.29.0" } }, { @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/async-kit.git", "state" : { - "revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31", - "version" : "1.20.0" + "revision" : "6f3615ccf2ac3c2ae0c8087d527546e9544a43dd", + "version" : "1.21.0" } }, { @@ -51,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/console-kit.git", "state" : { - "revision" : "78c0dd739df8cb9ee14a8bbbf770facc4fc3402a", - "version" : "4.15.0" + "revision" : "742f624a998cba2a9e653d9b1e91ad3f3a5dff6b", + "version" : "4.15.2" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/fluent.git", "state" : { - "revision" : "223b27d04ab2b51c25503c9922eecbcdf6c12f89", - "version" : "4.12.0" + "revision" : "2fe9e36daf4bdb5edcf193e0d0806ba2074d2864", + "version" : "4.13.0" } }, { @@ -69,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/fluent-kit.git", "state" : { - "revision" : "614d3ec27cdef50cfb9fc3cfd382b6a4d9578cff", - "version" : "1.49.0" + "revision" : "8baacd7e8f7ebf68886c496b43bbe6cdcc5b57e0", + "version" : "1.52.2" } }, { @@ -78,8 +78,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/fluent-postgres-driver.git", "state" : { - "revision" : "fd57101e426d3edf66a32ba63a7d0b8ced4d7499", - "version" : "2.10.0" + "revision" : "cd47a7042a529735e401bdfaa070823d151f7f94", + "version" : "2.11.0" } }, { @@ -91,22 +91,13 @@ "version" : "1.1.1" } }, - { - "identity" : "jwt-kit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/vapor/jwt-kit.git", - "state" : { - "revision" : "c2595b9ad7f512d7f334830b4df1fed6e917946a", - "version" : "4.13.4" - } - }, { "identity" : "leaf", "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/leaf.git", "state" : { - "revision" : "dd799be2a92e7207ad86c567e4fc7dd37413ff60", - "version" : "4.4.0" + "revision" : "b70a6108e4917f338f6b8848407bf655aa7e405f", + "version" : "4.5.1" } }, { @@ -114,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/leaf-kit.git", "state" : { - "revision" : "d0ca4417166ef7868d28ad21bc77d36b8735a0fc", - "version" : "1.11.1" + "revision" : "0c325fc46d42455914abd0105e88fe4561fc31a8", + "version" : "1.14.0" } }, { @@ -132,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/multipart-kit.git", "state" : { - "revision" : "a31236f24bfd2ea2f520a74575881f6731d7ae68", - "version" : "4.7.0" + "revision" : "3498e60218e6003894ff95192d756e238c01f44e", + "version" : "4.7.1" } }, { @@ -150,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/postgres-kit.git", "state" : { - "revision" : "0b72fa83b1023c4b82072e4049a3db6c29781fff", - "version" : "2.13.5" + "revision" : "d2fd3172c2e318bd292a4c1297e4c65a418cf6f3", + "version" : "2.14.1" } }, { @@ -159,8 +150,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/postgres-nio.git", "state" : { - "revision" : "cd5318a01a1efcb1e0b3c82a0ce5c9fefaf1cb2d", - "version" : "1.22.1" + "revision" : "312444ea512ac9ed77fe58dcf737265ee48503cf", + "version" : "1.28.0" } }, { @@ -168,8 +159,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/routing-kit.git", "state" : { - "revision" : "8c9a227476555c55837e569be71944e02a056b72", - "version" : "4.9.1" + "revision" : "93f7222c8e195cbad39fafb5a0e4cc85a8def7ea", + "version" : "4.9.2" } }, { @@ -186,8 +177,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/sql-kit.git", "state" : { - "revision" : "e0b35ff07601465dd9f3af19a1c23083acaae3bd", - "version" : "3.32.0" + "revision" : "1a9ab0523fb742d9629558cede64290165c4285b", + "version" : "3.33.2" } }, { @@ -195,8 +186,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-algorithms.git", "state" : { - "revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42", - "version" : "1.2.0" + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", + "version" : "1.2.1" } }, { @@ -204,8 +195,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6", - "version" : "1.3.0" + "revision" : "f70225981241859eb4aa1a18a75531d26637c8cc", + "version" : "1.4.0" } }, { @@ -213,8 +204,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-async-algorithms.git", "state" : { - "revision" : "5c8bd186f48c16af0775972700626f0b74588278", - "version" : "1.0.2" + "revision" : "042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b", + "version" : "1.0.4" } }, { @@ -222,8 +213,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-atomics.git", "state" : { - "revision" : "cd142fd2f64be2100422d658e7411e39489da985", - "version" : "1.2.0" + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-certificates", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-certificates.git", + "state" : { + "revision" : "4b092f15164144c24554e0a75e080a960c5190a6", + "version" : "1.14.0" } }, { @@ -231,8 +231,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-cmark.git", "state" : { - "revision" : "3ccff77b2dc5b96b77db3da0d68d28068593fa53", - "version" : "0.5.0" + "revision" : "b97d09472e847a416629f026eceae0e2afcfad65", + "version" : "0.7.0" } }, { @@ -240,8 +240,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" } }, { @@ -249,17 +249,35 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "21f7878f2b39d46fd8ba2b06459ccb431cdf876c", - "version" : "3.8.1" + "revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc", + "version" : "3.15.1" + } + }, + { + "identity" : "swift-distributed-tracing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-distributed-tracing.git", + "state" : { + "revision" : "baa932c1336f7894145cbaafcd34ce2dd0b77c97", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-http-structured-headers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-http-structured-headers.git", + "state" : { + "revision" : "1625f271afb04375bf48737a5572613248d0e7a0", + "version" : "1.4.0" } }, { "identity" : "swift-http-types", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-http-types", + "location" : "https://github.com/apple/swift-http-types.git", "state" : { - "revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd", - "version" : "1.3.0" + "revision" : "a0a57e949a8903563aba4615869310c0ebf14c03", + "version" : "1.4.0" } }, { @@ -267,8 +285,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", - "version" : "1.6.1" + "revision" : "ce592ae52f982c847a4efc0dd881cc9eb32d29f2", + "version" : "1.6.4" } }, { @@ -276,8 +294,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-metrics.git", "state" : { - "revision" : "e0165b53d49b413dd987526b641e05e246782685", - "version" : "2.5.0" + "revision" : "0743a9364382629da3bf5677b46a2c4b1ce5d2a6", + "version" : "2.7.1" } }, { @@ -285,8 +303,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "f7dc3f527576c398709b017584392fb58592e7f5", - "version" : "2.75.0" + "revision" : "a18bddb0acf7a40d982b2f128ce73ce4ee31f352", + "version" : "2.86.2" } }, { @@ -294,8 +312,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-extras.git", "state" : { - "revision" : "2e9746cfc57554f70b650b021b6ae4738abef3e6", - "version" : "1.24.1" + "revision" : "a55c3dd3a81d035af8a20ce5718889c0dcab073d", + "version" : "1.29.0" } }, { @@ -303,8 +321,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "eaa71bb6ae082eee5a07407b1ad0cbd8f48f9dca", - "version" : "1.34.1" + "revision" : "5e9e99ec96c53bc2c18ddd10c1e25a3cd97c55e5", + "version" : "1.38.0" } }, { @@ -312,8 +330,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl.git", "state" : { - "revision" : "d7ceaf0e4d8001cd35cdc12e42cdd281e9e564e8", - "version" : "2.28.0" + "revision" : "b2b043a8810ab6d51b3ff4df17f057d87ef1ec7c", + "version" : "2.34.1" } }, { @@ -330,8 +348,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-transport-services.git", "state" : { - "revision" : "dbace16f126fdcd80d58dc54526c561ca17327d7", - "version" : "1.22.0" + "revision" : "df6c28355051c72c884574a6c858bc54f7311ff9", + "version" : "1.25.2" } }, { @@ -339,8 +357,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-numerics.git", "state" : { - "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b", - "version" : "1.0.2" + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", + "version" : "1.1.1" + } + }, + { + "identity" : "swift-service-context", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-service-context.git", + "state" : { + "revision" : "1983448fefc717a2bc2ebde5490fe99873c5b8a6", + "version" : "1.2.1" } }, { @@ -348,8 +375,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swift-server/swift-service-lifecycle.git", "state" : { - "revision" : "24c800fb494fbee6e42bc156dc94232dc08971af", - "version" : "2.6.1" + "revision" : "0fcc4c9c2d58dd98504c06f7308c86de775396ff", + "version" : "2.9.0" } }, { @@ -357,8 +384,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "c8a44d836fe7913603e246acab7c528c2e780168", - "version" : "1.4.0" + "revision" : "395a77f0aa927f0ff73941d7ac35f2b46d47c9db", + "version" : "1.6.3" } }, { @@ -366,8 +393,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/vapor.git", "state" : { - "revision" : "fb619ab6485a88787ef9c78ba70e7415f8ebf981", - "version" : "4.106.4" + "revision" : "773ea6a63595ae4f6bc46a366d78769d4cb8b08c", + "version" : "4.117.0" } }, { @@ -375,8 +402,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/websocket-kit.git", "state" : { - "revision" : "4232d34efa49f633ba61afde365d3896fc7f8740", - "version" : "2.15.0" + "revision" : "8666c92dbbb3c8eefc8008c9c8dcf50bfd302167", + "version" : "2.16.1" } } ], diff --git a/Package.swift b/Package.swift index 64382b91..ee5a8b13 100644 --- a/Package.swift +++ b/Package.swift @@ -1,10 +1,10 @@ -// swift-tools-version:5.10 +// swift-tools-version:6.0 import PackageDescription let package = Package( name: "swift-leeds", platforms: [ - .macOS(.v12), + .macOS(.v13), ], dependencies: [ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), @@ -12,13 +12,14 @@ let package = Package( .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"), .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"), .package(url: "https://github.com/swift-aws/aws-sdk-swift.git", from: "4.7.0"), - .package(url: "https://github.com/vapor/apns.git", from: "1.0.0"), + .package(url: "https://github.com/vapor/apns.git", from: "5.0.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), .package(url: "https://github.com/handya/markdown.git", branch: "fix/xcode-16"), // This package is used by AWSSDKSwiftCore on Linux only. We add it here (but don't utilise it) in order to - // add it to the Package.resolved file. This ensures that when Docker or Heroku resolves this project, it will not - // ignore the versions pinned (causing a disparity between production Linux releases and local macOS builds). + // add it to the Package.resolved file. This ensures that when Docker resolves this project, it will not ignore + // the versions pinned (causing a disparity between production Linux releases and local macOS builds). This also + // massively improves build times by preventing multiple resolution cycles on deploy. .package(url: "https://github.com/apple/swift-nio-ssl-support.git", from: "1.0.0"), ], targets: [ @@ -31,7 +32,7 @@ let package = Package( .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"), .product(name: "S3", package: "aws-sdk-swift"), .product(name: "SwiftMarkdown", package: "markdown"), - .product(name: "APNS", package: "apns"), + .product(name: "VaporAPNS", package: "apns"), .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOPosix", package: "swift-nio"), ], @@ -41,7 +42,7 @@ let package = Package( name: "AppTests", dependencies: [ .target(name: "App"), - .product(name: "XCTVapor", package: "vapor"), + .product(name: "VaporTesting", package: "vapor"), ], swiftSettings: swiftSettings ), @@ -49,6 +50,5 @@ let package = Package( ) var swiftSettings: [SwiftSetting] { [ - .enableUpcomingFeature("DisableOutwardActorInference"), - .enableExperimentalFeature("StrictConcurrency"), + .enableUpcomingFeature("ExistentialAny"), ] } diff --git a/Sources/App/Features/Activities/Controllers/ActivityRouteController.swift b/Sources/App/Features/Activities/Controllers/ActivityRouteController.swift index b6f1f3e0..fb183e36 100644 --- a/Sources/App/Features/Activities/Controllers/ActivityRouteController.swift +++ b/Sources/App/Features/Activities/Controllers/ActivityRouteController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct ActivityRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) @@ -21,7 +21,7 @@ struct ActivityRouteController: RouteCollection { return try await request.view.render("Admin/Form/activity_form", context) } - private func buildContext(from db: Database, activity: Activity?) async throws -> ActivityContext { + private func buildContext(from db: any Database, activity: Activity?) async throws -> ActivityContext { let events = try await Event.query(on: db).sort(\.$date).all() return ActivityContext(activity: activity, events: events) } diff --git a/Sources/App/Features/Activities/Migrations/Activity+Migration+V1.swift b/Sources/App/Features/Activities/Migrations/Activity+Migration+V1.swift index cadebfb3..e69edec0 100644 --- a/Sources/App/Features/Activities/Migrations/Activity+Migration+V1.swift +++ b/Sources/App/Features/Activities/Migrations/Activity+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class ActivityMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.activity) .id() .field("title", .string, .required) @@ -13,7 +13,7 @@ final class ActivityMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.activity).delete() } } diff --git a/Sources/App/Features/Auth/Controllers/AuthController.swift b/Sources/App/Features/Auth/Controllers/AuthController.swift index 743977ee..5bb67834 100644 --- a/Sources/App/Features/Auth/Controllers/AuthController.swift +++ b/Sources/App/Features/Auth/Controllers/AuthController.swift @@ -3,7 +3,7 @@ import Foundation import Vapor struct AuthController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get("logout", use: logout) let grouped = routes.grouped("api", "v1", "auth") grouped.post("create", use: create) diff --git a/Sources/App/Features/Auth/Models/Migrations/User+Migration+V1.swift b/Sources/App/Features/Auth/Models/Migrations/User+Migration+V1.swift index 7755d895..70ae57de 100644 --- a/Sources/App/Features/Auth/Models/Migrations/User+Migration+V1.swift +++ b/Sources/App/Features/Auth/Models/Migrations/User+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class UserMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.user) .id() .field("name", .string, .required) @@ -12,7 +12,7 @@ final class UserMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.user).delete() } } diff --git a/Sources/App/Features/Auth/Models/Migrations/UserToken+Migration+V1.swift b/Sources/App/Features/Auth/Models/Migrations/UserToken+Migration+V1.swift index 5b8b9a1a..e46b02a7 100644 --- a/Sources/App/Features/Auth/Models/Migrations/UserToken+Migration+V1.swift +++ b/Sources/App/Features/Auth/Models/Migrations/UserToken+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent struct UserTokenMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.userToken) .id() .field("value", .string, .required) @@ -12,7 +12,7 @@ struct UserTokenMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.userToken).delete() } } diff --git a/Sources/App/Features/Auth/Models/User.swift b/Sources/App/Features/Auth/Models/User.swift index 5036be1f..8e16ce75 100644 --- a/Sources/App/Features/Auth/Models/User.swift +++ b/Sources/App/Features/Auth/Models/User.swift @@ -15,8 +15,8 @@ final class User: Authenticatable, ModelAuthenticatable, Content, ModelSessionAu return id ?? .init() } - static let usernameKey = \User.$email - static let passwordHashKey = \User.$passwordHash + static var usernameKey: KeyPath> { \User.$email } + static var passwordHashKey: KeyPath> { \User.$passwordHash } // Unique identifier for this user. @ID() @@ -64,19 +64,19 @@ final class User: Authenticatable, ModelAuthenticatable, Content, ModelSessionAu public static func credentialsAuthenticator( database _: DatabaseID? = nil - ) -> Authenticator { + ) -> any Authenticator { return CustomCredentialsAuthenticator() } public static func sessionAuthenticator( _: DatabaseID? = nil - ) -> Authenticator { + ) -> any Authenticator { return SessionAuthenticator() } public static func authenticator( database _: DatabaseID? = nil - ) -> Authenticator { + ) -> any Authenticator { return BearerAuthenticatable() } diff --git a/Sources/App/Features/Auth/Models/UserToken.swift b/Sources/App/Features/Auth/Models/UserToken.swift index 8e9852cb..d9773535 100644 --- a/Sources/App/Features/Auth/Models/UserToken.swift +++ b/Sources/App/Features/Auth/Models/UserToken.swift @@ -7,8 +7,8 @@ final class UserToken: Model, Content, ModelTokenAuthenticatable, Codable, @unch typealias User = App.User - static let valueKey = \UserToken.$value - static let userKey = \UserToken.$user + static var valueKey: KeyPath> { \UserToken.$value } + static var userKey: KeyPath> { \UserToken.$user } var isValid: Bool { return timestamp > Date() diff --git a/Sources/App/Features/CheckIn/Controllers/CheckInAPIController.swift b/Sources/App/Features/CheckIn/Controllers/CheckInAPIController.swift index bf086c75..23f08286 100644 --- a/Sources/App/Features/CheckIn/Controllers/CheckInAPIController.swift +++ b/Sources/App/Features/CheckIn/Controllers/CheckInAPIController.swift @@ -1,7 +1,7 @@ import Vapor struct CheckInAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(":secret", use: onGet) } diff --git a/Sources/App/Features/DropIns/Controllers/DropInRouteController.swift b/Sources/App/Features/DropIns/Controllers/DropInRouteController.swift index 3f1e88ea..8048bf95 100644 --- a/Sources/App/Features/DropIns/Controllers/DropInRouteController.swift +++ b/Sources/App/Features/DropIns/Controllers/DropInRouteController.swift @@ -6,7 +6,7 @@ struct DropInRouteController: RouteCollection { let sessions: [DropInSession] } - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/DropIns/Models/Migrations/AddDropInGroupsMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/AddDropInGroupsMigration.swift index 83fcafd4..5a831b28 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/AddDropInGroupsMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/AddDropInGroupsMigration.swift @@ -1,7 +1,7 @@ import Fluent final class AddDropInGroupsMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.dropInSessions) .field("max_tickets", .int, .sql(.default(1)), .required) .field("exclusivity_key", .string, .sql(.default("A")), .required) @@ -11,7 +11,7 @@ final class AddDropInGroupsMigration: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.dropInSessions) .deleteField("max_tickets") .deleteField("exclusivity_key") diff --git a/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionMigration.swift index 03cb85ce..4fb0fa05 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionMigration.swift @@ -1,7 +1,7 @@ import Fluent final class AddDropInSessionMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.dropInSessions) .id() .field("title", .string, .required) @@ -13,7 +13,7 @@ final class AddDropInSessionMigration: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.dropInSessions).delete() } } diff --git a/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionSlotsMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionSlotsMigration.swift index bca02394..113d6b3d 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionSlotsMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/AddDropInSessionSlotsMigration.swift @@ -1,7 +1,7 @@ import Fluent final class AddDropInSessionSlotsMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.dropInSessionSlots) .id() .field("session_id", .uuid, .references(Schema.dropInSessions, "id")) @@ -11,7 +11,7 @@ final class AddDropInSessionSlotsMigration: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.dropInSessionSlots).delete() } } diff --git a/Sources/App/Features/DropIns/Models/Migrations/AddDropInTBAMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/AddDropInTBAMigration.swift index 9d2b3eb9..d40491f2 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/AddDropInTBAMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/AddDropInTBAMigration.swift @@ -1,13 +1,13 @@ import Fluent final class AddDropInTBAMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.dropInSessions) .field("is_public", .bool, .sql(.default(false)), .required) // default to hidden .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.dropInSessions) .deleteField("is_public") .update() diff --git a/Sources/App/Features/DropIns/Models/Migrations/AddDurationToDropInMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/AddDurationToDropInMigration.swift index a4c3bf5d..ffb9160a 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/AddDurationToDropInMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/AddDurationToDropInMigration.swift @@ -1,13 +1,13 @@ import Fluent final class AddDurationToDropInMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.dropInSessionSlots) .field("duration", .int, .sql(.default(15)), .required) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.dropInSessionSlots) .deleteField("duration") .update() diff --git a/Sources/App/Features/DropIns/Models/Migrations/UseArrayDropInOwnerMigration.swift b/Sources/App/Features/DropIns/Models/Migrations/UseArrayDropInOwnerMigration.swift index d3355515..43f66b8a 100644 --- a/Sources/App/Features/DropIns/Models/Migrations/UseArrayDropInOwnerMigration.swift +++ b/Sources/App/Features/DropIns/Models/Migrations/UseArrayDropInOwnerMigration.swift @@ -1,7 +1,7 @@ import Fluent final class UseArrayDropInOwnerMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { // I'll be honest, I tried so many ways to get this to be a non-breaking change... but I couldn't get it to work // So it's a destructive update and I've manually fixed production (James Sherlock - May 31st 2024) try await database.schema(Schema.dropInSessionSlots) @@ -12,7 +12,7 @@ final class UseArrayDropInOwnerMigration: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { // Note, this is a destructive change and all data will be lost. try await database.schema(Schema.dropInSessionSlots) .deleteField("ticket") diff --git a/Sources/App/Features/Event Day/Controllers/EventDayRouteController.swift b/Sources/App/Features/Event Day/Controllers/EventDayRouteController.swift index e10a0893..0a839769 100644 --- a/Sources/App/Features/Event Day/Controllers/EventDayRouteController.swift +++ b/Sources/App/Features/Event Day/Controllers/EventDayRouteController.swift @@ -6,7 +6,7 @@ import LeafKit import Vapor struct EventDayRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Event Day/Migrations/EventDay+Migration+V1.swift b/Sources/App/Features/Event Day/Migrations/EventDay+Migration+V1.swift index 43a1835a..b4b6ce99 100644 --- a/Sources/App/Features/Event Day/Migrations/EventDay+Migration+V1.swift +++ b/Sources/App/Features/Event Day/Migrations/EventDay+Migration+V1.swift @@ -2,7 +2,7 @@ import Foundation import Fluent final class EventDayMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.eventDay) .id() .field("event_id", .uuid, .required, .references(Schema.event, "id")) @@ -14,7 +14,7 @@ final class EventDayMigrationV1: AsyncMigration { // We use this local-only model (instead of the 'real' Slot) to solve a migration step problem, // whilst also allowing us to drop event and date from Slot - final class MigrationSlot: Model { + final class MigrationSlot: Model, @unchecked Sendable { static let schema = Schema.slot @ID(key: .id) var id: UUID? @@ -47,7 +47,7 @@ final class EventDayMigrationV1: AsyncMigration { } } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.eventDay).delete() } } diff --git a/Sources/App/Features/Events/Controllers/EventRouteController.swift b/Sources/App/Features/Events/Controllers/EventRouteController.swift index b07a1933..dd734e54 100644 --- a/Sources/App/Features/Events/Controllers/EventRouteController.swift +++ b/Sources/App/Features/Events/Controllers/EventRouteController.swift @@ -6,7 +6,7 @@ import LeafKit import Vapor struct EventRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Events/Models/Event.swift b/Sources/App/Features/Events/Models/Event.swift index a8392446..445e3eb6 100644 --- a/Sources/App/Features/Events/Models/Event.swift +++ b/Sources/App/Features/Events/Models/Event.swift @@ -46,7 +46,7 @@ final class Event: Model, Content, @unchecked Sendable { } extension Event { - static func getCurrent(on db: Database) async throws -> Event { + static func getCurrent(on db: any Database) async throws -> Event { guard let event = try await Event.query(on: db).filter(\.$isCurrent == true).first() else { throw Abort(.notFound, reason: "could not locate current event") } diff --git a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V1.swift b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V1.swift index faf80442..e0e8cff1 100644 --- a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V1.swift +++ b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class EventMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.event) .id() .field("name", .string, .required) @@ -10,7 +10,7 @@ final class EventMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.event).delete() } } diff --git a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V2.swift b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V2.swift index 2b5713d2..26e7b9c8 100644 --- a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V2.swift +++ b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V2.swift @@ -1,13 +1,13 @@ import Fluent final class EventMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.event) .field("is_current", .bool, .sql(.default(false)), .required) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.event) .deleteField("is_current") .update() diff --git a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V3.swift b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V3.swift index eca11301..36de8518 100644 --- a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V3.swift +++ b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V3.swift @@ -1,13 +1,13 @@ import Fluent final class EventMigrationV3: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.event) .field("tito_event", .string) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.event) .deleteField("tito_event") .update() diff --git a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V4.swift b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V4.swift index 2d31d81f..a1fc51c8 100644 --- a/Sources/App/Features/Events/Models/Migrations/Event+Migration+V4.swift +++ b/Sources/App/Features/Events/Models/Migrations/Event+Migration+V4.swift @@ -1,14 +1,14 @@ import Fluent final class EventMigrationV4: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.event) .field("sessionize_key", .string) .field("show_schedule", .bool, .sql(.default(false)), .required) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.event) .deleteField("sessionize_key") .deleteField("show_schedule") diff --git a/Sources/App/Features/Home/HomeRouteController.swift b/Sources/App/Features/Home/HomeRouteController.swift index ae779cd2..dd62f6ee 100644 --- a/Sources/App/Features/Home/HomeRouteController.swift +++ b/Sources/App/Features/Home/HomeRouteController.swift @@ -1,7 +1,7 @@ import Vapor struct HomeRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { let route = routes.grouped(User.sessionAuthenticator()) route.get(use: get) route.get("schedule", use: schedule) diff --git a/Sources/App/Features/Jobs/Controllers/JobRouteController.swift b/Sources/App/Features/Jobs/Controllers/JobRouteController.swift index 21f37881..d81931a7 100644 --- a/Sources/App/Features/Jobs/Controllers/JobRouteController.swift +++ b/Sources/App/Features/Jobs/Controllers/JobRouteController.swift @@ -7,7 +7,7 @@ struct JobRouteController: RouteCollection { let sponsors: [Sponsor] } - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Jobs/Model/Middleware/JobMiddleware.swift b/Sources/App/Features/Jobs/Model/Middleware/JobMiddleware.swift index 2a0400e2..b9c397c7 100644 --- a/Sources/App/Features/Jobs/Model/Middleware/JobMiddleware.swift +++ b/Sources/App/Features/Jobs/Model/Middleware/JobMiddleware.swift @@ -2,22 +2,22 @@ import Foundation import Fluent struct JobMiddleware: AsyncModelMiddleware { - func create(model: Job, on db: Database, next: AnyAsyncModelResponder) async throws { + func create(model: Job, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.create(model, on: db) try await updateTimestamp(on: db, title: model.title) } - func update(model: Job, on db: Database, next: AnyAsyncModelResponder) async throws { + func update(model: Job, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.update(model, on: db) try await updateTimestamp(on: db, title: model.title) } - func delete(model: Job, force: Bool, on db: Database, next: AnyAsyncModelResponder) async throws { + func delete(model: Job, force: Bool, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.delete(model, force: force, on: db) try await updateTimestamp(on: db, title: model.title) } - private func updateTimestamp(on db: Database, title: String) async throws { + private func updateTimestamp(on db: any Database, title: String) async throws { guard let lastUpdated = try await LastUpdated .query(on: db) .first() diff --git a/Sources/App/Features/Jobs/Model/Migrations/JobMigrationV1.swift b/Sources/App/Features/Jobs/Model/Migrations/JobMigrationV1.swift index a93f3f20..0a801a3f 100644 --- a/Sources/App/Features/Jobs/Model/Migrations/JobMigrationV1.swift +++ b/Sources/App/Features/Jobs/Model/Migrations/JobMigrationV1.swift @@ -1,7 +1,7 @@ import Fluent final class JobMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.job) .id() .field("title", .string, .required) @@ -12,7 +12,7 @@ final class JobMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.job).delete() } } diff --git a/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV1.swift b/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV1.swift index 26203949..5a4a6e4c 100644 --- a/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV1.swift +++ b/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV1.swift @@ -1,7 +1,7 @@ import Fluent final class LastUpdatedMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.lastUpdated) .id() .field("sponsors", .datetime, .required) @@ -13,7 +13,7 @@ final class LastUpdatedMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.lastUpdated).delete() } } diff --git a/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV2.swift b/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV2.swift index 145cdad2..52bc7145 100644 --- a/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV2.swift +++ b/Sources/App/Features/LastUpdated/Model/Migrations/LastUpdatedMigrationV2.swift @@ -1,12 +1,12 @@ import Fluent final class LastUpdatedMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { let lastUpdated = LastUpdated(id: nil, sponsors: .distantPast, presentations: .distantPast, activities: .distantPast, speakers: .distantPast, slots: .distantPast, events: .distantPast) try await lastUpdated.save(on: database) } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await LastUpdated.query(on: database).delete() } } diff --git a/Sources/App/Features/Local/LocalAPIController.swift b/Sources/App/Features/Local/LocalAPIController.swift index 261d0c64..c4241304 100644 --- a/Sources/App/Features/Local/LocalAPIController.swift +++ b/Sources/App/Features/Local/LocalAPIController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct LocalAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(use: onGet) } diff --git a/Sources/App/Features/Location Categories/Migrations/LocationCategory+Migration+V1.swift b/Sources/App/Features/Location Categories/Migrations/LocationCategory+Migration+V1.swift index 75e0cd6e..b6ce66f8 100644 --- a/Sources/App/Features/Location Categories/Migrations/LocationCategory+Migration+V1.swift +++ b/Sources/App/Features/Location Categories/Migrations/LocationCategory+Migration+V1.swift @@ -3,7 +3,7 @@ import Foundation import Vapor final class LocationCategoryMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.locationCategory) .id() .field("name", .string, .required) @@ -11,7 +11,7 @@ final class LocationCategoryMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.locationCategory).delete() } } diff --git a/Sources/App/Features/Locations/Migrations/Location+Migration+V1.swift b/Sources/App/Features/Locations/Migrations/Location+Migration+V1.swift index 75c53545..aef3c027 100644 --- a/Sources/App/Features/Locations/Migrations/Location+Migration+V1.swift +++ b/Sources/App/Features/Locations/Migrations/Location+Migration+V1.swift @@ -3,7 +3,7 @@ import Foundation import Vapor final class LocationMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.location) .id() .field("name", .string, .required) @@ -16,7 +16,7 @@ final class LocationMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.location).delete() } } diff --git a/Sources/App/Features/Presentations/Controllers/PresentationRouteController.swift b/Sources/App/Features/Presentations/Controllers/PresentationRouteController.swift index fa9582dc..8e0b0ef2 100644 --- a/Sources/App/Features/Presentations/Controllers/PresentationRouteController.swift +++ b/Sources/App/Features/Presentations/Controllers/PresentationRouteController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct PresentationRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V1.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V1.swift index 1e35cb8d..782e2a2e 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V1.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class PresentationMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.presentation) .id() .field("title", .string, .required) @@ -15,7 +15,7 @@ final class PresentationMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation).delete() } } diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V2.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V2.swift index 3d8474ab..7cc62afe 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V2.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V2.swift @@ -1,13 +1,13 @@ import Fluent final class PresentationMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.presentation) .field("speaker_two_id", .uuid, .references(Schema.speaker, "id", onDelete: .setNull, onUpdate: .cascade)) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation) .deleteField("speaker_two_id") .update() diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V3.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V3.swift index 6227eb36..d8ac658e 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V3.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V3.swift @@ -1,13 +1,13 @@ import Fluent final class PresentationMigrationV3: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.presentation) .field("slido_url", .string) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation) .deleteField("slido_url") .update() diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V4.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V4.swift index a3f52e35..cc05aa22 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V4.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V4.swift @@ -1,13 +1,13 @@ import Fluent final class PresentationMigrationV4: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.presentation) .deleteField("image") .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation) .field("image", .string) .update() diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V5.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V5.swift index 2011a71b..d4181c5c 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V5.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V5.swift @@ -1,13 +1,13 @@ import Fluent final class PresentationMigrationV5: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.presentation) .field("video_url", .string) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation) .deleteField("video_url") .update() diff --git a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V6.swift b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V6.swift index c5270f9e..24d9eb89 100644 --- a/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V6.swift +++ b/Sources/App/Features/Presentations/Models/Migrations/Presentation+Migration+V6.swift @@ -1,7 +1,7 @@ import Fluent final class PresentationMigrationV6: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { _ = try await database.enum("video_visibility") .case("unlisted") .case("shared") @@ -15,7 +15,7 @@ final class PresentationMigrationV6: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.enum("video_visibility").delete() try await database.schema(Schema.presentation) diff --git a/Sources/App/Features/Push/Controllers/PushController.swift b/Sources/App/Features/Push/Controllers/PushController.swift index 016438d5..c07b5199 100644 --- a/Sources/App/Features/Push/Controllers/PushController.swift +++ b/Sources/App/Features/Push/Controllers/PushController.swift @@ -1,9 +1,10 @@ -import APNS +import APNSCore +import VaporAPNS import Fluent import Vapor struct PushController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { let push = routes.grouped("push") push.post(use: create) push.post("testNotification", use: testNotification) @@ -42,8 +43,14 @@ struct PushController: RouteCollection { return .notFound } - let alert = APNSwiftAlert(title: "SwiftLeeds Rocks!", body: "Push is working 🚀") - _ = req.apns.send(alert, to: token.token) + let notification = APNSAlertNotification( + alert: APNSAlertNotificationContent(title: .raw("SwiftLeeds Rocks!"), body: .raw("Push is working 🚀")), + expiration: .none, + priority: .immediately, + topic: "uk.co.swiftleeds.SwiftLeeds" + ) + + try await req.apnsClient.sendAlertNotification(notification, deviceToken: token.token) return .noContent } @@ -62,12 +69,29 @@ struct PushController: RouteCollection { } let tokens = try await Token.query(on: req.db).all() - let alert = APNSwiftAlert(title: "SwiftLeeds", body: notificationRequest.message) - tokens.forEach { token in - _ = req.apns.send(alert, to: token.token) + let notification = APNSAlertNotification( + alert: APNSAlertNotificationContent(title: .raw("SwiftLeeds"), body: .raw(notificationRequest.message)), + expiration: .none, + priority: .immediately, + topic: "uk.co.swiftleeds.SwiftLeeds" + ) + + for token in tokens { + try await req.apnsClient.sendAlertNotification(notification, deviceToken: token.token) } return .noContent } } + +extension Request { + var apnsClient: APNSGenericClient { + get async { + switch application.environment { + case .production: return await apns.client(.production) + default: return await apns.client(.development) + } + } + } +} diff --git a/Sources/App/Features/Push/Migrations/PushMigration.swift b/Sources/App/Features/Push/Migrations/PushMigration.swift index 7c1678b9..54163706 100644 --- a/Sources/App/Features/Push/Migrations/PushMigration.swift +++ b/Sources/App/Features/Push/Migrations/PushMigration.swift @@ -1,7 +1,7 @@ import Fluent struct PushMigration: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.tokens) .id() .field("token", .string, .required) @@ -10,7 +10,7 @@ struct PushMigration: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.tokens).delete() } } diff --git a/Sources/App/Features/Push/Migrations/PushMigrationV2.swift b/Sources/App/Features/Push/Migrations/PushMigrationV2.swift index bb81c4f6..e76af96f 100644 --- a/Sources/App/Features/Push/Migrations/PushMigrationV2.swift +++ b/Sources/App/Features/Push/Migrations/PushMigrationV2.swift @@ -1,13 +1,13 @@ import Fluent struct PushMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.tokens) .field("updated_at", .string) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.tokens) .deleteField("updated_at") .update() diff --git a/Sources/App/Features/Schedule/ScheduleAPIController+V2.swift b/Sources/App/Features/Schedule/ScheduleAPIController+V2.swift index 5a991300..a7bd8f11 100644 --- a/Sources/App/Features/Schedule/ScheduleAPIController+V2.swift +++ b/Sources/App/Features/Schedule/ScheduleAPIController+V2.swift @@ -7,7 +7,7 @@ import Vapor // The enhancements here ensure the application understands how many days there are, // what slots are on what day, and what the name of the day is (such as 'Talkshow'). struct ScheduleAPIControllerV2: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(use: onGet) } diff --git a/Sources/App/Features/Schedule/ScheduleAPIController.swift b/Sources/App/Features/Schedule/ScheduleAPIController.swift index cdb24210..99f76940 100644 --- a/Sources/App/Features/Schedule/ScheduleAPIController.swift +++ b/Sources/App/Features/Schedule/ScheduleAPIController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct ScheduleAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(use: onGet) } diff --git a/Sources/App/Features/Sessionize/Controllers/SessionizeSyncRouteController.swift b/Sources/App/Features/Sessionize/Controllers/SessionizeSyncRouteController.swift index fe33ff1a..7fe542f0 100644 --- a/Sources/App/Features/Sessionize/Controllers/SessionizeSyncRouteController.swift +++ b/Sources/App/Features/Sessionize/Controllers/SessionizeSyncRouteController.swift @@ -1,7 +1,7 @@ import Vapor struct SessionizeSyncRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get("modal", ":id", use: modal) routes.post("modal", ":id", "accept", use: commit) } diff --git a/Sources/App/Features/Slots/Controllers/SlotRouteController.swift b/Sources/App/Features/Slots/Controllers/SlotRouteController.swift index 5aab31d1..e607630b 100644 --- a/Sources/App/Features/Slots/Controllers/SlotRouteController.swift +++ b/Sources/App/Features/Slots/Controllers/SlotRouteController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct SlotRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) @@ -22,7 +22,7 @@ struct SlotRouteController: RouteCollection { return try await request.view.render("Admin/Form/slot_form", context) } - private func buildContext(from db: Database, slot: Slot?) async throws -> PresentationContext { + private func buildContext(from db: any Database, slot: Slot?) async throws -> PresentationContext { let eventDays = try await EventDay.query(on: db).with(\.$event).sort(\.$date).all() let presentations = try await Presentation.query(on: db).with(\.$speaker).sort(\.$title).all() let activities = try await Activity.query(on: db).sort(\.$title).all() @@ -67,7 +67,7 @@ struct SlotRouteController: RouteCollection { } try await eventDay.$event.load(on: request.db) - let event = eventDay.event +// let event = eventDay.event // We can have either, but not both. If both are provided, prioritise the activity. if let activityID = input.activityID, activityID.isEmpty == false { @@ -76,13 +76,13 @@ struct SlotRouteController: RouteCollection { presentation = try await Presentation.find(.init(uuidString: presentationID), on: request.db) } - let minutes: Int = input.startTime.components(separatedBy: ":").enumerated().reduce(into: 0) { builder, value in - guard let number = Int(value.element) else { return } - if value.offset == 0 { builder += number * 60 } - else { builder += number } - } +// let minutes: Int = input.startTime.components(separatedBy: ":").enumerated().reduce(into: 0) { builder, value in +// guard let number = Int(value.element) else { return } +// if value.offset == 0 { builder += number * 60 } +// else { builder += number } +// } - let inputDate = eventDay.date.addingTimeInterval(TimeInterval(minutes * 60)) +// let inputDate = eventDay.date.addingTimeInterval(TimeInterval(minutes * 60)) let duration = input.duration.flatMap(Double.init) let mutableSlot = slot ?? Slot() diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v1.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v1.swift index 4dfd8826..94ec666f 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v1.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v1.swift @@ -1,7 +1,7 @@ import Fluent final class SlotMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.slot) .id() .field("start_date", .string, .required) @@ -32,7 +32,7 @@ final class SlotMigrationV1: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { // remove foreign keys & restore deleted fields try await database .schema(Schema.presentation) diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v2.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v2.swift index d8186b64..4da306d7 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v2.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v2.swift @@ -1,14 +1,14 @@ import Fluent struct SlotMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database .schema(Schema.slot) .field("date", .datetime) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database .schema(Schema.slot) .deleteField("date") diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v3.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v3.swift index 827408bb..7d512216 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v3.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v3.swift @@ -2,13 +2,13 @@ import Foundation import Fluent struct SlotMigrationV3: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.slot) .field("day_id", .uuid, .references(Schema.eventDay, "id")) .update() // Define a local-only model to access the old structure - final class MigrationSlot: Model { + final class MigrationSlot: Model, @unchecked Sendable { static let schema = Schema.slot @ID(key: .id) var id: UUID? @@ -31,7 +31,7 @@ struct SlotMigrationV3: AsyncMigration { } } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.slot) .deleteField("day_id") .update() diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v4.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v4.swift index 25ff6917..5271f0fe 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v4.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v4.swift @@ -2,9 +2,9 @@ import Foundation import Fluent struct SlotMigrationV4: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { - final class MigrationSlot: Model { + final class MigrationSlot: Model, @unchecked Sendable { static let schema = Schema.slot @ID(key: .id) var id: UUID? @@ -13,7 +13,7 @@ struct SlotMigrationV4: AsyncMigration { @OptionalParent(key: "activity_id") var activity: Activity? } - final class MigrationPresentation: Model { + final class MigrationPresentation: Model, @unchecked Sendable { static let schema = Schema.presentation @ID(key: .id) var id: UUID? @@ -21,7 +21,7 @@ struct SlotMigrationV4: AsyncMigration { @OptionalField(key: "slot_id") var slotID: UUID? } - final class MigrationActivity: Model { + final class MigrationActivity: Model, @unchecked Sendable { static let schema = Schema.activity @ID(key: .id) var id: UUID? @@ -81,7 +81,7 @@ struct SlotMigrationV4: AsyncMigration { } } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.slot) .deleteField("presentation_id") .deleteField("activity_id") diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v5.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v5.swift index bd72c64e..add1d006 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v5.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v5.swift @@ -2,7 +2,7 @@ import Foundation import Fluent struct SlotMigrationV5: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.activity) .deleteField("slot_id") .update() @@ -12,7 +12,7 @@ struct SlotMigrationV5: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.presentation) .field("slot_id", .uuid, .references(Schema.slot, "id", onDelete: .setNull, onUpdate: .cascade)) .unique(on: "slot_id") diff --git a/Sources/App/Features/Slots/Migrations/Slot+Migration+v6.swift b/Sources/App/Features/Slots/Migrations/Slot+Migration+v6.swift index a95461f4..a156ceb2 100644 --- a/Sources/App/Features/Slots/Migrations/Slot+Migration+v6.swift +++ b/Sources/App/Features/Slots/Migrations/Slot+Migration+v6.swift @@ -2,14 +2,14 @@ import Foundation import Fluent struct SlotMigrationV6: AsyncMigration { - func prepare(on db: Database) async throws { + func prepare(on db: any Database) async throws { try await db.schema(Schema.slot) .deleteField("date") .deleteField("event_id") .update() } - func revert(on db: Database) async throws { + func revert(on db: any Database) async throws { try await db.schema(Schema.slot) .field("date", .datetime) .field("event_id", .uuid, .references(Schema.event, "id")) diff --git a/Sources/App/Features/Speakers/Controllers/SpeakerRouteController.swift b/Sources/App/Features/Speakers/Controllers/SpeakerRouteController.swift index ce384fc9..ee927d83 100644 --- a/Sources/App/Features/Speakers/Controllers/SpeakerRouteController.swift +++ b/Sources/App/Features/Speakers/Controllers/SpeakerRouteController.swift @@ -4,7 +4,7 @@ import S3 import Vapor struct SpeakerRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V1.swift b/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V1.swift index 54d69f71..ff3070d4 100644 --- a/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V1.swift +++ b/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class SpeakerMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Speaker.schema) .id() .field("name", .string, .required) @@ -12,7 +12,7 @@ final class SpeakerMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.speaker).delete() } } diff --git a/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V2.swift b/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V2.swift index f0c1f936..033fdc1f 100644 --- a/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V2.swift +++ b/Sources/App/Features/Speakers/Models/Migrations/Speaker+Migration+V2.swift @@ -1,7 +1,7 @@ import Fluent final class SpeakerMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.speaker) .field("linkedin", .string) .field("website", .string) @@ -10,7 +10,7 @@ final class SpeakerMigrationV2: AsyncMigration { .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.speaker) .deleteField("linkedin") .deleteField("website") diff --git a/Sources/App/Features/Sponsors/Controllers/SponsorAPIController.swift b/Sources/App/Features/Sponsors/Controllers/SponsorAPIController.swift index 9bee2b43..75de9724 100644 --- a/Sources/App/Features/Sponsors/Controllers/SponsorAPIController.swift +++ b/Sources/App/Features/Sponsors/Controllers/SponsorAPIController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct SponsorAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(use: onGet) } diff --git a/Sources/App/Features/Sponsors/Controllers/SponsorRouteController.swift b/Sources/App/Features/Sponsors/Controllers/SponsorRouteController.swift index 1dc57541..daab1171 100644 --- a/Sources/App/Features/Sponsors/Controllers/SponsorRouteController.swift +++ b/Sources/App/Features/Sponsors/Controllers/SponsorRouteController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct SponsorRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { // Modal routes.get(use: onRead) routes.get(":id", use: onRead) diff --git a/Sources/App/Features/Sponsors/Models/Middleware/SponsorMiddleware.swift b/Sources/App/Features/Sponsors/Models/Middleware/SponsorMiddleware.swift index 96e239d4..20c9be9e 100644 --- a/Sources/App/Features/Sponsors/Models/Middleware/SponsorMiddleware.swift +++ b/Sources/App/Features/Sponsors/Models/Middleware/SponsorMiddleware.swift @@ -2,22 +2,22 @@ import Foundation import Fluent struct SponsorMiddleware: AsyncModelMiddleware { - func create(model: Sponsor, on db: Database, next: AnyAsyncModelResponder) async throws { + func create(model: Sponsor, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.create(model, on: db) try await updateTimestamp(on: db, name: model.name) } - func update(model: Sponsor, on db: Database, next: AnyAsyncModelResponder) async throws { + func update(model: Sponsor, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.update(model, on: db) try await updateTimestamp(on: db, name: model.name) } - func delete(model: Sponsor, force: Bool, on db: Database, next: AnyAsyncModelResponder) async throws { + func delete(model: Sponsor, force: Bool, on db: any Database, next: any AnyAsyncModelResponder) async throws { try await next.delete(model, force: force, on: db) try await updateTimestamp(on: db, name: model.name) } - private func updateTimestamp(on db: Database, name: String) async throws { + private func updateTimestamp(on db: any Database, name: String) async throws { guard let lastUpdated = try await LastUpdated .query(on: db) .first() diff --git a/Sources/App/Features/Sponsors/Models/Migrations/Sponsor+Migration+V1.swift b/Sources/App/Features/Sponsors/Models/Migrations/Sponsor+Migration+V1.swift index 794c8095..9069979c 100644 --- a/Sources/App/Features/Sponsors/Models/Migrations/Sponsor+Migration+V1.swift +++ b/Sources/App/Features/Sponsors/Models/Migrations/Sponsor+Migration+V1.swift @@ -1,7 +1,7 @@ import Fluent final class SponsorMigrationV1: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { _ = try await database.enum("sponsor_level") .case("silver") .case("gold") @@ -20,7 +20,7 @@ final class SponsorMigrationV1: AsyncMigration { .create() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.enum("sponsor_level").delete() try await database.schema(Schema.sponsor).delete() } diff --git a/Sources/App/Features/Sponsors/Models/Migrations/SponsorMigrationV2.swift b/Sources/App/Features/Sponsors/Models/Migrations/SponsorMigrationV2.swift index 2d819cf9..dfc19abb 100644 --- a/Sources/App/Features/Sponsors/Models/Migrations/SponsorMigrationV2.swift +++ b/Sources/App/Features/Sponsors/Models/Migrations/SponsorMigrationV2.swift @@ -1,13 +1,13 @@ import Fluent final class SponsorMigrationV2: AsyncMigration { - func prepare(on database: Database) async throws { + func prepare(on database: any Database) async throws { try await database.schema(Schema.sponsor) .field("subtitle", .string) .update() } - func revert(on database: Database) async throws { + func revert(on database: any Database) async throws { try await database.schema(Schema.sponsor) .deleteField("subtitle") .update() diff --git a/Sources/App/Features/Talks/TalkRouteController.swift b/Sources/App/Features/Talks/TalkRouteController.swift index 0c1a0fa0..f1eee2d1 100644 --- a/Sources/App/Features/Talks/TalkRouteController.swift +++ b/Sources/App/Features/Talks/TalkRouteController.swift @@ -1,7 +1,7 @@ import Vapor struct TalkRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { let route = routes.grouped(User.sessionAuthenticator()) route.get("talk", ":talk", use: talk) } diff --git a/Sources/App/Features/Team/Controllers/TeamAPIController.swift b/Sources/App/Features/Team/Controllers/TeamAPIController.swift index 92a3ddc0..5c8123d7 100644 --- a/Sources/App/Features/Team/Controllers/TeamAPIController.swift +++ b/Sources/App/Features/Team/Controllers/TeamAPIController.swift @@ -1,7 +1,7 @@ import Vapor struct TeamAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(use: getTeam) } diff --git a/Sources/App/Features/Ticket Hub/Controllers/TicketHubRouteController.swift b/Sources/App/Features/Ticket Hub/Controllers/TicketHubRouteController.swift index ee3fc5a9..8797fe41 100644 --- a/Sources/App/Features/Ticket Hub/Controllers/TicketHubRouteController.swift +++ b/Sources/App/Features/Ticket Hub/Controllers/TicketHubRouteController.swift @@ -2,7 +2,7 @@ import Fluent import Vapor struct TicketHubRouteController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.grouped(ValidTicketMiddleware()).group("ticket") { builder in builder.get { req -> View in // guard let currentEvent = try await Event.query(on: req.db).filter(\.$name == "SwiftLeeds 2023").first() else { diff --git a/Sources/App/Features/Tickets/Controllers/TicketLoginController.swift b/Sources/App/Features/Tickets/Controllers/TicketLoginController.swift index daf27ba4..887a3269 100644 --- a/Sources/App/Features/Tickets/Controllers/TicketLoginController.swift +++ b/Sources/App/Features/Tickets/Controllers/TicketLoginController.swift @@ -2,7 +2,7 @@ import Vapor import Fluent struct TicketLoginController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get("ticketLogin") { req async throws -> View in if let query: String = req.query["returnUrl"] { req.session.data["ticketRedirectUrl"] = query diff --git a/Sources/App/Features/Tickets/Controllers/TicketsAPIController.swift b/Sources/App/Features/Tickets/Controllers/TicketsAPIController.swift index 0222b615..5516d42b 100644 --- a/Sources/App/Features/Tickets/Controllers/TicketsAPIController.swift +++ b/Sources/App/Features/Tickets/Controllers/TicketsAPIController.swift @@ -1,7 +1,7 @@ import Vapor struct TicketsAPIController: RouteCollection { - func boot(routes: RoutesBuilder) throws { + func boot(routes: any RoutesBuilder) throws { routes.get(":event", use: onGet) } diff --git a/Sources/App/Features/Tickets/Middleware/ValidTicketMiddleware.swift b/Sources/App/Features/Tickets/Middleware/ValidTicketMiddleware.swift index a883b7ae..c4b123da 100644 --- a/Sources/App/Features/Tickets/Middleware/ValidTicketMiddleware.swift +++ b/Sources/App/Features/Tickets/Middleware/ValidTicketMiddleware.swift @@ -2,7 +2,7 @@ import Vapor import Fluent struct ValidTicketMiddleware: AsyncMiddleware { - func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { + func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response { guard let currentEvent = try await Event.query(on: request.db).filter(\.$isCurrent == true).first() else { throw Abort(.badRequest, reason: "unable to identify current event") } diff --git a/Sources/App/Middleware/AdminMiddleware.swift b/Sources/App/Middleware/AdminMiddleware.swift index f7f5fcff..e3415c3b 100644 --- a/Sources/App/Middleware/AdminMiddleware.swift +++ b/Sources/App/Middleware/AdminMiddleware.swift @@ -1,7 +1,7 @@ import Vapor struct AdminMiddleware: AsyncMiddleware { - func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { + func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response { guard let user = request.user else { if !request.application.environment.isRelease { request.logger.warning("attempted to access admin page, but no user is logged in") diff --git a/Sources/App/Middleware/AppleAppSiteAssociationMiddleware.swift b/Sources/App/Middleware/AppleAppSiteAssociationMiddleware.swift index 8bc26315..3c842da9 100644 --- a/Sources/App/Middleware/AppleAppSiteAssociationMiddleware.swift +++ b/Sources/App/Middleware/AppleAppSiteAssociationMiddleware.swift @@ -1,7 +1,7 @@ import Vapor struct AppleAppSiteAssociationMiddleware: AsyncMiddleware { - func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { + func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response { guard request.url.string == "/.well-known/apple-app-site-association" else { return try await next.respond(to: request) } diff --git a/Sources/App/Migrations.swift b/Sources/App/Migrations.swift index 9768767e..62151a23 100644 --- a/Sources/App/Migrations.swift +++ b/Sources/App/Migrations.swift @@ -109,6 +109,8 @@ class Migrations { do { if app.environment != .testing { try await app.autoMigrate() + } else { + app.logger.warning("Skipping database migration") } } catch { app.logger.error("Failed to migrate DB with error \(error)") diff --git a/Sources/App/Services/ImageService.swift b/Sources/App/Services/ImageService.swift index 5d72dada..11644092 100644 --- a/Sources/App/Services/ImageService.swift +++ b/Sources/App/Services/ImageService.swift @@ -1,4 +1,4 @@ -import S3 +@preconcurrency import S3 import Vapor final class ImageService { diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 4ab58668..85b9f793 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -1,4 +1,4 @@ -import APNS +import VaporAPNS import Leaf import Vapor @@ -38,11 +38,16 @@ public func configure(_ app: Application) async throws { app.leaf.cache.isEnabled = false #endif - // Migrations + // Setup database and define migrations try await Migrations.migrate(app) - // Model middleware - app.databases.middleware.use(SponsorMiddleware(), on: .psql) + // Check that the database has been setup + if app.databases.ids().isEmpty == false { + // Model middleware + app.databases.middleware.use(SponsorMiddleware(), on: .psql) + } else { + app.logger.warning("Skipping database middleware") + } // Routes app.routes.defaultMaxBodySize = "10mb" @@ -55,8 +60,8 @@ public func configure(_ app: Application) async throws { let data = Data(base64Encoded: encodedKey), let p8Key = String(data: data, encoding: .utf8) { - let apnsEnvironment: APNSwiftConfiguration.Environment = app.environment == .production ? .production : .sandbox - let auth: APNSwiftConfiguration.AuthenticationMethod = try .jwt(key: .private(pem: p8Key), keyIdentifier: "K4D2BJ235Y", teamIdentifier: "K33K6V7FBA") - app.apns.configuration = .init(authenticationMethod: auth, topic: "uk.co.swiftleeds.SwiftLeeds", environment: apnsEnvironment) + try await app.apns.configure(.jwt(privateKey: .loadFrom(string: p8Key), keyIdentifier: "K4D2BJ235Y", teamIdentifier: "K33K6V7FBA")) + } else { + app.logger.warning("Skipping APNS Setup") } } diff --git a/Tests/AppTests/AppTests.swift b/Tests/AppTests/AppTests.swift index 9f2be299..ce6eeb4d 100644 --- a/Tests/AppTests/AppTests.swift +++ b/Tests/AppTests/AppTests.swift @@ -1,33 +1,31 @@ @testable import App -import XCTVapor +import VaporTesting +import Testing -final class AppTests: XCTestCase { - func testAASA() throws { - let app = Application(.testing) - defer { app.shutdown() } - try configure(app) +@Suite("Application Tests") +struct AppTests { + private func withApp(_ test: (Application) async throws -> ()) async throws { + let app = try await Application.make(.testing) - try app.test(.GET, ".well-known/apple-app-site-association", afterResponse: { res in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.headers.first(name: .contentType), "application/json") // mime type must be set - XCTAssertLessThan(res.body.readableBytes, 128000) // Max size is 128Kb - }) - } - - func testPhaseDateCalculation() { - do { // 2014 (Unannounced) - let event = Event(id: nil, name: "SwiftLeeds 2014", date: Date(timeIntervalSince1970: 1412899200), location: "", isCurrent: false) - XCTAssertEqual(HomeRouteController().buildConferenceDateString(for: event), nil) - } - - do { // 2021 (1 day event) - let event = Event(id: nil, name: "SwiftLeeds 2021", date: Date(timeIntervalSince1970: 1633737600), location: "", isCurrent: false) - XCTAssertEqual(HomeRouteController().buildConferenceDateString(for: event), "9 OCT") + do { + try await configure(app) + try await test(app) + } catch { + try await app.asyncShutdown() + throw error } - do { // 2023 (2 day event) - let event = Event(id: nil, name: "SwiftLeeds 2023", date: Date(timeIntervalSince1970: 1696809600), location: "", isCurrent: false) - XCTAssertEqual(HomeRouteController().buildConferenceDateString(for: event), "9-10 OCT") + try await app.asyncShutdown() + } + + @Test("Ensure AASA has correct format") + func verifyAASA() async throws { + try await withApp { app in + try await app.testing().test(.GET, ".well-known/apple-app-site-association", afterResponse: { res in + #expect(res.status == .ok) + #expect(res.headers.first(name: .contentType) == "application/json") // mime type must be set + #expect(res.body.readableBytes < 128000) // Max size is 128Kb + }) } } }