from flask import Blueprint, render_template, session, redirect, url_for, flash import hmac, hashlib, time, urllib.parse, os OTB_PORTAL_SHARED_SECRET = os.getenv("OTB_PORTAL_SHARED_SECRET", "!2Eas678") OTB_CLOUD_URL = os.getenv("OTB_CLOUD_URL", "https://otb-cloud.outsidethebox.top") portal_services_bp = Blueprint("portal_services", __name__) def _portal_user_is_logged_in() -> bool: return bool( session.get("portal_user_id") or session.get("client_user_id") or session.get("portal_client_id") or session.get("client_id") or session.get("user_id") ) @portal_services_bp.route("/portal/services") def portal_services_home(): if not _portal_user_is_logged_in(): flash("Please sign in to access services.", "warning") return redirect(url_for("portal_login")) client = { "contact_name": session.get("portal_contact_name"), "company_name": session.get("portal_company_name"), "email": session.get("portal_email"), } client_name = ( client.get("contact_name") or client.get("company_name") or client.get("email") or "Client" ) services = [ { "key": "follow_me", "name": "Follow-me Tracker", "summary": "Create and manage your GPS tracking network. Free for up to 2 users.", "status": "beta", "enabled": True, "href": "/portal/services/follow-me-launch", "button_text": "Open Follow-me", }, { "key": "video_render", "name": "Video Rendering / Streaming", "summary": "Submit video rendering, conversion, and hosted streaming jobs.", "status": "coming_soon", "enabled": False, "href": "#", "button_text": "Coming Soon", }, { "key": "otb_cloud", "name": "OTB Cloud Backup & Storage", "summary": "Secure backup and storage for documents, photos, videos, and device uploads.", "status": "beta", "enabled": True, "href": "/portal/services/otb-cloud-launch", "button_text": "Open OTB Cloud", }, { "key": "miner_rentals", "name": "Miner Rentals", "summary": "Rent available OTB hashpower by time or package.", "status": "coming_soon", "enabled": False, "href": "#", "button_text": "Coming Soon", }, ] return render_template( "portal/services_here.html", client=client, client_name=client_name, services=services, ) def build_otb_cloud_handoff_url(uid, email): ts = str(int(time.time())) payload = f"{uid}|{email}|{ts}".encode("utf-8") sig = hmac.new( OTB_PORTAL_SHARED_SECRET.encode(), payload, hashlib.sha256 ).hexdigest() query = urllib.parse.urlencode({ "uid": uid, "email": email, "ts": ts, "sig": sig, }) return f"{OTB_CLOUD_URL}/auth/handoff?{query}" @portal_services_bp.route("/portal/services/otb-cloud-launch") def portal_launch_otb_cloud(): if not _portal_user_is_logged_in(): flash("Please sign in to access services.", "warning") return redirect(url_for("portal_login")) uid = session.get("portal_client_id") email = session.get("portal_email") if not uid or not email: flash("Unable to launch OTB Cloud because the portal session is missing required account details.", "danger") return redirect(url_for("portal_services.portal_services_home")) url = build_otb_cloud_handoff_url(uid, email) return redirect(url)