diff --git a/.env.example b/.env.example index 74bec8d..9e3fb26 100644 --- a/.env.example +++ b/.env.example @@ -37,6 +37,9 @@ THROTTLE_LIMIT=120 MULTER_PRODUCT_THUMB_ALLOWED_MIMES=image/jpeg,image/png MULTER_PRODUCT_THUMB_MAX_FILE_BYTES=1048576 # 1MB +MULTER_BLOG_IMAGE_ALLOWED_MIMES=image/jpeg,image/png +MULTER_BLOG_IMAGE_MAX_FILE_BYTES=1048576 # 1MB + MULTER_SHOP_LOGO_ALLOWED_MIMES=image/jpeg,image/png MULTER_SHOP_LOGO_MAX_FILE_BYTES=1048576 # 1MB @@ -135,6 +138,8 @@ VITE_API_BASE_URL=http://localhost:3000/api VITE_SHOP_FIAT_CURRENCY=USD VITE_PRODUCT_THUMB_ALLOWED_MIMES=image/jpeg,image/png VITE_PRODUCT_THUMB_MAX_FILE_BYTES=1048576 # 1MB +VITE_BLOG_IMAGE_ALLOWED_MIMES=image/jpeg,image/png +VITE_BLOG_IMAGE_MAX_FILE_BYTES=1048576 # 1MB VITE_SHOP_LOGO_ALLOWED_MIMES=image/jpeg,image/png VITE_SHOP_LOGO_MAX_FILE_BYTES=1048576 # 1MB VITE_SHOP_FAVICON_ALLOWED_MIMES=image/png,image/x-icon,image/vnd.microsoft.icon @@ -142,6 +147,7 @@ VITE_SHOP_FAVICON_MAX_FILE_BYTES=262144 # 256KB VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES=application/pdf,application/zip,image/jpeg,image/png VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES=5242880 # 5MB VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH=100 +VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH=200 VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH=50 VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH=32 VITE_VALIDATION_VARIANT_IMAGES_MAX=10 diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 22abba6..8235476 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -128,6 +128,17 @@ export const getProductThumbMulterConfig = (): MulterConfig => { return { allowedMimes, maxFileBytes }; }; +export const getBlogImageMulterConfig = (): MulterConfig => { + const allowedMimes = env('MULTER_BLOG_IMAGE_ALLOWED_MIMES') + .split(',') + .map(v => v.trim()) + .filter(Boolean); + + const maxFileBytes = envInt('MULTER_BLOG_IMAGE_MAX_FILE_BYTES'); + + return { allowedMimes, maxFileBytes }; +}; + export const getShopLogoMulterConfig = (): MulterConfig => { const allowedMimes = env('MULTER_SHOP_LOGO_ALLOWED_MIMES') .split(',') diff --git a/backend/src/config/validate.ts b/backend/src/config/validate.ts index 9a7eca8..39b6901 100644 --- a/backend/src/config/validate.ts +++ b/backend/src/config/validate.ts @@ -75,6 +75,16 @@ class EnvironmentVariables { @Max(10 * 1024 * 1024) // 10MB MULTER_PRODUCT_THUMB_MAX_FILE_BYTES: number; + @IsNotEmpty() + @IsString() + MULTER_BLOG_IMAGE_ALLOWED_MIMES: string; + + @IsNotEmpty() + @IsNumber() + @Min(1) + @Max(10 * 1024 * 1024) // 10MB + MULTER_BLOG_IMAGE_MAX_FILE_BYTES: number; + @IsNotEmpty() @IsString() MULTER_SHOP_LOGO_ALLOWED_MIMES: string; diff --git a/backend/src/modules/blog/config/blogImageUpload.ts b/backend/src/modules/blog/config/blogImageUpload.ts index 38f4927..7ed9361 100644 --- a/backend/src/modules/blog/config/blogImageUpload.ts +++ b/backend/src/modules/blog/config/blogImageUpload.ts @@ -1,4 +1,4 @@ -import { getProductThumbMulterConfig } from '../../../config'; +import { getBlogImageMulterConfig } from '../../../config'; import { getBlogImagesDir } from '../../../config/uploadPaths'; import { createDiskStorageUploadOptions } from '../../../utils/createDiskStorageUploadOptions'; import { createUploadFilePipe } from '../../../utils/createUploadFilePipe'; @@ -7,6 +7,6 @@ const uploadDir = getBlogImagesDir(); export const blogImageUploadOptions = createDiskStorageUploadOptions(uploadDir); -const { allowedMimes, maxFileBytes } = getProductThumbMulterConfig(); +const { allowedMimes, maxFileBytes } = getBlogImageMulterConfig(); export const blogImageFilePipe = createUploadFilePipe(allowedMimes, maxFileBytes); diff --git a/cms/src/config/index.ts b/cms/src/config/index.ts index 619db67..f7a8135 100644 --- a/cms/src/config/index.ts +++ b/cms/src/config/index.ts @@ -19,6 +19,11 @@ export const config = { maxFileBytes: parseInt(env('VITE_PRODUCT_THUMB_MAX_FILE_BYTES'), 10) }, + blogImage: { + accept: env('VITE_BLOG_IMAGE_ALLOWED_MIMES'), + maxFileBytes: parseInt(env('VITE_BLOG_IMAGE_MAX_FILE_BYTES'), 10) + }, + shopLogo: { accept: env('VITE_SHOP_LOGO_ALLOWED_MIMES'), maxFileBytes: parseInt(env('VITE_SHOP_LOGO_MAX_FILE_BYTES'), 10) @@ -36,6 +41,7 @@ export const config = { validation: { productTitleMaxLength: parseInt(env('VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH'), 10), + blogPostTitleMaxLength: parseInt(env('VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH'), 10), categoryNameMaxLength: parseInt(env('VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH'), 10), discountCodeMaxLength: parseInt(env('VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH'), 10), variantImagesMax: parseInt(env('VITE_VALIDATION_VARIANT_IMAGES_MAX'), 10), diff --git a/cms/src/vite-env.d.ts b/cms/src/vite-env.d.ts index a9827d8..328ac97 100644 --- a/cms/src/vite-env.d.ts +++ b/cms/src/vite-env.d.ts @@ -15,6 +15,8 @@ interface ImportMetaEnv { readonly VITE_API_BASE_URL?: string; readonly VITE_PRODUCT_THUMB_ALLOWED_MIMES?: string; readonly VITE_PRODUCT_THUMB_MAX_FILE_BYTES?: string; + readonly VITE_BLOG_IMAGE_ALLOWED_MIMES?: string; + readonly VITE_BLOG_IMAGE_MAX_FILE_BYTES?: string; readonly VITE_SHOP_LOGO_ALLOWED_MIMES?: string; readonly VITE_SHOP_LOGO_MAX_FILE_BYTES?: string; readonly VITE_SHOP_FAVICON_ALLOWED_MIMES?: string; @@ -22,6 +24,7 @@ interface ImportMetaEnv { readonly VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES?: string; readonly VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES?: string; readonly VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH?: string; + readonly VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH?: string; readonly VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH?: string; readonly VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH?: string; readonly VITE_VALIDATION_VARIANT_IMAGES_MAX?: string; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 620bf65..ee30a26 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -126,6 +126,8 @@ services: VITE_SHOP_FIAT_CURRENCY: ${VITE_SHOP_FIAT_CURRENCY} VITE_PRODUCT_THUMB_ALLOWED_MIMES: ${VITE_PRODUCT_THUMB_ALLOWED_MIMES} VITE_PRODUCT_THUMB_MAX_FILE_BYTES: ${VITE_PRODUCT_THUMB_MAX_FILE_BYTES} + VITE_BLOG_IMAGE_ALLOWED_MIMES: ${VITE_BLOG_IMAGE_ALLOWED_MIMES} + VITE_BLOG_IMAGE_MAX_FILE_BYTES: ${VITE_BLOG_IMAGE_MAX_FILE_BYTES} VITE_SHOP_LOGO_ALLOWED_MIMES: ${VITE_SHOP_LOGO_ALLOWED_MIMES} VITE_SHOP_LOGO_MAX_FILE_BYTES: ${VITE_SHOP_LOGO_MAX_FILE_BYTES} VITE_SHOP_FAVICON_ALLOWED_MIMES: ${VITE_SHOP_FAVICON_ALLOWED_MIMES} @@ -133,6 +135,7 @@ services: VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES: ${VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES} VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES: ${VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES} VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH: ${VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH} + VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH: ${VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH} VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH: ${VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH} VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH: ${VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH} VITE_VALIDATION_VARIANT_IMAGES_MAX: ${VITE_VALIDATION_VARIANT_IMAGES_MAX} diff --git a/nginx/Dockerfile.prod b/nginx/Dockerfile.prod index 518cd06..4260859 100644 --- a/nginx/Dockerfile.prod +++ b/nginx/Dockerfile.prod @@ -11,6 +11,8 @@ ARG VITE_API_BASE_URL ARG VITE_SHOP_FIAT_CURRENCY ARG VITE_PRODUCT_THUMB_ALLOWED_MIMES ARG VITE_PRODUCT_THUMB_MAX_FILE_BYTES +ARG VITE_BLOG_IMAGE_ALLOWED_MIMES +ARG VITE_BLOG_IMAGE_MAX_FILE_BYTES ARG VITE_SHOP_LOGO_ALLOWED_MIMES ARG VITE_SHOP_LOGO_MAX_FILE_BYTES ARG VITE_SHOP_FAVICON_ALLOWED_MIMES @@ -18,6 +20,7 @@ ARG VITE_SHOP_FAVICON_MAX_FILE_BYTES ARG VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES ARG VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES ARG VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH +ARG VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH ARG VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH ARG VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH ARG VITE_VALIDATION_VARIANT_IMAGES_MAX @@ -32,6 +35,8 @@ ENV VITE_API_BASE_URL=$VITE_API_BASE_URL \ VITE_SHOP_FIAT_CURRENCY=$VITE_SHOP_FIAT_CURRENCY \ VITE_PRODUCT_THUMB_ALLOWED_MIMES=$VITE_PRODUCT_THUMB_ALLOWED_MIMES \ VITE_PRODUCT_THUMB_MAX_FILE_BYTES=$VITE_PRODUCT_THUMB_MAX_FILE_BYTES \ + VITE_BLOG_IMAGE_ALLOWED_MIMES=$VITE_BLOG_IMAGE_ALLOWED_MIMES \ + VITE_BLOG_IMAGE_MAX_FILE_BYTES=$VITE_BLOG_IMAGE_MAX_FILE_BYTES \ VITE_SHOP_LOGO_ALLOWED_MIMES=$VITE_SHOP_LOGO_ALLOWED_MIMES \ VITE_SHOP_LOGO_MAX_FILE_BYTES=$VITE_SHOP_LOGO_MAX_FILE_BYTES \ VITE_SHOP_FAVICON_ALLOWED_MIMES=$VITE_SHOP_FAVICON_ALLOWED_MIMES \ @@ -39,6 +44,7 @@ ENV VITE_API_BASE_URL=$VITE_API_BASE_URL \ VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES=$VITE_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES \ VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES=$VITE_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES \ VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH=$VITE_VALIDATION_PRODUCT_TITLE_MAX_LENGTH \ + VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH=$VITE_VALIDATION_BLOG_POST_TITLE_MAX_LENGTH \ VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH=$VITE_VALIDATION_CATEGORY_NAME_MAX_LENGTH \ VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH=$VITE_VALIDATION_DISCOUNT_CODE_MAX_LENGTH \ VITE_VALIDATION_VARIANT_IMAGES_MAX=$VITE_VALIDATION_VARIANT_IMAGES_MAX \