126 lines
5.9 KiB
Python
126 lines
5.9 KiB
Python
import traceback
|
|
|
|
from sanic import Sanic, response
|
|
from uuid import uuid4
|
|
import traceback as _traceback
|
|
|
|
from app.core.logger import make_log
|
|
|
|
app = Sanic(__name__)
|
|
|
|
from app.api.middleware import attach_user_to_request, close_db_session, close_request_handler
|
|
|
|
app.register_middleware(attach_user_to_request, "request")
|
|
app.register_middleware(close_db_session, "response")
|
|
|
|
from app.api.routes._index import s_index, s_favicon
|
|
from app.api.routes._system import s_api_v1_node, s_api_system_version, s_api_system_send_status, s_api_v1_node_friendly
|
|
from app.api.routes.auth import s_api_v1_auth_twa, s_api_v1_auth_select_wallet, s_api_v1_auth_me
|
|
from app.api.routes.statics import s_api_tonconnect_manifest, s_api_platform_metadata
|
|
from app.api.routes.node_storage import s_api_v1_storage_post, s_api_v1_storage_get, \
|
|
s_api_v1_storage_decode_cid
|
|
from app.api.routes.progressive_storage import s_api_v1_5_storage_get, s_api_v1_5_storage_post
|
|
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, s_api_v1_content_view, s_api_v1_content_friendly_list, s_api_v1_5_content_list
|
|
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"])
|
|
|
|
app.add_route(s_api_v1_node, "/api/v1/node", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_v1_node_friendly, "/api/v1/nodeFriendly", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_system_version, "/api/system.version", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_system_send_status, "/api/system.sendStatus", methods=["POST", "OPTIONS"])
|
|
|
|
app.add_route(s_api_tonconnect_manifest, "/api/tonconnect-manifest.json", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_platform_metadata, "/api/platform-metadata.json", methods=["GET", "OPTIONS"])
|
|
|
|
app.add_route(s_api_v1_auth_twa, "/api/v1/auth.twa", methods=["POST", "OPTIONS"])
|
|
app.add_route(s_api_v1_auth_me, "/api/v1/auth.me", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_v1_auth_select_wallet, "/api/v1/auth.selectWallet", 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_5_storage_post, "/api/v1.5/storage", methods=["POST", "OPTIONS"])
|
|
app.add_route(s_api_v1_5_storage_get, "/api/v1.5/storage/<file_hash>", methods=["GET", "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/<file_hash>", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_v1_storage_decode_cid, "/api/v1/storage.decodeContentId/<content_id>", methods=["GET", "OPTIONS"])
|
|
|
|
app.add_route(s_api_v1_account_get, "/api/v1/account", methods=["GET", "OPTIONS"])
|
|
|
|
app.add_route(s_api_v1_blockchain_send_new_content_message, "/api/v1/blockchain.sendNewContentMessage", methods=["POST", "OPTIONS"])
|
|
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/<content_address>", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_v1_content_friendly_list, "/api/v1/content.friendlyList", methods=["GET", "OPTIONS"])
|
|
app.add_route(s_api_v1_5_content_list, "/api/v1.5/content.list", methods=["GET", "OPTIONS"])
|
|
|
|
|
|
@app.exception(BaseException)
|
|
async def s_handle_exception(request, exception):
|
|
# Correlate error to request
|
|
session_id = getattr(request.ctx, 'session_id', None) or uuid4().hex[:16]
|
|
error_id = uuid4().hex[:8]
|
|
|
|
status = 500
|
|
code = type(exception).__name__
|
|
message = "Internal HTTP Error"
|
|
|
|
try:
|
|
raise exception
|
|
except AssertionError as e:
|
|
status = 400
|
|
code = 'AssertionError'
|
|
message = str(e) or 'Bad Request'
|
|
except BaseException as e:
|
|
# keep default 500, but expose exception message to aid debugging
|
|
message = str(e) or message
|
|
|
|
# Build structured log with full context and traceback
|
|
try:
|
|
tb = _traceback.format_exc()
|
|
user_id = getattr(getattr(request.ctx, 'user', None), 'id', None)
|
|
log_ctx = {
|
|
'sid': session_id,
|
|
'eid': error_id,
|
|
'path': request.path,
|
|
'method': request.method,
|
|
'query': dict(request.args) if hasattr(request, 'args') else {},
|
|
'user_id': user_id,
|
|
'remote': (request.headers.get('X-Forwarded-For') or request.remote_addr or request.ip),
|
|
'code': code,
|
|
'message': message,
|
|
'traceback': tb,
|
|
}
|
|
make_log('http_exception', 'API exception', level='error', **log_ctx)
|
|
except BaseException:
|
|
pass
|
|
|
|
# Return enriched error response for the client
|
|
payload = {
|
|
'error': True,
|
|
'code': code,
|
|
'message': message,
|
|
'session_id': session_id,
|
|
'error_id': error_id,
|
|
'path': request.path,
|
|
'method': request.method,
|
|
}
|
|
|
|
response_buffer = response.json(payload, status=status)
|
|
response_buffer = await close_db_session(request, response_buffer)
|
|
response_buffer.headers["Access-Control-Allow-Origin"] = "*"
|
|
response_buffer.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
|
|
response_buffer.headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Accept, Authorization, Referer, User-Agent, Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, x-request-id"
|
|
response_buffer.headers["Access-Control-Allow-Credentials"] = "true"
|
|
response_buffer.headers["X-Session-Id"] = session_id
|
|
response_buffer.headers["X-Error-Id"] = error_id
|
|
return response_buffer
|