90dc-core 1.19.4 → 1.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/Errors/AppError.js +104 -0
- package/dist/lib/Errors/AppError.js.map +1 -0
- package/dist/lib/Errors/Errors.js +42 -0
- package/dist/lib/Errors/Errors.js.map +1 -0
- package/dist/lib/classes/Database.js +154 -0
- package/dist/lib/classes/Database.js.map +1 -0
- package/dist/lib/classes/Redis.js +63 -0
- package/dist/lib/classes/Redis.js.map +1 -0
- package/dist/lib/clients/EmailClient.js +188 -0
- package/dist/lib/clients/EmailClient.js.map +1 -0
- package/dist/lib/clients/FirebasePushNotificationClient.js +688 -0
- package/dist/lib/clients/FirebasePushNotificationClient.js.map +1 -0
- package/dist/lib/clients/ImagesClient.d.ts +2 -1
- package/dist/lib/clients/ImagesClient.d.ts.map +1 -1
- package/dist/lib/clients/ImagesClient.js +436 -0
- package/dist/lib/clients/ImagesClient.js.map +1 -0
- package/dist/lib/clients/MailingClient.js +84 -0
- package/dist/lib/clients/MailingClient.js.map +1 -0
- package/dist/lib/clients/PushNotificationClient.js +478 -0
- package/dist/lib/clients/PushNotificationClient.js.map +1 -0
- package/dist/lib/clients/types/email.types.js +3 -0
- package/dist/lib/clients/types/email.types.js.map +1 -0
- package/dist/lib/clients/types/images.types.js +3 -0
- package/dist/lib/clients/types/images.types.js.map +1 -0
- package/dist/lib/clients/types/mailing.types.js +3 -0
- package/dist/lib/clients/types/mailing.types.js.map +1 -0
- package/dist/lib/config/ConfigValidator.js +162 -0
- package/dist/lib/config/ConfigValidator.js.map +1 -0
- package/dist/lib/controllers/BaseController.js +64 -0
- package/dist/lib/controllers/BaseController.js.map +1 -0
- package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.js +60 -0
- package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.js.map +1 -0
- package/dist/lib/dbmodels/coaching/Answer.js +51 -0
- package/dist/lib/dbmodels/coaching/Answer.js.map +1 -0
- package/dist/lib/dbmodels/coaching/ClientNote.js +75 -0
- package/dist/lib/dbmodels/coaching/ClientNote.js.map +1 -0
- package/dist/lib/dbmodels/coaching/ClientTag.js +45 -0
- package/dist/lib/dbmodels/coaching/ClientTag.js.map +1 -0
- package/dist/lib/dbmodels/coaching/Question.js +60 -0
- package/dist/lib/dbmodels/coaching/Question.js.map +1 -0
- package/dist/lib/dbmodels/coaching/Questionnaire.js +39 -0
- package/dist/lib/dbmodels/coaching/Questionnaire.js.map +1 -0
- package/dist/lib/dbmodels/coaching/QuestionnaireResponse.js +42 -0
- package/dist/lib/dbmodels/coaching/QuestionnaireResponse.js.map +1 -0
- package/dist/lib/dbmodels/coaching/WeeklyCheckIn.js +69 -0
- package/dist/lib/dbmodels/coaching/WeeklyCheckIn.js.map +1 -0
- package/dist/lib/dbmodels/coaching/WeightRecord.js +49 -0
- package/dist/lib/dbmodels/coaching/WeightRecord.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietDay.js +50 -0
- package/dist/lib/dbmodels/diet/DietDay.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietMeal.js +74 -0
- package/dist/lib/dbmodels/diet/DietMeal.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietMealCompletion.js +69 -0
- package/dist/lib/dbmodels/diet/DietMealCompletion.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietMealRecipe.js +67 -0
- package/dist/lib/dbmodels/diet/DietMealRecipe.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietMealRecipeIngredient.js +46 -0
- package/dist/lib/dbmodels/diet/DietMealRecipeIngredient.js.map +1 -0
- package/dist/lib/dbmodels/diet/DietProgram.js +98 -0
- package/dist/lib/dbmodels/diet/DietProgram.js.map +1 -0
- package/dist/lib/dbmodels/diet/Ingredient.js +88 -0
- package/dist/lib/dbmodels/diet/Ingredient.js.map +1 -0
- package/dist/lib/dbmodels/diet/IngredientTag.js +64 -0
- package/dist/lib/dbmodels/diet/IngredientTag.js.map +1 -0
- package/dist/lib/dbmodels/diet/IngredientTags.js +29 -0
- package/dist/lib/dbmodels/diet/IngredientTags.js.map +1 -0
- package/dist/lib/dbmodels/diet/Recipe.js +173 -0
- package/dist/lib/dbmodels/diet/Recipe.js.map +1 -0
- package/dist/lib/dbmodels/diet/RecipeIngredient.js +78 -0
- package/dist/lib/dbmodels/diet/RecipeIngredient.js.map +1 -0
- package/dist/lib/dbmodels/diet/RecipeTag.js +44 -0
- package/dist/lib/dbmodels/diet/RecipeTag.js.map +1 -0
- package/dist/lib/dbmodels/diet/RecipeTags.js +24 -0
- package/dist/lib/dbmodels/diet/RecipeTags.js.map +1 -0
- package/dist/lib/dbmodels/diet/ShoppingList.js +69 -0
- package/dist/lib/dbmodels/diet/ShoppingList.js.map +1 -0
- package/dist/lib/dbmodels/diet/ShoppingListItem.js +66 -0
- package/dist/lib/dbmodels/diet/ShoppingListItem.js.map +1 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipe.js +122 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipe.js.map +1 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipeTag.js +47 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipeTag.js.map +1 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipeTags.js +24 -0
- package/dist/lib/dbmodels/diet/TranslatedRecipeTags.js.map +1 -0
- package/dist/lib/dbmodels/diet/UserDietPreferences.js +85 -0
- package/dist/lib/dbmodels/diet/UserDietPreferences.js.map +1 -0
- package/dist/lib/dbmodels/gamification/Badge.js +62 -0
- package/dist/lib/dbmodels/gamification/Badge.js.map +1 -0
- package/dist/lib/dbmodels/gamification/StreaksLog.js +37 -0
- package/dist/lib/dbmodels/gamification/StreaksLog.js.map +1 -0
- package/dist/lib/dbmodels/gamification/TranslatedBadge.js +45 -0
- package/dist/lib/dbmodels/gamification/TranslatedBadge.js.map +1 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/UserRankHistory.js +48 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/UserRankHistory.js.map +1 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/XpEvent.js +53 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/XpEvent.js.map +1 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/XpTransaction.js +68 -0
- package/dist/lib/dbmodels/gamification/xpAndLeaderboards/XpTransaction.js.map +1 -0
- package/dist/lib/dbmodels/index.js +288 -0
- package/dist/lib/dbmodels/index.js.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgram.d.ts +32 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgram.d.ts.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgram.js +155 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgram.js.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgramWebContent.d.ts +22 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgramWebContent.d.ts.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgramWebContent.js +108 -0
- package/dist/lib/dbmodels/nonconsprogram/NonConsumableProgramWebContent.js.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedConsumableProgram.d.ts +20 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedConsumableProgram.d.ts.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedConsumableProgram.js +97 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedConsumableProgram.js.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedNonConsumableProgramWebContent.d.ts +22 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedNonConsumableProgramWebContent.d.ts.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedNonConsumableProgramWebContent.js +115 -0
- package/dist/lib/dbmodels/nonconsprogram/TranslatedNonConsumableProgramWebContent.js.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/UserNonConsumableProgram.d.ts +7 -0
- package/dist/lib/dbmodels/nonconsprogram/UserNonConsumableProgram.d.ts.map +1 -0
- package/dist/lib/dbmodels/nonconsprogram/UserNonConsumableProgram.js +41 -0
- package/dist/lib/dbmodels/nonconsprogram/UserNonConsumableProgram.js.map +1 -0
- package/dist/lib/dbmodels/notifications/NotificationModels.js +38 -0
- package/dist/lib/dbmodels/notifications/NotificationModels.js.map +1 -0
- package/dist/lib/dbmodels/notifications/TranslatedNotification.js +45 -0
- package/dist/lib/dbmodels/notifications/TranslatedNotification.js.map +1 -0
- package/dist/lib/dbmodels/program/Challenge.d.ts +13 -0
- package/dist/lib/dbmodels/program/Challenge.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/Challenge.js +63 -0
- package/dist/lib/dbmodels/program/Challenge.js.map +1 -0
- package/dist/lib/dbmodels/program/ChallengeBlueprint.js +75 -0
- package/dist/lib/dbmodels/program/ChallengeBlueprint.js.map +1 -0
- package/dist/lib/dbmodels/program/CircularProgramDraft.js +82 -0
- package/dist/lib/dbmodels/program/CircularProgramDraft.js.map +1 -0
- package/dist/lib/dbmodels/program/CoachExerciseNote.js +61 -0
- package/dist/lib/dbmodels/program/CoachExerciseNote.js.map +1 -0
- package/dist/lib/dbmodels/program/CustomProgramBlueprint.js +84 -0
- package/dist/lib/dbmodels/program/CustomProgramBlueprint.js.map +1 -0
- package/dist/lib/dbmodels/program/CustomStrengthTest.js +55 -0
- package/dist/lib/dbmodels/program/CustomStrengthTest.js.map +1 -0
- package/dist/lib/dbmodels/program/CustomStrengthTestExercises.js +69 -0
- package/dist/lib/dbmodels/program/CustomStrengthTestExercises.js.map +1 -0
- package/dist/lib/dbmodels/program/CustomWorkoutBlueprint.js +58 -0
- package/dist/lib/dbmodels/program/CustomWorkoutBlueprint.js.map +1 -0
- package/dist/lib/dbmodels/program/Exercise.d.ts +23 -0
- package/dist/lib/dbmodels/program/Exercise.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/Exercise.js +101 -0
- package/dist/lib/dbmodels/program/Exercise.js.map +1 -0
- package/dist/lib/dbmodels/program/ExerciseModels.d.ts +28 -0
- package/dist/lib/dbmodels/program/ExerciseModels.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/ExerciseModels.js +149 -0
- package/dist/lib/dbmodels/program/ExerciseModels.js.map +1 -0
- package/dist/lib/dbmodels/program/Program.d.ts +15 -0
- package/dist/lib/dbmodels/program/Program.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/Program.js +76 -0
- package/dist/lib/dbmodels/program/Program.js.map +1 -0
- package/dist/lib/dbmodels/program/ProgressEntry.d.ts +26 -0
- package/dist/lib/dbmodels/program/ProgressEntry.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/ProgressEntry.js +77 -0
- package/dist/lib/dbmodels/program/ProgressEntry.js.map +1 -0
- package/dist/lib/dbmodels/program/RestDay.js +33 -0
- package/dist/lib/dbmodels/program/RestDay.js.map +1 -0
- package/dist/lib/dbmodels/program/StrengthTest.d.ts +10 -0
- package/dist/lib/dbmodels/program/StrengthTest.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/StrengthTest.js +39 -0
- package/dist/lib/dbmodels/program/StrengthTest.js.map +1 -0
- package/dist/lib/dbmodels/program/StrengthTestExercise.d.ts +15 -0
- package/dist/lib/dbmodels/program/StrengthTestExercise.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/StrengthTestExercise.js +72 -0
- package/dist/lib/dbmodels/program/StrengthTestExercise.js.map +1 -0
- package/dist/lib/dbmodels/program/StrengthTestSession.js +73 -0
- package/dist/lib/dbmodels/program/StrengthTestSession.js.map +1 -0
- package/dist/lib/dbmodels/program/Superset.d.ts +13 -0
- package/dist/lib/dbmodels/program/Superset.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/Superset.js +46 -0
- package/dist/lib/dbmodels/program/Superset.js.map +1 -0
- package/dist/lib/dbmodels/program/ThirtyDayChallenge.js +70 -0
- package/dist/lib/dbmodels/program/ThirtyDayChallenge.js.map +1 -0
- package/dist/lib/dbmodels/program/ThirtyDayChallengeStrengthTest.js +55 -0
- package/dist/lib/dbmodels/program/ThirtyDayChallengeStrengthTest.js.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedChallenge.d.ts +15 -0
- package/dist/lib/dbmodels/program/TranslatedChallenge.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedChallenge.js +69 -0
- package/dist/lib/dbmodels/program/TranslatedChallenge.js.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedExerciseModel.d.ts +25 -0
- package/dist/lib/dbmodels/program/TranslatedExerciseModel.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedExerciseModel.js +123 -0
- package/dist/lib/dbmodels/program/TranslatedExerciseModel.js.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTest.d.ts +12 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTest.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTest.js +45 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTest.js.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTestExercise.d.ts +16 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTestExercise.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTestExercise.js +78 -0
- package/dist/lib/dbmodels/program/TranslatedStrengthTestExercise.js.map +1 -0
- package/dist/lib/dbmodels/program/UserChallenge.d.ts +10 -0
- package/dist/lib/dbmodels/program/UserChallenge.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/UserChallenge.js +47 -0
- package/dist/lib/dbmodels/program/UserChallenge.js.map +1 -0
- package/dist/lib/dbmodels/program/UserExerciseNote.js +61 -0
- package/dist/lib/dbmodels/program/UserExerciseNote.js.map +1 -0
- package/dist/lib/dbmodels/program/UserStrengthTests.d.ts +11 -0
- package/dist/lib/dbmodels/program/UserStrengthTests.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/UserStrengthTests.js +54 -0
- package/dist/lib/dbmodels/program/UserStrengthTests.js.map +1 -0
- package/dist/lib/dbmodels/program/Workout.d.ts +14 -0
- package/dist/lib/dbmodels/program/Workout.d.ts.map +1 -0
- package/dist/lib/dbmodels/program/Workout.js +66 -0
- package/dist/lib/dbmodels/program/Workout.js.map +1 -0
- package/dist/lib/dbmodels/program/WorkoutCompletion.js +65 -0
- package/dist/lib/dbmodels/program/WorkoutCompletion.js.map +1 -0
- package/dist/lib/dbmodels/program/WorkoutSession.js +93 -0
- package/dist/lib/dbmodels/program/WorkoutSession.js.map +1 -0
- package/dist/lib/dbmodels/subscription/Subscription.d.ts +19 -0
- package/dist/lib/dbmodels/subscription/Subscription.d.ts.map +1 -0
- package/dist/lib/dbmodels/subscription/Subscription.js +91 -0
- package/dist/lib/dbmodels/subscription/Subscription.js.map +1 -0
- package/dist/lib/dbmodels/subscription/SubscriptionEvent.js +79 -0
- package/dist/lib/dbmodels/subscription/SubscriptionEvent.js.map +1 -0
- package/dist/lib/dbmodels/subscription/SubscriptionLog.d.ts +9 -0
- package/dist/lib/dbmodels/subscription/SubscriptionLog.d.ts.map +1 -0
- package/dist/lib/dbmodels/subscription/SubscriptionLog.js +42 -0
- package/dist/lib/dbmodels/subscription/SubscriptionLog.js.map +1 -0
- package/dist/lib/dbmodels/subscription/SubscriptionRefund.js +92 -0
- package/dist/lib/dbmodels/subscription/SubscriptionRefund.js.map +1 -0
- package/dist/lib/dbmodels/user/DeviceTokens.js +34 -0
- package/dist/lib/dbmodels/user/DeviceTokens.js.map +1 -0
- package/dist/lib/dbmodels/user/PersistedUser.d.ts +43 -0
- package/dist/lib/dbmodels/user/PersistedUser.d.ts.map +1 -0
- package/dist/lib/dbmodels/user/PersistedUser.js +237 -0
- package/dist/lib/dbmodels/user/PersistedUser.js.map +1 -0
- package/dist/lib/dbmodels/user/UserAddons.js +26 -0
- package/dist/lib/dbmodels/user/UserAddons.js.map +1 -0
- package/dist/lib/dbmodels/user/UserBadges.js +63 -0
- package/dist/lib/dbmodels/user/UserBadges.js.map +1 -0
- package/dist/lib/dbmodels/user/UserCoach.js +40 -0
- package/dist/lib/dbmodels/user/UserCoach.js.map +1 -0
- package/dist/lib/dbmodels/user/UserDiscount.js +24 -0
- package/dist/lib/dbmodels/user/UserDiscount.js.map +1 -0
- package/dist/lib/dbmodels/user/UserOptions.js +34 -0
- package/dist/lib/dbmodels/user/UserOptions.js.map +1 -0
- package/dist/lib/dbmodels/user/UserProgressPhoto.js +59 -0
- package/dist/lib/dbmodels/user/UserProgressPhoto.js.map +1 -0
- package/dist/lib/dbmodels/user/UserStreaks.js +64 -0
- package/dist/lib/dbmodels/user/UserStreaks.js.map +1 -0
- package/dist/lib/dbmodels/user/UsersFriends.js +46 -0
- package/dist/lib/dbmodels/user/UsersFriends.js.map +1 -0
- package/dist/lib/enums/ProgramEnums.d.ts +18 -0
- package/dist/lib/enums/ProgramEnums.d.ts.map +1 -0
- package/dist/lib/enums/ProgramEnums.js +22 -0
- package/dist/lib/enums/ProgramEnums.js.map +1 -0
- package/dist/lib/enums/ProgramTemplates.js +738 -0
- package/dist/lib/enums/ProgramTemplates.js.map +1 -0
- package/dist/lib/middlewares/ErrorMiddleware.js +141 -0
- package/dist/lib/middlewares/ErrorMiddleware.js.map +1 -0
- package/dist/lib/middlewares/ValidationMiddleware.js +24 -0
- package/dist/lib/middlewares/ValidationMiddleware.js.map +1 -0
- package/dist/lib/models/BlueprintInterfaces.js +3 -0
- package/dist/lib/models/BlueprintInterfaces.js.map +1 -0
- package/dist/lib/models/ExerciseInterfaces.d.ts +68 -0
- package/dist/lib/models/ExerciseInterfaces.d.ts.map +1 -0
- package/dist/lib/models/ExerciseInterfaces.js +3 -0
- package/dist/lib/models/ExerciseInterfaces.js.map +1 -0
- package/dist/lib/models/NotificationInterfaces.js +11 -0
- package/dist/lib/models/NotificationInterfaces.js.map +1 -0
- package/dist/lib/models/ProgramInterfaces.js +3 -0
- package/dist/lib/models/ProgramInterfaces.js.map +1 -0
- package/dist/lib/models/UserInterfaces.js +3 -0
- package/dist/lib/models/UserInterfaces.js.map +1 -0
- package/dist/lib/models/WorkoutInterfaces.js +3 -0
- package/dist/lib/models/WorkoutInterfaces.js.map +1 -0
- package/dist/lib/scripts/cli.js +82 -0
- package/dist/lib/scripts/cli.js.map +1 -0
- package/dist/lib/scripts/populate-exercise-thumbnails.js +64 -0
- package/dist/lib/scripts/populate-exercise-thumbnails.js.map +1 -0
- package/dist/lib/scripts/setup-database.js +43 -0
- package/dist/lib/scripts/setup-database.js.map +1 -0
- package/dist/lib/scripts/verify-indexes.js +94 -0
- package/dist/lib/scripts/verify-indexes.js.map +1 -0
- package/dist/lib/testing/testFixtures.js +520 -0
- package/dist/lib/testing/testFixtures.js.map +1 -0
- package/dist/lib/testing/testHelpers.js +149 -0
- package/dist/lib/testing/testHelpers.js.map +1 -0
- package/dist/lib/utils/AuthenticationUtil.js +481 -0
- package/dist/lib/utils/AuthenticationUtil.js.map +1 -0
- package/dist/lib/utils/Logger.js +242 -0
- package/dist/lib/utils/Logger.js.map +1 -0
- package/dist/lib/utils/NotificationClient.js +371 -0
- package/dist/lib/utils/NotificationClient.js.map +1 -0
- package/dist/lib/utils/NotificationsUtil.js +95 -0
- package/dist/lib/utils/NotificationsUtil.js.map +1 -0
- package/dist/lib/utils/SecretManager.js +107 -0
- package/dist/lib/utils/SecretManager.js.map +1 -0
- package/dist/lib/utils/SentryUtil.js +140 -0
- package/dist/lib/utils/SentryUtil.js.map +1 -0
- package/dist/lib/utils/imageValidation.js +31 -0
- package/dist/lib/utils/imageValidation.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/NotificationClient.ts"],"sourcesContent":["import axios from \"axios\";\nimport type { AxiosInstance, AxiosError } from \"axios\";\nimport type {\n NotificationPayload,\n EmailReminderPayload,\n GroupNotificationRequest,\n NotificationRequest,\n CreateNotificationRequest,\n NotificationTranslationRequest,\n} from '../models/NotificationInterfaces.js';\n\nexport interface NotificationClientConfig {\n baseURL: string;\n timeout?: number;\n headers?: Record<string, string>;\n}\n\n/**\n * Abandoned cart notification payload\n */\nexport interface AbandonedCartPayload {\n email: string;\n}\n\n/**\n * Password restoration types\n */\nexport type PasswordRestorationType = \"change\" | \"reset\" | \"platform-reset\";\n\n/**\n * Password restoration request\n */\nexport interface PasswordRestorationRequest {\n email: string;\n type: PasswordRestorationType;\n}\n\n/**\n * Email confirmation request\n */\nexport interface EmailConfirmationRequest {\n type: \"main\" | \"utility\";\n}\n\n/**\n * Shopify email request\n */\nexport interface ShopifyEmailRequest {\n email: string;\n name: string;\n orderNumber: string;\n items: Array<{\n name: string;\n quantity: number;\n price: number;\n }>;\n}\n\n/**\n * Challenge friend email request\n */\nexport interface ChallengeFriendRequest {\n friendsEmail: string;\n name: string;\n}\n\n/**\n * NotificationClient\n *\n * Provides methods to interact with the notification service via HTTP API.\n * This allows services to send notifications without direct database access\n * or code duplication.\n */\nexport class NotificationClient {\n private client: AxiosInstance;\n\n /**\n * Create a new notification client\n *\n * @param config - Client configuration with base URL and optional settings\n *\n * @example\n * ```typescript\n * const notificationClient = new NotificationClient({\n * baseURL: 'http://localhost:3037',\n * timeout: 5000\n * });\n * ```\n */\n constructor(config: NotificationClientConfig) {\n this.client = axios.create({\n baseURL: config.baseURL,\n timeout: config.timeout || 10000,\n headers: {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n },\n });\n }\n\n // ==================== Push Notification Methods ====================\n\n /**\n * Schedule a push notification for a specific user\n *\n * @param payload - Notification details including user UUID and notification type\n *\n * @example\n * ```typescript\n * await client.scheduleNotification({\n * userUuid: '123e4567-e89b-12d3-a456-426614174000',\n * name: 'workout_reminder'\n * });\n * ```\n */\n async scheduleNotification(payload: NotificationPayload): Promise<void> {\n try {\n await this.client.post(\"/notifications/save-job\", payload);\n } catch (error) {\n this.handleError(\"Failed to schedule notification\", error);\n }\n }\n\n async sendToUser(\n userUuid: string,\n notification: NotificationRequest\n ): Promise<string> {\n try {\n const response = await this.client.post(\"/notifications/send-to-user\", {\n userUuid,\n notification,\n });\n return response.data.messageId;\n } catch (error) {\n this.handleError(\"Failed to send notification to user\", error);\n throw error;\n }\n }\n\n /**\n * Send notification to multiple users in batch\n *\n * @param userUuids - Array of user UUIDs\n * @param notification - Notification content\n *\n * @example\n * ```typescript\n * await client.sendToUsers(['uuid1', 'uuid2'], {\n * title: 'New Feature',\n * body: 'Check out what's new!'\n * });\n * ```\n */\n async sendToUsers(\n userUuids: string[],\n notification: NotificationRequest\n ): Promise<void> {\n try {\n await this.client.post(\"/notifications/send-to-users\", {\n userUuids,\n notification,\n });\n } catch (error) {\n this.handleError(\"Failed to send batch notification\", error);\n }\n }\n\n /**\n * Send notification to all users\n *\n * @param notification - Notification content\n * @returns Total number of users notified\n *\n * @example\n * ```typescript\n * const totalSent = await client.sendToAllUsers({\n * title: 'System Update',\n * body: 'App will be updated soon'\n * });\n * ```\n */\n async sendToAllUsers(notification: NotificationRequest): Promise<number> {\n try {\n const response = await this.client.post(\"/notifications/send-to-all\", {\n notification,\n });\n return response.data.totalSent;\n } catch (error) {\n this.handleError(\"Failed to send to all users\", error);\n throw error;\n }\n }\n\n /**\n * Send a push notification to a specific group of users\n *\n * @param request - Group notification request with target group and message\n *\n * @example\n * ```typescript\n * await client.sendGroupNotification({\n * group: NotificationGroups.PREMIUM,\n * notification: {\n * title: 'New Feature!',\n * body: 'Check out our new workout plans'\n * }\n * });\n * ```\n */\n async sendGroupNotification(\n request: GroupNotificationRequest\n ): Promise<void> {\n try {\n await this.client.post(\"/notifications/send-group\", request);\n } catch (error) {\n this.handleError(\"Failed to send group notification\", error);\n }\n }\n\n // ==================== Email Methods ====================\n\n /**\n * Schedule an email checkout reminder\n *\n * @param payload - Email reminder details\n */\n async scheduleEmailReminder(payload: EmailReminderPayload): Promise<void> {\n try {\n await this.client.post(\n \"/notifications/save-email-checkout-reminder\",\n payload\n );\n } catch (error) {\n this.handleError(\"Failed to schedule email reminder\", error);\n }\n }\n\n /**\n * Send abandoned cart notification\n *\n * @param payload - Abandoned cart details with user email\n */\n async sendAbandonedCart(payload: AbandonedCartPayload): Promise<void> {\n try {\n await this.client.post(\"/notifications/abandoned-cart\", payload);\n } catch (error) {\n this.handleError(\"Failed to send abandoned cart notification\", error);\n }\n }\n\n /**\n * Send password restoration email\n *\n * @param request - Password restoration request\n */\n async sendPasswordRestoration(\n request: PasswordRestorationRequest\n ): Promise<void> {\n try {\n await this.client.post(\"/mail/send-password-restoration\", request);\n } catch (error) {\n this.handleError(\"Failed to send password restoration email\", error);\n }\n }\n\n /**\n * Send email confirmation\n *\n * @param request - Email confirmation request\n * @param authToken - User authentication token (required)\n */\n async sendEmailConfirmation(\n request: EmailConfirmationRequest,\n authToken: string\n ): Promise<void> {\n try {\n await this.client.post(\"/mail/send-confirmation\", request, {\n headers: {\n Authorization: `Bearer ${authToken}`,\n },\n });\n } catch (error) {\n this.handleError(\"Failed to send email confirmation\", error);\n }\n }\n\n /**\n * Send Shopify email\n *\n * @param request - Shopify email details\n */\n async sendShopifyEmail(request: ShopifyEmailRequest): Promise<void> {\n try {\n await this.client.post(\"/mail/send-shopify-email\", request);\n } catch (error) {\n this.handleError(\"Failed to send Shopify email\", error);\n }\n }\n\n /**\n * Send challenge friend invitation email\n *\n * @param request - Challenge friend request\n */\n async sendChallengeFriendEmail(\n request: ChallengeFriendRequest\n ): Promise<void> {\n try {\n await this.client.post(\"/mail/send-challenge-email\", request);\n } catch (error) {\n this.handleError(\"Failed to send challenge email\", error);\n }\n }\n\n /**\n * Send account deletion email\n *\n * @param authToken - User authentication token (required)\n */\n async sendDeleteAccountEmail(authToken: string): Promise<void> {\n try {\n await this.client.post(\n \"/mail/send-delete-email\",\n {},\n {\n headers: {\n Authorization: `Bearer ${authToken}`,\n },\n }\n );\n } catch (error) {\n this.handleError(\"Failed to send delete account email\", error);\n }\n }\n\n /**\n * Send export finished email\n *\n * @param authToken - User authentication token (required)\n */\n async sendExportFinishedEmail(authToken: string): Promise<void> {\n try {\n await this.client.post(\n \"/mail/send-export-finished-email\",\n {},\n {\n headers: {\n Authorization: `Bearer ${authToken}`,\n },\n }\n );\n } catch (error) {\n this.handleError(\"Failed to send export finished email\", error);\n }\n }\n\n /**\n * Send student questionnaire response to coach\n *\n * @param to - Coach email address\n * @param subject - Email subject line\n * @param html - HTML content of the email\n */\n async sendStudentResponseToCoach(params: {\n to: string;\n subject: string;\n html: string;\n }): Promise<void> {\n try {\n await this.client.post(\"/mail/send-student-response-to-coach\", params);\n } catch (error) {\n this.handleError(\"Failed to send student response to coach\", error);\n }\n }\n\n // ==================== Notification Management Methods ====================\n\n /**\n * Create a new notification template\n *\n * @param request - Notification template details\n * @returns The created notification UUID\n */\n async createNotification(\n request: CreateNotificationRequest\n ): Promise<string> {\n try {\n const response = await this.client.post(\n \"/notifications/create-notification\",\n request\n );\n return response.data.uuid;\n } catch (error) {\n this.handleError(\"Failed to create notification\", error);\n throw error;\n }\n }\n\n /**\n * Create a notification translation\n *\n * @param request - Translation details\n */\n async createNotificationTranslation(\n request: NotificationTranslationRequest\n ): Promise<void> {\n try {\n await this.client.post(\n \"/notifications/create-notification-translation\",\n request\n );\n } catch (error) {\n this.handleError(\"Failed to create notification translation\", error);\n }\n }\n\n /**\n * Update a notification template\n *\n * @param request - Updated notification details\n */\n async updateNotification(\n request: NotificationTranslationRequest\n ): Promise<void> {\n try {\n await this.client.put(\"/notifications/update-notification\", request);\n } catch (error) {\n this.handleError(\"Failed to update notification\", error);\n }\n }\n\n /**\n * Update a notification translation\n *\n * @param request - Updated translation details\n */\n async updateNotificationTranslation(\n request: NotificationTranslationRequest\n ): Promise<void> {\n try {\n await this.client.put(\n \"/notifications/update-notification-translation\",\n request\n );\n } catch (error) {\n this.handleError(\"Failed to update notification translation\", error);\n }\n }\n\n /**\n * Get all notifications, optionally filtered by language\n *\n * @param language - Optional language code to filter translations\n * @returns Array of notifications with translations\n */\n async getNotifications(language?: string): Promise<any[]> {\n try {\n const url = language\n ? `/notifications/get-notifications/${language}`\n : \"/notifications/get-notifications\";\n const response = await this.client.get(url);\n return response.data;\n } catch (error) {\n this.handleError(\"Failed to get notifications\", error);\n throw error;\n }\n }\n\n /**\n * Delete a notification or translation\n *\n * @param uuid - Notification UUID\n * @param language - Optional language code to delete only translation\n */\n async deleteNotification(uuid: string, language?: string): Promise<void> {\n try {\n const params = language ? { language } : {};\n await this.client.delete(`/notifications/delete-notifications/${uuid}`, {\n params,\n });\n } catch (error) {\n this.handleError(\"Failed to delete notification\", error);\n }\n }\n\n // ==================== Coaching Reminder Methods ====================\n\n async scheduleCheckinReminder(\n params: {\n userUuid: string;\n name: string;\n reminderType: string;\n delayMs: number;\n },\n authToken?: string\n ): Promise<void> {\n try {\n await this.client.post(\n \"/schedule\",\n params,\n authToken\n ? { headers: { Authorization: `Bearer ${authToken}` } }\n : {}\n );\n } catch (error) {\n this.handleError(\"Failed to schedule check-in reminder\", error);\n }\n }\n\n async cancelCheckinReminder(\n userUuid: string,\n authToken?: string\n ): Promise<void> {\n try {\n await this.client.delete(\n `/schedule/weekly-checkin/${userUuid}`,\n authToken\n ? { headers: { Authorization: `Bearer ${authToken}` } }\n : {}\n );\n } catch (error) {\n this.handleError(\"Failed to cancel check-in reminder\", error);\n }\n }\n\n // ==================== Error Handling ====================\n\n /**\n * Handle and format errors from notification service\n */\n private handleError(message: string, error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n const status = axiosError.response?.status;\n const data = axiosError.response?.data;\n\n console.error(`[NotificationClient] ${message}:`, {\n status,\n message: axiosError.message,\n data,\n });\n\n throw new Error(\n `${message}: ${status ? `HTTP ${status}` : axiosError.message}`\n );\n }\n\n console.error(`[NotificationClient] ${message}:`, error);\n throw new Error(`${message}: ${error}`);\n }\n}\n"],"names":["axios","NotificationClient","client","config","create","baseURL","timeout","headers","scheduleNotification","payload","post","error","handleError","sendToUser","userUuid","notification","response","data","messageId","sendToUsers","userUuids","sendToAllUsers","totalSent","sendGroupNotification","request","scheduleEmailReminder","sendAbandonedCart","sendPasswordRestoration","sendEmailConfirmation","authToken","Authorization","sendShopifyEmail","sendChallengeFriendEmail","sendDeleteAccountEmail","sendExportFinishedEmail","sendStudentResponseToCoach","params","createNotification","uuid","createNotificationTranslation","updateNotification","put","updateNotificationTranslation","getNotifications","language","url","get","deleteNotification","delete","scheduleCheckinReminder","cancelCheckinReminder","message","isAxiosError","axiosError","status","console","Error"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAkE1B;;;;;;CAMC,GACD,OAAO,MAAMC;IACHC,OAAsB;IAE9B;;;;;;;;;;;;GAYC,GACD,YAAYC,MAAgC,CAAE;QAC5C,IAAI,CAACD,MAAM,GAAGF,MAAMI,MAAM,CAAC;YACzBC,SAASF,OAAOE,OAAO;YACvBC,SAASH,OAAOG,OAAO,IAAI;YAC3BC,SAAS;gBACP,gBAAgB;gBAChB,GAAGJ,OAAOI,OAAO;YACnB;QACF;IACF;IAEA,sEAAsE;IAEtE;;;;;;;;;;;;GAYC,GACD,MAAMC,qBAAqBC,OAA4B,EAAiB;QACtE,IAAI;YACF,MAAM,IAAI,CAACP,MAAM,CAACQ,IAAI,CAAC,2BAA2BD;QACpD,EAAE,OAAOE,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,mCAAmCD;QACtD;IACF;IAEA,MAAME,WACJC,QAAgB,EAChBC,YAAiC,EAChB;QACjB,IAAI;YACF,MAAMC,WAAW,MAAM,IAAI,CAACd,MAAM,CAACQ,IAAI,CAAC,+BAA+B;gBACrEI;gBACAC;YACF;YACA,OAAOC,SAASC,IAAI,CAACC,SAAS;QAChC,EAAE,OAAOP,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,uCAAuCD;YACxD,MAAMA;QACR;IACF;IAEA;;;;;;;;;;;;;GAaC,GACD,MAAMQ,YACJC,SAAmB,EACnBL,YAAiC,EAClB;QACf,IAAI;YACF,MAAM,IAAI,CAACb,MAAM,CAACQ,IAAI,CAAC,gCAAgC;gBACrDU;gBACAL;YACF;QACF,EAAE,OAAOJ,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,qCAAqCD;QACxD;IACF;IAEA;;;;;;;;;;;;;GAaC,GACD,MAAMU,eAAeN,YAAiC,EAAmB;QACvE,IAAI;YACF,MAAMC,WAAW,MAAM,IAAI,CAACd,MAAM,CAACQ,IAAI,CAAC,8BAA8B;gBACpEK;YACF;YACA,OAAOC,SAASC,IAAI,CAACK,SAAS;QAChC,EAAE,OAAOX,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,+BAA+BD;YAChD,MAAMA;QACR;IACF;IAEA;;;;;;;;;;;;;;;GAeC,GACD,MAAMY,sBACJC,OAAiC,EAClB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACQ,IAAI,CAAC,6BAA6Bc;QACtD,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,qCAAqCD;QACxD;IACF;IAEA,0DAA0D;IAE1D;;;;GAIC,GACD,MAAMc,sBAAsBhB,OAA6B,EAAiB;QACxE,IAAI;YACF,MAAM,IAAI,CAACP,MAAM,CAACQ,IAAI,CACpB,+CACAD;QAEJ,EAAE,OAAOE,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,qCAAqCD;QACxD;IACF;IAEA;;;;GAIC,GACD,MAAMe,kBAAkBjB,OAA6B,EAAiB;QACpE,IAAI;YACF,MAAM,IAAI,CAACP,MAAM,CAACQ,IAAI,CAAC,iCAAiCD;QAC1D,EAAE,OAAOE,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,8CAA8CD;QACjE;IACF;IAEA;;;;GAIC,GACD,MAAMgB,wBACJH,OAAmC,EACpB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACQ,IAAI,CAAC,mCAAmCc;QAC5D,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,6CAA6CD;QAChE;IACF;IAEA;;;;;GAKC,GACD,MAAMiB,sBACJJ,OAAiC,EACjCK,SAAiB,EACF;QACf,IAAI;YACF,MAAM,IAAI,CAAC3B,MAAM,CAACQ,IAAI,CAAC,2BAA2Bc,SAAS;gBACzDjB,SAAS;oBACPuB,eAAe,CAAC,OAAO,EAAED,WAAW;gBACtC;YACF;QACF,EAAE,OAAOlB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,qCAAqCD;QACxD;IACF;IAEA;;;;GAIC,GACD,MAAMoB,iBAAiBP,OAA4B,EAAiB;QAClE,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACQ,IAAI,CAAC,4BAA4Bc;QACrD,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,gCAAgCD;QACnD;IACF;IAEA;;;;GAIC,GACD,MAAMqB,yBACJR,OAA+B,EAChB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACQ,IAAI,CAAC,8BAA8Bc;QACvD,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,kCAAkCD;QACrD;IACF;IAEA;;;;GAIC,GACD,MAAMsB,uBAAuBJ,SAAiB,EAAiB;QAC7D,IAAI;YACF,MAAM,IAAI,CAAC3B,MAAM,CAACQ,IAAI,CACpB,2BACA,CAAC,GACD;gBACEH,SAAS;oBACPuB,eAAe,CAAC,OAAO,EAAED,WAAW;gBACtC;YACF;QAEJ,EAAE,OAAOlB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,uCAAuCD;QAC1D;IACF;IAEA;;;;GAIC,GACD,MAAMuB,wBAAwBL,SAAiB,EAAiB;QAC9D,IAAI;YACF,MAAM,IAAI,CAAC3B,MAAM,CAACQ,IAAI,CACpB,oCACA,CAAC,GACD;gBACEH,SAAS;oBACPuB,eAAe,CAAC,OAAO,EAAED,WAAW;gBACtC;YACF;QAEJ,EAAE,OAAOlB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,wCAAwCD;QAC3D;IACF;IAEA;;;;;;GAMC,GACD,MAAMwB,2BAA2BC,MAIhC,EAAiB;QAChB,IAAI;YACF,MAAM,IAAI,CAAClC,MAAM,CAACQ,IAAI,CAAC,wCAAwC0B;QACjE,EAAE,OAAOzB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,4CAA4CD;QAC/D;IACF;IAEA,4EAA4E;IAE5E;;;;;GAKC,GACD,MAAM0B,mBACJb,OAAkC,EACjB;QACjB,IAAI;YACF,MAAMR,WAAW,MAAM,IAAI,CAACd,MAAM,CAACQ,IAAI,CACrC,sCACAc;YAEF,OAAOR,SAASC,IAAI,CAACqB,IAAI;QAC3B,EAAE,OAAO3B,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,iCAAiCD;YAClD,MAAMA;QACR;IACF;IAEA;;;;GAIC,GACD,MAAM4B,8BACJf,OAAuC,EACxB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACQ,IAAI,CACpB,kDACAc;QAEJ,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,6CAA6CD;QAChE;IACF;IAEA;;;;GAIC,GACD,MAAM6B,mBACJhB,OAAuC,EACxB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACuC,GAAG,CAAC,sCAAsCjB;QAC9D,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,iCAAiCD;QACpD;IACF;IAEA;;;;GAIC,GACD,MAAM+B,8BACJlB,OAAuC,EACxB;QACf,IAAI;YACF,MAAM,IAAI,CAACtB,MAAM,CAACuC,GAAG,CACnB,kDACAjB;QAEJ,EAAE,OAAOb,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,6CAA6CD;QAChE;IACF;IAEA;;;;;GAKC,GACD,MAAMgC,iBAAiBC,QAAiB,EAAkB;QACxD,IAAI;YACF,MAAMC,MAAMD,WACR,CAAC,iCAAiC,EAAEA,UAAU,GAC9C;YACJ,MAAM5B,WAAW,MAAM,IAAI,CAACd,MAAM,CAAC4C,GAAG,CAACD;YACvC,OAAO7B,SAASC,IAAI;QACtB,EAAE,OAAON,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,+BAA+BD;YAChD,MAAMA;QACR;IACF;IAEA;;;;;GAKC,GACD,MAAMoC,mBAAmBT,IAAY,EAAEM,QAAiB,EAAiB;QACvE,IAAI;YACF,MAAMR,SAASQ,WAAW;gBAAEA;YAAS,IAAI,CAAC;YAC1C,MAAM,IAAI,CAAC1C,MAAM,CAAC8C,MAAM,CAAC,CAAC,oCAAoC,EAAEV,MAAM,EAAE;gBACtEF;YACF;QACF,EAAE,OAAOzB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,iCAAiCD;QACpD;IACF;IAEA,sEAAsE;IAEtE,MAAMsC,wBACJb,MAKC,EACDP,SAAkB,EACH;QACf,IAAI;YACF,MAAM,IAAI,CAAC3B,MAAM,CAACQ,IAAI,CACpB,aACA0B,QACAP,YACI;gBAAEtB,SAAS;oBAAEuB,eAAe,CAAC,OAAO,EAAED,WAAW;gBAAC;YAAE,IACpD,CAAC;QAET,EAAE,OAAOlB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,wCAAwCD;QAC3D;IACF;IAEA,MAAMuC,sBACJpC,QAAgB,EAChBe,SAAkB,EACH;QACf,IAAI;YACF,MAAM,IAAI,CAAC3B,MAAM,CAAC8C,MAAM,CACtB,CAAC,yBAAyB,EAAElC,UAAU,EACtCe,YACI;gBAAEtB,SAAS;oBAAEuB,eAAe,CAAC,OAAO,EAAED,WAAW;gBAAC;YAAE,IACpD,CAAC;QAET,EAAE,OAAOlB,OAAO;YACd,IAAI,CAACC,WAAW,CAAC,sCAAsCD;QACzD;IACF;IAEA,2DAA2D;IAE3D;;GAEC,GACD,AAAQC,YAAYuC,OAAe,EAAExC,KAAc,EAAQ;QACzD,IAAIX,MAAMoD,YAAY,CAACzC,QAAQ;YAC7B,MAAM0C,aAAa1C;YACnB,MAAM2C,SAASD,WAAWrC,QAAQ,EAAEsC;YACpC,MAAMrC,OAAOoC,WAAWrC,QAAQ,EAAEC;YAElCsC,QAAQ5C,KAAK,CAAC,CAAC,qBAAqB,EAAEwC,QAAQ,CAAC,CAAC,EAAE;gBAChDG;gBACAH,SAASE,WAAWF,OAAO;gBAC3BlC;YACF;YAEA,MAAM,IAAIuC,MACR,GAAGL,QAAQ,EAAE,EAAEG,SAAS,CAAC,KAAK,EAAEA,QAAQ,GAAGD,WAAWF,OAAO,EAAE;QAEnE;QAEAI,QAAQ5C,KAAK,CAAC,CAAC,qBAAqB,EAAEwC,QAAQ,CAAC,CAAC,EAAExC;QAClD,MAAM,IAAI6C,MAAM,GAAGL,QAAQ,EAAE,EAAExC,OAAO;IACxC;AACF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { PushNotificationClient } from "../clients/PushNotificationClient.js";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use PushNotificationClient.getInstance() instead
|
|
4
|
+
* This utility is maintained for backward compatibility
|
|
5
|
+
*/ export class NotificationsUtil {
|
|
6
|
+
static getClient() {
|
|
7
|
+
return PushNotificationClient.getInstance();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Use PushNotificationClient.getInstance().sendToUser() instead
|
|
11
|
+
* Send notification to a specific user
|
|
12
|
+
* Automatically determines platform and sends to appropriate service
|
|
13
|
+
*/ static async sendNotification(userUuid, notification) {
|
|
14
|
+
await this.getClient().sendToUser({
|
|
15
|
+
userUuid,
|
|
16
|
+
notification: {
|
|
17
|
+
title: notification.title,
|
|
18
|
+
body: notification.body,
|
|
19
|
+
...notification.redirectPath && {
|
|
20
|
+
redirectPath: notification.redirectPath
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Use PushNotificationClient.getInstance().sendToUsers() instead
|
|
27
|
+
* Send notification to multiple users
|
|
28
|
+
*/ static async sendNotificationToUsers(userUuids, notification) {
|
|
29
|
+
await this.getClient().sendToUsers({
|
|
30
|
+
userUuids,
|
|
31
|
+
notification: {
|
|
32
|
+
title: notification.title,
|
|
33
|
+
body: notification.body,
|
|
34
|
+
...notification.redirectPath && {
|
|
35
|
+
redirectPath: notification.redirectPath
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Split array into batches
|
|
42
|
+
*/ static splitIntoBatches(array, batchSize) {
|
|
43
|
+
const batches = [];
|
|
44
|
+
for(let i = 0; i < array.length; i += batchSize){
|
|
45
|
+
batches.push(array.slice(i, i + batchSize));
|
|
46
|
+
}
|
|
47
|
+
return batches;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Chunk array into smaller arrays
|
|
51
|
+
*/ static chunkArray(array, chunkSize) {
|
|
52
|
+
const result = [];
|
|
53
|
+
for(let i = 0; i < array.length; i += chunkSize){
|
|
54
|
+
result.push(array.slice(i, i + chunkSize));
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
// ==================== Time Calculation Utilities ====================
|
|
59
|
+
/**
|
|
60
|
+
* Calculate delay until 10 AM in user's timezone
|
|
61
|
+
*/ static calculateDelayUntil10AM(timeZone, additionalDelay = 0) {
|
|
62
|
+
const now = new Date(new Date().toLocaleString("en-US", {
|
|
63
|
+
timeZone
|
|
64
|
+
}));
|
|
65
|
+
// Create a Date object for 10 AM today in the user's time zone
|
|
66
|
+
const next10AM = new Date(now);
|
|
67
|
+
next10AM.setHours(10, 0, 0, 0);
|
|
68
|
+
// If the current time is past 10 AM, set the next 10 AM to tomorrow
|
|
69
|
+
if (now.getTime() > next10AM.getTime()) {
|
|
70
|
+
next10AM.setDate(next10AM.getDate() + 1);
|
|
71
|
+
}
|
|
72
|
+
next10AM.setDate(next10AM.getDate() + additionalDelay);
|
|
73
|
+
// Calculate the delay in milliseconds
|
|
74
|
+
return next10AM.getTime() - now.getTime();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Calculate delay until 6 PM in user's timezone
|
|
78
|
+
*/ static calculateDelayUntil6PM(timeZone) {
|
|
79
|
+
// Get the current date and time in the user's time zone
|
|
80
|
+
const now = new Date(new Date().toLocaleString("en-US", {
|
|
81
|
+
timeZone
|
|
82
|
+
}));
|
|
83
|
+
// Create a Date object for 6 PM today in the user's time zone
|
|
84
|
+
const next6PM = new Date(now);
|
|
85
|
+
next6PM.setHours(18, 0, 0, 0);
|
|
86
|
+
// If the current time is past 6 PM, set the next 6 PM to tomorrow
|
|
87
|
+
if (now.getTime() > next6PM.getTime()) {
|
|
88
|
+
next6PM.setDate(next6PM.getDate() + 1);
|
|
89
|
+
}
|
|
90
|
+
// Calculate the delay in milliseconds
|
|
91
|
+
return next6PM.getTime() - now.getTime();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=NotificationsUtil.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/NotificationsUtil.ts"],"sourcesContent":["import { PushNotificationClient } from \"../clients/PushNotificationClient.js\";\nimport type { NotificationRequest } from \"../models/NotificationInterfaces.js\";\n\n/**\n * @deprecated Use PushNotificationClient.getInstance() instead\n * This utility is maintained for backward compatibility\n */\nexport class NotificationsUtil {\n private static getClient(): PushNotificationClient {\n return PushNotificationClient.getInstance();\n }\n\n /**\n * @deprecated Use PushNotificationClient.getInstance().sendToUser() instead\n * Send notification to a specific user\n * Automatically determines platform and sends to appropriate service\n */\n public static async sendNotification(userUuid: string, notification: NotificationRequest): Promise<void> {\n await this.getClient().sendToUser({\n userUuid,\n notification: {\n title: notification.title,\n body: notification.body,\n ...(notification.redirectPath && { redirectPath: notification.redirectPath }),\n },\n });\n }\n\n /**\n * @deprecated Use PushNotificationClient.getInstance().sendToUsers() instead\n * Send notification to multiple users\n */\n public static async sendNotificationToUsers(userUuids: string[], notification: NotificationRequest): Promise<void> {\n await this.getClient().sendToUsers({\n userUuids,\n notification: {\n title: notification.title,\n body: notification.body,\n ...(notification.redirectPath && { redirectPath: notification.redirectPath }),\n },\n });\n }\n\n /**\n * Split array into batches\n */\n public static splitIntoBatches<T>(array: T[], batchSize: number): T[][] {\n const batches: T[][] = [];\n for (let i = 0; i < array.length; i += batchSize) {\n batches.push(array.slice(i, i + batchSize));\n }\n return batches;\n }\n\n /**\n * Chunk array into smaller arrays\n */\n public static chunkArray<T>(array: T[], chunkSize: number): T[][] {\n const result: T[][] = [];\n for (let i = 0; i < array.length; i += chunkSize) {\n result.push(array.slice(i, i + chunkSize));\n }\n return result;\n }\n\n // ==================== Time Calculation Utilities ====================\n\n /**\n * Calculate delay until 10 AM in user's timezone\n */\n public static calculateDelayUntil10AM(\n timeZone: string,\n additionalDelay = 0\n ): number {\n const now = new Date(new Date().toLocaleString(\"en-US\", { timeZone }));\n\n // Create a Date object for 10 AM today in the user's time zone\n const next10AM = new Date(now);\n next10AM.setHours(10, 0, 0, 0);\n\n // If the current time is past 10 AM, set the next 10 AM to tomorrow\n if (now.getTime() > next10AM.getTime()) {\n next10AM.setDate(next10AM.getDate() + 1);\n }\n next10AM.setDate(next10AM.getDate() + additionalDelay);\n\n // Calculate the delay in milliseconds\n return next10AM.getTime() - now.getTime();\n }\n\n /**\n * Calculate delay until 6 PM in user's timezone\n */\n public static calculateDelayUntil6PM(timeZone: string): number {\n // Get the current date and time in the user's time zone\n const now = new Date(new Date().toLocaleString(\"en-US\", { timeZone }));\n\n // Create a Date object for 6 PM today in the user's time zone\n const next6PM = new Date(now);\n next6PM.setHours(18, 0, 0, 0);\n\n // If the current time is past 6 PM, set the next 6 PM to tomorrow\n if (now.getTime() > next6PM.getTime()) {\n next6PM.setDate(next6PM.getDate() + 1);\n }\n\n // Calculate the delay in milliseconds\n return next6PM.getTime() - now.getTime();\n }\n}\n"],"names":["PushNotificationClient","NotificationsUtil","getClient","getInstance","sendNotification","userUuid","notification","sendToUser","title","body","redirectPath","sendNotificationToUsers","userUuids","sendToUsers","splitIntoBatches","array","batchSize","batches","i","length","push","slice","chunkArray","chunkSize","result","calculateDelayUntil10AM","timeZone","additionalDelay","now","Date","toLocaleString","next10AM","setHours","getTime","setDate","getDate","calculateDelayUntil6PM","next6PM"],"mappings":"AAAA,SAASA,sBAAsB,QAAQ,uCAAuC;AAG9E;;;CAGC,GACD,OAAO,MAAMC;IACX,OAAeC,YAAoC;QACjD,OAAOF,uBAAuBG,WAAW;IAC3C;IAEA;;;;GAIC,GACD,aAAoBC,iBAAiBC,QAAgB,EAAEC,YAAiC,EAAiB;QACvG,MAAM,IAAI,CAACJ,SAAS,GAAGK,UAAU,CAAC;YAChCF;YACAC,cAAc;gBACZE,OAAOF,aAAaE,KAAK;gBACzBC,MAAMH,aAAaG,IAAI;gBACvB,GAAIH,aAAaI,YAAY,IAAI;oBAAEA,cAAcJ,aAAaI,YAAY;gBAAC,CAAC;YAC9E;QACF;IACF;IAEA;;;GAGC,GACD,aAAoBC,wBAAwBC,SAAmB,EAAEN,YAAiC,EAAiB;QACjH,MAAM,IAAI,CAACJ,SAAS,GAAGW,WAAW,CAAC;YACjCD;YACAN,cAAc;gBACZE,OAAOF,aAAaE,KAAK;gBACzBC,MAAMH,aAAaG,IAAI;gBACvB,GAAIH,aAAaI,YAAY,IAAI;oBAAEA,cAAcJ,aAAaI,YAAY;gBAAC,CAAC;YAC9E;QACF;IACF;IAEA;;GAEC,GACD,OAAcI,iBAAoBC,KAAU,EAAEC,SAAiB,EAAS;QACtE,MAAMC,UAAiB,EAAE;QACzB,IAAK,IAAIC,IAAI,GAAGA,IAAIH,MAAMI,MAAM,EAAED,KAAKF,UAAW;YAChDC,QAAQG,IAAI,CAACL,MAAMM,KAAK,CAACH,GAAGA,IAAIF;QAClC;QACA,OAAOC;IACT;IAEA;;GAEC,GACD,OAAcK,WAAcP,KAAU,EAAEQ,SAAiB,EAAS;QAChE,MAAMC,SAAgB,EAAE;QACxB,IAAK,IAAIN,IAAI,GAAGA,IAAIH,MAAMI,MAAM,EAAED,KAAKK,UAAW;YAChDC,OAAOJ,IAAI,CAACL,MAAMM,KAAK,CAACH,GAAGA,IAAIK;QACjC;QACA,OAAOC;IACT;IAEA,uEAAuE;IAEvE;;GAEC,GACD,OAAcC,wBACZC,QAAgB,EAChBC,kBAAkB,CAAC,EACX;QACR,MAAMC,MAAM,IAAIC,KAAK,IAAIA,OAAOC,cAAc,CAAC,SAAS;YAAEJ;QAAS;QAEnE,+DAA+D;QAC/D,MAAMK,WAAW,IAAIF,KAAKD;QAC1BG,SAASC,QAAQ,CAAC,IAAI,GAAG,GAAG;QAE5B,oEAAoE;QACpE,IAAIJ,IAAIK,OAAO,KAAKF,SAASE,OAAO,IAAI;YACtCF,SAASG,OAAO,CAACH,SAASI,OAAO,KAAK;QACxC;QACAJ,SAASG,OAAO,CAACH,SAASI,OAAO,KAAKR;QAEtC,sCAAsC;QACtC,OAAOI,SAASE,OAAO,KAAKL,IAAIK,OAAO;IACzC;IAEA;;GAEC,GACD,OAAcG,uBAAuBV,QAAgB,EAAU;QAC7D,wDAAwD;QACxD,MAAME,MAAM,IAAIC,KAAK,IAAIA,OAAOC,cAAc,CAAC,SAAS;YAAEJ;QAAS;QAEnE,8DAA8D;QAC9D,MAAMW,UAAU,IAAIR,KAAKD;QACzBS,QAAQL,QAAQ,CAAC,IAAI,GAAG,GAAG;QAE3B,kEAAkE;QAClE,IAAIJ,IAAIK,OAAO,KAAKI,QAAQJ,OAAO,IAAI;YACrCI,QAAQH,OAAO,CAACG,QAAQF,OAAO,KAAK;QACtC;QAEA,sCAAsC;QACtC,OAAOE,QAAQJ,OAAO,KAAKL,IAAIK,OAAO;IACxC;AACF"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
|
|
2
|
+
import { Log } from "./Logger.js";
|
|
3
|
+
export class SecretManager {
|
|
4
|
+
static client;
|
|
5
|
+
static cache = new Map();
|
|
6
|
+
static logger = Log.getInstance().extend("secret-manager");
|
|
7
|
+
static defaultProjectId = process.env.GCP_PROJECT_ID || "";
|
|
8
|
+
static getClient() {
|
|
9
|
+
if (!this.client) {
|
|
10
|
+
this.client = new SecretManagerServiceClient();
|
|
11
|
+
this.logger.info("Secret Manager client initialized");
|
|
12
|
+
}
|
|
13
|
+
return this.client;
|
|
14
|
+
}
|
|
15
|
+
static setDefaultProjectId(projectId) {
|
|
16
|
+
this.defaultProjectId = projectId;
|
|
17
|
+
this.logger.info(`Default project ID set to: ${projectId}`);
|
|
18
|
+
}
|
|
19
|
+
static async loadSecret(secretName, projectId, version = "latest") {
|
|
20
|
+
const effectiveProjectId = projectId || this.defaultProjectId;
|
|
21
|
+
if (!effectiveProjectId) {
|
|
22
|
+
throw new Error("No project ID provided. Set GCP_PROJECT_ID env var or call setDefaultProjectId()");
|
|
23
|
+
}
|
|
24
|
+
const cacheKey = `${effectiveProjectId}/${secretName}/${version}`;
|
|
25
|
+
if (this.cache.has(cacheKey)) {
|
|
26
|
+
this.logger.debug(`Secret ${secretName} loaded from cache`);
|
|
27
|
+
return this.cache.get(cacheKey);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
this.logger.info(`Loading secret ${secretName} from project ${effectiveProjectId}`);
|
|
31
|
+
const name = `projects/${effectiveProjectId}/secrets/${secretName}/versions/${version}`;
|
|
32
|
+
const [secretVersion] = await this.getClient().accessSecretVersion({
|
|
33
|
+
name
|
|
34
|
+
});
|
|
35
|
+
const secretValue = secretVersion.payload?.data?.toString("utf8") ?? "";
|
|
36
|
+
if (!secretValue) {
|
|
37
|
+
throw new Error(`Secret ${secretName} is empty`);
|
|
38
|
+
}
|
|
39
|
+
this.cache.set(cacheKey, secretValue);
|
|
40
|
+
this.logger.info(`Secret ${secretName} loaded and cached successfully`);
|
|
41
|
+
return secretValue;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
this.logger.error(`Failed to load secret ${secretName}:`, error);
|
|
44
|
+
throw new Error(`Failed to load secret ${secretName}: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
static async loadSecrets(secretNames, projectId) {
|
|
48
|
+
this.logger.info(`Loading ${secretNames.length} secrets in parallel`);
|
|
49
|
+
const promises = secretNames.map((name)=>this.loadSecret(name, projectId).then((value)=>({
|
|
50
|
+
name,
|
|
51
|
+
value
|
|
52
|
+
})));
|
|
53
|
+
const results = await Promise.all(promises);
|
|
54
|
+
return results.reduce((acc, { name, value })=>{
|
|
55
|
+
acc[name] = value;
|
|
56
|
+
return acc;
|
|
57
|
+
}, {});
|
|
58
|
+
}
|
|
59
|
+
static async loadSecretJSON(secretName, projectId) {
|
|
60
|
+
const secretValue = await this.loadSecret(secretName, projectId);
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(secretValue);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.logger.error(`Failed to parse secret ${secretName} as JSON:`, error);
|
|
65
|
+
throw new Error(`Secret ${secretName} is not valid JSON: ${error.message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
static isCached(secretName, projectId, version = "latest") {
|
|
69
|
+
const effectiveProjectId = projectId || this.defaultProjectId;
|
|
70
|
+
const cacheKey = `${effectiveProjectId}/${secretName}/${version}`;
|
|
71
|
+
return this.cache.has(cacheKey);
|
|
72
|
+
}
|
|
73
|
+
static clearCache(secretName) {
|
|
74
|
+
if (secretName) {
|
|
75
|
+
// Clear all versions of a specific secret
|
|
76
|
+
const keysToDelete = [];
|
|
77
|
+
for (const key of this.cache.keys()){
|
|
78
|
+
if (key.includes(`/${secretName}/`)) {
|
|
79
|
+
keysToDelete.push(key);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
keysToDelete.forEach((key)=>this.cache.delete(key));
|
|
83
|
+
this.logger.info(`Cache cleared for secret: ${secretName}`);
|
|
84
|
+
} else {
|
|
85
|
+
this.cache.clear();
|
|
86
|
+
this.logger.info("All secret cache cleared");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
static getCacheStats() {
|
|
90
|
+
return {
|
|
91
|
+
size: this.cache.size,
|
|
92
|
+
keys: Array.from(this.cache.keys())
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
static async preloadSecrets(secretNames, projectId) {
|
|
96
|
+
this.logger.info(`Preloading ${secretNames.length} secrets...`);
|
|
97
|
+
try {
|
|
98
|
+
await this.loadSecrets(secretNames, projectId);
|
|
99
|
+
this.logger.info("Secrets preloaded successfully");
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.logger.error("Failed to preload secrets:", error);
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=SecretManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/SecretManager.ts"],"sourcesContent":["\n\nimport { SecretManagerServiceClient } from \"@google-cloud/secret-manager\";\nimport { Log } from \"./Logger.js\";\n\nexport class SecretManager {\n private static client: SecretManagerServiceClient;\n private static cache = new Map<string, string>();\n private static logger: Log = Log.getInstance().extend(\"secret-manager\");\n private static defaultProjectId: string = process.env.GCP_PROJECT_ID || \"\";\n\n\n private static getClient(): SecretManagerServiceClient {\n if (!this.client) {\n this.client = new SecretManagerServiceClient();\n this.logger.info(\"Secret Manager client initialized\");\n }\n return this.client;\n }\n\n\n static setDefaultProjectId(projectId: string): void {\n this.defaultProjectId = projectId;\n this.logger.info(`Default project ID set to: ${projectId}`);\n }\n\n\n static async loadSecret(\n secretName: string,\n projectId?: string,\n version: string = \"latest\"\n ): Promise<string> {\n const effectiveProjectId = projectId || this.defaultProjectId;\n\n if (!effectiveProjectId) {\n throw new Error(\n \"No project ID provided. Set GCP_PROJECT_ID env var or call setDefaultProjectId()\"\n );\n }\n\n const cacheKey = `${effectiveProjectId}/${secretName}/${version}`;\n\n if (this.cache.has(cacheKey)) {\n this.logger.debug(`Secret ${secretName} loaded from cache`);\n return this.cache.get(cacheKey)!;\n }\n\n try {\n this.logger.info(\n `Loading secret ${secretName} from project ${effectiveProjectId}`\n );\n\n const name = `projects/${effectiveProjectId}/secrets/${secretName}/versions/${version}`;\n const [secretVersion] = await this.getClient().accessSecretVersion({\n name,\n });\n\n const secretValue = secretVersion.payload?.data?.toString(\"utf8\") ?? \"\";\n\n if (!secretValue) {\n throw new Error(`Secret ${secretName} is empty`);\n }\n\n this.cache.set(cacheKey, secretValue);\n this.logger.info(`Secret ${secretName} loaded and cached successfully`);\n\n return secretValue;\n } catch (error) {\n this.logger.error(`Failed to load secret ${secretName}:`, error);\n throw new Error(\n `Failed to load secret ${secretName}: ${(error as Error).message}`\n );\n }\n }\n\n static async loadSecrets(\n secretNames: string[],\n projectId?: string\n ): Promise<Record<string, string>> {\n this.logger.info(`Loading ${secretNames.length} secrets in parallel`);\n\n const promises = secretNames.map((name) =>\n this.loadSecret(name, projectId).then((value) => ({ name, value }))\n );\n\n const results = await Promise.all(promises);\n\n return results.reduce(\n (acc, { name, value }) => {\n acc[name] = value;\n return acc;\n },\n {} as Record<string, string>\n );\n }\n\n static async loadSecretJSON<T = any>(\n secretName: string,\n projectId?: string\n ): Promise<T> {\n const secretValue = await this.loadSecret(secretName, projectId);\n\n try {\n return JSON.parse(secretValue) as T;\n } catch (error) {\n this.logger.error(`Failed to parse secret ${secretName} as JSON:`, error);\n throw new Error(\n `Secret ${secretName} is not valid JSON: ${(error as Error).message}`\n );\n }\n }\n\n\n static isCached(\n secretName: string,\n projectId?: string,\n version: string = \"latest\"\n ): boolean {\n const effectiveProjectId = projectId || this.defaultProjectId;\n const cacheKey = `${effectiveProjectId}/${secretName}/${version}`;\n return this.cache.has(cacheKey);\n }\n\n\n static clearCache(secretName?: string): void {\n if (secretName) {\n // Clear all versions of a specific secret\n const keysToDelete: string[] = [];\n for (const key of this.cache.keys()) {\n if (key.includes(`/${secretName}/`)) {\n keysToDelete.push(key);\n }\n }\n keysToDelete.forEach((key) => this.cache.delete(key));\n this.logger.info(`Cache cleared for secret: ${secretName}`);\n } else {\n this.cache.clear();\n this.logger.info(\"All secret cache cleared\");\n }\n }\n\n\n static getCacheStats(): { size: number; keys: string[] } {\n return {\n size: this.cache.size,\n keys: Array.from(this.cache.keys()),\n };\n }\n\n static async preloadSecrets(\n secretNames: string[],\n projectId?: string\n ): Promise<void> {\n this.logger.info(`Preloading ${secretNames.length} secrets...`);\n\n try {\n await this.loadSecrets(secretNames, projectId);\n this.logger.info(\"Secrets preloaded successfully\");\n } catch (error) {\n this.logger.error(\"Failed to preload secrets:\", error);\n throw error;\n }\n }\n}\n"],"names":["SecretManagerServiceClient","Log","SecretManager","client","cache","Map","logger","getInstance","extend","defaultProjectId","process","env","GCP_PROJECT_ID","getClient","info","setDefaultProjectId","projectId","loadSecret","secretName","version","effectiveProjectId","Error","cacheKey","has","debug","get","name","secretVersion","accessSecretVersion","secretValue","payload","data","toString","set","error","message","loadSecrets","secretNames","length","promises","map","then","value","results","Promise","all","reduce","acc","loadSecretJSON","JSON","parse","isCached","clearCache","keysToDelete","key","keys","includes","push","forEach","delete","clear","getCacheStats","size","Array","from","preloadSecrets"],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/node";
|
|
2
|
+
let sentryInitialized = false;
|
|
3
|
+
export function initializeSentry() {
|
|
4
|
+
if (sentryInitialized) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const dsn = process.env.SENTRY_DSN;
|
|
8
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
9
|
+
if (!dsn || nodeEnv !== "production") {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
Sentry.init({
|
|
13
|
+
dsn,
|
|
14
|
+
environment: nodeEnv,
|
|
15
|
+
enabled: true,
|
|
16
|
+
tracesSampleRate: 0
|
|
17
|
+
});
|
|
18
|
+
sentryInitialized = true;
|
|
19
|
+
}
|
|
20
|
+
export function isSentryEnabled() {
|
|
21
|
+
return sentryInitialized;
|
|
22
|
+
}
|
|
23
|
+
const SENSITIVE_FIELDS = [
|
|
24
|
+
"password",
|
|
25
|
+
"token",
|
|
26
|
+
"apikey",
|
|
27
|
+
"secret",
|
|
28
|
+
"cookie"
|
|
29
|
+
];
|
|
30
|
+
export function scrubObject(obj, depth = 0) {
|
|
31
|
+
if (depth > 10) {
|
|
32
|
+
return "[Max Depth]";
|
|
33
|
+
}
|
|
34
|
+
if (obj === null || obj === undefined) {
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
if (typeof obj !== "object") {
|
|
38
|
+
return obj;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(obj)) {
|
|
41
|
+
return obj.map((item)=>scrubObject(item, depth + 1));
|
|
42
|
+
}
|
|
43
|
+
const scrubbedObj = {};
|
|
44
|
+
for (const [key, value] of Object.entries(obj)){
|
|
45
|
+
const keyLower = key.toLowerCase();
|
|
46
|
+
const isSensitive = SENSITIVE_FIELDS.some((field)=>keyLower.includes(field));
|
|
47
|
+
if (isSensitive) {
|
|
48
|
+
scrubbedObj[key] = "[REDACTED]";
|
|
49
|
+
} else if (typeof value === "object" && value !== null) {
|
|
50
|
+
scrubbedObj[key] = scrubObject(value, depth + 1);
|
|
51
|
+
} else {
|
|
52
|
+
scrubbedObj[key] = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return scrubbedObj;
|
|
56
|
+
}
|
|
57
|
+
const MAX_BODY_SIZE = 100 * 1024; // 100KB - increased from 10KB for full payload visibility
|
|
58
|
+
export function captureRequestBody(body) {
|
|
59
|
+
if (!body) {
|
|
60
|
+
return body;
|
|
61
|
+
}
|
|
62
|
+
const bodyString = JSON.stringify(body);
|
|
63
|
+
if (bodyString.length > MAX_BODY_SIZE) {
|
|
64
|
+
return {
|
|
65
|
+
_truncated: true,
|
|
66
|
+
_originalSize: bodyString.length,
|
|
67
|
+
preview: bodyString.substring(0, MAX_BODY_SIZE)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return scrubObject(body);
|
|
71
|
+
}
|
|
72
|
+
export function extractUserContext(ctx) {
|
|
73
|
+
const user = ctx.state?.user;
|
|
74
|
+
if (user?.userUuid) {
|
|
75
|
+
return {
|
|
76
|
+
id: user.userUuid
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
export function buildRequestContext(ctx) {
|
|
82
|
+
return {
|
|
83
|
+
method: ctx.method,
|
|
84
|
+
url: ctx.url,
|
|
85
|
+
query: scrubObject(ctx.query),
|
|
86
|
+
headers: scrubObject(ctx.headers),
|
|
87
|
+
data: captureRequestBody(ctx.request.body),
|
|
88
|
+
ip: ctx.ip
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function buildResponseContext(ctx) {
|
|
92
|
+
return {
|
|
93
|
+
status_code: ctx.status
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function reportErrorToSentry(error, ctx, options) {
|
|
97
|
+
if (!isSentryEnabled()) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
Sentry.withScope((scope)=>{
|
|
101
|
+
if (ctx) {
|
|
102
|
+
scope.setContext("request", buildRequestContext(ctx));
|
|
103
|
+
scope.setContext("response", buildResponseContext(ctx));
|
|
104
|
+
scope.setUser(extractUserContext(ctx));
|
|
105
|
+
}
|
|
106
|
+
if (options?.operational !== undefined) {
|
|
107
|
+
scope.setTag("error.operational", options.operational);
|
|
108
|
+
}
|
|
109
|
+
if (options?.errorType) {
|
|
110
|
+
scope.setTag("error.type", options.errorType);
|
|
111
|
+
}
|
|
112
|
+
if (options?.statusCode) {
|
|
113
|
+
scope.setTag("http.status_code", options.statusCode);
|
|
114
|
+
}
|
|
115
|
+
scope.setTag("source", "middleware");
|
|
116
|
+
Sentry.captureException(error);
|
|
117
|
+
});
|
|
118
|
+
error._sentryReported = true;
|
|
119
|
+
}
|
|
120
|
+
export function reportMessageToSentry(message, level, context) {
|
|
121
|
+
if (!isSentryEnabled()) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
Sentry.withScope((scope)=>{
|
|
125
|
+
if (context) {
|
|
126
|
+
for (const [key, value] of Object.entries(context)){
|
|
127
|
+
if (key === "logger_name" || key === "source") {
|
|
128
|
+
scope.setTag(key, value);
|
|
129
|
+
} else {
|
|
130
|
+
// Deeply serialize objects and arrays to avoid [Object] and [Array] in Sentry
|
|
131
|
+
const serializedValue = typeof value === "object" && value !== null ? scrubObject(value) : value;
|
|
132
|
+
scope.setContext(key, serializedValue);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
Sentry.captureMessage(message, level);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=SentryUtil.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/SentryUtil.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\nimport type { Context } from \"koa\";\n\nlet sentryInitialized = false;\n\nexport function initializeSentry(): void {\n if (sentryInitialized) {\n return;\n }\n\n const dsn = process.env.SENTRY_DSN;\n const nodeEnv = process.env.NODE_ENV;\n\n if (!dsn || nodeEnv !== \"production\") {\n return;\n }\n\n Sentry.init({\n dsn,\n environment: nodeEnv,\n enabled: true,\n tracesSampleRate: 0,\n });\n\n sentryInitialized = true;\n}\n\nexport function isSentryEnabled(): boolean {\n return sentryInitialized;\n}\n\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"apikey\",\n \"secret\",\n \"cookie\",\n];\n\nexport function scrubObject(obj: any, depth = 0): any {\n if (depth > 10) {\n return \"[Max Depth]\";\n }\n\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj !== \"object\") {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => scrubObject(item, depth + 1));\n }\n\n const scrubbedObj: any = {};\n for (const [key, value] of Object.entries(obj)) {\n const keyLower = key.toLowerCase();\n const isSensitive = SENSITIVE_FIELDS.some((field) =>\n keyLower.includes(field),\n );\n\n if (isSensitive) {\n scrubbedObj[key] = \"[REDACTED]\";\n } else if (typeof value === \"object\" && value !== null) {\n scrubbedObj[key] = scrubObject(value, depth + 1);\n } else {\n scrubbedObj[key] = value;\n }\n }\n\n return scrubbedObj;\n}\n\nconst MAX_BODY_SIZE = 100 * 1024; // 100KB - increased from 10KB for full payload visibility\n\nexport function captureRequestBody(body: any): any {\n if (!body) {\n return body;\n }\n\n const bodyString = JSON.stringify(body);\n if (bodyString.length > MAX_BODY_SIZE) {\n return {\n _truncated: true,\n _originalSize: bodyString.length,\n preview: bodyString.substring(0, MAX_BODY_SIZE),\n };\n }\n\n return scrubObject(body);\n}\n\nexport function extractUserContext(ctx: Context): { id?: string } {\n const user = (ctx.state as any)?.user;\n if (user?.userUuid) {\n return { id: user.userUuid };\n }\n return {};\n}\n\nexport function buildRequestContext(ctx: Context): {\n method: string;\n url: string;\n query: any;\n headers: any;\n data: any;\n ip?: string;\n} {\n return {\n method: ctx.method,\n url: ctx.url,\n query: scrubObject(ctx.query),\n headers: scrubObject(ctx.headers),\n data: captureRequestBody((ctx.request as any).body),\n ip: ctx.ip,\n };\n}\n\nexport function buildResponseContext(ctx: Context): { status_code: number } {\n return {\n status_code: ctx.status,\n };\n}\n\nexport interface ErrorReportOptions {\n operational?: boolean;\n errorType?: string;\n statusCode?: number;\n}\n\nexport function reportErrorToSentry(\n error: Error,\n ctx?: Context,\n options?: ErrorReportOptions,\n): void {\n if (!isSentryEnabled()) {\n return;\n }\n\n Sentry.withScope((scope) => {\n if (ctx) {\n scope.setContext(\"request\", buildRequestContext(ctx));\n scope.setContext(\"response\", buildResponseContext(ctx));\n scope.setUser(extractUserContext(ctx));\n }\n\n if (options?.operational !== undefined) {\n scope.setTag(\"error.operational\", options.operational);\n }\n if (options?.errorType) {\n scope.setTag(\"error.type\", options.errorType);\n }\n if (options?.statusCode) {\n scope.setTag(\"http.status_code\", options.statusCode);\n }\n\n scope.setTag(\"source\", \"middleware\");\n\n Sentry.captureException(error);\n });\n\n (error as any)._sentryReported = true;\n}\n\nexport type SeverityLevel = \"fatal\" | \"error\" | \"warning\" | \"info\" | \"debug\";\n\nexport function reportMessageToSentry(\n message: string,\n level: SeverityLevel,\n context?: Record<string, any>,\n): void {\n if (!isSentryEnabled()) {\n return;\n }\n\n Sentry.withScope((scope) => {\n if (context) {\n for (const [key, value] of Object.entries(context)) {\n if (key === \"logger_name\" || key === \"source\") {\n scope.setTag(key, value);\n } else {\n // Deeply serialize objects and arrays to avoid [Object] and [Array] in Sentry\n const serializedValue =\n typeof value === \"object\" && value !== null\n ? scrubObject(value)\n : value;\n scope.setContext(key, serializedValue);\n }\n }\n }\n\n Sentry.captureMessage(message, level);\n });\n}\n"],"names":["Sentry","sentryInitialized","initializeSentry","dsn","process","env","SENTRY_DSN","nodeEnv","NODE_ENV","init","environment","enabled","tracesSampleRate","isSentryEnabled","SENSITIVE_FIELDS","scrubObject","obj","depth","undefined","Array","isArray","map","item","scrubbedObj","key","value","Object","entries","keyLower","toLowerCase","isSensitive","some","field","includes","MAX_BODY_SIZE","captureRequestBody","body","bodyString","JSON","stringify","length","_truncated","_originalSize","preview","substring","extractUserContext","ctx","user","state","userUuid","id","buildRequestContext","method","url","query","headers","data","request","ip","buildResponseContext","status_code","status","reportErrorToSentry","error","options","withScope","scope","setContext","setUser","operational","setTag","errorType","statusCode","captureException","_sentryReported","reportMessageToSentry","message","level","context","serializedValue","captureMessage"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class ImageValidationError extends Error {
|
|
2
|
+
constructor(message){
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "ImageValidationError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function validateBase64Image(base64) {
|
|
8
|
+
// Remove data URL prefix if present
|
|
9
|
+
const base64Data = base64.replace(/^data:image\/\w+;base64,/, "");
|
|
10
|
+
// Validate base64 format
|
|
11
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
|
|
12
|
+
throw new ImageValidationError("Invalid base64 format");
|
|
13
|
+
}
|
|
14
|
+
// Decode and check size (15MB = 15 * 1024 * 1024 bytes)
|
|
15
|
+
const sizeInBytes = base64Data.length * 3 / 4;
|
|
16
|
+
const maxSizeInBytes = 15 * 1024 * 1024;
|
|
17
|
+
if (sizeInBytes > maxSizeInBytes) {
|
|
18
|
+
throw new ImageValidationError(`Image size exceeds 15MB limit (${(sizeInBytes / (1024 * 1024)).toFixed(2)}MB)`);
|
|
19
|
+
}
|
|
20
|
+
// Decode to buffer for magic number validation
|
|
21
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
22
|
+
// Check magic numbers for PNG, JPEG, WebP
|
|
23
|
+
const isPNG = buffer[0] === 0x89 && buffer[1] === 0x50;
|
|
24
|
+
const isJPEG = buffer[0] === 0xff && buffer[1] === 0xd8;
|
|
25
|
+
const isWebP = buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46;
|
|
26
|
+
if (!isPNG && !isJPEG && !isWebP) {
|
|
27
|
+
throw new ImageValidationError("Invalid image format. Only PNG, JPEG, and WebP are supported");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=imageValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/imageValidation.ts"],"sourcesContent":["export class ImageValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ImageValidationError\";\n }\n}\n\nexport function validateBase64Image(base64: string): void {\n // Remove data URL prefix if present\n const base64Data = base64.replace(/^data:image\\/\\w+;base64,/, \"\");\n\n // Validate base64 format\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {\n throw new ImageValidationError(\"Invalid base64 format\");\n }\n\n // Decode and check size (15MB = 15 * 1024 * 1024 bytes)\n const sizeInBytes = (base64Data.length * 3) / 4;\n const maxSizeInBytes = 15 * 1024 * 1024;\n\n if (sizeInBytes > maxSizeInBytes) {\n throw new ImageValidationError(\n `Image size exceeds 15MB limit (${(sizeInBytes / (1024 * 1024)).toFixed(2)}MB)`\n );\n }\n\n // Decode to buffer for magic number validation\n const buffer = Buffer.from(base64Data, \"base64\");\n\n // Check magic numbers for PNG, JPEG, WebP\n const isPNG = buffer[0] === 0x89 && buffer[1] === 0x50;\n const isJPEG = buffer[0] === 0xff && buffer[1] === 0xd8;\n const isWebP =\n buffer[0] === 0x52 &&\n buffer[1] === 0x49 &&\n buffer[2] === 0x46 &&\n buffer[3] === 0x46;\n\n if (!isPNG && !isJPEG && !isWebP) {\n throw new ImageValidationError(\n \"Invalid image format. Only PNG, JPEG, and WebP are supported\"\n );\n }\n}\n"],"names":["ImageValidationError","Error","message","name","validateBase64Image","base64","base64Data","replace","test","sizeInBytes","length","maxSizeInBytes","toFixed","buffer","Buffer","from","isPNG","isJPEG","isWebP"],"mappings":"AAAA,OAAO,MAAMA,6BAA6BC;IACxC,YAAYC,OAAe,CAAE;QAC3B,KAAK,CAACA;QACN,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,SAASC,oBAAoBC,MAAc;IAChD,oCAAoC;IACpC,MAAMC,aAAaD,OAAOE,OAAO,CAAC,4BAA4B;IAE9D,yBAAyB;IACzB,IAAI,CAAC,yBAAyBC,IAAI,CAACF,aAAa;QAC9C,MAAM,IAAIN,qBAAqB;IACjC;IAEA,wDAAwD;IACxD,MAAMS,cAAc,AAACH,WAAWI,MAAM,GAAG,IAAK;IAC9C,MAAMC,iBAAiB,KAAK,OAAO;IAEnC,IAAIF,cAAcE,gBAAgB;QAChC,MAAM,IAAIX,qBACR,CAAC,+BAA+B,EAAE,AAACS,CAAAA,cAAe,CAAA,OAAO,IAAG,CAAC,EAAGG,OAAO,CAAC,GAAG,GAAG,CAAC;IAEnF;IAEA,+CAA+C;IAC/C,MAAMC,SAASC,OAAOC,IAAI,CAACT,YAAY;IAEvC,0CAA0C;IAC1C,MAAMU,QAAQH,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK;IAClD,MAAMI,SAASJ,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK;IACnD,MAAMK,SACJL,MAAM,CAAC,EAAE,KAAK,QACdA,MAAM,CAAC,EAAE,KAAK,QACdA,MAAM,CAAC,EAAE,KAAK,QACdA,MAAM,CAAC,EAAE,KAAK;IAEhB,IAAI,CAACG,SAAS,CAACC,UAAU,CAACC,QAAQ;QAChC,MAAM,IAAIlB,qBACR;IAEJ;AACF"}
|