mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2026-05-09 15:35:46 -04:00
* Refactor AdventureLog Bot workflow to improve issue validation handling and encapsulate comment and close logic * feat: add API key management to settings page - Implemented API key creation, deletion, and display functionality. - Updated the settings page to fetch and show existing API keys. - Added UI elements for creating new API keys and copying them to clipboard. - Enhanced request handling to ensure proper trailing slashes for API endpoints. * feat: add API Keys documentation and update contributing guidelines * fix: update appVersion to reflect the latest build * fix: update @tailwindcss/typography to version 0.5.19 * fix: update @tailwindcss/typography to version 0.5.19 * chore: update dependencies in pnpm-lock.yaml - dompurify: upgraded from 3.3.1 to 3.3.3 - emoji-picker-element: upgraded from 1.29.0 to 1.29.1 - @sveltejs/adapter-node: updated to use @sveltejs/kit@2.55.0 - @sveltejs/adapter-vercel: updated to use @sveltejs/kit@2.55.0 - @sveltejs/kit: upgraded from 2.53.3 to 2.55.0 - @types/node: upgraded from 22.19.13 to 22.19.15 - autoprefixer: updated postcss version from 8.5.6 to 8.5.8 - baseline-browser-mapping: upgraded from 2.10.0 to 2.10.8 - daisyui: updated postcss version from 8.5.6 to 8.5.8 - prettier-plugin-svelte: upgraded from 3.5.0 to 3.5.1 - svelte-check: updated postcss version from 8.5.6 to 8.5.8 - devalue: upgraded from 5.6.3 to 5.6.4 - electron-to-chromium: upgraded from 1.5.302 to 1.5.313 - caniuse-lite: upgraded from 1.0.30001774 to 1.0.30001780 - mlly: upgraded from 1.8.0 to 1.8.1 - node-releases: upgraded from 2.0.27 to 2.0.36 - tar: upgraded from 7.5.9 to 7.5.11 - tinyexec: upgraded from 1.0.2 to 1.0.4 * fix: update appVersion to include the latest build identifier * fix: enhance authentication fallback for protected media access * feat(auth): add 'mobile-qr' to trailing slash list for URL handling * Translated using Weblate (French) Currently translated at 99.9% (1091 of 1092 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/fr/ * Translated using Weblate (Korean) Currently translated at 100.0% (1092 of 1092 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/ko/ * Translated using Weblate (German) Currently translated at 100.0% (1092 of 1092 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/de/ * Translated using Weblate (Swedish) Currently translated at 100.0% (1092 of 1092 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/sv/ * Added translation using Weblate (Catalan) * Translated using Weblate (Catalan) Currently translated at 1.2% (14 of 1092 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/ca/ * Docs: Reorder immich API permissions to natural order (#1086) * Refactor AdventureLog Bot workflow to improve issue validation handling and encapsulate comment and close logic (#1068) * Reorder immich API permissions to natural order --------- Co-authored-by: Sean Morley <git@seanmorley.com> * Translated using Weblate (Turkish) Currently translated at 100.0% (1093 of 1093 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/tr/ * Translated using Weblate (Swedish) Currently translated at 100.0% (1093 of 1093 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/sv/ * Translated using Weblate (German) Currently translated at 100.0% (1093 of 1093 strings) Translation: AdventureLog/Web App Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/de/ * Add ENABLE_RATE_LIMITS configuration for backend rate limiting * Set tabindex to -1 for dropdown menus to improve accessibility * Refactor settings page: Simplify HTML structure and improve date formatting for API keys * Update DEFAULT_SCHEMA_CLASS to use OpenAPI schema in REST framework settings * fix: update error message for key copying and enhance usage instructions for API key * Implement feature X to enhance user experience and fix bug Y in module Z * feat: add .dockerignore and update Dockerfile for improved build process * fix: add missing svelte-i18n>esbuild override in pnpm-lock and pnpm-workspace files * refactor: update frontend CI workflow for improved quality checks and dependency management * Refactor code structure for improved readability and maintainability * fix: add vite>esbuild override in pnpm-lock and pnpm-workspace files * refactor: enhance accessibility and semantics of button elements across multiple components * feat: update API key deletion confirmation messages in multiple languages and improve server URL configuration * fix: update djangorestframework version constraint and drf-yasg version in requirements * fix: update appVersion to v0.12.0-main-040426 and refactor button elements to improve accessibility in CollectionCard and CollectionItineraryPlanner components * feat: implement developer unlock feature for mobile login in Avatar component --------- Co-authored-by: lesensei <alain-gh@lespeps.eu> Co-authored-by: Hosted Weblate user 141821 <clearstripe@users.noreply.hosted.weblate.org> Co-authored-by: Alex <div@alexe.at> Co-authored-by: AntonPalmqvist <apq@users.noreply.hosted.weblate.org> Co-authored-by: Marc Llopart <marc@medullar.com> Co-authored-by: Stephan Zwicknagl <64196842+stephanzwicknagl@users.noreply.github.com> Co-authored-by: Orhun <orhunavcu@gmail.com> Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
108 lines
3.8 KiB
Python
108 lines
3.8 KiB
Python
import hashlib
|
||
import secrets
|
||
import uuid
|
||
from django.contrib.auth.models import AbstractUser
|
||
from django.db import models
|
||
from django_resized import ResizedImageField
|
||
|
||
|
||
CURRENCY_CHOICES = (
|
||
('USD', 'US Dollar'),
|
||
('EUR', 'Euro'),
|
||
('GBP', 'British Pound'),
|
||
('JPY', 'Japanese Yen'),
|
||
('AUD', 'Australian Dollar'),
|
||
('CAD', 'Canadian Dollar'),
|
||
('CHF', 'Swiss Franc'),
|
||
('CNY', 'Chinese Yuan'),
|
||
('HKD', 'Hong Kong Dollar'),
|
||
('SGD', 'Singapore Dollar'),
|
||
('SEK', 'Swedish Krona'),
|
||
('NOK', 'Norwegian Krone'),
|
||
('DKK', 'Danish Krone'),
|
||
('NZD', 'New Zealand Dollar'),
|
||
('INR', 'Indian Rupee'),
|
||
('MXN', 'Mexican Peso'),
|
||
('BRL', 'Brazilian Real'),
|
||
('ZAR', 'South African Rand'),
|
||
('AED', 'UAE Dirham'),
|
||
('TRY', 'Turkish Lira'),
|
||
)
|
||
|
||
class CustomUser(AbstractUser):
|
||
email = models.EmailField(unique=True) # Override the email field with unique constraint
|
||
profile_pic = ResizedImageField(force_format="WEBP", quality=75, null=True, blank=True, upload_to='profile-pics/')
|
||
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||
public_profile = models.BooleanField(default=False)
|
||
disable_password = models.BooleanField(default=False)
|
||
measurement_system = models.CharField(max_length=10, choices=[('metric', 'Metric'), ('imperial', 'Imperial')], default='metric')
|
||
default_currency = models.CharField(max_length=5, choices=CURRENCY_CHOICES, default='USD')
|
||
|
||
|
||
def __str__(self):
|
||
return self.username
|
||
|
||
|
||
class APIKey(models.Model):
|
||
"""
|
||
Personal API keys for authenticating programmatic access.
|
||
|
||
Security design:
|
||
- A 32-byte cryptographically random token is generated with the prefix ``al_``.
|
||
- Only a SHA-256 hash of the full token is persisted; the plaintext is returned
|
||
exactly once at creation time and never stored.
|
||
- The first 12 characters of the token are kept as ``key_prefix`` so users can
|
||
identify their keys without revealing the secret.
|
||
"""
|
||
|
||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||
user = models.ForeignKey(
|
||
CustomUser, on_delete=models.CASCADE, related_name='api_keys'
|
||
)
|
||
name = models.CharField(max_length=100)
|
||
key_prefix = models.CharField(max_length=12, editable=False)
|
||
key_hash = models.CharField(max_length=64, unique=True, editable=False)
|
||
created_at = models.DateTimeField(auto_now_add=True)
|
||
last_used_at = models.DateTimeField(null=True, blank=True)
|
||
|
||
class Meta:
|
||
ordering = ['-created_at']
|
||
|
||
def __str__(self):
|
||
return f"{self.user.username} – {self.name} ({self.key_prefix}…)"
|
||
|
||
@classmethod
|
||
def generate(cls, user, name: str) -> tuple['APIKey', str]:
|
||
"""
|
||
Create a new APIKey for *user* with the given *name*.
|
||
|
||
Returns a ``(instance, raw_key)`` tuple. The raw key is shown to the
|
||
user once and must never be stored anywhere after that.
|
||
"""
|
||
raw_key = f"al_{secrets.token_urlsafe(32)}"
|
||
key_hash = hashlib.sha256(raw_key.encode()).hexdigest()
|
||
key_prefix = raw_key[:12]
|
||
instance = cls.objects.create(
|
||
user=user,
|
||
name=name,
|
||
key_prefix=key_prefix,
|
||
key_hash=key_hash,
|
||
)
|
||
return instance, raw_key
|
||
|
||
@classmethod
|
||
def authenticate(cls, raw_key: str):
|
||
"""
|
||
Look up an APIKey by its raw value.
|
||
|
||
Returns the matching ``APIKey`` instance (updating ``last_used_at``) or
|
||
``None`` if not found.
|
||
"""
|
||
key_hash = hashlib.sha256(raw_key.encode()).hexdigest()
|
||
try:
|
||
api_key = cls.objects.select_related('user').get(key_hash=key_hash)
|
||
except cls.DoesNotExist:
|
||
return None
|
||
from django.utils import timezone
|
||
cls.objects.filter(pk=api_key.pk).update(last_used_at=timezone.now())
|
||
return api_key |