90dc-core 1.18.1 → 1.19.0

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.
Files changed (28) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/clients/ImagesClient.d.ts +5 -0
  6. package/dist/lib/clients/ImagesClient.d.ts.map +1 -1
  7. package/dist/lib/clients/ImagesClient.js +109 -0
  8. package/dist/lib/clients/ImagesClient.js.map +1 -1
  9. package/dist/lib/clients/types/images.types.d.ts +20 -0
  10. package/dist/lib/clients/types/images.types.d.ts.map +1 -1
  11. package/dist/lib/clients/types/images.types.js.map +1 -1
  12. package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.d.ts +7 -0
  13. package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.d.ts.map +1 -0
  14. package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.js +60 -0
  15. package/dist/lib/db/migrations/20260330000000-create-user-progress-photos.js.map +1 -0
  16. package/dist/lib/db/models/UserProgressPhoto.d.ts +13 -0
  17. package/dist/lib/db/models/UserProgressPhoto.d.ts.map +1 -0
  18. package/dist/lib/db/models/UserProgressPhoto.js +59 -0
  19. package/dist/lib/db/models/UserProgressPhoto.js.map +1 -0
  20. package/dist/lib/db/models/index.d.ts +2 -0
  21. package/dist/lib/db/models/index.d.ts.map +1 -0
  22. package/dist/lib/db/models/index.js +3 -0
  23. package/dist/lib/db/models/index.js.map +1 -0
  24. package/dist/lib/utils/imageValidation.d.ts +5 -0
  25. package/dist/lib/utils/imageValidation.d.ts.map +1 -0
  26. package/dist/lib/utils/imageValidation.js +31 -0
  27. package/dist/lib/utils/imageValidation.js.map +1 -0
  28. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -13,11 +13,13 @@ export * as EmailTypes from "./lib/clients/types/email.types.js";
13
13
  export * as ImagesTypes from "./lib/clients/types/images.types.js";
14
14
  export * as MailingTypes from "./lib/clients/types/mailing.types.js";
15
15
  export type { NotificationPayload } from "./lib/clients/PushNotificationClient.js";
16
+ export type { UserProgressPhotoInput, UserProgressPhotoRecord, UpdateProgressPhotoDateInput, } from "./lib/clients/types/images.types.js";
16
17
  export { AuthenticationUtil } from "./lib/utils/AuthenticationUtil.js";
17
18
  export { NotificationsUtil } from "./lib/utils/NotificationsUtil.js";
18
19
  export { NotificationClient } from "./lib/utils/NotificationClient.js";
19
20
  export { Log } from "./lib/utils/Logger.js";
20
21
  export { SecretManager } from "./lib/utils/SecretManager.js";
22
+ export { validateBase64Image, ImageValidationError } from "./lib/utils/imageValidation.js";
21
23
  export { initializeSentry, isSentryEnabled, scrubObject, captureRequestBody, extractUserContext, buildRequestContext, buildResponseContext, reportErrorToSentry, reportMessageToSentry, type ErrorReportOptions, type SeverityLevel, } from "./lib/utils/SentryUtil.js";
22
24
  export { createMockContext, createMockUser, createAuthenticatedContext, mockDatabase, flushPromises, assertions, executeRoute, type BaseContext, type AuthenticatedContext, type RouterContext, type RouterMiddleware, type RouterLayer, type RouterLike, type SequelizeModelMethods, } from "./lib/testing/testHelpers.js";
23
25
  export { TEST_UUIDS, TEST_DATES, TEST_DATA, isValidUUID, generateTestEmail, createTestUser, createTestCoach, createTestHeadCoach, createTestFinanceUser, createTestAdmin, createTestClient, wait, freezeTime, testAssertions, mockResponses, isObject, isArray, } from "./lib/testing/testFixtures.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wCAAwC,CAAC;AAGvD,cAAc,yBAAyB,CAAA;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAA;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAA;AAGhG,OAAO,KAAK,UAAU,MAAM,oCAAoC,CAAA;AAChE,OAAO,KAAK,WAAW,MAAM,qCAAqC,CAAA;AAClE,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,YAAY,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAGlF,OAAO,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAC,iBAAiB,EAAC,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAC,GAAG,EAAC,MAAM,uBAAuB,CAAA;AACzC,OAAO,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,2BAA2B,CAAA;AAGlC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,0BAA0B,EAC1B,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,GAC3B,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,IAAI,EACJ,UAAU,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,+BAA+B,CAAA;AAGtC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EACL,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,cAAc,EACd,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACX,MAAM,0BAA0B,CAAA;AAGjC,OAAO,EACL,eAAe,GAChB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAG5F,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAGvF,OAAO,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAC,cAAc,EAAE,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wCAAwC,CAAC;AAGvD,cAAc,yBAAyB,CAAA;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAA;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,iDAAiD,CAAA;AAGhG,OAAO,KAAK,UAAU,MAAM,oCAAoC,CAAA;AAChE,OAAO,KAAK,WAAW,MAAM,qCAAqC,CAAA;AAClE,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,YAAY,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAGlF,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,qCAAqC,CAAA;AAG5C,OAAO,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAC,iBAAiB,EAAC,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAC,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAC,GAAG,EAAC,MAAM,uBAAuB,CAAA;AACzC,OAAO,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AAC1F,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,2BAA2B,CAAA;AAGlC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,0BAA0B,EAC1B,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,GAC3B,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,IAAI,EACJ,UAAU,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,+BAA+B,CAAA;AAGtC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EACL,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,cAAc,EACd,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACX,MAAM,0BAA0B,CAAA;AAGjC,OAAO,EACL,eAAe,GAChB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAG5F,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAGvF,OAAO,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAC,cAAc,EAAE,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAA"}
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ export { NotificationsUtil } from "./lib/utils/NotificationsUtil.js";
22
22
  export { NotificationClient } from "./lib/utils/NotificationClient.js";
23
23
  export { Log } from "./lib/utils/Logger.js";
24
24
  export { SecretManager } from "./lib/utils/SecretManager.js";
25
+ export { validateBase64Image, ImageValidationError } from "./lib/utils/imageValidation.js";
25
26
  export { initializeSentry, isSentryEnabled, scrubObject, captureRequestBody, extractUserContext, buildRequestContext, buildResponseContext, reportErrorToSentry, reportMessageToSentry } from "./lib/utils/SentryUtil.js";
26
27
  //Testing Utilities
