Files
AdventureLog/backend/server/users/media_utils.py
Sean Morley 59a103ea8c Cloud (#1170)
* Add new default light and dark themes to match AdventureLog styles

* Allow different theme for site and maps
Fixes #1130

* Update settings page to use translation keys for currency and map style descriptions

* Add global flag to CollectionItineraryItemSerializer and implement map view persistence in frontend

* Update appVersion to v0.12.0-main-052126

* Enhance permissions for shared collections and restrict publicity changes to owners only

* Update appVersion to v0.12.1-beta-052126

* Add new localization strings and descriptions for various languages

- Updated Spanish (es.json) with new map style and preferred currency descriptions.
- Enhanced French (fr.json) with additional details for Strava activities and currency descriptions.
- Added new strings in Hungarian (hu.json) for map style and preferred currency.
- Included additional details in Italian (it.json) for Strava activities and currency descriptions.
- Updated Japanese (ja.json) with new strings for map style and preferred currency.
- Enhanced Korean (ko.json) with additional details for Strava activities and currency descriptions.
- Added new strings in Dutch (nl.json) for map style and preferred currency.
- Updated Norwegian (no.json) with new strings for map style and preferred currency.
- Enhanced Polish (pl.json) with additional details for Strava activities and currency descriptions.
- Updated Brazilian Portuguese (pt-br.json) with new map style and preferred currency descriptions.
- Added new strings in Romanian (ro.json) for additional details and currency descriptions.
- Enhanced Russian (ru.json) with new strings for map style and preferred currency.
- Updated Slovak (sk.json) with additional details for map style and preferred currency.
- Enhanced Swedish (sv.json) with new strings for map style and preferred currency.
- Added new strings in Turkish (tr.json) for map style and preferred currency.
- Updated Ukrainian (uk.json) with new strings for map style and preferred currency.
- Enhanced Chinese (zh.json) with additional details for map style and preferred currency.

* feat: Implement cloud billing system with subscription management

- Added billing app with models, serializers, views, and admin configuration for Subscription.
- Integrated Stripe for payment processing and subscription management.
- Created middleware for cloud access control based on user subscriptions.
- Developed frontend components for subscription status and billing management.
- Updated settings to include cloud mode and Stripe configuration.
- Added routes for billing and subscription management in the API.
- Implemented user subscription creation and management on user registration and checkout.

* feat: Implement media storage limit and usage tracking

- Added media storage limit configuration in settings.
- Introduced utility functions for managing media storage limits and usage.
- Updated models to delete media files using storage backend.
- Enhanced serializers to build media URLs dynamically.
- Implemented media usage tracking in user views and added endpoint for fetching media usage.
- Updated frontend to display media storage usage and limits.
- Added translations for media storage related strings in multiple languages.

* feat: Implement conditional throttling for various external services and update settings for rate limits
2026-05-30 11:02:44 -04:00

89 lines
2.5 KiB
Python

from django.conf import settings
from django.core.files.storage import default_storage
from adventures.models import ContentAttachment, ContentImage
def get_media_storage_limit_bytes():
limit = getattr(settings, 'MEDIA_STORAGE_LIMIT_BYTES', 0) or 0
return limit if limit > 0 else None
def get_uploaded_file_size(file_obj):
if not file_obj:
return 0
size = getattr(file_obj, 'size', None)
if size is None:
try:
size = len(file_obj)
except TypeError:
size = 0
return size or 0
def _sum_storage_sizes(names):
total = 0
for name in names:
if not name:
continue
try:
total += default_storage.size(name)
except (OSError, ValueError, FileNotFoundError):
continue
return total
def get_user_media_usage(user, exclude_names=None):
exclude_names = set(filter(None, exclude_names or []))
image_names = set(
ContentImage.objects.filter(user=user)
.exclude(image__isnull=True)
.exclude(image='')
.values_list('image', flat=True)
)
attachment_names = set(
ContentAttachment.objects.filter(user=user)
.exclude(file__isnull=True)
.exclude(file='')
.values_list('file', flat=True)
)
profile_names = set()
if user.profile_pic and user.profile_pic.name:
profile_names.add(user.profile_pic.name)
image_names -= exclude_names
attachment_names -= exclude_names
profile_names -= exclude_names
images_bytes = _sum_storage_sizes(image_names)
attachments_bytes = _sum_storage_sizes(attachment_names)
profile_pics_bytes = _sum_storage_sizes(profile_names)
return {
"total_bytes": images_bytes + attachments_bytes + profile_pics_bytes,
"images_bytes": images_bytes,
"attachments_bytes": attachments_bytes,
"profile_pics_bytes": profile_pics_bytes,
"images_files": len(image_names),
"attachments_files": len(attachment_names),
"profile_pics_files": len(profile_names),
}
def enforce_media_storage_limit(user, incoming_bytes, exclude_names=None):
limit = get_media_storage_limit_bytes()
if not limit:
return True, None
usage = get_user_media_usage(user, exclude_names=exclude_names)
projected = usage["total_bytes"] + max(incoming_bytes, 0)
if projected > limit:
return False, {
"limit_bytes": limit,
"current_bytes": usage["total_bytes"],
"incoming_bytes": incoming_bytes,
}
return True, None