Spaces:
Sleeping
Sleeping
P0-R6-E1-R4-1D-C
#145
by GuXiaopeng - opened
- app.py +14 -4
- managed_auth.py +24 -8
app.py
CHANGED
|
@@ -815,6 +815,13 @@ def _process_pending_cancel() -> None:
|
|
| 815 |
st.rerun()
|
| 816 |
|
| 817 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 818 |
def _process_pending_resume() -> None:
|
| 819 |
task_id = str(st.session_state.get("sp_pending_resume_task_id") or "").strip()
|
| 820 |
if not task_id:
|
|
@@ -1728,8 +1735,9 @@ def render_workspace_controls(user):
|
|
| 1728 |
entitlement_exhausted = bool(product_ui_ready and selected_remaining is not None and selected_remaining <= 0)
|
| 1729 |
if entitlement_exhausted:
|
| 1730 |
st.warning(tr(lang_code, "product_entitlement_required"))
|
| 1731 |
-
if st.button(tr(lang_code,
|
| 1732 |
-
|
|
|
|
| 1733 |
can_start = bool(
|
| 1734 |
preflight.get("can_start_generation")
|
| 1735 |
and product_ui_ready
|
|
@@ -1792,11 +1800,13 @@ if isinstance(_notice, dict) and _notice.get("message"):
|
|
| 1792 |
_process_pending_cancel()
|
| 1793 |
_process_pending_resume()
|
| 1794 |
_process_pending_launch(lang_code)
|
|
|
|
| 1795 |
|
| 1796 |
if st.session_state.get("sp_payment_required"):
|
| 1797 |
st.warning(tr(lang_code, "product_entitlement_required"))
|
| 1798 |
-
if st.button(tr(lang_code,
|
| 1799 |
-
|
|
|
|
| 1800 |
|
| 1801 |
# R3.2.4 keeps the approved task-monitor location. Active progress is updated
|
| 1802 |
# inside the browser by read-only edge polling; Streamlit reruns only on a
|
|
|
|
| 815 |
st.rerun()
|
| 816 |
|
| 817 |
|
| 818 |
+
def _process_pending_payment_dialog(lang_code: str) -> None:
|
| 819 |
+
"""Reopen purchase dialog until the initial browser bridge creates its trusted order."""
|
| 820 |
+
if not bool(st.session_state.get("sp_payment_dialog_pending")):
|
| 821 |
+
return
|
| 822 |
+
render_payment_purchase(lang_code)
|
| 823 |
+
|
| 824 |
+
|
| 825 |
def _process_pending_resume() -> None:
|
| 826 |
task_id = str(st.session_state.get("sp_pending_resume_task_id") or "").strip()
|
| 827 |
if not task_id:
|
|
|
|
| 1735 |
entitlement_exhausted = bool(product_ui_ready and selected_remaining is not None and selected_remaining <= 0)
|
| 1736 |
if entitlement_exhausted:
|
| 1737 |
st.warning(tr(lang_code, "product_entitlement_required"))
|
| 1738 |
+
if st.button(f"💳 {tr(lang_code, 'payment_open')}", key="btn_open_payment_for_selected_tier"):
|
| 1739 |
+
st.session_state["sp_payment_dialog_pending"] = True
|
| 1740 |
+
st.rerun()
|
| 1741 |
can_start = bool(
|
| 1742 |
preflight.get("can_start_generation")
|
| 1743 |
and product_ui_ready
|
|
|
|
| 1800 |
_process_pending_cancel()
|
| 1801 |
_process_pending_resume()
|
| 1802 |
_process_pending_launch(lang_code)
|
| 1803 |
+
_process_pending_payment_dialog(lang_code)
|
| 1804 |
|
| 1805 |
if st.session_state.get("sp_payment_required"):
|
| 1806 |
st.warning(tr(lang_code, "product_entitlement_required"))
|
| 1807 |
+
if st.button(f"💳 {tr(lang_code, 'payment_open')}", key="btn_open_payment_after_rejection"):
|
| 1808 |
+
st.session_state["sp_payment_dialog_pending"] = True
|
| 1809 |
+
st.rerun()
|
| 1810 |
|
| 1811 |
# R3.2.4 keeps the approved task-monitor location. Active progress is updated
|
| 1812 |
# inside the browser by read-only edge polling; Streamlit reruns only on a
|
managed_auth.py
CHANGED
|
@@ -141,6 +141,7 @@ def _clear_generation_session_state() -> None:
|
|
| 141 |
"up_asq_data", "sp_payment_required", "sp_payment_order",
|
| 142 |
"sp_payment_request_id", "sp_payment_package_sku",
|
| 143 |
"sp_payment_channel", "sp_payment_order_channel",
|
|
|
|
| 144 |
):
|
| 145 |
st.session_state.pop(key, None)
|
| 146 |
|
|
@@ -150,6 +151,7 @@ def _clear_session() -> None:
|
|
| 150 |
"sp_session_expires_at", "sp_user",
|
| 151 |
"sp_last_verified_at", "sp_edge_assertion", "sp_auth_api_origin",
|
| 152 |
"sp_product_ui_projection", "sp_product_tier",
|
|
|
|
| 153 |
):
|
| 154 |
st.session_state.pop(key, None)
|
| 155 |
|
|
@@ -322,14 +324,22 @@ def get_product_ui_projection() -> dict[str, Any]:
|
|
| 322 |
|
| 323 |
|
| 324 |
def get_account_snapshot(*, block: bool = False) -> dict[str, Any] | None:
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
revision = int(st.session_state.get("sp_account_revision") or 0)
|
| 327 |
-
command_id =
|
|
|
|
|
|
|
|
|
|
| 328 |
result = _edge_call("account", command_id, {}, key_prefix="sp_account")
|
| 329 |
if result is None:
|
| 330 |
if block:
|
| 331 |
st.stop()
|
| 332 |
return None
|
|
|
|
| 333 |
if not result.get("ok"):
|
| 334 |
if block:
|
| 335 |
_raise_edge_error(result, "account_query_failed")
|
|
@@ -651,6 +661,12 @@ def _render_payment_purchase_body(language: str) -> None:
|
|
| 651 |
st.session_state["sp_product_ui_projection"] = _normalize_product_ui_projection(projected)
|
| 652 |
|
| 653 |
order = st.session_state.get("sp_payment_order") or {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 654 |
qr_code = str(order.get("qr_code") or "").strip()
|
| 655 |
if qr_code:
|
| 656 |
payment_provider = str(order.get("payment_provider") or "").strip().upper()
|
|
@@ -827,8 +843,8 @@ def render_logged_in_account(
|
|
| 827 |
padding: 0.48rem clamp(0.55rem, 0.85vw, 0.9rem);
|
| 828 |
border: 1px solid rgba(148, 163, 184, 0.30);
|
| 829 |
border-radius: 13px;
|
| 830 |
-
background:
|
| 831 |
-
color:
|
| 832 |
font-size: clamp(0.76rem, 0.84vw, 0.92rem);
|
| 833 |
font-weight: 500;
|
| 834 |
line-height: 1.25;
|
|
@@ -866,8 +882,8 @@ def render_logged_in_account(
|
|
| 866 |
padding: 0.45rem 0.72rem;
|
| 867 |
border: 1px solid rgba(148, 163, 184, 0.30);
|
| 868 |
border-radius: 0.5rem;
|
| 869 |
-
background:
|
| 870 |
-
color:
|
| 871 |
text-decoration: none !important;
|
| 872 |
white-space: nowrap !important;
|
| 873 |
overflow-wrap: normal !important;
|
|
@@ -882,8 +898,8 @@ def render_logged_in_account(
|
|
| 882 |
|
| 883 |
.sp-logout-link:hover {
|
| 884 |
border-color: rgba(255, 75, 75, 0.78);
|
| 885 |
-
background:
|
| 886 |
-
color:
|
| 887 |
}
|
| 888 |
|
| 889 |
.sp-logout-link:focus-visible {
|
|
|
|
| 141 |
"up_asq_data", "sp_payment_required", "sp_payment_order",
|
| 142 |
"sp_payment_request_id", "sp_payment_package_sku",
|
| 143 |
"sp_payment_channel", "sp_payment_order_channel",
|
| 144 |
+
"sp_payment_dialog_pending",
|
| 145 |
):
|
| 146 |
st.session_state.pop(key, None)
|
| 147 |
|
|
|
|
| 151 |
"sp_session_expires_at", "sp_user",
|
| 152 |
"sp_last_verified_at", "sp_edge_assertion", "sp_auth_api_origin",
|
| 153 |
"sp_product_ui_projection", "sp_product_tier",
|
| 154 |
+
"sp_account_pending_command_id",
|
| 155 |
):
|
| 156 |
st.session_state.pop(key, None)
|
| 157 |
|
|
|
|
| 324 |
|
| 325 |
|
| 326 |
def get_account_snapshot(*, block: bool = False) -> dict[str, Any] | None:
|
| 327 |
+
# External entitlement mutations (administrator adjustment/payment credit/
|
| 328 |
+
# another device) cannot increment this browser's local revision. Keep one
|
| 329 |
+
# command id stable only for the browser-bridge handshake, then retire it
|
| 330 |
+
# after a completed response so the next Streamlit rerun performs a fresh
|
| 331 |
+
# Worker/D1 account read instead of reusing a time-bucketed cached result.
|
| 332 |
revision = int(st.session_state.get("sp_account_revision") or 0)
|
| 333 |
+
command_id = str(st.session_state.get("sp_account_pending_command_id") or "").strip()
|
| 334 |
+
if not command_id:
|
| 335 |
+
command_id = f"account:{revision}:{secrets.token_urlsafe(10)}"
|
| 336 |
+
st.session_state["sp_account_pending_command_id"] = command_id
|
| 337 |
result = _edge_call("account", command_id, {}, key_prefix="sp_account")
|
| 338 |
if result is None:
|
| 339 |
if block:
|
| 340 |
st.stop()
|
| 341 |
return None
|
| 342 |
+
st.session_state.pop("sp_account_pending_command_id", None)
|
| 343 |
if not result.get("ok"):
|
| 344 |
if block:
|
| 345 |
_raise_edge_error(result, "account_query_failed")
|
|
|
|
| 661 |
st.session_state["sp_product_ui_projection"] = _normalize_product_ui_projection(projected)
|
| 662 |
|
| 663 |
order = st.session_state.get("sp_payment_order") or {}
|
| 664 |
+
if str(order.get("merchant_order_no") or "").strip():
|
| 665 |
+
# The app-level pending flag exists only to survive the initial
|
| 666 |
+
# browser-bridge handshake. Once the trusted order exists, clear it
|
| 667 |
+
# inside the dialog fragment so manually closing the dialog does not
|
| 668 |
+
# cause an unrelated later rerun to reopen it.
|
| 669 |
+
st.session_state["sp_payment_dialog_pending"] = False
|
| 670 |
qr_code = str(order.get("qr_code") or "").strip()
|
| 671 |
if qr_code:
|
| 672 |
payment_provider = str(order.get("payment_provider") or "").strip().upper()
|
|
|
|
| 843 |
padding: 0.48rem clamp(0.55rem, 0.85vw, 0.9rem);
|
| 844 |
border: 1px solid rgba(148, 163, 184, 0.30);
|
| 845 |
border-radius: 13px;
|
| 846 |
+
background: #1e293b;
|
| 847 |
+
color: #f8fafc;
|
| 848 |
font-size: clamp(0.76rem, 0.84vw, 0.92rem);
|
| 849 |
font-weight: 500;
|
| 850 |
line-height: 1.25;
|
|
|
|
| 882 |
padding: 0.45rem 0.72rem;
|
| 883 |
border: 1px solid rgba(148, 163, 184, 0.30);
|
| 884 |
border-radius: 0.5rem;
|
| 885 |
+
background: #1e293b;
|
| 886 |
+
color: #f8fafc !important;
|
| 887 |
text-decoration: none !important;
|
| 888 |
white-space: nowrap !important;
|
| 889 |
overflow-wrap: normal !important;
|
|
|
|
| 898 |
|
| 899 |
.sp-logout-link:hover {
|
| 900 |
border-color: rgba(255, 75, 75, 0.78);
|
| 901 |
+
background: #334155;
|
| 902 |
+
color: #f8fafc !important;
|
| 903 |
}
|
| 904 |
|
| 905 |
.sp-logout-link:focus-visible {
|