Media & CDN Strategy
This document details how Skin Club Pro handles media assets, including system images (logos, banners) and sensitive patient data (before/after photos).
1. Overview
The platform uses a centralized media strategy leveraging AWS S3 for storage and AWS CloudFront for content delivery. We distinguish between Public Assets and Private/Sensitive Assets.
2. Public Assets (System Assets)
Public assets are images used across the application that do not contain sensitive data.
- Storage: Stored in the
public/prefix of the S3 bucket. - Access: Publicly readable via a CloudFront distribution.
- Caching: Aggressively cached at the edge (TTL: 1 year) with
Cache-Control: public, max-age=31536000. - Examples: Clinic logos, email header banners, marketing images.
Uploading System Assets
We use a dedicated script scripts/upload-system-assets.js to manage these.
node scripts/upload-system-assets.js --env=prod
3. Private Assets (Patient Data)
Sensitive assets like patient photos require strict access control.
- Storage: Stored in tenant-specific prefixes:
{tenantId}/{uuid}-{filename}. - Access: Restricted at the S3 bucket level. Access is only granted via Presigned URLs.
- Expiration: Presigned URLs are generated with a short TTL (e.g., 1 hour) to ensure they cannot be shared or leaked.
- Examples: Before/After treatment photos, scanned consent forms.
4. Infrastructure Details
S3 Bucket Configuration
- Bucket Name:
{env}-skinclub-pro-media - Encryption: AES-256 server-side encryption enabled.
- CORS: Configured to allow requests from
*.skinclubpro.com.
CloudFront Distribution
- Origin: S3 bucket.
- SSL: Managed via ACM certificate for
media.skinclubpro.com. - Behaviors:
/public/*: AllowedGETandHEADrequests.- Default: Restricted access (requires signed URLs or internal service access).
5. Implementation in Services
Services interact with media via the MediaService in the CMS or Clinic services.
Generating a Presigned URL
async getSignedUrl(key: string): Promise<string> {
const command = new GetObjectCommand({
Bucket: this.bucket,
Key: key,
});
return getSignedUrl(this.s3Client, command, { expiresIn: 3600 });
}
Public Asset URL Fallback
If a CDN is not configured (e.g., in local development), the service falls back to direct S3 URLs or LocalStack endpoints.