BTHLABS-58: Share Extension in Apple Apps

This commit is contained in:
2025-10-04 08:02:13 +02:00
parent 0c12f52569
commit 99e9226338
122 changed files with 5488 additions and 411 deletions

View File

@@ -0,0 +1,29 @@
# Generated by Django 5.2.3 on 2025-09-22 07:20
import uuid6
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0005_accesstoken'),
]
operations = [
migrations.CreateModel(
name='AuthKey',
fields=[
('id', models.UUIDField(default=uuid6.uuid7, editable=False, primary_key=True, serialize=False)),
('account_uuid', models.UUIDField(db_index=True, default=None)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, db_index=True, default=None, editable=False, null=True)),
('key', models.CharField(db_index=True, default=None, editable=False, max_length=128, unique=True)),
],
options={
'verbose_name': 'Auth Key',
'verbose_name_plural': 'Auth Keys',
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.3 on 2025-10-01 07:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0006_authkey'),
]
operations = [
migrations.AddField(
model_name='authkey',
name='consumed_at',
field=models.DateTimeField(blank=True, db_index=True, default=None, editable=False, null=True),
),
]

View File

@@ -1,2 +1,3 @@
from .access_token import AccessToken # noqa: F401
from .account import Account # noqa: F401
from .auth_key import AuthKey # noqa: F401

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
from django.db import models
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.core.models import Model
class ActiveAuthKeysManager(models.Manager):
def get_queryset(self) -> models.QuerySet[AuthKey]:
return super().get_queryset().filter(
deleted_at__isnull=True,
)
class AuthKey(Model):
key = models.CharField(
blank=False,
default=None,
null=False,
max_length=128,
db_index=True,
unique=True,
editable=False,
)
consumed_at = models.DateTimeField(
blank=True,
null=True,
default=None,
db_index=True,
editable=False,
)
objects = models.Manager()
active_objects = ActiveAuthKeysManager()
class Meta:
verbose_name = _('Auth Key')
verbose_name_plural = _('Auth Keys')
def __str__(self) -> str:
return f'<AuthKey pk={self.pk} key={self.key}>'

View File

@@ -1 +1,3 @@
from .access_tokens import AccessTokensService # noqa: F401
from .accounts import AccountsService # noqa: F401
from .auth_keys import AuthKeysService # noqa: F401

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import logging
import uuid
from hotpocket_backend.apps.accounts.models import Account
LOGGER = logging.getLogger(__name__)
class AccountsService:
class AccountsServiceError(Exception):
pass
class AccountNotFound(AccountsServiceError):
pass
def get(self, *, pk: uuid.UUID) -> Account:
try:
query_set = Account.objects.filter(is_active=True)
return query_set.get(pk=pk)
except Account.DoesNotExist as exception:
raise self.AccountNotFound(
f'Account not found: pk=`{pk}`',
) from exception

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import datetime
import logging
import uuid
from django.utils.timezone import now
import uuid6
from hotpocket_backend.apps.accounts.models import AuthKey
from hotpocket_backend.apps.core.conf import settings
LOGGER = logging.getLogger(__name__)
class AuthKeysService:
class AuthKeysServiceError(Exception):
pass
class AuthKeyNotFound(AuthKeysServiceError):
pass
class AuthKeyExpired(AuthKeysServiceError):
pass
class AuthKeyAccessDenied(AuthKeysServiceError):
pass
def create(self, *, account_uuid: uuid.UUID) -> AuthKey:
key = str(uuid6.uuid7())
return AuthKey.objects.create(
account_uuid=account_uuid,
key=key,
)
def get(self, *, pk: uuid.UUID) -> AuthKey:
try:
query_set = AuthKey.active_objects
return query_set.get(pk=pk)
except AuthKey.DoesNotExist as exception:
raise self.AuthKeyNotFound(
f'Auth Key not found: pk=`{pk}`',
) from exception
def get_by_key(self, *, key: str, ttl: int | None = None) -> AuthKey:
try:
query_set = AuthKey.active_objects
result = query_set.get(key=key)
if ttl is None:
ttl = settings.AUTH_KEY_TTL
if ttl > 0:
if result.created_at < now() - datetime.timedelta(seconds=ttl):
raise self.AuthKeyExpired(
f'Auth Key expired: pk=`{key}`',
)
if result.consumed_at is not None:
raise self.AuthKeyExpired(
f'Auth Key already consumed: pk=`{key}`',
)
return result
except AuthKey.DoesNotExist as exception:
raise self.AuthKeyNotFound(
f'Auth Key not found: key=`{key}`',
) from exception