o
    d۷i`                     @   s  d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dlm	Z	m
Z
 d dlmZ d dlmZmZ d dlZd dlZd dlZd dlmZ d dlmZmZmZmZmZ d dlmZ d d	lmZ d d
lm Z  d dl!m"Z" d dl#m$Z$m%Z%m&Z&m'Z'm(Z( d dl)m*Z*m+Z+m,Z, d dl-m.Z. d dl/m0Z0m1Z1 e2e3Z4de5dB de5dB fddZ6ee5dB ee6f Z7de5ddfddZ8de	de5fddZ9de	de:fddZ;de	de	de	fddZ<G dd  d eZ=de>e= fd!d"Z?G d#d$ d$eZ@d%e5d&e5dB de@fd'd(ZAd)e5d*e5d&e5dB de@fd+d,ZBG d-d. d.eZCd%e5deCfd/d0ZDG d1d2 d2eZEde5d3e	ddfd4d5ZFd6e5de@dB fd7d8ZGd)e5de>e@ fd9d:ZHg d;ZIg d<ZJd=ed>e	de*fd?d@ZKd=edAe%d%e5dBeCddf
dCdDZLd=ed%e5dBeCddfdEdFZMG dGdH dHeZNdIeNd=eddfdJdKZOd=eddfdLdMZP		N	d[dee	dB ejQdOdPf dQee:ej dRdSdPf dTee5dB ej dUdVdWdXf defdYdZZRdS )\    N)cycle)PathPurePosixPath)dedent)	AnnotatedAny)Client)AfterValidator	BaseModelEmailStrTypeAdapterValidationError)Text)RichToolkit)Option)login)SUCCESSFUL_STATUSES	APIClientDeploymentStatusStreamLogErrorTooManyRetriesError)	AppConfigget_app_configwrite_app_config)Identity)get_rich_toolkithandle_http_errorsvreturnc                 C   sx   | d u rd S |   } | sd S | drtdt| }| r#tdd|jv r,td| }td|s:td|S )N~zcannot start with '~'z%must be a relative path, not absolutez..z!cannot contain '..' path segmentsz[A-Za-z0-9._/ -]+zGcontains invalid characters (allowed: letters, numbers, space, / . _ -))	strip
startswith
ValueErrorr   is_absolutepartsas_posixre	fullmatch)r   path
normalized r*   W/home/ubuntu/vllm_env/lib/python3.10/site-packages/fastapi_cloud_cli/commands/deploy.pyvalidate_app_directory$   s$   

r,   deployment_idc              
   C   s   t d|  z*t }|d|  d}|  t d W d    W d S 1 s)w   Y  W d S  tyI } zt d| W Y d }~d S d }~ww )Nz$Cancelling upload for deployment: %s/deployments/z/upload-cancelledz2Upload cancellation notification sent successfullyz5Failed to notify server about upload cancellation: %s)loggerdebugr   postraise_for_status	Exception)r-   clientresponseer*   r*   r+   _cancel_uploadE   s   &r7   r(   c                 C   s   | j S N)namer(   r*   r*   r+   _get_app_nameR   s   r;   c                    sN   g d}t  fdd|D rdS  jdkrdS  jdks# jdr%dS dS )	N)z.venv__pycache__z.mypy_cachez.pytest_cachez.gitz
.gitignore.fastapicloudignorec                 3   s    | ]}| j v V  qd S r8   )r$   ).0partr:   r*   r+   	<genexpr>b   s    z(_should_exclude_entry.<locals>.<genexpr>Tz.pycz.envz.env.F)anysuffixr9   r!   )r(   parts_to_excluder*   r:   r+   _should_exclude_entryW   s   

rD   tar_pathc                 C   s   t d|  tj| tdgdd}t d| d}t|d(}|D ]}| r(q!|| }t d| |j	||d	 |d
7 }q!W d    n1 sIw   Y  t d| |S )Nz&Starting archive creation for path: %sr=   F)should_exclude_entryadditional_ignore_pathsignore_hiddenzArchive will be created at: %sr   zw:zstzAdding %s to archive)arcname   z*Archive created successfully with %s files)
r/   r0   rignorewalkrD   fastaropenis_dirrelative_toappend)r(   rE   files
file_counttarfilenamerI   r*   r*   r+   archiven   s*   


rV   c                   @   s&   e Zd ZU eed< eed< eed< dS )Teamidslugr9   N__name__
__module____qualname__str__annotations__r*   r*   r*   r+   rW      s   
 rW   c                  C   sR   t  } | d}|  | d }W d    n1 sw   Y  dd |D S )Nz/teams/datac                 S      g | ]}t |qS r*   )rW   model_validater>   teamr*   r*   r+   
<listcomp>       z_get_teams.<locals>.<listcomp>r   getr2   json)r4   r5   r`   r*   r*   r+   
_get_teams   s   
rj   c                   @   s*   e Zd ZU eed< eed< edB ed< dS )AppResponserX   rY   N	directoryrZ   r*   r*   r*   r+   rk      s   
 rk   app_idrl   c                 C   sX   t  }|jd|  d|id}|  t| W  d    S 1 s%w   Y  d S )N/apps/rl   ri   )r   patchr2   rk   rb   ri   )rm   rl   r4   r5   r*   r*   r+   _update_app   s   $rq   team_idapp_namec                 C   sV   t  }|jd|| |dd}|  t| W  d    S 1 s$w   Y  d S )Nrn   )r9   rr   rl   ro   )r   r1   r2   rk   rb   ri   )rr   rs   rl   r4   r5   r*   r*   r+   _create_app   s   
