o
    oif#                     @  s  d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZmZ ddlmZmZmZmZ ddlmZmZmZm Z m!Z!m"Z" ee#$ j%d Z&e&d d Z'e&d d Z(d>ddZ)d?ddZ*d@ddZ+dAddZ,dBd!d"Z-dCd#d$Z.dCd%d&Z/dDd(d)Z0dCd*d+Z1dCd,d-Z2dEd/d0Z3dFd3d4Z4dFd5d6Z5dGd8d9Z6dHd;d<Z7e8d=kre9e7 dS )Ia  Provision a full tenant environment for the SaaS platform.

The script creates:
1. A tenant data-plane database named ``tenant_<tenant>``
2. Bootstrap tables inside that tenant database
3. An initial admin user in the tenant database
4. Control-plane records for the tenant, app enablement, and default limits
5. Per-tenant storage and log directories

Existing applications under ``/apps`` remain untouched.
    )annotationsN)Path)create_engineselecttext)Sessionsessionmaker)Config)get_control_plane_engine)build_database_url)build_tenant_db_namesanitize_routing_key)TenantAppConfigTenantBootstrapBaseTenantUsageRecord
TenantUser)AppBasePlanTenant	TenantAppTenantLimit   storagetenantslogsreturnargparse.Namespacec                  C  s4   t jdd} | jdddd | jdddd |  S )	NzProvision a tenant environment.)descriptionz--tenantTz"Tenant slug. Allowed: ^[a-z0-9_]+$)requiredhelpz--emailzInitial tenant admin email.)argparseArgumentParseradd_argument
parse_args)parser r&   scripts/provision_tenant.py_parse_args$   s   r(   strc                
   C  s:   t jstdt j dt j dt j dt j dt j d
S )NzMissing PROVISION_DB_USERz://:@z/mysql)r	   PROVISION_DB_USERRuntimeError	DB_ENGINEPROVISION_DB_PASSWORDPROVISION_DB_HOSTPROVISION_DB_PORTr&   r&   r&   r'   _provision_database_url+   s   r2   db_nameNonec                 C  sR   t t dd}| }|td|  d W d    d S 1 s"w   Y  d S )NTfuturezCREATE DATABASE IF NOT EXISTS ``)r   r2   beginexecuter   )r3   engineconnr&   r&   r'   _create_database4   s   
"r<   admin_emailc                 C  sz   t t| dd}tj| t|dddd}| }t|| t| t| |	  W d    d S 1 s6w   Y  d S )NTr5   Fbind
autocommit	autoflushr6   )
r   r   r   metadata
create_allr   _upsert_tenant_admin_upsert_tenant_app_config_seed_usage_markercommit)r3   r=   tenant_enginesession_factorysessionr&   r&   r'   _ensure_tenant_database_schema:   s   

"rK   rJ   r   c                 C  sb   |  tttj|  kd }|d u r)| 	t|  dd d S d|_
d|_d S )Nr   admin)emailroleT)r9   r   r   whererM   striplowerlimitscalar_one_or_noneaddrN   	is_active)rJ   r=   existingr&   r&   r'   rD   F   s    
rD   c                 C  sh   ddidddd}|  D ]#\}}| t|}|d u r&t|d}| | ||_tj|dd|_qd S )	NenabledTx     requests_per_minutemonthly_jobs)zwp_invoices.enabledzusage.limits.default)
config_key)	sort_keys)itemsgetr   rT   config_jsonjsondumpsconfig_value)rJ   defaultskeypayloadrecordr&   r&   r'   rE   Q   s   

rE   c                 C  sL   |  tttjdkd }|d u r$| tddddid d S d S )Nztenant.provisionedr   1sourcescripts.provision_tenant)metric_namemetric_valuemetadata_json)r9   r   r   rO   rl   rR   rS   rT   )rJ   rV   r&   r&   r'   rF   _   s   rF   tenant_slugc                 C  s   t  }tj| t|dddd}| ,}t| t| t|| |d}t||j	dd t
||j	dd |  W d    d S 1 sCw   Y  d S )NFTr>   )ro   r3   wp_invoices)	tenant_idapp_id)r
   r   rB   rC   r   _ensure_plan_ensure_app_catalog_upsert_tenant_upsert_tenant_apprq   _upsert_default_limitsrG   )ro   r=   r3   r:   rI   rJ   tenantr&   r&   r'   _ensure_control_plane_recordsm   s   
