import json from typing import TYPE_CHECKING import jingrow from jingrow.core.utils import find from jcloude.api.account import get_account_request_from_key from jcloude.jcloude.pagetype.site.jerp_site import get_jerp_domain from jcloude.jcloude.pagetype.site.saas_pool import get as get_pooled_saas_site from jcloude.jcloude.pagetype.site.saas_site import ( SaasSite, get_default_team_for_app, get_saas_domain, get_saas_site_plan, set_site_in_subscription_docs, ) from jcloude.jcloude.pagetype.team.team import Team from jcloude.utils import log_error from jcloude.utils.telemetry import capture, identify if TYPE_CHECKING: from jcloude.jcloude.pagetype.site.site import Site # ----------------------------- SIGNUP APIs --------------------------------- @jingrow.whitelist(allow_guest=True) def account_request( subdomain, email, first_name, last_name, country, app, url_args=None, ): """ return: Stripe setup intent and AR key if stripe flow, else None """ from jingrow.utils.html_utils import clean_html email = email.strip().lower() jingrow.utils.validate_email_address(email, True) if not check_subdomain_availability(subdomain, app): jingrow.throw(f"Subdomain {subdomain} is already taken") all_countries = jingrow.db.get_all("Country", pluck="name") country = find(all_countries, lambda x: x.lower() == country.lower()) if not country: jingrow.throw("Country field should be a valid country name") team = jingrow.db.get_value("Team", {"user": email}) if team and jingrow.db.exists("Invoice", {"team": team, "status": "Unpaid", "type": "Subscription"}): jingrow.throw(f"Account {email} already exists with unpaid invoices") current_user = jingrow.session.user try: jingrow.set_user("Administrator") account_request = jingrow.get_pg( { "pagetype": "Account Request", "saas": True, "saas_app": app, "jerp": False, "subdomain": subdomain, "email": email, "role": "Jcloude Admin", "first_name": clean_html(first_name), "last_name": clean_html(last_name), "country": country, "url_args": url_args or json.dumps({}), "send_email": True, } ) site_name = account_request.get_site_name() identify( site_name, app=account_request.saas_app, source=json.loads(url_args).get("source") if url_args else "fc", ) account_request.insert(ignore_permissions=True) capture("completed_server_account_request", "fc_product_trial", site_name) except Exception as e: log_error("Account Request Creation Failed", data=e) raise finally: jingrow.set_user(current_user) create_or_rename_saas_site(app, account_request) def create_or_rename_saas_site(app, account_request): """ Creates site for Saas App. These are differentiated by `standby_for` field in site pg """ current_user = jingrow.session.user current_session_data = jingrow.session.data jingrow.set_user("Administrator") try: enable_hybrid_pools = jingrow.db.get_value("Saas Settings", app, "enable_hybrid_pools") hybrid_saas_pool = get_hybrid_saas_pool(account_request) if enable_hybrid_pools else "" pooled_site = get_pooled_saas_site(app, hybrid_saas_pool) if pooled_site: SaasSite(site=pooled_site, app=app).rename_pooled_site(account_request) else: saas_site = SaasSite( account_request=account_request, app=app, hybrid_saas_pool=hybrid_saas_pool ).insert(ignore_permissions=True) set_site_in_subscription_docs(saas_site.subscription_docs, saas_site.name) capture("completed_server_site_created", "fc_product_trial", account_request.get_site_name()) except Exception as e: log_error("Saas Site Creation or Rename failed", data=e) finally: jingrow.set_user(current_user) jingrow.session.data = current_session_data @jingrow.whitelist() def new_saas_site(subdomain, app): jingrow.only_for("System Manager") pooled_site = get_pooled_saas_site(app) if pooled_site: site = SaasSite(site=pooled_site, app=app).rename_pooled_site(subdomain=subdomain) else: site = SaasSite(app=app, subdomain=subdomain).insert(ignore_permissions=True) site.create_subscription(get_saas_site_plan(app)) site.reload() site.team = get_default_team_for_app(app) site.save(ignore_permissions=True) jingrow.db.commit() return site @jingrow.whitelist() def get_saas_site_status(site): if jingrow.db.exists("Site", site): return {"site": site, "status": jingrow.db.get_value("Site", site, "status")} return {"site": site, "status": "Pending"} def get_hybrid_saas_pool(account_request): """ 1. Get all hybrid pools and their rules 2. Filter based on rules and return Hybrid pool 3. Returns the first rule match return: The hybrid pool name that site belongs to based on the Account Request conditions """ hybrid_pool = "" all_pools = jingrow.get_all("Hybrid Saas Pool", {"app": account_request.saas_app}, pluck="name") ar_rules = jingrow.get_all( "Account Request Rules", {"parent": ("in", all_pools)}, ["parent", "field", "condition", "value"], group_by="parent", ) for rule in ar_rules: eval_locals = eval_locals = dict( account_request=account_request, ) if jingrow.safe_eval( f"account_request.{rule.field} {rule.condition} '{rule.value}'", None, eval_locals ): hybrid_pool = rule.parent return hybrid_pool # noqa: RET504 return hybrid_pool @jingrow.whitelist(allow_guest=True) def check_subdomain_availability(subdomain, app): """ Checks if subdomain is available to create a new site """ # Only for ERPNext domains if len(subdomain) <= 4: return False banned_domains = jingrow.get_all("Blocked Domain", {"block_for_all": 1}, pluck="name") if banned_domains and subdomain in banned_domains: return False exists = bool( jingrow.db.exists("Blocked Domain", {"name": subdomain, "root_domain": get_jerp_domain()}) or jingrow.db.exists( "Site", { "subdomain": subdomain, "domain": get_saas_domain(app), "status": ("!=", "Archived"), }, ) ) if exists: return False return True @jingrow.whitelist(allow_guest=True) def setup_account(key, business_data=None): """ Includes the data collection step in setup-account.html """ account_request = get_account_request_from_key(key) if not account_request: jingrow.throw("Invalid or Expired Key") capture( "init_server_setup_account", "fc_product_trial", account_request.get_site_name(), ) jingrow.set_user("Administrator") if business_data: business_data = jingrow.parse_json(business_data) if isinstance(business_data, dict): business_data = { key: business_data.get(key) for key in [ "company", "no_of_employees", "industry", "no_of_users", "designation", "phone_number", "referral_source", "agreed_to_partner_consent", ] } account_request.update(business_data) account_request.save(ignore_permissions=True) create_marketplace_subscription(account_request) capture( "completed_server_setup_account", "fc_product_trial", account_request.get_site_name(), ) @jingrow.whitelist(allow_guest=True) def headless_setup_account(key): """ Ignores the data collection step in setup-account.html """ account_request = get_account_request_from_key(key) if not account_request: jingrow.throw("Invalid or Expired Key") capture( "init_server_setup_account", "fc_product_trial", account_request.get_site_name(), ) jingrow.set_user("Administrator") create_marketplace_subscription(account_request) # create team and enable the subscriptions for site capture( "completed_server_setup_account", "fc_product_trial", account_request.get_site_name(), ) jingrow.local.response["type"] = "redirect" jingrow.local.response["location"] = f"/prepare-site?key={key}&app={account_request.saas_app}" def create_marketplace_subscription(account_request): """ Create team, subscription for site and Saas Subscription """ team_pg = create_team(account_request) site_name = jingrow.db.get_value("Site", {"account_request": account_request.name}) if site_name: jingrow.db.set_value("Site", site_name, "team", team_pg.name) subscription = jingrow.db.exists("Subscription", {"document_name": site_name}) if subscription: jingrow.db.set_value("Subscription", subscription, "team", team_pg.name) marketplace_subscriptions = jingrow.get_all( "Subscription", {"document_type": "Marketplace App", "site": site_name, "enabled": 0}, pluck="name", ) for subscription in marketplace_subscriptions: jingrow.db.set_value( "Subscription", subscription, {"enabled": 1, "team": team_pg.name}, ) jingrow.set_user(team_pg.user) jingrow.local.login_manager.login_as(team_pg.user) return site_name def create_team(account_request, get_stripe_id=False): """ Create team and return pg """ email = account_request.email if not jingrow.db.exists("Team", {"user": email}): team_pg = Team.create_new( account_request, account_request.first_name, account_request.last_name, country=account_request.country, is_us_eu=account_request.is_us_eu, via_jerp=True, user_exists=jingrow.db.exists("User", email), ) else: team_pg = jingrow.get_pg("Team", {"user": email}) if get_stripe_id: return team_pg.stripe_customer_id return team_pg @jingrow.whitelist(allow_guest=True) def get_site_status(key, app=None): """ return: Site status """ account_request = get_account_request_from_key(key) if not account_request: jingrow.throw("Invalid or Expired Key") domain = get_saas_domain(app) if app else get_jerp_domain() site = jingrow.db.get_value( "Site", {"subdomain": account_request.subdomain, "domain": domain, "status": "Active"}, ["status", "subdomain", "name"], as_dict=1, ) if site: capture("completed_site_allocation", "fc_product_trial", site.name) return site return {"status": "Pending"} @jingrow.whitelist(allow_guest=True) def get_site_url_and_sid(key, app=None): """ return: Site url and session id for login-redirect """ account_request = get_account_request_from_key(key) if not account_request: jingrow.throw("Invalid or Expired Key") domain = get_saas_domain(app) if app else get_jerp_domain() name = jingrow.db.get_value("Site", {"subdomain": account_request.subdomain, "domain": domain}) site: "Site" = jingrow.get_pg("Site", name) if site.additional_system_user_created: return site.login_as_team() return site.login_as_admin()