$rt   c                   @   s>   e Zd ZU eed< eed< eed< eed< eed< eed< dS )CreateDeploymentResponserX   rm   rY   statusdashboard_urlurlN)r[   r\   r]   r^   r_   r   r*   r*   r*   r+   ru      s   
 ru   c                 C   sR   t  }|d|  d}|  t| W  d    S 1 s"w   Y  d S )Nrn   r.   )r   r1   r2   ru   rb   ri   )rm   r4   r5   r*   r*   r+   _create_deployment   s
   $ry   c                   @   s&   e Zd ZU eed< eeef ed< dS )RequestUploadResponserx   fieldsN)r[   r\   r]   r^   r_   dictr*   r*   r*   r+   rz      s   
 rz   archive_pathc           	   
   C   sN  t d|  t d|| j t }t n}t d |d|  d}|  t	|
 }t d|j t d t|d}|j|j|jd	|id
}W d    n1 sYw   Y  |  t d t d |d|  d}|  t d W d    n1 sw   Y  W d    d S W d    d S 1 sw   Y  d S )Nz-Starting deployment upload for deployment: %sz Archive path: %s, size: %s byteszRequesting upload URL from APIr.   z/uploadzReceived upload URL: %szStarting file upload to S3rbfile)r`   rR   z"File upload completed successfullyz%Notifying API that upload is completez/upload-completez%Upload notification sent successfully)r/   r0   statst_sizer   r   r1   r2   rz   rb   ri   rx   rN   r{   )	r-   r}   fastapi_clientr4   r5   upload_dataarchive_fileupload_responsenotify_responser*   r*   r+   _upload_deployment   s@   




Pr   app_slugc                 C   sl   t  &}|d|  }|jdkr	 W d    d S |  | }W d    n1 s,w   Y  t|S )Nrn   i  )r   rh   status_coder2   ri   rk   rb   )r   r4   r5   r`   r*   r*   r+   _get_app   s   



r   c                 C   sZ   t  }|jdd| id}|  | d }W d    n1 s!w   Y  dd |D S )Nrn   rr   )paramsr`   c                 S   ra   r*   )rk   rb   r>   appr*   r*   r+   re   
  rf   z_get_apps.<locals>.<listcomp>rg   )rr   r4   r5   r`   r*   r*   r+   	_get_apps  s   r   )