27
28
  export { createMockContext, createMockUser, createAuthenticatedContext, mockDatabase, flushPromises, assertions, executeRoute } from "./lib/testing/testHelpers.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["//Interfaces\nexport * from \"./lib/models/ProgramInterfaces.js\";\nexport * from \"./lib/models/ExerciseInterfaces.js\";\nexport * from \"./lib/models/WorkoutInterfaces.js\";\nexport * from \"./lib/models/UserInterfaces.js\";\nexport * from \"./lib/models/NotificationInterfaces.js\";\n\n//DB Models - Export all models and model arrays\nexport * from \"./lib/dbmodels/index.js\"\n\n//Clients\nexport { EmailClient } from \"./lib/clients/EmailClient.js\"\nexport { ImagesClient } from \"./lib/clients/ImagesClient.js\"\nexport { MailingClient } from \"./lib/clients/MailingClient.js\"\nexport { PushNotificationClient } from \"./lib/clients/PushNotificationClient.js\"\nexport { FirebasePushNotificationClient } from \"./lib/clients/FirebasePushNotificationClient.js\"\n\n//Client Types (namespace imports)\nexport * as EmailTypes from \"./lib/clients/types/email.types.js\"\nexport * as ImagesTypes from \"./lib/clients/types/images.types.js\"\nexport * as MailingTypes from \"./lib/clients/types/mailing.types.js\"\nexport type { NotificationPayload } from \"./lib/clients/PushNotificationClient.js\"\n\n//Utils\nexport {AuthenticationUtil} from \"./lib/utils/AuthenticationUtil.js\"\nexport {NotificationsUtil} from \"./lib/utils/NotificationsUtil.js\"\nexport {NotificationClient} from \"./lib/utils/NotificationClient.js\"\nexport {Log} from \"./lib/utils/Logger.js\"\nexport {SecretManager} from \"./lib/utils/SecretManager.js\"\nexport {\n initializeSentry,\n isSentryEnabled,\n scrubObject,\n captureRequestBody,\n extractUserContext,\n buildRequestContext,\n buildResponseContext,\n reportErrorToSentry,\n reportMessageToSentry,\n type ErrorReportOptions,\n type SeverityLevel,\n} from \"./lib/utils/SentryUtil.js\"\n\n//Testing Utilities\nexport {\n createMockContext,\n createMockUser,\n createAuthenticatedContext,\n mockDatabase,\n flushPromises,\n assertions,\n executeRoute,\n type BaseContext,\n type AuthenticatedContext,\n type RouterContext,\n type RouterMiddleware,\n type RouterLayer,\n type RouterLike,\n type SequelizeModelMethods,\n} from \"./lib/testing/testHelpers.js\"\n\n// Testing Fixtures\nexport {\n TEST_UUIDS,\n TEST_DATES,\n TEST_DATA,\n isValidUUID,\n generateTestEmail,\n createTestUser,\n createTestCoach,\n createTestHeadCoach,\n createTestFinanceUser,\n createTestAdmin,\n createTestClient,\n wait,\n freezeTime,\n testAssertions,\n mockResponses,\n isObject,\n isArray,\n} from \"./lib/testing/testFixtures.js\"\n\n//Config\nexport {\n ConfigValidator,\n BaseConfigSchema,\n CommonSchemas,\n createConfig,\n ConfigurationError,\n type BaseConfig\n} from \"./lib/config/ConfigValidator.js\"\n\n//Errors\nexport {\n AppError,\n ValidationError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n UnprocessableEntityError,\n RateLimitError,\n InternalServerError,\n ServiceUnavailableError,\n DatabaseError,\n ExternalAPIError,\n isAppError,\n isOperationalError,\n toAppError\n} from \"./lib/Errors/AppError.js\"\n\n//Middlewares\nexport {\n ErrorMiddleware,\n} from \"./lib/middlewares/ErrorMiddleware.js\"\nexport { validate, type ValidationConfig } from \"./lib/middlewares/ValidationMiddleware.js\";\n\n//Controllers\nexport { BaseController, type RouteConfig } from \"./lib/controllers/BaseController.js\";\n\n\nexport {RedisClient} from \"./lib/classes/Redis.js\"\nexport {DatabaseClient, type DatabaseConfig} from \"./lib/classes/Database.js\""],"names":["EmailClient","ImagesClient","MailingClient","PushNotificationClient","FirebasePushNotificationClient","EmailTypes","ImagesTypes","MailingTypes","AuthenticationUtil","NotificationsUtil","NotificationClient","Log","SecretManager","initializeSentry","isSentryEnabled","scrubObject","captureRequestBody","extractUserContext","buildRequestContext","buildResponseContext","reportErrorToSentry","reportMessageToSentry","createMockContext","createMockUser","createAuthenticatedContext","mockDatabase","flushPromises","assertions","executeRoute","TEST_UUIDS","TEST_DATES","TEST_DATA","isValidUUID","generateTestEmail","createTestUser","createTestCoach","createTestHeadCoach","createTestFinanceUser","createTestAdmin","createTestClient","wait","freezeTime","testAssertions","mockResponses","isObject","isArray","ConfigValidator","BaseConfigSchema","CommonSchemas","createConfig","ConfigurationError","AppError","ValidationError","AuthenticationError","ForbiddenError","NotFoundError","ConflictError","UnprocessableEntityError","RateLimitError","InternalServerError","ServiceUnavailableError","DatabaseError","ExternalAPIError","isAppError","isOperationalError","toAppError","ErrorMiddleware","validate","BaseController","RedisClient","DatabaseClient"],"mappings":"AAAA,YAAY;AACZ,cAAc,oCAAoC;AAClD,cAAc,qCAAqC;AACnD,cAAc,oCAAoC;AAClD,cAAc,iCAAiC;AAC/C,cAAc,yCAAyC;AAEvD,gDAAgD;AAChD,cAAc,iBAAyB;AAEvC,SAAS;AACT,SAASA,WAAW,QAAQ,+BAA8B;AAC1D,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,SAASC,aAAa,QAAQ,iCAAgC;AAC9D,SAASC,sBAAsB,QAAQ,0CAAyC;AAChF,SAASC,8BAA8B,QAAQ,kDAAiD;AAEhG,kCAAkC;AAClC,OAAO,KAAKC,UAAU,MAAM,qCAAoC;AAChE,OAAO,KAAKC,WAAW,MAAM,sCAAqC;AAClE,OAAO,KAAKC,YAAY,MAAM,uCAAsC;AAGpE,OAAO;AACP,SAAQC,kBAAkB,QAAO,oCAAmC;AACpE,SAAQC,iBAAiB,QAAO,mCAAkC;AAClE,SAAQC,kBAAkB,QAAO,oCAAmC;AACpE,SAAQC,GAAG,QAAO,wBAAuB;AACzC,SAAQC,aAAa,QAAO,+BAA8B;AAC1D,SACEC,gBAAgB,EAChBC,eAAe,EACfC,WAAW,EACXC,kBAAkB,EAClBC,kBAAkB,EAClBC,mBAAmB,EACnBC,oBAAoB,EACpBC,mBAAmB,EACnBC,qBAAqB,QAGhB,4BAA2B;AAElC,mBAAmB;AACnB,SACEC,iBAAiB,EACjBC,cAAc,EACdC,0BAA0B,EAC1BC,YAAY,EACZC,aAAa,EACbC,UAAU,EACVC,YAAY,QAQP,+BAA8B;AAErC,mBAAmB;AACnB,SACEC,UAAU,EACVC,UAAU,EACVC,SAAS,EACTC,WAAW,EACXC,iBAAiB,EACjBC,cAAc,EACdC,eAAe,EACfC,mBAAmB,EACnBC,qBAAqB,EACrBC,eAAe,EACfC,gBAAgB,EAChBC,IAAI,EACJC,UAAU,EACVC,cAAc,EACdC,aAAa,EACbC,QAAQ,EACRC,OAAO,QACF,gCAA+B;AAEtC,QAAQ;AACR,SACEC,eAAe,EACfC,gBAAgB,EAChBC,aAAa,EACbC,YAAY,EACZC,kBAAkB,QAEb,kCAAiC;AAExC,QAAQ;AACR,SACEC,QAAQ,EACRC,eAAe,EACfC,mBAAmB,EACnBC,cAAc,EACdC,aAAa,EACbC,aAAa,EACbC,wBAAwB,EACxBC,cAAc,EACdC,mBAAmB,EACnBC,uBAAuB,EACvBC,aAAa,EACbC,gBAAgB,EAChBC,UAAU,EACVC,kBAAkB,EAClBC,UAAU,QACL,2BAA0B;AAEjC,aAAa;AACb,SACEC,eAAe,QACV,uCAAsC;AAC7C,SAASC,QAAQ,QAA+B,4CAA4C;AAE5F,aAAa;AACb,SAASC,cAAc,QAA0B,sCAAsC;AAGvF,SAAQC,WAAW,QAAO,yBAAwB;AAClD,SAAQC,cAAc,QAA4B,4BAA2B"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["//Interfaces\nexport * from \"./lib/models/ProgramInterfaces.js\";\nexport * from \"./lib/models/ExerciseInterfaces.js\";\nexport * from \"./lib/models/WorkoutInterfaces.js\";\nexport * from \"./lib/models/UserInterfaces.js\";\nexport * from \"./lib/models/NotificationInterfaces.js\";\n\n//DB Models - Export all models and model arrays\nexport * from \"./lib/dbmodels/index.js\"\n\n//Clients\nexport { EmailClient } from \"./lib/clients/EmailClient.js\"\nexport { ImagesClient } from \"./lib/clients/ImagesClient.js\"\nexport { MailingClient } from \"./lib/clients/MailingClient.js\"\nexport { PushNotificationClient } from \"./lib/clients/PushNotificationClient.js\"\nexport { FirebasePushNotificationClient } from \"./lib/clients/FirebasePushNotificationClient.js\"\n\n//Client Types (namespace imports)\nexport * as EmailTypes from \"./lib/clients/types/email.types.js\"\nexport * as ImagesTypes from \"./lib/clients/types/images.types.js\"\nexport * as MailingTypes from \"./lib/clients/types/mailing.types.js\"\nexport type { NotificationPayload } from \"./lib/clients/PushNotificationClient.js\"\n\n// Progress Photos Types\nexport type {\n UserProgressPhotoInput,\n UserProgressPhotoRecord,\n UpdateProgressPhotoDateInput,\n} from \"./lib/clients/types/images.types.js\"\n\n//Utils\nexport {AuthenticationUtil} from \"./lib/utils/AuthenticationUtil.js\"\nexport {NotificationsUtil} from \"./lib/utils/NotificationsUtil.js\"\nexport {NotificationClient} from \"./lib/utils/NotificationClient.js\"\nexport {Log} from \"./lib/utils/Logger.js\"\nexport {SecretManager} from \"./lib/utils/SecretManager.js\"\nexport { validateBase64Image, ImageValidationError } from \"./lib/utils/imageValidation.js\"\nexport {\n initializeSentry,\n isSentryEnabled,\n scrubObject,\n captureRequestBody,\n extractUserContext,\n buildRequestContext,\n buildResponseContext,\n reportErrorToSentry,\n reportMessageToSentry,\n type ErrorReportOptions,\n type SeverityLevel,\n} from \"./lib/utils/SentryUtil.js\"\n\n//Testing Utilities\nexport {\n createMockContext,\n createMockUser,\n createAuthenticatedContext,\n mockDatabase,\n flushPromises,\n assertions,\n executeRoute,\n type BaseContext,\n type AuthenticatedContext,\n type RouterContext,\n type RouterMiddleware,\n type RouterLayer,\n type RouterLike,\n type SequelizeModelMethods,\n} from \"./lib/testing/testHelpers.js\"\n\n// Testing Fixtures\nexport {\n TEST_UUIDS,\n TEST_DATES,\n TEST_DATA,\n isValidUUID,\n generateTestEmail,\n createTestUser,\n createTestCoach,\n createTestHeadCoach,\n createTestFinanceUser,\n createTestAdmin,\n createTestClient,\n wait,\n freezeTime,\n testAssertions,\n mockResponses,\n isObject,\n isArray,\n} from \"./lib/testing/testFixtures.js\"\n\n//Config\nexport {\n ConfigValidator,\n BaseConfigSchema,\n CommonSchemas,\n createConfig,\n ConfigurationError,\n type BaseConfig\n} from \"./lib/config/ConfigValidator.js\"\n\n//Errors\nexport {\n AppError,\n ValidationError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n UnprocessableEntityError,\n RateLimitError,\n InternalServerError,\n ServiceUnavailableError,\n DatabaseError,\n ExternalAPIError,\n isAppError,\n isOperationalError,\n toAppError\n} from \"./lib/Errors/AppError.js\"\n\n//Middlewares\nexport {\n ErrorMiddleware,\n} from \"./lib/middlewares/ErrorMiddleware.js\"\nexport { validate, type ValidationConfig } from \"./lib/middlewares/ValidationMiddleware.js\";\n\n//Controllers\nexport { BaseController, type RouteConfig } from \"./lib/controllers/BaseController.js\";\n\n\nexport {RedisClient} from \"./lib/classes/Redis.js\"\nexport {DatabaseClient, type DatabaseConfig} from \"./lib/classes/Database.js\""],"names":["EmailClient","ImagesClient","MailingClient","PushNotificationClient","FirebasePushNotificationClient","EmailTypes","ImagesTypes","MailingTypes","AuthenticationUtil","NotificationsUtil","NotificationClient","Log","SecretManager","validateBase64Image","ImageValidationError","initializeSentry","isSentryEnabled","scrubObject","captureRequestBody","extractUserContext","buildRequestContext","buildResponseContext","reportErrorToSentry","reportMessageToSentry","createMockContext","createMockUser","createAuthenticatedContext","mockDatabase","flushPromises","assertions","executeRoute","TEST_UUIDS","TEST_DATES","TEST_DATA","isValidUUID","generateTestEmail","createTestUser","createTestCoach","createTestHeadCoach","createTestFinanceUser","createTestAdmin","createTestClient","wait","freezeTime","testAssertions","mockResponses","isObject","isArray","ConfigValidator","BaseConfigSchema","CommonSchemas","createConfig","ConfigurationError","AppError","ValidationError","AuthenticationError","ForbiddenError","NotFoundError","ConflictError","UnprocessableEntityError","RateLimitError","InternalServerError","ServiceUnavailableError","DatabaseError","ExternalAPIError","isAppError","isOperationalError","toAppError","ErrorMiddleware","validate","BaseController","RedisClient","DatabaseClient"],"mappings":"AAAA,YAAY;AACZ,cAAc,oCAAoC;AAClD,cAAc,qCAAqC;AACnD,cAAc,oCAAoC;AAClD,cAAc,iCAAiC;AAC/C,cAAc,yCAAyC;AAEvD,gDAAgD;AAChD,cAAc,iBAAyB;AAEvC,SAAS;AACT,SAASA,WAAW,QAAQ,+BAA8B;AAC1D,SAASC,YAAY,QAAQ,gCAA+B;AAC5D,SAASC,aAAa,QAAQ,iCAAgC;AAC9D,SAASC,sBAAsB,QAAQ,0CAAyC;AAChF,SAASC,8BAA8B,QAAQ,kDAAiD;AAEhG,kCAAkC;AAClC,OAAO,KAAKC,UAAU,MAAM,qCAAoC;AAChE,OAAO,KAAKC,WAAW,MAAM,sCAAqC;AAClE,OAAO,KAAKC,YAAY,MAAM,uCAAsC;AAUpE,OAAO;AACP,SAAQC,kBAAkB,QAAO,oCAAmC;AACpE,SAAQC,iBAAiB,QAAO,mCAAkC;AAClE,SAAQC,kBAAkB,QAAO,oCAAmC;AACpE,SAAQC,GAAG,QAAO,wBAAuB;AACzC,SAAQC,aAAa,QAAO,+BAA8B;AAC1D,SAASC,mBAAmB,EAAEC,oBAAoB,QAAQ,iCAAgC;AAC1F,SACEC,gBAAgB,EAChBC,eAAe,EACfC,WAAW,EACXC,kBAAkB,EAClBC,kBAAkB,EAClBC,mBAAmB,EACnBC,oBAAoB,EACpBC,mBAAmB,EACnBC,qBAAqB,QAGhB,4BAA2B;AAElC,mBAAmB;AACnB,SACEC,iBAAiB,EACjBC,cAAc,EACdC,0BAA0B,EAC1BC,YAAY,EACZC,aAAa,EACbC,UAAU,EACVC,YAAY,QAQP,+BAA8B;AAErC,mBAAmB;AACnB,SACEC,UAAU,EACVC,UAAU,EACVC,SAAS,EACTC,WAAW,EACXC,iBAAiB,EACjBC,cAAc,EACdC,eAAe,EACfC,mBAAmB,EACnBC,qBAAqB,EACrBC,eAAe,EACfC,gBAAgB,EAChBC,IAAI,EACJC,UAAU,EACVC,cAAc,EACdC,aAAa,EACbC,QAAQ,EACRC,OAAO,QACF,gCAA+B;AAEtC,QAAQ;AACR,SACEC,eAAe,EACfC,gBAAgB,EAChBC,aAAa,EACbC,YAAY,EACZC,kBAAkB,QAEb,kCAAiC;AAExC,QAAQ;AACR,SACEC,QAAQ,EACRC,eAAe,EACfC,mBAAmB,EACnBC,cAAc,EACdC,aAAa,EACbC,aAAa,EACbC,wBAAwB,EACxBC,cAAc,EACdC,mBAAmB,EACnBC,uBAAuB,EACvBC,aAAa,EACbC,gBAAgB,EAChBC,UAAU,EACVC,kBAAkB,EAClBC,UAAU,QACL,2BAA0B;AAEjC,aAAa;AACb,SACEC,eAAe,QACV,uCAAsC;AAC7C,SAASC,QAAQ,QAA+B,4CAA4C;AAE5F,aAAa;AACb,SAASC,cAAc,QAA0B,sCAAsC;AAGvF,SAAQC,WAAW,QAAO,yBAAwB;AAClD,SAAQC,cAAc,QAA4B,4BAA2B"}
