From f54eac363b2b16818fa89594b9998aa5bb0faf98 Mon Sep 17 00:00:00 2001 From: chi Date: Fri, 16 Feb 2024 16:16:04 -0500 Subject: [PATCH 1/8] announcement json string change --- .../Controllers/AnnouncementsController.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index 97dfc14..e727c82 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -27,9 +27,24 @@ struct AnnouncementsController: RouteCollection where DecoderType: private func create(_ request: Request) async throws -> Announcement { let announcement = try request.content.decode(Announcement.self, using: self.decoder) + + // new changes 2/13 + // ** announcement json string ** + guard let data = ("\(announcement.id) || \(announcement.subject) || \(announcement.start) || \(announcement.end) || + \(announcement.scheduleType) || \(announcement.body) || \(announcement.interruptionLevel)").data(using: .utf8) else { + throw Abort(.internalServerError) + } + + /* guard let data = (announcement.subject + announcement.body).data(using: .utf8) else { throw Abort(.internalServerError) } + */ + + + + + if try CryptographyUtilities.verify(signature: announcement.signature, of: data) { try await announcement.save(on: request.db(.psql)) From 3fda9d15816383576fda91375a918b30b2ab1249 Mon Sep 17 00:00:00 2001 From: Veevek Dave Date: Tue, 27 Feb 2024 17:13:27 -0500 Subject: [PATCH 2/8] Added Gaurd Statement and Query Template to recieve all announcements --- .../Server/Controllers/AnnouncementsController.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index e727c82..7b21f8e 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -27,8 +27,15 @@ struct AnnouncementsController: RouteCollection where DecoderType: private func create(_ request: Request) async throws -> Announcement { let announcement = try request.content.decode(Announcement.self, using: self.decoder) - - // new changes 2/13 + + let books = try await announcement + .query(on: request.db(.psql)) + .filter(\.$pageCount > 400) + .all() + .map { (book) in + return book.title + } + // new changes 2/13 // ** announcement json string ** guard let data = ("\(announcement.id) || \(announcement.subject) || \(announcement.start) || \(announcement.end) || \(announcement.scheduleType) || \(announcement.body) || \(announcement.interruptionLevel)").data(using: .utf8) else { From 6b9815bd4a29bcefd25242a3497f0f55b9b0735b Mon Sep 17 00:00:00 2001 From: Veevek Dave Date: Tue, 27 Feb 2024 17:20:12 -0500 Subject: [PATCH 3/8] Fixed query template + made gaurd template --- Sources/Server/Controllers/AnnouncementsController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index 7b21f8e..2a86470 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -28,13 +28,13 @@ struct AnnouncementsController: RouteCollection where DecoderType: private func create(_ request: Request) async throws -> Announcement { let announcement = try request.content.decode(Announcement.self, using: self.decoder) - let books = try await announcement + let books = try await Announcement .query(on: request.db(.psql)) - .filter(\.$pageCount > 400) .all() - .map { (book) in - return book.title + .filter { (candidate) in + candidate.id == announcement.id } + guard isEmpty = (books).isEmpty(); // new changes 2/13 // ** announcement json string ** guard let data = ("\(announcement.id) || \(announcement.subject) || \(announcement.start) || \(announcement.end) || From 7807031c2622e4f7a18be51ec627e7288defb970 Mon Sep 17 00:00:00 2001 From: Veevek Dave Date: Tue, 27 Feb 2024 17:21:43 -0500 Subject: [PATCH 4/8] Fixed query template + made gaurd template --- Sources/Server/Controllers/AnnouncementController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Server/Controllers/AnnouncementController.swift b/Sources/Server/Controllers/AnnouncementController.swift index 000e719..e02bf3f 100644 --- a/Sources/Server/Controllers/AnnouncementController.swift +++ b/Sources/Server/Controllers/AnnouncementController.swift @@ -35,7 +35,7 @@ struct AnnouncementController: RouteCollection where DecoderType: C .first() guard let announcement else { throw Abort(.notFound) - } + } //testing return announcement } From 5f4739399cfcde558d92b7bbf05e979fde792d3e Mon Sep 17 00:00:00 2001 From: chi Date: Fri, 15 Mar 2024 16:30:02 -0400 Subject: [PATCH 5/8] issue 74 --- .../Controllers/AnnouncementsController.swift | 25 ++++--- Sources/Server/Jobs/AnnouncementJobs.swift | 66 +++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 Sources/Server/Jobs/AnnouncementJobs.swift diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index e727c82..49b1bae 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -28,6 +28,21 @@ struct AnnouncementsController: RouteCollection where DecoderType: private func create(_ request: Request) async throws -> Announcement { let announcement = try request.content.decode(Announcement.self, using: self.decoder) + // Check if the announcement start date is at least an hour in the future + let now = Date() + let timeUntilStart = announcement.start.timeIntervalSince(now) + if timeUntilStart > 3600 { // More than an hour ahead + // Schedule the notification job here + // You will need to create a custom job type for sending APNS notifications + let job = SendAnnouncementNotificationJob(announcementID: announcement.id) + // Calculate the delay for the job based on the announcement's start date + let delay = DispatchTimeInterval.seconds(Int(timeUntilStart)) + application.queues.dispatch(job, after: delay) + } else { + // If the announcement is for less than an hour in the future, send immediately or handle as needed + } + + // new changes 2/13 // ** announcement json string ** guard let data = ("\(announcement.id) || \(announcement.subject) || \(announcement.start) || \(announcement.end) || @@ -35,16 +50,6 @@ struct AnnouncementsController: RouteCollection where DecoderType: throw Abort(.internalServerError) } - /* - guard let data = (announcement.subject + announcement.body).data(using: .utf8) else { - throw Abort(.internalServerError) - } - */ - - - - - if try CryptographyUtilities.verify(signature: announcement.signature, of: data) { try await announcement.save(on: request.db(.psql)) diff --git a/Sources/Server/Jobs/AnnouncementJobs.swift b/Sources/Server/Jobs/AnnouncementJobs.swift new file mode 100644 index 0000000..0d01df3 --- /dev/null +++ b/Sources/Server/Jobs/AnnouncementJobs.swift @@ -0,0 +1,66 @@ +import Vapor +import Fluent +import FluentPostgresDriver // or whichever Fluent driver you're using +import APNS + +// Define a job for sending announcement notifications +struct SendAnnouncementNotificationJob: Job { + // Define properties needed for the job + let announcementID: UUID + + // Implement the job execution logic + func dequeue(_ context: QueueContext, _ task: Task) async throws { + // Fetch the announcement from the database using the provided announcementID + guard let announcement = try await Announcement.find(announcementID, on: context.application.db(.psql)).get() else { + // Handle the case where the announcement could not be found + context.logger.error("Announcement with ID \(announcementID) not found.") + return + } + + // Prepare the APNS notification payload and other settings based on the announcement details + let interruptionLevel: APNSAlertNotificationInterruptionLevel + switch announcement.interruptionLevel { + case .passive: + interruptionLevel = .passive + case .active: + interruptionLevel = .active + case .timeSensitive: + interruptionLevel = .timeSensitive + case .critical: + interruptionLevel = .critical + } + + let payload = ... // Construct your notification payload here + + // Fetch all APNS devices from the database + let devices = try await APNSDevice.query(on: context.application.db(.psql)).all() + + // Send the notification to each device + for device in devices { + let deviceToken = device.token + do { + try await context.application.apns.client.sendAlertNotification( + APNSAlertNotification( + alert: APNSAlertNotificationContent( + title: .raw("Announcement"), + subtitle: .raw(announcement.subject), + body: .raw(announcement.body), + launchImage: nil + ), + expiration: announcement.end, // Adjust the expiration based on the announcement's end date + priority: .immediately, + topic: Constants.apnsTopic, + payload: payload, + sound: .default, + mutableContent: 1, + interruptionLevel: interruptionLevel, + apnsID: announcement.id + ), + deviceToken: deviceToken + ) + } catch { + context.logger.error("Failed to send APNS notification: \(error)") + } + } + } +} From 8c693aec3d0c1d673994b263ca5f1af439212a5a Mon Sep 17 00:00:00 2001 From: chi Date: Fri, 15 Mar 2024 17:04:10 -0400 Subject: [PATCH 6/8] issue 74 --- Sources/Server/Controllers/AnnouncementsController.swift | 3 +-- Sources/Server/Jobs/AnnouncementJobs.swift | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index 49b1bae..999823c 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -33,13 +33,12 @@ struct AnnouncementsController: RouteCollection where DecoderType: let timeUntilStart = announcement.start.timeIntervalSince(now) if timeUntilStart > 3600 { // More than an hour ahead // Schedule the notification job here - // You will need to create a custom job type for sending APNS notifications let job = SendAnnouncementNotificationJob(announcementID: announcement.id) // Calculate the delay for the job based on the announcement's start date let delay = DispatchTimeInterval.seconds(Int(timeUntilStart)) application.queues.dispatch(job, after: delay) } else { - // If the announcement is for less than an hour in the future, send immediately or handle as needed + } diff --git a/Sources/Server/Jobs/AnnouncementJobs.swift b/Sources/Server/Jobs/AnnouncementJobs.swift index 0d01df3..32ad070 100644 --- a/Sources/Server/Jobs/AnnouncementJobs.swift +++ b/Sources/Server/Jobs/AnnouncementJobs.swift @@ -1,7 +1,7 @@ -import Vapor +import Vapor import Fluent import FluentPostgresDriver // or whichever Fluent driver you're using -import APNS +import APNS // Define a job for sending announcement notifications struct SendAnnouncementNotificationJob: Job { @@ -63,4 +63,4 @@ struct SendAnnouncementNotificationJob: Job { } } } -} +} \ No newline at end of file From 618089cbec2c287a74b1668e1bddcd21f8c4ccb2 Mon Sep 17 00:00:00 2001 From: chi Date: Fri, 29 Mar 2024 16:40:46 -0400 Subject: [PATCH 7/8] added cryptographic authenticaton to analytics entries --- .../AnalyticsEntryController.swift | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Sources/Server/Controllers/AnalyticsEntryController.swift b/Sources/Server/Controllers/AnalyticsEntryController.swift index 6e6925c..bab19a4 100644 --- a/Sources/Server/Controllers/AnalyticsEntryController.swift +++ b/Sources/Server/Controllers/AnalyticsEntryController.swift @@ -18,14 +18,34 @@ struct AnalyticsEntryController: RouteCollection { } private func read(_ request: Request) async throws -> AnalyticsEntry { - let entry = try await AnalyticsEntry.find( - request.parameters.get("id"), - on: request.db(.psql) - ) - guard let entry else { - throw Abort(.notFound) + + // decode retrieval request + + let retrievalRequest = try request.query.decode(AnalyticsEntry.RetrievalRequest.self) + + guard let idString = request.parameters.get("id"), let id = UUID(uuidString: idString) else { + throw Abort(.badRequest) + } + + // cryptogrpahic verification + guard let data = id.uuidString.data(using: .utf8) else { + throw Abort(.internalServerError) + } + + // crytographic signature + if try CryptographyUtilities.verify(signature: retrievalRequest.signature, of: data) { + let entry = try await AnalyticsEntry.find( + id, + on: request.db(.psql) + ) + guard let entry else { + throw Abort(.notFound) + } + return entry + } else { + throw Abort(.forbidden) } - return entry } } + From e3714ff0b7de771f65295c272ac8dc3dd51962a4 Mon Sep 17 00:00:00 2001 From: chi Date: Fri, 29 Mar 2024 16:44:02 -0400 Subject: [PATCH 8/8] Revert "added cryptographic authenticaton to analytics entries" This reverts commit 618089cbec2c287a74b1668e1bddcd21f8c4ccb2. --- .../AnalyticsEntryController.swift | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/Sources/Server/Controllers/AnalyticsEntryController.swift b/Sources/Server/Controllers/AnalyticsEntryController.swift index bab19a4..6e6925c 100644 --- a/Sources/Server/Controllers/AnalyticsEntryController.swift +++ b/Sources/Server/Controllers/AnalyticsEntryController.swift @@ -18,34 +18,14 @@ struct AnalyticsEntryController: RouteCollection { } private func read(_ request: Request) async throws -> AnalyticsEntry { - - // decode retrieval request - - let retrievalRequest = try request.query.decode(AnalyticsEntry.RetrievalRequest.self) - - guard let idString = request.parameters.get("id"), let id = UUID(uuidString: idString) else { - throw Abort(.badRequest) - } - - // cryptogrpahic verification - guard let data = id.uuidString.data(using: .utf8) else { - throw Abort(.internalServerError) - } - - // crytographic signature - if try CryptographyUtilities.verify(signature: retrievalRequest.signature, of: data) { - let entry = try await AnalyticsEntry.find( - id, - on: request.db(.psql) - ) - guard let entry else { - throw Abort(.notFound) - } - return entry - } else { - throw Abort(.forbidden) + let entry = try await AnalyticsEntry.find( + request.parameters.get("id"), + on: request.db(.psql) + ) + guard let entry else { + throw Abort(.notFound) } + return entry } } -