u+   🚀 Preparing for liftoff! Almost there...uA   👹 Sneaking past the dependency gremlins... Don't wake them up!u>   🤏 Squishing code into a tiny digital sandwich. Nom nom nom.u;   🐱 Removing cat videos from our servers to free up space.uM   🐢 Uploading at blazing speeds of 1 byte per hour. Patience, young padawan.uN   🔌 Connecting to server... Please stand by while we argue with the firewall.uQ   💥 Oops! We've angered the Python God. Sacrificing a rubber duck to appease it.u3   🧙 Sprinkling magic deployment dust. Abracadabra!uB   👀 Hoping that @tiangolo doesn't find out about this deployment.uF   🍪 Cookie monster detected on server. Deploying anti-cookie shields.)uS   😅 Well, that's embarrassing. We're still waiting for the deployment to finish...u9   🤔 Maybe we should have brought snacks for this wait...u   🥱 Yawn... Still waiting...uK   🤯 Time is relative... Especially when you're waiting for a deployment...toolkitpath_to_deployc              	   C   s  | j d| ddd |   | d!}t|dd t }W d    n1 s)w   Y  W d    n1 s8w   Y  |   | jdd	d
d |D d}|   | jdddd}|   d }|s| d#}t|dd t|j}W d    n1 s~w   Y  W d    n1 sw   Y  |   |s|  d t	
d| jddd |D d}|r|jn| jdt|d}|   |r|jnd}	| jdd|	pddttd}
|
r|
nd }|   | j dd d |   |  d!|j d" |  d#| d" |  d$|pd% d" |   | jd&d'td(d)d*td+d,d*gd}|   |d,kr4|  d- t	
d.|r||jkr}| jd/d00}t| t|j|d1}|d2|pWd% d3 W d    n	1 sgw   Y  W d    n	1 sww   Y  n@|}n=| jd4d0.}t| t|j||d1}W d    n	1 sw   Y  |d5|j  W d    n	1 sw   Y  t|j|jd6}t|| |S )7NzSetting up and deploying [blue]z[/blue]r(   tagzFetching teams...z-Error fetching teams. Please try again later.)default_messagez&Select the team you want to deploy to:rd   c                 S      g | ]
}t |j|d qS r9   value)r   r9   rc   r*   r*   r+   re   2      z"_configure_app.<locals>.<listcomp>r   optionsz Do you want to create a new app?r   T)r   defaultzFetching apps...z,Error fetching apps. Please try again later.z=No apps found in this team. You can create a new app instead.rJ   z%Select the app you want to deploy to:c                 S   r   r   )r   rY   r   r*   r*   r+   re   Q  r   )r   zWhat's your app name?)titler    z>Path to the directory containing your app (e.g. src, backend):dirz:[italic]Leave empty if it's the current directory[/italic])r   r   r   placeholder	validatorzDeployment configuration:summaryzTeam: [bold]z[/bold]zApp name: [bold]zDirectory: [bold].zDoes everything look right?confirmzYes, start the deployment!deployr   zNo, let me start overcancelzDeployment cancelled.r   zUpdating app directory...)r   )rl   zApp directory updated to ''zCreating app...z$App created successfully! App slug: )rm   rr   )print
print_lineprogressr   rj   askr   r   rX   typerExitrY   inputr;   rl   r   AppDirectoryr9   r   rq   logrt   r   r   )r   r   r   teamsrd   create_new_appselected_appappsrs   initial_directorydirectory_inputrl   choicer   
app_configr*   r*   r+   _configure_app"  s   
	



 

r   r4   
deploymentc                 C   s   | j ddddk}z	|||j}W n$ tttfy6   d|jd< d|j d|j d	|_Y W d    d S w |t	v rHd
|j
 d|j
 d	|_n#d|jd< d|_t|}|d| d|j d|j d	 tdW d    d S 1 svw   Y  d S )NzVerifying deployment...Tu   ✅)r   inline_logs
