From 66cef7836dac39ebda613332712391aee18d7532 Mon Sep 17 00:00:00 2001 From: user Date: Tue, 23 Apr 2024 11:31:42 +0300 Subject: [PATCH] dev@locazia: view track api endpoints --- app/api/__init__.py | 8 +++- app/api/routes/auth.py | 4 ++ app/api/routes/content.py | 38 ++++++++++++++++++ app/api/routes/tonconnect.py | 59 ++++++++++++++++++++++++++++ app/client_bot/routers/content.py | 13 ++---- app/core/models/wallet_connection.py | 10 +++++ 6 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 app/api/routes/tonconnect.py diff --git a/app/api/__init__.py b/app/api/__init__.py index bc3a0be..ac9b680 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -20,7 +20,9 @@ from app.api.routes.node_storage import s_api_v1_storage_post, s_api_v1_storage_ from app.api.routes.account import s_api_v1_account_get from app.api.routes._blockchain import s_api_v1_blockchain_send_new_content_message, \ s_api_v1_blockchain_send_purchase_content_message -from app.api.routes.content import s_api_v1_content_list +from app.api.routes.content import s_api_v1_content_list, s_api_v1_content_view +from app.api.routes.tonconnect import s_api_v1_tonconnect_new, s_api_v1_tonconnect_logout + app.add_route(s_index, "/", methods=["GET", "OPTIONS"]) app.add_route(s_favicon, "/favicon.ico", methods=["GET", "OPTIONS"]) @@ -34,6 +36,9 @@ app.add_route(s_api_platform_metadata, "/api/platform-metadata.json", methods=[" app.add_route(s_api_v1_auth_twa, "/api/v1/auth.twa", methods=["POST", "OPTIONS"]) +app.add_route(s_api_v1_tonconnect_new, "/api/v1/tonconnect.new", methods=["GET", "OPTIONS"]) +app.add_route(s_api_v1_tonconnect_logout, "/api/v1/tonconnect.logout", methods=["POST", "OPTIONS"]) + app.add_route(s_api_v1_storage_post, "/api/v1/storage", methods=["POST", "OPTIONS"]) app.add_route(s_api_v1_storage_get, "/api/v1/storage/", methods=["GET", "OPTIONS"]) app.add_route(s_api_v1_storage_decode_cid, "/api/v1/storage.decodeContentId/", methods=["GET", "OPTIONS"]) @@ -44,6 +49,7 @@ app.add_route(s_api_v1_blockchain_send_new_content_message, "/api/v1/blockchain. app.add_route(s_api_v1_blockchain_send_purchase_content_message, "/api/v1/blockchain.sendPurchaseContentMessage", methods=["POST", "OPTIONS"]) app.add_route(s_api_v1_content_list, "/api/v1/content.list", methods=["GET", "OPTIONS"]) +app.add_route(s_api_v1_content_view, "/api/v1/content.view/", methods=["GET", "OPTIONS"]) @app.exception(BaseException) diff --git a/app/api/routes/auth.py b/app/api/routes/auth.py index c5e514a..c9235e5 100644 --- a/app/api/routes/auth.py +++ b/app/api/routes/auth.py @@ -39,7 +39,11 @@ async def s_api_v1_auth_twa(request): assert known_user, "User not created" new_user_key = await known_user.create_api_token_v1(request.ctx.db_session, "USER_API_V1") + + connected_wallet_data = known_user.wallet_connection(request.ctx.db_session) + return response.json({ 'user': known_user.json_format(), + 'connected_wallet': connected_wallet_data.json_format() if connected_wallet_data else None, 'auth_v1_token': new_user_key['auth_v1_token'] }) diff --git a/app/api/routes/content.py b/app/api/routes/content.py index 2e6a8f0..e931453 100644 --- a/app/api/routes/content.py +++ b/app/api/routes/content.py @@ -2,6 +2,8 @@ from sanic import response from app.core.logger import make_log from app.core.models.node_storage import StoredContent +from app.core.models.keys import KnownKey +from app.core.models.content.user_content import UserContent async def s_api_v1_content_list(request): @@ -24,3 +26,39 @@ async def s_api_v1_content_list(request): result[content_json["cid"]] = content_json return response.json(result) + + +async def s_api_v1_content_view(request, content_address: str): + # content_address can be CID or TON address + r_content = StoredContent.from_cid(request.ctx.db_session, content_address) + content = r_content.open_content(request.ctx.db_session) + + opts = {} + if content['encrypted_content'].key_id: + known_key = request.ctx.db_session.query(KnownKey).filter( + KnownKey.id == content + ).first() + if known_key: + opts['key_hash'] = known_key.seed_hash + + have_access = False + if request.ctx.user: + user_wallet_address = request.ctx.user.wallet_address(request.ctx.db_session) + have_access = ( + (content.owner_address == user_wallet_address) + or bool(request.ctx.db_session.query(UserContent).filter_by(owner_address=user_wallet_address, status='active', + content_id=content.id).first()) + ) + + display_options = { + 'content_url': content['decrypted_content'].web_url + ( + '?seconds_limit=30' if not have_access else '' + ) + } + + return response.json({ + **opts, + 'encrypted': content['encrypted_content'].json_format(), + 'display_options': display_options + }) + diff --git a/app/api/routes/tonconnect.py b/app/api/routes/tonconnect.py new file mode 100644 index 0000000..8cc9fb1 --- /dev/null +++ b/app/api/routes/tonconnect.py @@ -0,0 +1,59 @@ +from datetime import datetime + +from aiogram.utils.web_app import safe_parse_webapp_init_data +from sanic import response + +from app.core._blockchain.ton.connect import TonConnect, unpack_wallet_info, WalletConnection +from app.core._config import TELEGRAM_API_KEY +from app.core.models.user import User +from app.core.logger import make_log + + +async def pause_ton_connection(ton_connect: TonConnect): + if ton_connect.connected: + ton_connect._sdk_client.pause_connection() + + +async def s_api_v1_tonconnect_new(request): + if not request.ctx.user: + return response.json({"error": "User not found"}, status=400) + + wallet_app_name = request.args.get("wallet_app_name", "tonkeeper") + + db_session = request.ctx.db_session + user = request.ctx.user + memory = request.ctx.memory + ton_connect, ton_connection = TonConnect.by_user(db_session, user) + await ton_connect.restore_connection() + make_log("TonConnect_API", f"SDK connected?: {ton_connect.connected}", level='info') + if ton_connect.connected: + return response.json({"error": "Already connected"}, status=400) + + connection_link = await ton_connect.new_connection(wallet_app_name) + ton_connect.connected + memory.add_task(pause_ton_connection, ton_connect, delay_s=60 * 3) + make_log("TonConnect_API", f"New connection link for {wallet_app_name}: {connection_link}", level='debug') + return response.json({ + "connection_link": connection_link, + "wallet_app_name": wallet_app_name + }) + + +async def s_api_v1_tonconnect_logout(request): + if not request.ctx.user: + return response.json({"error": "User not found"}, status=400) + + db_session = request.ctx.db_session + user = request.ctx.user + memory = request.ctx.memory + + wallet_connections = db_session.query(WalletConnection).filter( + WalletConnection.user_id == user.id, + WalletConnection.invalidated == False + ).all() + for wallet_connection in wallet_connections: + wallet_connection.invalidated = True + + db_session.commit() + return response.json({"success": True}) + diff --git a/app/client_bot/routers/content.py b/app/client_bot/routers/content.py index 3e6162d..5b01485 100644 --- a/app/client_bot/routers/content.py +++ b/app/client_bot/routers/content.py @@ -96,16 +96,9 @@ async def t_inline_query_node_content(query: types.InlineQuery, memory=None, use content_list = [] content = db_session.query(StoredContent).filter_by(hash=cid.content_hash_b58).first() - decrypted_content = None - if content: - if content.encrypted: - decrypted_content = db_session.query(StoredContent).filter_by(id=content.decrypted_content_id).first() - else: - decrypted_content = content - - if not decrypted_content: - make_log("OwnedContent", f"Can't get decrypted content: {content.id}", level='warning') - return await query.answer(content_list, cache_time=1) + content_prod = content.open_content(db_session) + encrypted_content = content_prod['encrypted_content'] + decrypted_content = content_prod['decrypted_content'] decrypted_content_meta = decrypted_content.json_format() try: diff --git a/app/core/models/wallet_connection.py b/app/core/models/wallet_connection.py index 54c3c4d..fd799c0 100644 --- a/app/core/models/wallet_connection.py +++ b/app/core/models/wallet_connection.py @@ -25,3 +25,13 @@ class WalletConnection(AlchemyBase): user = relationship('User', uselist=False, back_populates='wallet_connections', foreign_keys=[user_id]) + @property + def ton_balance(self): + return round(int(self.meta.get('wallet_ton_balance', 0)) / 1e9, 9) + + def json_format(self): + return { + 'version': self.meta.get('wallet_contract_version', 'uv'), # uv = unknown version + 'address': self.wallet_address, + 'ton_balance': self.meta.get('wallet_ton_balance', 0), + }