@@ -11,6 +11,11 @@ export declare class ImagesClient {
11
11
  getProgressPhotos(userUuid: string, programId: string): Promise<t.ProgressPhoto[]>;
12
12
  saveRecipePhotos(input: t.SaveRecipePhotosInput): Promise<void>;
13
13
  getRecipePhotos(recipeUuid: string): Promise<t.RecipePhotos | undefined>;
14
+ saveUserProgressPhoto(input: t.UserProgressPhotoInput): Promise<t.UserProgressPhotoRecord>;
15
+ getUserProgressPhotos(userUuid: string): Promise<t.UserProgressPhotoRecord[]>;
16
+ getProgressPhotosByProgram(programId: string): Promise<t.UserProgressPhotoRecord[]>;
17
+ updateProgressPhotoDate(input: t.UpdateProgressPhotoDateInput): Promise<t.UserProgressPhotoRecord | null>;
18
+ deleteProgressPhoto(uuid: string, userUuid: string): Promise<boolean>;
14
19
  private base64ToBuffer;
15
20
  private handleError;
16
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ImagesClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/ImagesClient.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,CAAC,MAAM,yBAAyB,CAAC;AAalD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA6C;gBAE/C,MAAM,EAAE,CAAC,CAAC,MAAM;IAKf,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IActD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAkBnD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIxD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAalF,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,GAAG,SAAS,CAAC;IAiBrF,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,WAAW;CAOpB"}
1
+ {"version":3,"file":"ImagesClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/ImagesClient.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,CAAC,MAAM,yBAAyB,CAAC;AAalD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA6C;gBAE/C,MAAM,EAAE,CAAC,CAAC,MAAM;IAKf,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IActD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAkBnD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIxD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAalF,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,GAAG,SAAS,CAAC;IAiBxE,qBAAqB,CAChC,KAAK,EAAE,CAAC,CAAC,sBAAsB,GAC9B,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAkC/B,qBAAqB,CACzB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC;IAUjC,0BAA0B,CAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC;IAUjC,uBAAuB,CAC3B,KAAK,EAAE,CAAC,CAAC,4BAA4B,GACpC,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAuBtC,mBAAmB,CACvB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IA4BnB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,WAAW;CAOpB"}
@@ -1,6 +1,9 @@
1
1
  import admin from "firebase-admin";
2
+ import { v4 as uuidv4 } from "uuid";
2
3
  import { ExternalAPIError } from "../Errors/AppError.js";
3
4
  import { Log } from "../utils/Logger.js";
5
+ import { validateBase64Image } from "../utils/imageValidation.js";
6
+ import { UserProgressPhoto } from "../db/models/UserProgressPhoto.js";
4
7
  const PROGRESS_DAYS = [
5
8
  1,
6
9
  30,
@@ -101,6 +104,112 @@ export class ImagesClient {
101
104
  coverBase64: results[2] ?? ""
102
105
  };
103
106
  }
107
+ async saveUserProgressPhoto(input) {
108
+ // Validate image
109
+ validateBase64Image(input.base64);
110
+ // Generate UUID for the photo
111
+ const uuid = uuidv4();
112
+ // Prepare Firebase path
113
+ const firebasePath = `progress-pictures/${input.userUuid}/${uuid}.png`;
114
+ // Upload to Firebase Storage
115
+ const base64Data = input.base64.replace(/^data:image\/\w+;base64,/, "");
116
+ const buffer = Buffer.from(base64Data, "base64");
117
+ const file = this.bucket.file(firebasePath);
118
+ await file.save(buffer, {
119
+ metadata: {
120
+ contentType: "image/png"
121
+ }
122
+ });
123
+ // Save metadata to database
124
+ const photoDate = input.photoDate || new Date();
125
+ const record = await UserProgressPhoto.create({
126
+ uuid,
127
+ userUuid: input.userUuid,
128
+ firebasePath,
129
+ photoDate,
130
+ programId: input.programId || null
131
+ });
132
+ return record.toJSON();
133
+ }
134
+ async getUserProgressPhotos(userUuid) {
135
+ const records = await UserProgressPhoto.findAll({
136
+ where: {
137
+ userUuid
138
+ },
139
+ order: [
140
+ [
141
+ "photoDate",
142
+ "DESC"
143
+ ]
144
+ ],
145
+ raw: true
146
+ });
147
+ return records;
148
+ }
149
+ async getProgressPhotosByProgram(programId) {
150
+ const records = await UserProgressPhoto.findAll({
151
+ where: {
152
+ programId
153
+ },
154
+ order: [
155
+ [
156
+ "photoDate",
157
+ "DESC"
158
+ ]
159
+ ],
160
+ raw: true
161
+ });
162
+ return records;
163
+ }
164
+ async updateProgressPhotoDate(input) {
165
+ const [affectedRows] = await UserProgressPhoto.update({
166
+ photoDate: input.photoDate
167
+ }, {
168
+ where: {
169
+ uuid: input.uuid,
170
+ userUuid: input.userUuid
171
+ }
172
+ });
173
+ if (affectedRows === 0) {
174
+ return null; // Photo not found or user doesn't own it
175
+ }
176
+ const updated = await UserProgressPhoto.findOne({
177
+ where: {
178
+ uuid: input.uuid
179
+ },
180
+ raw: true
181
+ });
182
+ return updated;
183
+ }
184
+ async deleteProgressPhoto(uuid, userUuid) {
185
+ // Find the photo to get the firebasePath
186
+ const photo = await UserProgressPhoto.findOne({
187
+ where: {
188
+ uuid,
189
+ userUuid
190
+ },
191
+ raw: true
192
+ });
193
+ if (!photo) {
194
+ return false; // Photo not found or user doesn't own it
195
+ }
196
+ // Delete from Firebase Storage
197
+ const file = this.bucket.file(photo.firebasePath);
198
+ try {
199
+ await file.delete();
200
+ } catch (error) {
201
+ // If file doesn't exist in storage, continue with database deletion
202
+ console.warn(`Failed to delete file from storage: ${photo.firebasePath}`, error);
203
+ }
204
+ // Delete from database
205
+ await UserProgressPhoto.destroy({
206
+ where: {
207
+ uuid,
208
+ userUuid
209
+ }
210
+ });
211
+ return true;
212
+ }
104
213
  base64ToBuffer(base64) {
105
214
  const data = base64.includes(",") ? base64.split(",")[1] : base64;
106
215
  return Buffer.from(data, "base64");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/clients/ImagesClient.ts"],"sourcesContent":["import admin from \"firebase-admin\";\nimport { Bucket } from \"@google-cloud/storage\";\nimport { ExternalAPIError } from \"../Errors/AppError.js\";\nimport { Log } from \"../utils/Logger.js\";\nimport type * as t from \"./types/images.types.js\";\n\nconst PROGRESS_DAYS = [1, 30, 60, 90];\n\ntype RecipePhotoType = \"ingredients\" | \"cover\" | \"method\";\nconst RECIPE_PHOTO_TYPES: RecipePhotoType[] = [\"ingredients\", \"cover\", \"method\"];\n\nconst imageName = {\n avatar: (userUuid: string) => `${userUuid}--avatar.png`,\n progress: (userUuid: string, day: number, programId: string) => `${userUuid}--${day}day--${programId}.png`,\n recipe: (recipeUuid: string, type: RecipePhotoType) => `${recipeUuid}--${type}.png`,\n};\n\nexport class ImagesClient {\n private bucket: Bucket;\n private logger = Log.getInstance().extend(\"images-client\");\n\n constructor(config: t.Config) {\n const app = config.firebaseApp ?? admin.app();\n this.bucket = app.storage().bucket(config.storageBucket);\n }\n\n public async saveImage(base64: string, name: string): Promise<void> {\n try {\n this.logger.info(\"Saving image\", { name });\n\n const buffer = this.base64ToBuffer(base64);\n const file = this.bucket.file(name);\n await file.save(buffer, { contentType: \"image/png\" });\n\n this.logger.info(\"Image saved successfully\", { name });\n } catch (error) {\n this.handleError(error, \"saveImage\");\n }\n }\n\n public async getImage(name: string): Promise<string | undefined> {\n try {\n this.logger.info(\"Getting image\", { name });\n\n const file = this.bucket.file(name);\n const [exists] = await file.exists();\n if (!exists) {\n return undefined;\n }\n\n const [contents] = await file.download();\n return \"data:image/jpeg;base64,\" + contents.toString(\"base64\");\n } catch (error) {\n this.logger.error(`Failed to get image: ${name}`, { error });\n return undefined;\n }\n }\n\n public async saveAvatar(userUuid: string, base64: string): Promise<void> {\n await this.saveImage(base64, imageName.avatar(userUuid));\n }\n\n public async getAvatar(userUuid: string): Promise<string | undefined> {\n return this.getImage(imageName.avatar(userUuid));\n }\n\n public async saveProgressPhoto(userUuid: string, day: number, programId: string, base64: string): Promise<void> {\n await this.saveImage(base64, imageName.progress(userUuid, day, programId));\n }\n\n public async getProgressPhotos(userUuid: string, programId: string): Promise<t.ProgressPhoto[]> {\n const photos: t.ProgressPhoto[] = [];\n\n for (const day of PROGRESS_DAYS) {\n const base64 = await this.getImage(imageName.progress(userUuid, day, programId));\n if (base64) {\n photos.push({ day, base64 });\n }\n }\n\n return photos;\n }\n\n public async saveRecipePhotos(input: t.SaveRecipePhotosInput): Promise<void> {\n await Promise.all([\n this.saveImage(input.ingredientBase64, imageName.recipe(input.recipeUuid, \"ingredients\")),\n this.saveImage(input.coverBase64, imageName.recipe(input.recipeUuid, \"cover\")),\n this.saveImage(input.methodBase64, imageName.recipe(input.recipeUuid, \"method\")),\n ]);\n }\n\n public async getRecipePhotos(recipeUuid: string): Promise<t.RecipePhotos | undefined> {\n const results = await Promise.all(\n RECIPE_PHOTO_TYPES.map((type) => this.getImage(imageName.recipe(recipeUuid, type)))\n );\n\n if (results.every((r) => !r)) {\n return undefined;\n }\n\n // BUG: cover/method swapped — preserved from original ImageController.ts to keep backward compat with frontend\n return {\n ingredientBase64: results[0] ?? \"\",\n methodBase64: results[1] ?? \"\",\n coverBase64: results[2] ?? \"\",\n };\n }\n\n private base64ToBuffer(base64: string): Buffer {\n const data = base64.includes(\",\") ? base64.split(\",\")[1]! : base64;\n return Buffer.from(data, \"base64\");\n }\n\n private handleError(error: unknown, method: string): never {\n this.logger.error(`Firebase Storage error in ${method}`, { error });\n throw new ExternalAPIError(\n `Firebase Storage error: ${error instanceof Error ? error.message : String(error)}`,\n `Service: Firebase Storage, Method: ${method}`\n );\n }\n}\n"],"names":["admin","ExternalAPIError","Log","PROGRESS_DAYS","RECIPE_PHOTO_TYPES","imageName","avatar","userUuid","progress","day","programId","recipe","recipeUuid","type","ImagesClient","bucket","logger","getInstance","extend","config","app","firebaseApp","storage","storageBucket","saveImage","base64","name","info","buffer","base64ToBuffer","file","save","contentType","error","handleError","getImage","exists","undefined","contents","download","toString","saveAvatar","getAvatar","saveProgressPhoto","getProgressPhotos","photos","push","saveRecipePhotos","input","Promise","all","ingredientBase64","coverBase64","methodBase64","getRecipePhotos","results","map","every","r","data","includes","split","Buffer","from","method","Error","message","String"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/lib/clients/ImagesClient.ts"],"sourcesContent":["import admin from \"firebase-admin\";\nimport { Bucket } from \"@google-cloud/storage\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { ExternalAPIError } from \"../Errors/AppError.js\";\nimport { Log } from \"../utils/Logger.js\";\nimport { validateBase64Image } from \"../utils/imageValidation.js\";\nimport { UserProgressPhoto } from \"../db/models/UserProgressPhoto.js\";\nimport type * as t from \"./types/images.types.js\";\n\nconst PROGRESS_DAYS = [1, 30, 60, 90];\n\ntype RecipePhotoType = \"ingredients\" | \"cover\" | \"method\";\nconst RECIPE_PHOTO_TYPES: RecipePhotoType[] = [\"ingredients\", \"cover\", \"method\"];\n\nconst imageName = {\n avatar: (userUuid: string) => `${userUuid}--avatar.png`,\n progress: (userUuid: string, day: number, programId: string) => `${userUuid}--${day}day--${programId}.png`,\n recipe: (recipeUuid: string, type: RecipePhotoType) => `${recipeUuid}--${type}.png`,\n};\n\nexport class ImagesClient {\n private bucket: Bucket;\n private logger = Log.getInstance().extend(\"images-client\");\n\n constructor(config: t.Config) {\n const app = config.firebaseApp ?? admin.app();\n this.bucket = app.storage().bucket(config.storageBucket);\n }\n\n public async saveImage(base64: string, name: string): Promise<void> {\n try {\n this.logger.info(\"Saving image\", { name });\n\n const buffer = this.base64ToBuffer(base64);\n const file = this.bucket.file(name);\n await file.save(buffer, { contentType: \"image/png\" });\n\n this.logger.info(\"Image saved successfully\", { name });\n } catch (error) {\n this.handleError(error, \"saveImage\");\n }\n }\n\n public async getImage(name: string): Promise<string | undefined> {\n try {\n this.logger.info(\"Getting image\", { name });\n\n const file = this.bucket.file(name);\n const [exists] = await file.exists();\n if (!exists) {\n return undefined;\n }\n\n const [contents] = await file.download();\n return \"data:image/jpeg;base64,\" + contents.toString(\"base64\");\n } catch (error) {\n this.logger.error(`Failed to get image: ${name}`, { error });\n return undefined;\n }\n }\n\n public async saveAvatar(userUuid: string, base64: string): Promise<void> {\n await this.saveImage(base64, imageName.avatar(userUuid));\n }\n\n public async getAvatar(userUuid: string): Promise<string | undefined> {\n return this.getImage(imageName.avatar(userUuid));\n }\n\n public async saveProgressPhoto(userUuid: string, day: number, programId: string, base64: string): Promise<void> {\n await this.saveImage(base64, imageName.progress(userUuid, day, programId));\n }\n\n public async getProgressPhotos(userUuid: string, programId: string): Promise<t.ProgressPhoto[]> {\n const photos: t.ProgressPhoto[] = [];\n\n for (const day of PROGRESS_DAYS) {\n const base64 = await this.getImage(imageName.progress(userUuid, day, programId));\n if (base64) {\n photos.push({ day, base64 });\n }\n }\n\n return photos;\n }\n\n public async saveRecipePhotos(input: t.SaveRecipePhotosInput): Promise<void> {\n await Promise.all([\n this.saveImage(input.ingredientBase64, imageName.recipe(input.recipeUuid, \"ingredients\")),\n this.saveImage(input.coverBase64, imageName.recipe(input.recipeUuid, \"cover\")),\n this.saveImage(input.methodBase64, imageName.recipe(input.recipeUuid, \"method\")),\n ]);\n }\n\n public async getRecipePhotos(recipeUuid: string): Promise<t.RecipePhotos | undefined> {\n const results = await Promise.all(\n RECIPE_PHOTO_TYPES.map((type) => this.getImage(imageName.recipe(recipeUuid, type)))\n );\n\n if (results.every((r) => !r)) {\n return undefined;\n }\n\n // BUG: cover/method swapped — preserved from original ImageController.ts to keep backward compat with frontend\n return {\n ingredientBase64: results[0] ?? \"\",\n methodBase64: results[1] ?? \"\",\n coverBase64: results[2] ?? \"\",\n };\n }\n\n public async saveUserProgressPhoto(\n input: t.UserProgressPhotoInput\n ): Promise<t.UserProgressPhotoRecord> {\n // Validate image\n validateBase64Image(input.base64);\n\n // Generate UUID for the photo\n const uuid = uuidv4();\n\n // Prepare Firebase path\n const firebasePath = `progress-pictures/${input.userUuid}/${uuid}.png`;\n\n // Upload to Firebase Storage\n const base64Data = input.base64.replace(/^data:image\\/\\w+;base64,/, \"\");\n const buffer = Buffer.from(base64Data, \"base64\");\n\n const file = this.bucket.file(firebasePath);\n await file.save(buffer, {\n metadata: {\n contentType: \"image/png\",\n },\n });\n\n // Save metadata to database\n const photoDate = input.photoDate || new Date();\n const record = await UserProgressPhoto.create({\n uuid,\n userUuid: input.userUuid,\n firebasePath,\n photoDate,\n programId: input.programId || null,\n });\n\n return record.toJSON() as t.UserProgressPhotoRecord;\n }\n\n async getUserProgressPhotos(\n userUuid: string\n ): Promise<t.UserProgressPhotoRecord[]> {\n const records = await UserProgressPhoto.findAll({\n where: { userUuid },\n order: [[\"photoDate\", \"DESC\"]],\n raw: true,\n });\n\n return records as t.UserProgressPhotoRecord[];\n }\n\n async getProgressPhotosByProgram(\n programId: string\n ): Promise<t.UserProgressPhotoRecord[]> {\n const records = await UserProgressPhoto.findAll({\n where: { programId },\n order: [[\"photoDate\", \"DESC\"]],\n raw: true,\n });\n\n return records as t.UserProgressPhotoRecord[];\n }\n\n async updateProgressPhotoDate(\n input: t.UpdateProgressPhotoDateInput\n ): Promise<t.UserProgressPhotoRecord | null> {\n const [affectedRows] = await UserProgressPhoto.update(\n { photoDate: input.photoDate },\n {\n where: {\n uuid: input.uuid,\n userUuid: input.userUuid, // Ensure user owns the photo\n },\n }\n );\n\n if (affectedRows === 0) {\n return null; // Photo not found or user doesn't own it\n }\n\n const updated = await UserProgressPhoto.findOne({\n where: { uuid: input.uuid },\n raw: true,\n });\n\n return updated as t.UserProgressPhotoRecord;\n }\n\n async deleteProgressPhoto(\n uuid: string,\n userUuid: string\n ): Promise<boolean> {\n // Find the photo to get the firebasePath\n const photo = await UserProgressPhoto.findOne({\n where: { uuid, userUuid },\n raw: true,\n });\n\n if (!photo) {\n return false; // Photo not found or user doesn't own it\n }\n\n // Delete from Firebase Storage\n const file = this.bucket.file(photo.firebasePath);\n try {\n await file.delete();\n } catch (error) {\n // If file doesn't exist in storage, continue with database deletion\n console.warn(`Failed to delete file from storage: ${photo.firebasePath}`, error);\n }\n\n // Delete from database\n await UserProgressPhoto.destroy({\n where: { uuid, userUuid },\n });\n\n return true;\n }\n\n private base64ToBuffer(base64: string): Buffer {\n const data = base64.includes(\",\") ? base64.split(\",\")[1]! : base64;\n return Buffer.from(data, \"base64\");\n }\n\n private handleError(error: unknown, method: string): never {\n this.logger.error(`Firebase Storage error in ${method}`, { error });\n throw new ExternalAPIError(\n `Firebase Storage error: ${error instanceof Error ? error.message : String(error)}`,\n `Service: Firebase Storage, Method: ${method}`\n );\n }\n}\n"],"names":["admin","v4","uuidv4","ExternalAPIError","Log","validateBase64Image","UserProgressPhoto","PROGRESS_DAYS","RECIPE_PHOTO_TYPES","imageName","avatar","userUuid","progress","day","programId","recipe","recipeUuid","type","ImagesClient","bucket","logger","getInstance","extend","config","app","firebaseApp","storage","storageBucket","saveImage","base64","name","info","buffer","base64ToBuffer","file","save","contentType","error","handleError","getImage","exists","undefined","contents","download","toString","saveAvatar","getAvatar","saveProgressPhoto","getProgressPhotos","photos","push","saveRecipePhotos","input","Promise","all","ingredientBase64","coverBase64","methodBase64","getRecipePhotos","results","map","every","r","saveUserProgressPhoto","uuid","firebasePath","base64Data","replace","Buffer","from","metadata","photoDate","Date","record","create","toJSON","getUserProgressPhotos","records","findAll","where","order","raw","getProgressPhotosByProgram","updateProgressPhotoDate","affectedRows","update","updated","findOne","deleteProgressPhoto","photo","delete","console","warn","destroy","data","includes","split","method","Error","message","String"],"mappings":"AAAA,OAAOA,WAAW,iBAAiB;AAEnC,SAASC,MAAMC,MAAM,QAAQ,OAAO;AACpC,SAASC,gBAAgB,QAAQ,wBAAwB;AACzD,SAASC,GAAG,QAAQ,qBAAqB;AACzC,SAASC,mBAAmB,QAAQ,8BAA8B;AAClE,SAASC,iBAAiB,QAAQ,oCAAoC;AAGtE,MAAMC,gBAAgB;IAAC;IAAG;IAAI;IAAI;CAAG;AAGrC,MAAMC,qBAAwC;IAAC;IAAe;IAAS;CAAS;AAEhF,MAAMC,YAAY;IAChBC,QAAQ,CAACC,WAAqB,GAAGA,SAAS,YAAY,CAAC;IACvDC,UAAU,CAACD,UAAkBE,KAAaC,YAAsB,GAAGH,SAAS,EAAE,EAAEE,IAAI,KAAK,EAAEC,UAAU,IAAI,CAAC;IAC1GC,QAAQ,CAACC,YAAoBC,OAA0B,GAAGD,WAAW,EAAE,EAAEC,KAAK,IAAI,CAAC;AACrF;AAEA,OAAO,MAAMC;IACHC,OAAe;IACfC,SAAShB,IAAIiB,WAAW,GAAGC,MAAM,CAAC,iBAAiB;IAE3D,YAAYC,MAAgB,CAAE;QAC5B,MAAMC,MAAMD,OAAOE,WAAW,IAAIzB,MAAMwB,GAAG;QAC3C,IAAI,CAACL,MAAM,GAAGK,IAAIE,OAAO,GAAGP,MAAM,CAACI,OAAOI,aAAa;IACzD;IAEA,MAAaC,UAAUC,MAAc,EAAEC,IAAY,EAAiB;QAClE,IAAI;YACF,IAAI,CAACV,MAAM,CAACW,IAAI,CAAC,gBAAgB;gBAAED;YAAK;YAExC,MAAME,SAAS,IAAI,CAACC,cAAc,CAACJ;YACnC,MAAMK,OAAO,IAAI,CAACf,MAAM,CAACe,IAAI,CAACJ;YAC9B,MAAMI,KAAKC,IAAI,CAACH,QAAQ;gBAAEI,aAAa;YAAY;YAEnD,IAAI,CAAChB,MAAM,CAACW,IAAI,CAAC,4BAA4B;gBAAED;YAAK;QACtD,EAAE,OAAOO,OAAO;YACd,IAAI,CAACC,WAAW,CAACD,OAAO;QAC1B;IACF;IAEA,MAAaE,SAAST,IAAY,EAA+B;QAC/D,IAAI;YACF,IAAI,CAACV,MAAM,CAACW,IAAI,CAAC,iBAAiB;gBAAED;YAAK;YAEzC,MAAMI,OAAO,IAAI,CAACf,MAAM,CAACe,IAAI,CAACJ;YAC9B,MAAM,CAACU,OAAO,GAAG,MAAMN,KAAKM,MAAM;YAClC,IAAI,CAACA,QAAQ;gBACX,OAAOC;YACT;YAEA,MAAM,CAACC,SAAS,GAAG,MAAMR,KAAKS,QAAQ;YACtC,OAAO,4BAA4BD,SAASE,QAAQ,CAAC;QACvD,EAAE,OAAOP,OAAO;YACd,IAAI,CAACjB,MAAM,CAACiB,KAAK,CAAC,CAAC,qBAAqB,EAAEP,MAAM,EAAE;gBAAEO;YAAM;YAC1D,OAAOI;QACT;IACF;IAEA,MAAaI,WAAWlC,QAAgB,EAAEkB,MAAc,EAAiB;QACvE,MAAM,IAAI,CAACD,SAAS,CAACC,QAAQpB,UAAUC,MAAM,CAACC;IAChD;IAEA,MAAamC,UAAUnC,QAAgB,EAA+B;QACpE,OAAO,IAAI,CAAC4B,QAAQ,CAAC9B,UAAUC,MAAM,CAACC;IACxC;IAEA,MAAaoC,kBAAkBpC,QAAgB,EAAEE,GAAW,EAAEC,SAAiB,EAAEe,MAAc,EAAiB;QAC9G,MAAM,IAAI,CAACD,SAAS,CAACC,QAAQpB,UAAUG,QAAQ,CAACD,UAAUE,KAAKC;IACjE;IAEA,MAAakC,kBAAkBrC,QAAgB,EAAEG,SAAiB,EAA8B;QAC9F,MAAMmC,SAA4B,EAAE;QAEpC,KAAK,MAAMpC,OAAON,cAAe;YAC/B,MAAMsB,SAAS,MAAM,IAAI,CAACU,QAAQ,CAAC9B,UAAUG,QAAQ,CAACD,UAAUE,KAAKC;YACrE,IAAIe,QAAQ;gBACVoB,OAAOC,IAAI,CAAC;oBAAErC;oBAAKgB;gBAAO;YAC5B;QACF;QAEA,OAAOoB;IACT;IAEA,MAAaE,iBAAiBC,KAA8B,EAAiB;QAC3E,MAAMC,QAAQC,GAAG,CAAC;YAChB,IAAI,CAAC1B,SAAS,CAACwB,MAAMG,gBAAgB,EAAE9C,UAAUM,MAAM,CAACqC,MAAMpC,UAAU,EAAE;YAC1E,IAAI,CAACY,SAAS,CAACwB,MAAMI,WAAW,EAAE/C,UAAUM,MAAM,CAACqC,MAAMpC,UAAU,EAAE;YACrE,IAAI,CAACY,SAAS,CAACwB,MAAMK,YAAY,EAAEhD,UAAUM,MAAM,CAACqC,MAAMpC,UAAU,EAAE;SACvE;IACH;IAEA,MAAa0C,gBAAgB1C,UAAkB,EAAuC;QACpF,MAAM2C,UAAU,MAAMN,QAAQC,GAAG,CAC/B9C,mBAAmBoD,GAAG,CAAC,CAAC3C,OAAS,IAAI,CAACsB,QAAQ,CAAC9B,UAAUM,MAAM,CAACC,YAAYC;QAG9E,IAAI0C,QAAQE,KAAK,CAAC,CAACC,IAAM,CAACA,IAAI;YAC5B,OAAOrB;QACT;QAEA,+GAA+G;QAC/G,OAAO;YACLc,kBAAkBI,OAAO,CAAC,EAAE,IAAI;YAChCF,cAAcE,OAAO,CAAC,EAAE,IAAI;YAC5BH,aAAaG,OAAO,CAAC,EAAE,IAAI;QAC7B;IACF;IAEA,MAAaI,sBACXX,KAA+B,EACK;QACpC,iBAAiB;QACjB/C,oBAAoB+C,MAAMvB,MAAM;QAEhC,8BAA8B;QAC9B,MAAMmC,OAAO9D;QAEb,wBAAwB;QACxB,MAAM+D,eAAe,CAAC,kBAAkB,EAAEb,MAAMzC,QAAQ,CAAC,CAAC,EAAEqD,KAAK,IAAI,CAAC;QAEtE,6BAA6B;QAC7B,MAAME,aAAad,MAAMvB,MAAM,CAACsC,OAAO,CAAC,4BAA4B;QACpE,MAAMnC,SAASoC,OAAOC,IAAI,CAACH,YAAY;QAEvC,MAAMhC,OAAO,IAAI,CAACf,MAAM,CAACe,IAAI,CAAC+B;QAC9B,MAAM/B,KAAKC,IAAI,CAACH,QAAQ;YACtBsC,UAAU;gBACRlC,aAAa;YACf;QACF;QAEA,4BAA4B;QAC5B,MAAMmC,YAAYnB,MAAMmB,SAAS,IAAI,IAAIC;QACzC,MAAMC,SAAS,MAAMnE,kBAAkBoE,MAAM,CAAC;YAC5CV;YACArD,UAAUyC,MAAMzC,QAAQ;YACxBsD;YACAM;YACAzD,WAAWsC,MAAMtC,SAAS,IAAI;QAChC;QAEA,OAAO2D,OAAOE,MAAM;IACtB;IAEA,MAAMC,sBACJjE,QAAgB,EACsB;QACtC,MAAMkE,UAAU,MAAMvE,kBAAkBwE,OAAO,CAAC;YAC9CC,OAAO;gBAAEpE;YAAS;YAClBqE,OAAO;gBAAC;oBAAC;oBAAa;iBAAO;aAAC;YAC9BC,KAAK;QACP;QAEA,OAAOJ;IACT;IAEA,MAAMK,2BACJpE,SAAiB,EACqB;QACtC,MAAM+D,UAAU,MAAMvE,kBAAkBwE,OAAO,CAAC;YAC9CC,OAAO;gBAAEjE;YAAU;YACnBkE,OAAO;gBAAC;oBAAC;oBAAa;iBAAO;aAAC;YAC9BC,KAAK;QACP;QAEA,OAAOJ;IACT;IAEA,MAAMM,wBACJ/B,KAAqC,EACM;QAC3C,MAAM,CAACgC,aAAa,GAAG,MAAM9E,kBAAkB+E,MAAM,CACnD;YAAEd,WAAWnB,MAAMmB,SAAS;QAAC,GAC7B;YACEQ,OAAO;gBACLf,MAAMZ,MAAMY,IAAI;gBAChBrD,UAAUyC,MAAMzC,QAAQ;YAC1B;QACF;QAGF,IAAIyE,iBAAiB,GAAG;YACtB,OAAO,MAAM,yCAAyC;QACxD;QAEA,MAAME,UAAU,MAAMhF,kBAAkBiF,OAAO,CAAC;YAC9CR,OAAO;gBAAEf,MAAMZ,MAAMY,IAAI;YAAC;YAC1BiB,KAAK;QACP;QAEA,OAAOK;IACT;IAEA,MAAME,oBACJxB,IAAY,EACZrD,QAAgB,EACE;QAClB,yCAAyC;QACzC,MAAM8E,QAAQ,MAAMnF,kBAAkBiF,OAAO,CAAC;YAC5CR,OAAO;gBAAEf;gBAAMrD;YAAS;YACxBsE,KAAK;QACP;QAEA,IAAI,CAACQ,OAAO;YACV,OAAO,OAAO,yCAAyC;QACzD;QAEA,+BAA+B;QAC/B,MAAMvD,OAAO,IAAI,CAACf,MAAM,CAACe,IAAI,CAACuD,MAAMxB,YAAY;QAChD,IAAI;YACF,MAAM/B,KAAKwD,MAAM;QACnB,EAAE,OAAOrD,OAAO;YACd,oEAAoE;YACpEsD,QAAQC,IAAI,CAAC,CAAC,oCAAoC,EAAEH,MAAMxB,YAAY,EAAE,EAAE5B;QAC5E;QAEA,uBAAuB;QACvB,MAAM/B,kBAAkBuF,OAAO,CAAC;YAC9Bd,OAAO;gBAAEf;gBAAMrD;YAAS;QAC1B;QAEA,OAAO;IACT;IAEQsB,eAAeJ,MAAc,EAAU;QAC7C,MAAMiE,OAAOjE,OAAOkE,QAAQ,CAAC,OAAOlE,OAAOmE,KAAK,CAAC,IAAI,CAAC,EAAE,GAAInE;QAC5D,OAAOuC,OAAOC,IAAI,CAACyB,MAAM;IAC3B;IAEQxD,YAAYD,KAAc,EAAE4D,MAAc,EAAS;QACzD,IAAI,CAAC7E,MAAM,CAACiB,KAAK,CAAC,CAAC,0BAA0B,EAAE4D,QAAQ,EAAE;YAAE5D;QAAM;QACjE,MAAM,IAAIlC,iBACR,CAAC,wBAAwB,EAAEkC,iBAAiB6D,QAAQ7D,MAAM8D,OAAO,GAAGC,OAAO/D,QAAQ,EACnF,CAAC,mCAAmC,EAAE4D,QAAQ;IAElD;AACF"}