done_emojiu   ⚠️r   z@Could not confirm deployment status. Check the dashboard: [link=][/link]u3   Ready the chicken! 🐔 Your app is ready at [link=u   ❌zDeployment failedu   😔 Oh no! Deployment failed: z. Check out the logs at [link=rJ   )r   poll_deployment_statusrX   TimeoutErrorr   r   metadatarw   current_messager   rx   r   to_human_readabler   r   r   )r   r4   rm   r   r   final_statushuman_statusr*   r*   r+   _verify_deployment  sD   



"r   c                 C   s  t t}| jddd |   d}t }t }t }| jt|dddd}d	}	zc|	|j
D ]Z}
t | }|
jd
krJ|t|
j  |
jdkrVd}	d|_ n7|
jdkrs|d |d|j d|j d td|dkr{t t}t | dkrt||_t }q2W n* tttfy } z|td| d|j d|j d  tdd d }~ww W d    n1 sw   Y  |	r|   t| ||| W d    d S W d    d S 1 sw   Y  d S )Nu+   Checking the status of your deployment 👀cloudr   g        T   u   🚀)r   lines_to_showr   FmessagecompletezBuild complete!failedr   u>   😔 Oh no! Something went wrong. Check out the logs at [link=r   r   rJ         z8
                    [error]Build log streaming failed: z\[/]

                    Unable to stream build logs. Check the dashboard for status: [link=z[/link]
                    )r   WAITING_MESSAGESr   r   time	monotonicr   r   nextstream_build_logsrX   typer   r   	from_ansir   rstripr   rw   r   r   LONG_WAIT_MESSAGESr   r   r   	set_errorr   r    r   )r   rm   r   messagestime_elapsed
started_atlast_message_changed_atr4   r   build_completer   r6   r*   r*   r+   _wait_for_deployment  s|   





01"r   c                   @   s   e Zd ZU eed< dZedB ed< dZedB ed< dZedB ed< dZ	edB ed< dZ
edB ed< dZedB ed< dZedB ed	< dS )
SignupToWaitingListemailNr9   organizationrole	team_sizelocationuse_casesecret_code)r[   r\   r]   r   r_   r9   r^   r   r   r   r   r   r   r*   r*   r*   r+   r     s   
 r   resultc              
   C   s   | dB}t )}t| |jd|  d}|  W d    n1 s&w   Y  W d    n1 s5w   Y  |d W d    d S 1 sJw   Y  d S )NzSending your request...z/users/waiting-listro   u9   Let's go! Thanks for your interest in FastAPI Cloud! 🚀)r   r   r   r1   
model_dumpr2   r   )r   r   r   r4   r5   r*   r*   r+   _send_waitlist_form  s   

"r   c                 C   s  ddl m} | jddd |   | jddttd}|   td	|i}| j	d
ddr|   |d| j
d}|jdddd |jdddd |jdddd |jdddd |jdddd |jdddd |jd d!d"d | }ztd	|i|}W n ty   | d# Y d S w |   | j	d$d%dr|   t||  tt tjg d&tjtjd'd( W d    d S 1 sw   Y  d S d S ))Nr   )FormzkWe're currently in private beta. If you want to be notified when we launch, please fill out the form below.waitlistr   zEnter your email:T)requiredr   r   z?Do you want to get access faster by giving us more information?zWaitlist form)styler9   NamezJohn Doe)labelr   r   Organizationz	Acme Inc.rd   rW   zTeam Ar   Role	Developerr   LocationzSan Franciscor   z%How do you plan to use FastAPI Cloud?zI'm building a web appr   zSecret code123456z.[error]Invalid form data. Please try again.[/]zDo you agree to
- Terms of Service: [link=https://fastapicloud.com/legal/terms]https://fastapicloud.com/legal/terms[/link]
- Privacy Policy: [link=https://fastapicloud.com/legal/privacy-policy]https://fastapicloud.com/legal/privacy-policy[/link]
terms)rN   z-gu!   raycast://confetti?emojis=🐔⚡F)stdoutstderrcheck)rich_toolkit.formr   r   r   r   r   r   r   rb   r   r   	add_inputrunr   r   
contextlibsuppressr3   
subprocessDEVNULL)r   r   r   r   formr*   r*   r+   _waitlist_form!  s~   "r   Fz:A path to the folder containing the app you want to deploy)help	skip_waitz	--no-waitz"Skip waiting for deployment statusprovided_app_idz--app-idzApplication ID to deploy toFASTAPI_CLOUD_APP_ID)r  envvarc                 C   s
  t d t d| || t }t }| sht d |jddd |  |jr8| r8|j	ddd n|j	d	dd |  |j
