jcloude/press/api/developer/marketplace.py
2025-12-23 20:48:07 +08:00

235 lines
6.6 KiB
Python

from typing import Dict, List
import jingrow
from jingrow.utils import get_url
from jcloude.api.developer import raise_invalid_key_error
from jcloude.api.site import get_plans as get_site_plans
from jcloude.utils.telemetry import capture
class DeveloperApiHandler:
def __init__(self, secret_key: str) -> None:
self.secret_key = secret_key
self.validate_secret_key()
def validate_secret_key(self):
"""Validate secret_key and set app subscription name and pg"""
if not self.secret_key or not isinstance(self.secret_key, str):
raise_invalid_key_error()
app_subscription_name = jingrow.db.exists(
"Subscription", {"secret_key": self.secret_key, "enabled": 1}
)
if not app_subscription_name:
raise_invalid_key_error()
self.app_subscription_name = app_subscription_name
self.set_subscription_pg()
def set_subscription_pg(self):
"""To be called after `secret_key` validation"""
self.app_subscription_pg = jingrow.get_pg("Subscription", self.app_subscription_name)
def get_subscription_status(self) -> str:
return self.app_subscription_pg.status
def get_subscription_info(self) -> Dict:
"""Important rule for security: Send info back carefully"""
app_subscription_dict = self.app_subscription_pg.as_dict()
fields_to_send = [
"document_name",
"enabled",
"plan",
"site",
]
filtered_dict = {
x: app_subscription_dict[x] for x in app_subscription_dict if x in fields_to_send
}
return filtered_dict
def get_subscription(self) -> Dict:
team = self.app_subscription_pg.team
with SessionManager(team) as _:
currency, address = jingrow.db.get_value(
"Team", team, ["currency", "billing_address"]
)
team_pg = jingrow.get_pg("Team", team)
response = {
"currency": currency,
"address": jingrow.db.get_value(
"Address",
address,
["address_line1", "city", "state", "country", "pincode"],
as_dict=True,
)
if address
else {},
"team": self.app_subscription_pg.team,
"countries": jingrow.db.get_all("Country", pluck="name"),
"plans": get_site_plans(),
"has_billing_info": (
team_pg.default_payment_method
or team_pg.get_balance() > 0
or team_pg.free_account
),
"current_plan": jingrow.db.get_value("Site", self.app_subscription_pg.site, "plan"),
}
capture("attempted", "fc_subscribe", team)
return response
def update_billing_info(self, data: Dict) -> str:
team = self.app_subscription_pg.team
with SessionManager(team) as _:
team_pg = jingrow.get_pg("Team", team)
team_pg.update_billing_details(data)
capture("updated_address", "fc_subscribe", team)
return "success"
def get_publishable_key_and_setup_intent(self):
with SessionManager(self.app_subscription_pg.team) as _:
from jcloude.api.billing import get_publishable_key_and_setup_intent
return get_publishable_key_and_setup_intent()
def setup_intent_success(self, setup_intent):
team = self.app_subscription_pg.team
with SessionManager(team) as _:
from jcloude.api.billing import setup_intent_success
capture("added_card", "fc_subscribe", team)
return setup_intent_success(setup_intent)
def change_site_plan(self, plan):
team = self.app_subscription_pg.team
with SessionManager(team) as _:
site = jingrow.get_pg("Site", self.app_subscription_pg.site)
site.change_plan(plan)
capture("changed_plan", "fc_subscribe", team)
def send_login_link(self):
try:
login_url = self.get_login_url()
users = jingrow.get_pg("Team", self.app_subscription_pg.team).user
jingrow.sendmail(
subject="Login Verification Email",
recipients=[users],
template="remote_login",
args={"login_url": login_url, "site": self.app_subscription_pg.site},
now=True,
)
return "success"
except Exception as e:
return e
def get_login_url(self):
# check for active tokens
team = self.app_subscription_pg.team
if jingrow.db.exists(
"Saas Remote Login",
{
"team": team,
"status": "Attempted",
"expires_on": (">", jingrow.utils.now()),
},
):
pg = jingrow.get_pg(
"Saas Remote Login",
{
"team": team,
"status": "Attempted",
"expires_on": (">", jingrow.utils.now()),
},
)
token = pg.token
else:
token = jingrow.generate_hash("Saas Remote Login", 50)
jingrow.get_pg(
{
"doctype": "Saas Remote Login",
"team": team,
"token": token,
}
).insert(ignore_permissions=True)
jingrow.db.commit()
return get_url(
f"/api/method/jcloude.api.marketplace.login_via_token?token={token}&team={team}&site={self.app_subscription_pg.site}"
)
class SessionManager:
# set user for authenticated requests and then switch to guest once completed
def __init__(self, team: str):
jingrow.set_user(jingrow.db.get_value("Team", team, "user"))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
jingrow.set_user("Guest")
# ------------------------------------------------------------
# API ENDPOINTS
# ------------------------------------------------------------
@jingrow.whitelist(allow_guest=True)
def get_subscription_status(secret_key: str) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.get_subscription_status()
@jingrow.whitelist(allow_guest=True)
def get_subscription_info(secret_key: str) -> Dict:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.get_subscription_info()
@jingrow.whitelist(allow_guest=True)
def get_subscription(secret_key: str) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.get_subscription()
@jingrow.whitelist(allow_guest=True)
def get_plans(secret_key: str, subscription: str) -> List:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.get_plans(subscription)
@jingrow.whitelist(allow_guest=True)
def update_billing_info(secret_key: str, data) -> str:
data = jingrow.parse_json(data)
api_handler = DeveloperApiHandler(secret_key)
return api_handler.update_billing_info(data)
@jingrow.whitelist(allow_guest=True)
def get_publishable_key_and_setup_intent(secret_key: str) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.get_publishable_key_and_setup_intent()
@jingrow.whitelist(allow_guest=True)
def setup_intent_success(secret_key: str, setup_intent) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.setup_intent_success(setup_intent)
@jingrow.whitelist(allow_guest=True)
def change_site_plan(secret_key: str, plan: str) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.change_site_plan(plan)
@jingrow.whitelist(allow_guest=True)
def send_login_link(secret_key: str) -> str:
api_handler = DeveloperApiHandler(secret_key)
return api_handler.send_login_link()