"ry   c                 C  s:   |  td}|d u rtddddddd}| | d S d S )NdevDevelopmentz7Default development plan for newly provisioned tenants.r   AUD)plan_idcodenamer   price_monthlycurrency)r`   r   rT   )rJ   planr&   r&   r'   rs   {   s   rs   c                 C  s8   |  td}|d u rtdddddd}| | d S d S )Nrp   zWP Invoiceszapps.wp_invoicesz/api/v1/wp_invoicesT)rr   display_namemodule_pathroute_prefixrU   )r`   r   rT   )rJ   appr&   r&   r'   rt      s   rt   r   c                C  s   |  t|}|d u r&t|||dd ddd||ddid	}| | |S d|_d|_d|_||_||_	t
|jp:i }d|d< ||_|S )	N_ activeTrz   provisioned_byrk   )	rq   slugr   statusrU   r}   data_db_namestorage_keyrn   )r`   r   replacetitlerT   r   rU   r}   r   r   dictrn   )rJ   ro   r3   rx   rB   r&   r&   r'   ru      s0   
ru   rq   rr   c                C  sn   t ttj|ktj|kd}| | }|d u r-t||dddid}| | d S d|_	ddi|_
d S )Nr   TrW   )rq   rr   
is_enabledra   )r   r   rO   rq   rr   rR   r9   rS   rT   r   ra   )rJ   rq   rr   stmt
tenant_appr&   r&   r'   rv      s   "
rv   c                C  s   dddddd ddd}|  D ]D\}}tttj|ktj|ktj|kd}| |	 }|d u rAt|||d	}| 
| |d
 |_|d |_|d |_ddi|_qd S )NrX   <   block)limit_valuewindow_secondsoverage_policyrY   trackrZ   r   )rq   rr   
limit_namer   r   r   defaultT)r_   r   r   rO   rq   rr   r   rR   r9   rS   rT   r   r   r   rn   )rJ   rq   rr   re   r   rg   r   rh   r&   r&   r'   rw      s*   

	



rw   tuple[Path, Path]c                 C  s4   t |  }t|  }|jddd |jddd ||fS )NT)parentsexist_ok)STORAGE_ROOT	LOGS_ROOTmkdir)ro   storage_path	logs_pathr&   r&   r'   _ensure_filesystem   s
   r   intc                  C  s   t  } t| j}|d u rtd| j  }|stdt|}t|\}}t	| t
|| t|||d ||t|t|dd}ttj|dd dS )	Nztenant is requiredzemail is required)ro   r=   r3   created)rx   dbr   r   r      )indentr   )r(   r   rx   r-   rM   rP   rQ   r   r   r<   rK   ry   r)   printrb   rc   )argsro   r=   r3   r   r   summaryr&   r&   r'   main   s(   

r   __main__)r   r   )r   r)   )r3   r)   r   r4   )r3   r)   r=   r)   r   r4   )rJ   r   r=   r)   r   r4   )rJ   r   r   r4   )ro   r)   r=   r)   r3   r)   r   r4   )rJ   r   ro   r)   r3   r)   r   r   )rJ   r   rq   r)   rr   r)   r   r4   )ro   r)   r   r   )r   r   ):__doc__
__future__r   r!   rb   pathlibr   
sqlalchemyr   r   r   sqlalchemy.ormr   r   config.baser	   config.control_planer
   	config.dbr   config.db_routerr   r   !platform.tenants.bootstrap_modelsr   r   r   r   platform.tenants.modelsr   r   r   r   r   r   __file__resolver   BASE_DIRr   r   r(   r2   r<   rK   rD   rE   rF   ry   rs   rt   ru   rv   rw   r   r   __name__
SystemExitr&   r&   r&   r'   <module>   sD    


	