d
dtdddtdddgd}|dkr_t  n	t| td|jddd |  | pxt }t d| t|}|r|r|j|kr|	d| d|j d |  |j	ddd tdd|r|}n|r|j}nt d t||d}|  |j}|r|	d| d n|	d |  |jd d!d"3}	t|	 t d#| t|}
W d   n1 sw   Y  |
st d$ |	d% W d   n	1 sw   Y  |
s5|  |s0|j	d&dd tdt }t d' t|d( }t| pLt | |jd)d*d+S}	t|	> t d,|
j t|
j}z|	d-|j  |	d. t |j| |	d/ W n t!y   t"|j  w W d   n	1 sw   Y  W d   n	1 sw   Y  W d   n	1 sw   Y  |  |st d0 t#||
j|d1 nt d2 |	d3|j$ d4|j$ d5 W d   dS W d   dS 1 sw   Y  dS )6uB   
    Deploy a [bold]FastAPI[/bold] app to FastAPI Cloud. 🚀
    zDeploy command startedz*Deploy path: %s, skip_wait: %s, app_id: %sz3User not logged in, prompting for login or waitlistzWelcome to FastAPI Cloud!FastAPIr   z.Your session has expired. Please log in again.infoz4You need to be logged in to deploy to FastAPI Cloud.zWhat would you like to do?authzLogin to my existing accountr   r   zJoin the waiting listr   r   rJ   zStarting deploymentzDeploying from path: %sz[error]Error: Provided app ID (z#) does not match the local config (z).[/]zRun [bold]fastapi cloud unlink[/] to remove the local config, or remove --app-id / unset FASTAPI_CLOUD_APP_ID to use the configured app.tipNz(No app config found, configuring new app)r   zDeploying to app [blue]z
[/blue]...zDeploying app...zChecking app...T)	transientzChecking app with ID: %szApp not found in APIz>App not found. Make sure you're logged in the correct account.zeIf you deleted this app, you can run [bold]fastapi cloud unlink[/] to unlink the local configuration.zCreating archive for deploymentzarchive.tarzCreating deploymentu   📦)r   r   zCreating deployment for app: %sz2Deployment created successfully! Deployment slug: zUploading deployment...z!Deployment uploaded successfully!z"Waiting for deployment to complete)r   z%Skipping deployment wait as requestedz-Check the status of your deployment at [link=r   r   )%r/   r0   r   r   is_logged_inprint_titler   token
is_expiredr   r   r   r   r   r   r   r   cwdr   rm   r   r   r   r   r   tempfileTemporaryDirectoryrV   rX   ry   r   rY   r   KeyboardInterruptr7   r   rw   )r(   r  r  identityr   r   r   r   target_app_idr   r   temp_dirr}   r   r*   r*   r+   r   r  s   


	












 

   $r   )NFN)Sr   loggingr&   r   r  r   	itertoolsr   pathlibr   r   textwrapr   typingr   r   rM   rK   r   httpxr   pydanticr	   r
   r   r   r   	rich.textr   rich_toolkitr   rich_toolkit.menur    fastapi_cloud_cli.commands.loginr   fastapi_cloud_cli.utils.apir   r   r   r   r   fastapi_cloud_cli.utils.appsr   r   r   fastapi_cloud_cli.utils.authr   fastapi_cloud_cli.utils.clir   r   	getLoggerr[   r/   r^   r,   r   r7   r;   boolrD   rV   rW   listrj   rk   rq   rt   ru   ry   rz   r   r   r   r   r   r   r   r   r   r   r   Argumentr   r*   r*   r*   r+   <module>   s    

	)
z
$
H
W