@@ -18,4 +18,24 @@ export interface SaveRecipePhotosInput {
18
18
  coverBase64: string;
19
19
  methodBase64: string;
20
20
  }
21
+ export interface UserProgressPhotoInput {
22
+ userUuid: string;
23
+ base64: string;
24
+ photoDate?: Date;
25
+ programId?: string;
26
+ }
27
+ export interface UserProgressPhotoRecord {
28
+ uuid: string;
29
+ userUuid: string;
30
+ firebasePath: string;
31
+ photoDate: Date;
32
+ programId: string | null;
33
+ createdAt: Date;
34
+ updatedAt: Date;
35
+ }
36
+ export interface UpdateProgressPhotoDateInput {
37
+ uuid: string;
38
+ userUuid: string;
39
+ photoDate: Date;
40
+ }
21
41
  //# sourceMappingURL=images.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"images.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/clients/types/images.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,MAAM,WAAW,MAAM;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"images.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/clients/types/images.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,MAAM,WAAW,MAAM;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;CACjB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/lib/clients/types/images.types.ts"],"sourcesContent":["import type admin from \"firebase-admin\";\n\nexport interface Config {\n storageBucket: string;\n firebaseApp?: admin.app.App;\n}\n\nexport interface ProgressPhoto {\n day: number;\n base64: string;\n}\n\nexport interface RecipePhotos {\n ingredientBase64: string;\n coverBase64: string;\n methodBase64: string;\n}\n\nexport interface SaveRecipePhotosInput {\n recipeUuid: string;\n ingredientBase64: string;\n coverBase64: string;\n methodBase64: string;\n}\n"],"names":[],"mappings":"AAkBA,WAKC"}
