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

@@ -9,3 +9,5 @@ class AccessTokenOriginApp(enum.Enum):
SAFARI_WEB_EXTENSION = 'SAFARI_WEB_EXTENSION'
CHROME_EXTENSION = 'CHROME_EXTENSION'
FIREFOX_EXTENSION = 'FIREFOX_EXTENSION'
HOTPOCKET_DESKTOP = 'HOTPOCKET_DESKTOP'
HOTPOCKET_MOBILE = 'HOTPOCKET_MOBILE'

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import datetime
import urllib.parse
import uuid
@@ -8,7 +9,7 @@ import pydantic
from hotpocket_common.constants import AccessTokenOriginApp
from .base import ModelOut, Query
from .base import BaseModelOut, ModelOut, Query
class AccessTokenOut(ModelOut):
@@ -32,6 +33,12 @@ class AccessTokenOut(ModelOut):
case 'moz-extension':
return AccessTokenOriginApp.FIREFOX_EXTENSION
case 'hotpocket-desktop':
return AccessTokenOriginApp.HOTPOCKET_DESKTOP
case 'hotpocket-mobile':
return AccessTokenOriginApp.HOTPOCKET_MOBILE
case _:
return None
@@ -47,3 +54,16 @@ class AccessTokensQuery(Query):
class AccessTokenMetaUpdateIn(pydantic.BaseModel):
version: str | None = None
platform: str | None = None
class AuthKeyOut(ModelOut):
account_uuid: uuid.UUID
key: str
consumed_at: datetime.datetime | None = None
class AccountOut(BaseModelOut):
first_name: str
last_name: str
username: str
settings: dict

View File

@@ -7,13 +7,8 @@ import uuid
import pydantic
class ModelOut(pydantic.BaseModel):
class BaseModelOut(pydantic.BaseModel):
id: uuid.UUID
account_uuid: uuid.UUID
created_at: datetime.datetime
updated_at: datetime.datetime
deleted_at: datetime.datetime | None
is_active: bool
@property
def pk(self) -> uuid.UUID:
@@ -23,5 +18,13 @@ class ModelOut(pydantic.BaseModel):
return self.dict()
class ModelOut(BaseModelOut):
account_uuid: uuid.UUID
created_at: datetime.datetime
updated_at: datetime.datetime
deleted_at: datetime.datetime | None
is_active: bool
class Query(pydantic.BaseModel):
pass

View File

@@ -1,5 +1,7 @@
from .access_tokens import AccessTokensService # noqa: F401
from .accounts import AccountsService # noqa: F401
from .associations import AssociationsService # noqa: F401
from .auth_keys import AuthKeysService # noqa: F401
from .bot import BotService # noqa: F401
from .save_processor import SaveProcessorService # noqa: F401
from .saves import SavesService # noqa: F401

View File

@@ -76,13 +76,13 @@ class AccessTokensService(ProxyService):
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendAccessTokensService.AccessTokenNotFound) is True:
raise self.AccessTokenNotFound(f'account_uuid=`{account_uuid}` pk=`{pk}`') from exception
raise self.AccessTokenNotFound(*exception.args) from exception
else:
raise
def get_by_key(self,
*,
account_uuid: uuid.UUID,
account_uuid: uuid.UUID | None,
key: str,
) -> AccessTokenOut:
try:

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import uuid
from hotpocket_backend.apps.accounts.services import (
AccountsService as BackendAccountsService,
)
from hotpocket_soa.dto.accounts import AccountOut
from .base import ProxyService, SOAError
class AccountsService(ProxyService):
class AccountsServiceError(SOAError):
pass
class AccountNotFound(AccountsServiceError):
pass
def __init__(self):
super().__init__()
self.backend_accounts_service = BackendAccountsService()
def wrap_exception(self, exception: Exception) -> Exception:
new_exception_args = []
if len(exception.args) > 0:
new_exception_args = [exception.args[0]]
return self.AccountsServiceError(*new_exception_args)
def get(self, *, pk: uuid.UUID) -> AccountOut:
try:
result = AccountOut.model_validate(
self.call(
self.backend_accounts_service,
'get',
pk=pk,
),
from_attributes=True,
)
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendAccountsService.AccountNotFound) is True:
raise self.AccountNotFound(*exception.args) from exception
else:
raise

View File

@@ -91,7 +91,7 @@ class AssociationsService(ProxyService):
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendAssociationsService.AssociationNotFound) is True:
raise self.AssociationNotFound(f'pk=`{pk}`') from exception
raise self.AssociationNotFound(*exception.args) from exception
else:
raise

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import uuid
from hotpocket_backend.apps.accounts.services import (
AuthKeysService as BackendAuthKeysService,
)
from hotpocket_soa.dto.accounts import AuthKeyOut
from .base import ProxyService, SOAError
class AuthKeysService(ProxyService):
class AuthKeysServiceError(SOAError):
pass
class AuthKeyNotFound(AuthKeysServiceError):
pass
class AuthKeyAccessDenied(AuthKeysServiceError):
pass
def __init__(self):
super().__init__()
self.backend_auth_keys_service = BackendAuthKeysService()
def wrap_exception(self, exception: Exception) -> Exception:
new_exception_args = []
if len(exception.args) > 0:
new_exception_args = [exception.args[0]]
return self.AuthKeysServiceError(*new_exception_args)
def _check_auth_key_access(self,
auth_key: AuthKeyOut,
account_uuid: uuid.UUID | None,
) -> bool:
if account_uuid is not None:
return auth_key.account_uuid == account_uuid
return True
def create(self,
*,
account_uuid: uuid.UUID,
) -> AuthKeyOut:
return AuthKeyOut.model_validate(
self.call(
self.backend_auth_keys_service,
'create',
account_uuid=account_uuid,
),
from_attributes=True,
)
def get(self,
*,
account_uuid: uuid.UUID,
pk: uuid.UUID,
) -> AuthKeyOut:
try:
result = AuthKeyOut.model_validate(
self.call(
self.backend_auth_keys_service,
'get',
pk=pk,
),
from_attributes=True,
)
if self._check_auth_key_access(result, account_uuid) is False:
raise self.AuthKeyAccessDenied(
f'account_uuid=`{account_uuid}` pk=`{pk}`',
)
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendAuthKeysService.AuthKeyNotFound) is True:
raise self.AuthKeyNotFound(*exception.args) from exception
else:
raise
def get_by_key(self,
*,
account_uuid: uuid.UUID | None,
key: str,
) -> AuthKeyOut:
try:
result = AuthKeyOut.model_validate(
self.call(
self.backend_auth_keys_service,
'get_by_key',
key=key,
),
from_attributes=True,
)
if self._check_auth_key_access(result, account_uuid) is False:
raise self.AuthKeyAccessDenied(
f'account_uuid=`{account_uuid}` key=`{key}`',
)
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendAuthKeysService.AuthKeyNotFound) is True:
raise self.AuthKeyNotFound(*exception.args) from exception
else:
raise

View File

@@ -54,6 +54,6 @@ class SavesService(ProxyService):
return result
except SOAError as exception:
if isinstance(exception.__cause__, BackendSavesService.SaveNotFound) is True:
raise self.SaveNotFound(f'pk=`{pk}`') from exception
raise self.SaveNotFound(*exception.args) from exception
else:
raise