reflex-hosting-cli 0.1.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflex_cli/__init__.py +1 -0
- reflex_cli/cli.py +362 -0
- reflex_cli/constants/__init__.py +8 -0
- reflex_cli/constants/base.py +47 -0
- reflex_cli/constants/compiler.py +31 -0
- reflex_cli/constants/hosting.py +45 -0
- reflex_cli/deployments.py +354 -0
- reflex_cli/utils/__init__.py +1 -0
- reflex_cli/utils/console.py +155 -0
- reflex_cli/utils/dependency.py +132 -0
- reflex_cli/utils/hosting.py +1908 -0
- reflex_hosting_cli-0.1.13.dist-info/LICENSE +201 -0
- reflex_hosting_cli-0.1.13.dist-info/METADATA +35 -0
- reflex_hosting_cli-0.1.13.dist-info/RECORD +15 -0
- reflex_hosting_cli-0.1.13.dist-info/WHEEL +4 -0
reflex_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI library for the hosting service."""
|
reflex_cli/cli.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"""CLI for the hosting service."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import tempfile
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Callable
|
|
10
|
+
|
|
11
|
+
from reflex_cli import constants
|
|
12
|
+
from reflex_cli.utils import console
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def login(
|
|
16
|
+
loglevel: str = constants.LogLevel.INFO.value,
|
|
17
|
+
):
|
|
18
|
+
"""Authenticate with Reflex hosting service.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
loglevel: The log level to use.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
SystemExit: If the command fails.
|
|
25
|
+
"""
|
|
26
|
+
from reflex_cli.utils import hosting
|
|
27
|
+
|
|
28
|
+
# Set the log level.
|
|
29
|
+
console.set_log_level(constants.LogLevel(loglevel))
|
|
30
|
+
|
|
31
|
+
access_token, invitation_code = hosting.authenticated_token()
|
|
32
|
+
if access_token:
|
|
33
|
+
console.print("You already logged in.")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# If not already logged in, open a browser window/tab to the login page.
|
|
37
|
+
access_token = hosting.authenticate_on_browser(invitation_code)
|
|
38
|
+
|
|
39
|
+
if not access_token:
|
|
40
|
+
console.error(f"Unable to authenticate. Please try again or contact support.")
|
|
41
|
+
raise SystemExit(1)
|
|
42
|
+
|
|
43
|
+
console.print("Successfully logged in.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def logout(
|
|
47
|
+
loglevel: str = constants.LogLevel.INFO.value,
|
|
48
|
+
):
|
|
49
|
+
"""Log out of access to Reflex hosting service.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
loglevel: The log level to use.
|
|
53
|
+
"""
|
|
54
|
+
from reflex_cli.utils import hosting
|
|
55
|
+
|
|
56
|
+
console.set_log_level(constants.LogLevel(loglevel))
|
|
57
|
+
|
|
58
|
+
hosting.log_out_on_browser()
|
|
59
|
+
console.debug("Deleting access token from config locally")
|
|
60
|
+
hosting.delete_token_from_config(include_invitation_code=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def deploy(
|
|
64
|
+
app_name: str,
|
|
65
|
+
export_fn: Callable[[str, str, str, bool, bool, bool], None],
|
|
66
|
+
regions: list[str] | None = None,
|
|
67
|
+
key: str | None = None,
|
|
68
|
+
envs: list[str] | None = None,
|
|
69
|
+
cpus: int | None = None,
|
|
70
|
+
memory_mb: int | None = None,
|
|
71
|
+
auto_start: bool | None = None,
|
|
72
|
+
auto_stop: bool | None = None,
|
|
73
|
+
frontend_hostname: str | None = None,
|
|
74
|
+
interactive: bool = True,
|
|
75
|
+
with_metrics: str | None = None,
|
|
76
|
+
with_tracing: str | None = None,
|
|
77
|
+
share_deployment: bool | None = None,
|
|
78
|
+
loglevel: str = constants.LogLevel.INFO.value,
|
|
79
|
+
):
|
|
80
|
+
"""Deploy the app to the Reflex hosting service.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
app_name: The name of the app.
|
|
84
|
+
export_fn: The function from the reflex main framework to export the app.
|
|
85
|
+
regions: The regions to deploy to.
|
|
86
|
+
key: The deployment key.
|
|
87
|
+
envs: The environment variables to set.
|
|
88
|
+
cpus: The number of CPUs to allocate.
|
|
89
|
+
memory_mb: The amount of memory to allocate in MB.
|
|
90
|
+
auto_start: Whether to automatically start the deployment.
|
|
91
|
+
auto_stop: Whether to automatically stop the deployment.
|
|
92
|
+
frontend_hostname: The hostname to use for the frontend.
|
|
93
|
+
interactive: Whether to use interactive mode.
|
|
94
|
+
with_metrics: The metrics prefix to use if enabling metrics.
|
|
95
|
+
with_tracing: The tracing prefix to use if enabling tracing.
|
|
96
|
+
share_deployment: Whether to share the deployment. If None, prompt the user when in interactive mode.
|
|
97
|
+
loglevel: The log level to use.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
SystemExit: If the command fails.
|
|
101
|
+
"""
|
|
102
|
+
from reflex_cli.utils import hosting
|
|
103
|
+
|
|
104
|
+
# Set the log level.
|
|
105
|
+
console.set_log_level(constants.LogLevel(loglevel))
|
|
106
|
+
|
|
107
|
+
envs = envs or []
|
|
108
|
+
api_url = ""
|
|
109
|
+
deploy_url = ""
|
|
110
|
+
|
|
111
|
+
if not interactive and not key:
|
|
112
|
+
console.error(
|
|
113
|
+
"Please provide a name for the deployed instance when not in interactive mode."
|
|
114
|
+
)
|
|
115
|
+
raise SystemExit(1)
|
|
116
|
+
|
|
117
|
+
if interactive and share_deployment is None:
|
|
118
|
+
if (
|
|
119
|
+
console.ask(
|
|
120
|
+
"Do you want to share your app in the Gallery?",
|
|
121
|
+
choices=["y", "n"],
|
|
122
|
+
default="y",
|
|
123
|
+
)
|
|
124
|
+
== "y"
|
|
125
|
+
):
|
|
126
|
+
share_deployment = True
|
|
127
|
+
console.print(
|
|
128
|
+
"We will ask for more information later. Let's proceed to deploy."
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
share_deployment = False
|
|
132
|
+
console.print(
|
|
133
|
+
"No worries. You can do this later by running `reflex deployments share`."
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
enabled_regions = None
|
|
137
|
+
# If there is already a key, then it is passed in from CLI option in the non-interactive mode
|
|
138
|
+
if key is not None and not hosting.is_valid_deployment_key(key):
|
|
139
|
+
console.error(
|
|
140
|
+
f"Deployment key {key} is not valid. Please use only domain name safe characters."
|
|
141
|
+
)
|
|
142
|
+
raise SystemExit(1)
|
|
143
|
+
try:
|
|
144
|
+
# Send a request to server to obtain necessary information
|
|
145
|
+
# in preparation of a deployment. For example,
|
|
146
|
+
# server can return confirmation of a particular deployment key,
|
|
147
|
+
# is available, or suggest a new key, or return an existing deployment.
|
|
148
|
+
# Some of these are used in the interactive mode.
|
|
149
|
+
pre_deploy_response = hosting.prepare_deploy(
|
|
150
|
+
app_name, key=key, frontend_hostname=frontend_hostname
|
|
151
|
+
)
|
|
152
|
+
# Note: we likely won't need to fetch this twice
|
|
153
|
+
if pre_deploy_response.enabled_regions is not None:
|
|
154
|
+
enabled_regions = pre_deploy_response.enabled_regions
|
|
155
|
+
|
|
156
|
+
except Exception as ex:
|
|
157
|
+
console.error(f"Unable to prepare deployment")
|
|
158
|
+
raise SystemExit(1) from ex
|
|
159
|
+
|
|
160
|
+
# The app prefix should not change during the time of preparation
|
|
161
|
+
app_prefix = pre_deploy_response.app_prefix
|
|
162
|
+
if not interactive:
|
|
163
|
+
# in this case, the key was supplied for the pre_deploy call, at this point the reply is expected
|
|
164
|
+
if (reply := pre_deploy_response.reply) is None:
|
|
165
|
+
console.error(f"Unable to deploy at this name {key}.")
|
|
166
|
+
raise SystemExit(1)
|
|
167
|
+
api_url = reply.api_url
|
|
168
|
+
deploy_url = reply.deploy_url
|
|
169
|
+
else:
|
|
170
|
+
(
|
|
171
|
+
key_candidate,
|
|
172
|
+
api_url,
|
|
173
|
+
deploy_url,
|
|
174
|
+
) = hosting.interactive_get_deployment_key_from_user_input(
|
|
175
|
+
pre_deploy_response, app_name, frontend_hostname=frontend_hostname
|
|
176
|
+
)
|
|
177
|
+
if not key_candidate or not api_url or not deploy_url:
|
|
178
|
+
console.error("Unable to find a suitable deployment key.")
|
|
179
|
+
raise SystemExit(1)
|
|
180
|
+
|
|
181
|
+
# Now copy over the candidate to the key
|
|
182
|
+
key = key_candidate
|
|
183
|
+
|
|
184
|
+
regions = hosting.prompt_for_regions(
|
|
185
|
+
enabled_regions=enabled_regions, regions_args=regions
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# process the envs
|
|
189
|
+
envs = hosting.interactive_prompt_for_envs()
|
|
190
|
+
|
|
191
|
+
# Check the required params are valid
|
|
192
|
+
console.debug(
|
|
193
|
+
f"{key=}, {regions=}, {app_name=}, {app_prefix=}, {api_url=}, {deploy_url=}"
|
|
194
|
+
)
|
|
195
|
+
if (
|
|
196
|
+
not key
|
|
197
|
+
or not regions
|
|
198
|
+
or not app_name
|
|
199
|
+
or not app_prefix
|
|
200
|
+
or not api_url
|
|
201
|
+
or not deploy_url
|
|
202
|
+
):
|
|
203
|
+
console.error("Please provide all the required parameters.")
|
|
204
|
+
raise SystemExit(1)
|
|
205
|
+
# Note: if the user uses --no-interactive mode, there was no prepare_deploy call
|
|
206
|
+
# so we do not check the regions until the call to hosting server
|
|
207
|
+
|
|
208
|
+
processed_envs = hosting.process_envs(envs) if envs else None
|
|
209
|
+
|
|
210
|
+
# Compile the app in production mode: backend first then frontend.
|
|
211
|
+
tmp_dir = tempfile.mkdtemp()
|
|
212
|
+
|
|
213
|
+
# Try zipping backend first
|
|
214
|
+
try:
|
|
215
|
+
export_fn(tmp_dir, api_url, deploy_url, False, True, True)
|
|
216
|
+
except Exception as ex:
|
|
217
|
+
console.error(f"Unable to export due to: {ex}")
|
|
218
|
+
if os.path.exists(tmp_dir):
|
|
219
|
+
shutil.rmtree(tmp_dir)
|
|
220
|
+
raise SystemExit(1) from ex
|
|
221
|
+
|
|
222
|
+
backend_file_name = constants.ComponentName.BACKEND.zip()
|
|
223
|
+
|
|
224
|
+
console.print("Uploading Backend code and sending request ...")
|
|
225
|
+
backend_deploy_requested_at = datetime.now().astimezone()
|
|
226
|
+
console.debug(f"{backend_deploy_requested_at=}")
|
|
227
|
+
try:
|
|
228
|
+
backend_deploy_response = hosting.deploy_backend(
|
|
229
|
+
backend_file_name=backend_file_name,
|
|
230
|
+
export_dir=tmp_dir,
|
|
231
|
+
key=key,
|
|
232
|
+
app_name=app_name,
|
|
233
|
+
regions=regions,
|
|
234
|
+
app_prefix=app_prefix,
|
|
235
|
+
cpus=cpus,
|
|
236
|
+
memory_mb=memory_mb,
|
|
237
|
+
auto_start=auto_start,
|
|
238
|
+
auto_stop=auto_stop,
|
|
239
|
+
envs=processed_envs,
|
|
240
|
+
with_metrics=with_metrics,
|
|
241
|
+
with_tracing=with_tracing,
|
|
242
|
+
)
|
|
243
|
+
except Exception as ex:
|
|
244
|
+
console.error(f"Unable to deploy due to: {ex}")
|
|
245
|
+
if os.path.exists(tmp_dir):
|
|
246
|
+
shutil.rmtree(tmp_dir)
|
|
247
|
+
raise SystemExit(1) from ex
|
|
248
|
+
|
|
249
|
+
if backend_deploy_response.sidecar_url is None:
|
|
250
|
+
console.error(
|
|
251
|
+
"Deploy backend response from server missing sidecar_url. This is unexpected. Contact support."
|
|
252
|
+
)
|
|
253
|
+
raise SystemExit(1)
|
|
254
|
+
|
|
255
|
+
# Deployment will actually start when data plane reconciles this request
|
|
256
|
+
console.debug(f"deploy_response: {backend_deploy_response}")
|
|
257
|
+
console.print("[bold]Backend deployment will start shortly.")
|
|
258
|
+
|
|
259
|
+
# Zip frontend
|
|
260
|
+
try:
|
|
261
|
+
export_fn(tmp_dir, api_url, deploy_url, True, False, True)
|
|
262
|
+
except ImportError as ie:
|
|
263
|
+
console.error(
|
|
264
|
+
f"Encountered ImportError, did you install all the dependencies? {ie}"
|
|
265
|
+
)
|
|
266
|
+
if os.path.exists(tmp_dir):
|
|
267
|
+
shutil.rmtree(tmp_dir)
|
|
268
|
+
raise SystemExit(1) from ie
|
|
269
|
+
except Exception as ex:
|
|
270
|
+
console.error(f"Unable to export due to: {ex}")
|
|
271
|
+
if os.path.exists(tmp_dir):
|
|
272
|
+
shutil.rmtree(tmp_dir)
|
|
273
|
+
raise SystemExit(1) from ex
|
|
274
|
+
|
|
275
|
+
frontend_file_name = constants.ComponentName.FRONTEND.zip()
|
|
276
|
+
|
|
277
|
+
console.print("Uploading Frontend code and sending request ...")
|
|
278
|
+
frontend_deploy_requested_at = datetime.now().astimezone()
|
|
279
|
+
console.debug(f"{frontend_deploy_requested_at=}")
|
|
280
|
+
try:
|
|
281
|
+
frontend_deploy_response = hosting.deploy_frontend(
|
|
282
|
+
frontend_file_name=frontend_file_name,
|
|
283
|
+
export_dir=tmp_dir,
|
|
284
|
+
initiator_event_id=backend_deploy_response.event_id,
|
|
285
|
+
app_prefix=app_prefix,
|
|
286
|
+
frontend_hostname=frontend_hostname,
|
|
287
|
+
)
|
|
288
|
+
except Exception as ex:
|
|
289
|
+
console.error(f"Unable to deploy due to: {ex}")
|
|
290
|
+
raise SystemExit(1) from ex
|
|
291
|
+
finally:
|
|
292
|
+
if os.path.exists(tmp_dir):
|
|
293
|
+
shutil.rmtree(tmp_dir)
|
|
294
|
+
console.debug(f"deploy_response: {frontend_deploy_response}")
|
|
295
|
+
console.print("[bold]Frontend deployment will start shortly.")
|
|
296
|
+
|
|
297
|
+
console.rule("[bold]Deploying production app.")
|
|
298
|
+
console.print(
|
|
299
|
+
f"[bold]Deployment will start shortly: {frontend_deploy_response.url} \nClosing this command now will not affect your deployment."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# If user elected to share the deployment, instead of just waiting, collect information from them.
|
|
303
|
+
if share_deployment:
|
|
304
|
+
hosting.collect_deployment_info_interactive(
|
|
305
|
+
demo_url=frontend_deploy_response.url
|
|
306
|
+
)
|
|
307
|
+
else:
|
|
308
|
+
# It takes a few seconds for the deployment request to be picked up by server
|
|
309
|
+
hosting.wait_for_server_to_pick_up_request()
|
|
310
|
+
|
|
311
|
+
console.print("Waiting for server to report progress ...")
|
|
312
|
+
# Display the key events such as build, deploy, etc based on the deploy event IDs from hosting service
|
|
313
|
+
server_report_deploy_success = hosting.poll_deploy_milestones(
|
|
314
|
+
key,
|
|
315
|
+
from_iso_timestamp=backend_deploy_requested_at,
|
|
316
|
+
deploy_event_ids=[
|
|
317
|
+
backend_deploy_response.event_id,
|
|
318
|
+
frontend_deploy_response.event_id,
|
|
319
|
+
],
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if server_report_deploy_success is None:
|
|
323
|
+
console.warn("The deployment may still be in progress. Proceeding ...")
|
|
324
|
+
elif not server_report_deploy_success:
|
|
325
|
+
console.error("Hosting server reports failure.")
|
|
326
|
+
console.error(
|
|
327
|
+
f"Check for more server logs using `reflex deployments build-logs {key}`"
|
|
328
|
+
)
|
|
329
|
+
raise SystemExit(1)
|
|
330
|
+
|
|
331
|
+
console.print("Waiting for the new deployment to come up")
|
|
332
|
+
backend_reachable, backend_poll_err = hosting.poll_backend_with_retries(
|
|
333
|
+
key=key,
|
|
334
|
+
from_iso_timestamp=datetime.now().astimezone(),
|
|
335
|
+
backend_url=backend_deploy_response.url,
|
|
336
|
+
sidecar_url=backend_deploy_response.sidecar_url,
|
|
337
|
+
)
|
|
338
|
+
if not backend_reachable:
|
|
339
|
+
if backend_poll_err is not None:
|
|
340
|
+
print(backend_poll_err)
|
|
341
|
+
console.error("Backend unreachable")
|
|
342
|
+
console.warn(
|
|
343
|
+
f"Check for more logs in your App, using `reflex deployments logs {key}`"
|
|
344
|
+
)
|
|
345
|
+
raise SystemExit(1)
|
|
346
|
+
|
|
347
|
+
if not (
|
|
348
|
+
frontend_reachable := hosting.poll_frontend_with_retries(
|
|
349
|
+
frontend_url=frontend_deploy_response.url
|
|
350
|
+
)
|
|
351
|
+
):
|
|
352
|
+
console.error("Frontend unreachable")
|
|
353
|
+
raise SystemExit(1)
|
|
354
|
+
|
|
355
|
+
if backend_reachable and frontend_reachable:
|
|
356
|
+
console.print(
|
|
357
|
+
f"Your site [ {key} ] at {regions} is up: {frontend_deploy_response.url}"
|
|
358
|
+
)
|
|
359
|
+
return
|
|
360
|
+
console.warn(f"Your deployment is taking unusually long.")
|
|
361
|
+
console.warn(f"Check back later on its status: `reflex deployments status {key}`")
|
|
362
|
+
console.warn(f"For logs: `reflex deployments logs {key}`")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Base file for constants that don't fit any other categories."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from types import SimpleNamespace
|
|
7
|
+
|
|
8
|
+
from platformdirs import PlatformDirs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Reflex(SimpleNamespace):
|
|
12
|
+
"""Base constants concerning Reflex. This is duplicate of the same class in reflex main."""
|
|
13
|
+
|
|
14
|
+
# The name of the Reflex package.
|
|
15
|
+
MODULE_NAME = "reflex"
|
|
16
|
+
|
|
17
|
+
# Files and directories used to init a new project.
|
|
18
|
+
# The directory to store reflex dependencies.
|
|
19
|
+
DIR = (
|
|
20
|
+
# on windows, we use C:/Users/<username>/AppData/Local/reflex.
|
|
21
|
+
# on macOS, we use ~/Library/Application Support/reflex.
|
|
22
|
+
# on linux, we use ~/.local/share/reflex.
|
|
23
|
+
PlatformDirs(MODULE_NAME, False).user_data_dir
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Log levels
|
|
28
|
+
class LogLevel(str, Enum):
|
|
29
|
+
"""The log levels."""
|
|
30
|
+
|
|
31
|
+
DEBUG = "debug"
|
|
32
|
+
INFO = "info"
|
|
33
|
+
WARNING = "warning"
|
|
34
|
+
ERROR = "error"
|
|
35
|
+
CRITICAL = "critical"
|
|
36
|
+
|
|
37
|
+
def __le__(self, other: LogLevel) -> bool:
|
|
38
|
+
"""Compare log levels.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
other: The other log level.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
True if the log level is less than or equal to the other log level.
|
|
45
|
+
"""
|
|
46
|
+
levels = list(LogLevel)
|
|
47
|
+
return levels.index(self) <= levels.index(other)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Compiler variables."""
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Ext(SimpleNamespace):
|
|
7
|
+
"""Extension used in Reflex."""
|
|
8
|
+
|
|
9
|
+
# The extension for JS files.
|
|
10
|
+
JS = ".js"
|
|
11
|
+
# The extension for python files.
|
|
12
|
+
PY = ".py"
|
|
13
|
+
# The extension for css files.
|
|
14
|
+
CSS = ".css"
|
|
15
|
+
# The extension for zip files.
|
|
16
|
+
ZIP = ".zip"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ComponentName(Enum):
|
|
20
|
+
"""Component names."""
|
|
21
|
+
|
|
22
|
+
BACKEND = "Backend"
|
|
23
|
+
FRONTEND = "Frontend"
|
|
24
|
+
|
|
25
|
+
def zip(self):
|
|
26
|
+
"""Give the zip filename for the component.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The lower-case filename with zip extension.
|
|
30
|
+
"""
|
|
31
|
+
return self.value.lower() + Ext.ZIP
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Constants related to hosting."""
|
|
2
|
+
import os
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
from reflex_cli.constants.base import Reflex
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReflexHostingCli(SimpleNamespace):
|
|
9
|
+
"""Constants related to reflex-hosting-cli."""
|
|
10
|
+
|
|
11
|
+
MODULE_NAME = "reflex-hosting-cli"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Hosting(SimpleNamespace):
|
|
15
|
+
"""Constants related to hosting."""
|
|
16
|
+
|
|
17
|
+
# The hosting config json file
|
|
18
|
+
HOSTING_JSON = os.path.join(Reflex.DIR, "hosting_v0.json")
|
|
19
|
+
# The hosting service backend URL
|
|
20
|
+
CP_BACKEND_URL = os.environ.get(
|
|
21
|
+
"CP_BACKEND_URL", "https://rxcp-prod-control-plane.fly.dev"
|
|
22
|
+
)
|
|
23
|
+
# The hosting service webpage URL
|
|
24
|
+
CP_WEB_URL = os.environ.get("CP_WEB_URL", "https://control-plane.reflex.run")
|
|
25
|
+
|
|
26
|
+
# The number of times to try and wait for the user to complete web authentication.
|
|
27
|
+
WEB_AUTH_RETRIES = 60
|
|
28
|
+
# The time to sleep between requests to check if for authentication completion. In seconds.
|
|
29
|
+
WEB_AUTH_SLEEP_DURATION = 5
|
|
30
|
+
# The time to wait for the reflex app sidecar to come up. In seconds.
|
|
31
|
+
BACKEND_SIDECAR_WAIT_AND_PING_DURATION = 40
|
|
32
|
+
# The number of iterations to try reflex app /ping endpoint and query logs printed from app.
|
|
33
|
+
BACKEND_REFLEX_APP_PING_LOG_QUERY_RETRIES = 30
|
|
34
|
+
# The time to wait for the frontend to come up after user initiates deployment. In seconds.
|
|
35
|
+
FRONTEND_POLL_DURATION = 10
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RequirementsTxt(SimpleNamespace):
|
|
39
|
+
"""Requirements.txt constants."""
|
|
40
|
+
|
|
41
|
+
# The requirements.txt file.
|
|
42
|
+
FILE = "requirements.txt"
|
|
43
|
+
|
|
44
|
+
# Number of unused packages for which we will throw a warning.
|
|
45
|
+
UNUSED_WARN_THRESHOLD = 20
|