1
+ {"version":3,"sources":["../../../../src/lib/clients/types/images.types.ts"],"sourcesContent":["import type admin from \"firebase-admin\";\n\nexport interface Config {\n storageBucket: string;\n firebaseApp?: admin.app.App;\n}\n\nexport interface ProgressPhoto {\n day: number;\n base64: string;\n}\n\nexport interface RecipePhotos {\n ingredientBase64: string;\n coverBase64: string;\n methodBase64: string;\n}\n\nexport interface SaveRecipePhotosInput {\n recipeUuid: string;\n ingredientBase64: string;\n coverBase64: string;\n methodBase64: string;\n}\n\nexport interface UserProgressPhotoInput {\n userUuid: string;\n base64: string;\n photoDate?: Date; // Optional, defaults to now\n programId?: string; // Optional, for legacy program association\n}\n\nexport interface UserProgressPhotoRecord {\n uuid: string;\n userUuid: string;\n firebasePath: string;\n photoDate: Date;\n programId: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpdateProgressPhotoDateInput {\n uuid: string;\n userUuid: string;\n photoDate: Date;\n}\n"],"names":[],"mappings":"AA0CA,WAIC"}
@@ -0,0 +1,7 @@
1
+ import { QueryInterface } from "sequelize";
2
+ declare const _default: {
3
+ up(queryInterface: QueryInterface): Promise<void>;
4
+ down(queryInterface: QueryInterface): Promise<void>;
5
+ };
6
+ export default _default;
7
+ //# sourceMappingURL=20260330000000-create-user-progress-photos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260330000000-create-user-progress-photos.d.ts","sourceRoot":"","sources":["../../../../src/lib/db/migrations/20260330000000-create-user-progress-photos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAa,MAAM,WAAW,CAAC;;uBAG3B,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;yBAsD5B,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;;AAvD3D,wBA0DE"}
@@ -0,0 +1,60 @@
1
+ import { DataTypes } from "sequelize";
2
+ export default {
3
+ async up (queryInterface) {
4
+ // Ensure uuid-ossp extension exists
5
+ await queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";');
6
+ await queryInterface.createTable("UserProgressPhotos", {
7
+ uuid: {
8
+ type: DataTypes.UUID,
9
+ defaultValue: queryInterface.sequelize.literal("uuid_generate_v4()"),
10
+ allowNull: false,
11
+ primaryKey: true
12
+ },
13
+ userUuid: {
14
+ type: DataTypes.UUID,
15
+ allowNull: false,
16
+ references: {
17
+ model: "PersistedUsers",
18
+ key: "userUuid"
19
+ },
20
+ onUpdate: "CASCADE",
21
+ onDelete: "CASCADE"
22
+ },
23
+ firebasePath: {
24
+ type: DataTypes.STRING,
25
+ allowNull: false
26
+ },
27
+ photoDate: {
28
+ type: DataTypes.DATE,
29
+ allowNull: false
30
+ },
31
+ programId: {
32
+ type: DataTypes.UUID,
33
+ allowNull: true
34
+ },
35
+ createdAt: {
36
+ type: DataTypes.DATE,
37
+ allowNull: false,
38
+ defaultValue: queryInterface.sequelize.literal("CURRENT_TIMESTAMP")
39
+ },
40
+ updatedAt: {
41
+ type: DataTypes.DATE,
42
+ allowNull: false,
43
+ defaultValue: queryInterface.sequelize.literal("CURRENT_TIMESTAMP")
44
+ }
45
+ });
46
+ // Add index on userUuid for efficient querying
47
+ await queryInterface.addIndex("UserProgressPhotos", [
48
+ "userUuid"
49
+ ]);
50
+ // Add index on programId for legacy support
51
+ await queryInterface.addIndex("UserProgressPhotos", [
52
+ "programId"
53
+ ]);
54
+ },
55
+ async down (queryInterface) {
56
+ await queryInterface.dropTable("UserProgressPhotos");
57
+ }
58
+ };
59
+
60
+ //# sourceMappingURL=20260330000000-create-user-progress-photos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/lib/db/migrations/20260330000000-create-user-progress-photos.ts"],"sourcesContent":["import { QueryInterface, DataTypes } from \"sequelize\";\n\nexport default {\n async up(queryInterface: QueryInterface): Promise<void> {\n // Ensure uuid-ossp extension exists\n await queryInterface.sequelize.query(\n 'CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";'\n );\n\n await queryInterface.createTable(\"UserProgressPhotos\", {\n uuid: {\n type: DataTypes.UUID,\n defaultValue: queryInterface.sequelize.literal(\"uuid_generate_v4()\"),\n allowNull: false,\n primaryKey: true,\n },\n userUuid: {\n type: DataTypes.UUID,\n allowNull: false,\n references: {\n model: \"PersistedUsers\",\n key: \"userUuid\",\n },\n onUpdate: \"CASCADE\",\n onDelete: \"CASCADE\",\n },\n firebasePath: {\n type: DataTypes.STRING,\n allowNull: false,\n },\n photoDate: {\n type: DataTypes.DATE,\n allowNull: false,\n },\n programId: {\n type: DataTypes.UUID,\n allowNull: true,\n },\n createdAt: {\n type: DataTypes.DATE,\n allowNull: false,\n defaultValue: queryInterface.sequelize.literal(\"CURRENT_TIMESTAMP\"),\n },\n updatedAt: {\n type: DataTypes.DATE,\n allowNull: false,\n defaultValue: queryInterface.sequelize.literal(\"CURRENT_TIMESTAMP\"),\n },\n });\n\n // Add index on userUuid for efficient querying\n await queryInterface.addIndex(\"UserProgressPhotos\", [\"userUuid\"]);\n\n // Add index on programId for legacy support\n await queryInterface.addIndex(\"UserProgressPhotos\", [\"programId\"]);\n },\n\n async down(queryInterface: QueryInterface): Promise<void> {\n await queryInterface.dropTable(\"UserProgressPhotos\");\n },\n};\n"],"names":["DataTypes","up","queryInterface","sequelize","query","createTable","uuid","type","UUID","defaultValue","literal","allowNull","primaryKey","userUuid","references","model","key","onUpdate","onDelete","firebasePath","STRING","photoDate","DATE","programId","createdAt","updatedAt","addIndex","down","dropTable"],"mappings":"AAAA,SAAyBA,SAAS,QAAQ,YAAY;AAEtD,eAAe;IACb,MAAMC,IAAGC,cAA8B;QACrC,oCAAoC;QACpC,MAAMA,eAAeC,SAAS,CAACC,KAAK,CAClC;QAGF,MAAMF,eAAeG,WAAW,CAAC,sBAAsB;YACrDC,MAAM;gBACJC,MAAMP,UAAUQ,IAAI;gBACpBC,cAAcP,eAAeC,SAAS,CAACO,OAAO,CAAC;gBAC/CC,WAAW;gBACXC,YAAY;YACd;YACAC,UAAU;gBACRN,MAAMP,UAAUQ,IAAI;gBACpBG,WAAW;gBACXG,YAAY;oBACVC,OAAO;oBACPC,KAAK;gBACP;gBACAC,UAAU;gBACVC,UAAU;YACZ;YACAC,cAAc;gBACZZ,MAAMP,UAAUoB,MAAM;gBACtBT,WAAW;YACb;YACAU,WAAW;gBACTd,MAAMP,UAAUsB,IAAI;gBACpBX,WAAW;YACb;YACAY,WAAW;gBACThB,MAAMP,UAAUQ,IAAI;gBACpBG,WAAW;YACb;YACAa,WAAW;gBACTjB,MAAMP,UAAUsB,IAAI;gBACpBX,WAAW;gBACXF,cAAcP,eAAeC,SAAS,CAACO,OAAO,CAAC;YACjD;YACAe,WAAW;gBACTlB,MAAMP,UAAUsB,IAAI;gBACpBX,WAAW;gBACXF,cAAcP,eAAeC,SAAS,CAACO,OAAO,CAAC;YACjD;QACF;QAEA,+CAA+C;QAC/C,MAAMR,eAAewB,QAAQ,CAAC,sBAAsB;YAAC;SAAW;QAEhE,4CAA4C;QAC5C,MAAMxB,eAAewB,QAAQ,CAAC,sBAAsB;YAAC;SAAY;IACnE;IAEA,MAAMC,MAAKzB,cAA8B;QACvC,MAAMA,eAAe0B,SAAS,CAAC;IACjC;AACF,EAAE"}
@@ -0,0 +1,13 @@
1
+ import { Model } from "sequelize-typescript";
2
+ import { PersistedUser } from "../../dbmodels/user/PersistedUser";
3
+ export declare class UserProgressPhoto extends Model {
4
+ uuid: string;
5
+ userUuid: string;
6
+ user: PersistedUser;
7
+ firebasePath: string;
8
+ photoDate: Date;
9
+ programId: string | null;
10
+ createdAt: Date;
11
+ updatedAt: Date;
12
+ }
13
+ //# sourceMappingURL=UserProgressPhoto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserProgressPhoto.d.ts","sourceRoot":"","sources":["../../../../src/lib/db/models/UserProgressPhoto.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,EAMN,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAElE,qBAIa,iBAAkB,SAAQ,KAAK;IAQlC,IAAI,EAAE,MAAM,CAAC;IAQb,QAAQ,EAAE,MAAM,CAAC;IAGjB,IAAI,EAAE,aAAa,CAAC;IAOpB,YAAY,EAAE,MAAM,CAAC;IAOrB,SAAS,EAAE,IAAI,CAAC;IAOhB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACzB"}
@@ -0,0 +1,59 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { Table, Column, Model, DataType, ForeignKey, BelongsTo, Default, AllowNull } from "sequelize-typescript";
8
+ import { PersistedUser } from "../../dbmodels/user/PersistedUser";
9
+ export class UserProgressPhoto extends Model {
10
+ }
11
+ _ts_decorate([
12
+ Default(DataType.UUIDV4),
13
+ Column({
14
+ type: DataType.UUID,
15
+ defaultValue: DataType.UUID,
16
+ allowNull: false,
17
+ primaryKey: true
18
+ })
19
+ ], UserProgressPhoto.prototype, "uuid", void 0);
20
+ _ts_decorate([
21
+ ForeignKey(()=>PersistedUser),
22
+ AllowNull(false),
23
+ Column({
24
+ type: DataType.UUID,
25
+ allowNull: false
26
+ })
27
+ ], UserProgressPhoto.prototype, "userUuid", void 0);
28
+ _ts_decorate([
29
+ BelongsTo(()=>PersistedUser)
30
+ ], UserProgressPhoto.prototype, "user", void 0);
31
+ _ts_decorate([
32
+ AllowNull(false),
33
+ Column({
34
+ type: DataType.STRING,
35
+ allowNull: false
36
+ })
37
+ ], UserProgressPhoto.prototype, "firebasePath", void 0);
38
+ _ts_decorate([
39
+ AllowNull(false),
40
+ Column({
41
+ type: DataType.DATE,
42
+ allowNull: false
43
+ })
44
+ ], UserProgressPhoto.prototype, "photoDate", void 0);
45
+ _ts_decorate([
46
+ AllowNull(true),
47
+ Column({
48
+ type: DataType.UUID,
49
+ allowNull: true
50
+ })
51
+ ], UserProgressPhoto.prototype, "programId", void 0);
52
+ UserProgressPhoto = _ts_decorate([
53
+ Table({
54
+ tableName: "UserProgressPhotos",
55
+ timestamps: true
56
+ })
57
+ ], UserProgressPhoto);
58
+
59
+ //# sourceMappingURL=UserProgressPhoto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/lib/db/models/UserProgressPhoto.ts"],"sourcesContent":["import {\n Table,\n Column,\n Model,\n DataType,\n ForeignKey,\n BelongsTo,\n Default,\n AllowNull,\n} from \"sequelize-typescript\";\nimport { PersistedUser } from \"../../dbmodels/user/PersistedUser\";\n\n@Table({\n tableName: \"UserProgressPhotos\",\n timestamps: true,\n})\nexport class UserProgressPhoto extends Model {\n @Default(DataType.UUIDV4)\n @Column({\n type: DataType.UUID,\n defaultValue: DataType.UUID,\n allowNull: false,\n primaryKey: true,\n })\n declare uuid: string;\n\n @ForeignKey(() => PersistedUser)\n @AllowNull(false)\n @Column({\n type: DataType.UUID,\n allowNull: false,\n })\n declare userUuid: string;\n\n @BelongsTo(() => PersistedUser)\n declare user: PersistedUser;\n\n @AllowNull(false)\n @Column({\n type: DataType.STRING,\n allowNull: false,\n })\n declare firebasePath: string;\n\n @AllowNull(false)\n @Column({\n type: DataType.DATE,\n allowNull: false,\n })\n declare photoDate: Date;\n\n @AllowNull(true)\n @Column({\n type: DataType.UUID,\n allowNull: true,\n })\n declare programId: string | null;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n}\n"],"names":["Table","Column","Model","DataType","ForeignKey","BelongsTo","Default","AllowNull","PersistedUser","UserProgressPhoto","UUIDV4","type","UUID","defaultValue","allowNull","primaryKey","STRING","DATE","tableName","timestamps"],"mappings":";;;;;;AAAA,SACEA,KAAK,EACLC,MAAM,EACNC,KAAK,EACLC,QAAQ,EACRC,UAAU,EACVC,SAAS,EACTC,OAAO,EACPC,SAAS,QACJ,uBAAuB;AAC9B,SAASC,aAAa,QAAQ,oCAAoC;AAMlE,OAAO,MAAMC,0BAA0BP;AA4CvC;;qBA3CoBQ;;QAEhBC,MAAMR,SAASS,IAAI;QACnBC,cAAcV,SAASS,IAAI;QAC3BE,WAAW;QACXC,YAAY;;;;mBAIIP;;;QAGhBG,MAAMR,SAASS,IAAI;QACnBE,WAAW;;;;kBAIIN;;;;;QAKfG,MAAMR,SAASa,MAAM;QACrBF,WAAW;;;;;;QAMXH,MAAMR,SAASc,IAAI;QACnBH,WAAW;;;;;;QAMXH,MAAMR,SAASS,IAAI;QACnBE,WAAW;;;;;QAzCbI,WAAW;QACXC,YAAY"}
@@ -0,0 +1,2 @@
1
+ export { UserProgressPhoto } from "./UserProgressPhoto";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/db/models/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { UserProgressPhoto } from "./UserProgressPhoto";
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/lib/db/models/index.ts"],"sourcesContent":["export { UserProgressPhoto } from \"./UserProgressPhoto\";\n"],"names":["UserProgressPhoto"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,sBAAsB"}
@@ -0,0 +1,5 @@
1
+ export declare class ImageValidationError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function validateBase64Image(base64: string): void;
5
+ //# sourceMappingURL=imageValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageValidation.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/imageValidation.ts"],"names":[],"mappings":"AAAA,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAoCxD"}
@@ -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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "90dc-core",
3
- "version": "1.18.1",
3
+ "version": "1.19.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",