Skip to content

Allow users to modify component's host URLs #172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ celerybeat-schedule.*
*.sage.py

# Environments
.env
.venv
.env*/
.venv*/
env/
venv/
ENV/
Expand Down
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,18 @@ Using the following categories, list your changes in this order:

## [Unreleased]

- Nothing (yet)!
### Added

- The `component` template tag now accepts a `host_domain` argument to override the default host URL, allowing ReactPy websockets and HTTP to be hosted by completely separate Django application(s).

### Changed

- ReactPy will now provide a warning if HTTP URLs are not using the same URL prefix as websockets.

### Deprecated

- `reactpy_django.REACTPY_WEBSOCKET_PATH` is deprecated. Replace with `REACTPY_WEBSOCKET_ROUTE`.
- `settings.py:REACTPY_WEBSOCKET_URL` is deprecated. Replace with `REACTPY_URL_PREFIX`.

## [3.3.2] - 2023-08-13

Expand Down
4 changes: 2 additions & 2 deletions docs/python/configure-asgi-middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Broken load order, only used for linting
from channels.routing import ProtocolTypeRouter, URLRouter
from reactpy_django import REACTPY_WEBSOCKET_PATH
from reactpy_django import REACTPY_WEBSOCKET_ROUTE

django_asgi_app = ""

Expand All @@ -15,7 +15,7 @@
"websocket": SessionMiddlewareStack(
AuthMiddlewareStack(
URLRouter(
[REACTPY_WEBSOCKET_PATH],
[REACTPY_WEBSOCKET_ROUTE],
)
)
),
Expand Down
4 changes: 2 additions & 2 deletions docs/python/configure-asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@


from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402
from reactpy_django import REACTPY_WEBSOCKET_PATH # noqa: E402
from reactpy_django import REACTPY_WEBSOCKET_ROUTE # noqa: E402

application = ProtocolTypeRouter(
{
"http": django_asgi_app,
"websocket": URLRouter([REACTPY_WEBSOCKET_PATH]),
"websocket": URLRouter([REACTPY_WEBSOCKET_ROUTE]),
}
)
32 changes: 0 additions & 32 deletions docs/python/settings.py

This file was deleted.

18 changes: 13 additions & 5 deletions docs/src/features/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@

## Primary Configuration

These are ReactPy-Django's default settings values. You can modify these values in your **Django project's** `settings.py` to change the behavior of ReactPy.
<!--config-details-start-->

=== "settings.py"
These are ReactPy-Django's default settings values. You can modify these values in your **Django project's** `settings.py` to change the behavior of ReactPy.

```python
{% include "../../python/settings.py" %}
```
| Setting | Default Value | Example Value(s) | Description |
| --- | --- | --- | --- |
| `REACTPY_CACHE` | `#!python "default"` | `#!python "my-reactpy-cache"` | Cache used to store ReactPy web modules. ReactPy benefits from a fast, well indexed cache.<br/>We recommend installing [`redis`](https://redis.io/) or [`python-diskcache`](https://grantjenks.com/docs/diskcache/tutorial.html#djangocache). |
| `REACTPY_DATABASE` | `#!python "default"` | `#!python "my-reactpy-database"` | Database ReactPy uses to store session data. ReactPy requires a multiprocessing-safe and thread-safe database.<br/>If configuring `REACTPY_DATABASE`, it is mandatory to also configure `DATABASE_ROUTERS` like such:<br/>`#!python DATABASE_ROUTERS = ["reactpy_django.database.Router", ...]` |
| `REACTPY_RECONNECT_MAX` | `#!python 259200` | `#!python 96000`, `#!python 60`, `#!python 0` | Maximum seconds between reconnection attempts before giving up.<br/>Use `#!python 0` to prevent reconnection. |
| `REACTPY_URL_PREFIX` | `#!python "reactpy/"` | `#!python "rp/"`, `#!python "render/reactpy/"` | The prefix to be used for all ReactPy websocket and HTTP URLs. |
| `REACTPY_DEFAULT_QUERY_POSTPROCESSOR` | `#!python "reactpy_django.utils.django_query_postprocessor"` | `#!python "example_project.my_query_postprocessor"` | Dotted path to the default `reactpy_django.hooks.use_query` postprocessor function. |
| `REACTPY_AUTH_BACKEND` | `#!python "django.contrib.auth.backends.ModelBackend"` | `#!python "example_project.auth.MyModelBackend"` | Dotted path to the Django authentication backend to use for ReactPy components. This is only needed if:<br/> 1. You are using `AuthMiddlewareStack` and...<br/> 2. You are using Django's `AUTHENTICATION_BACKENDS` setting and...<br/> 3. Your Django user model does not define a `backend` attribute. |
| `REACTPY_BACKHAUL_THREAD` | `#!python False` | `#!python True` | Whether to render ReactPy components in a dedicated thread. This allows the webserver to process web traffic while during ReactPy rendering.<br/>Vastly improves throughput with web servers such as `hypercorn` and `uvicorn`. |

<!--config-details-end-->

??? question "Do I need to modify my settings?"

Expand Down
2 changes: 2 additions & 0 deletions docs/src/features/template-tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The `component` template tag can be used to insert any number of ReactPy compone
| --- | --- | --- | --- |
| `dotted_path` | `str` | The dotted path to the component to render. | N/A |
| `*args` | `Any` | The positional arguments to provide to the component. | N/A |
| `host_domain` | `str | None` | The host domain to use for the ReactPy connections. If set to `None`, the host will be automatically configured.<br/><br/>_Note: You typically will not need to register ReactPy HTTP and/or websocket paths on any application(s) that do not perform any component rendering._ | `None` |

| `**kwargs` | `Any` | The keyword arguments to provide to the component. | N/A |

<font size="4">**Returns**</font>
Expand Down
16 changes: 10 additions & 6 deletions docs/src/get-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ In your settings you will need to add `reactpy_django` to [`INSTALLED_APPS`](htt

??? note "Configure ReactPy settings (Optional)"

Below are a handful of values you can change within `settings.py` to modify the behavior of ReactPy.

```python linenums="0"
{% include "../../python/settings.py" %}
```
{% include "../features/settings.md" start="<!--config-details-start-->" end="<!--config-details-end-->" %}

## Step 3: Configure [`urls.py`](https://docs.djangoproject.com/en/dev/topics/http/urls/)

Expand All @@ -62,7 +58,7 @@ Add ReactPy HTTP paths to your `urlpatterns`.

## Step 4: Configure [`asgi.py`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/)

Register ReactPy's Websocket using `REACTPY_WEBSOCKET_PATH`.
Register ReactPy's Websocket using `REACTPY_WEBSOCKET_ROUTE`.

=== "asgi.py"

Expand Down Expand Up @@ -95,3 +91,11 @@ Run Django's database migrations to initialize ReactPy-Django's database table.
```bash linenums="0"
python manage.py migrate
```

## Step 6: Check your configuration

Run Django's check command to verify if ReactPy was set up correctly.

```bash linenums="0"
python manage.py check
```
53 changes: 39 additions & 14 deletions src/js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,59 @@
import { mountLayoutWithWebSocket } from "@reactpy/client";

// Set up a websocket at the base endpoint
const LOCATION = window.location;
let HTTP_PROTOCOL = window.location.protocol;
let WS_PROTOCOL = "";
if (LOCATION.protocol == "https:") {
WS_PROTOCOL = "wss://";
if (HTTP_PROTOCOL == "https:") {
WS_PROTOCOL = "wss:";
} else {
WS_PROTOCOL = "ws://";
WS_PROTOCOL = "ws:";
}
const WS_ENDPOINT_URL = WS_PROTOCOL + LOCATION.host + "/";

export function mountViewToElement(
mountElement,
reactpyWebsocketUrl,
reactpyWebModulesUrl,
maxReconnectTimeout,
componentPath
reactpyHostDomain,
reactpyUrlPrefix,
reactpyReconnectMax,
reactpyComponentPath,
reactpyResolvedWebModulesPath
) {
const WS_URL = WS_ENDPOINT_URL + reactpyWebsocketUrl + componentPath;
const WEB_MODULE_URL = LOCATION.origin + "/" + reactpyWebModulesUrl;
// Determine the Websocket route
let wsOrigin;
if (reactpyHostDomain) {
wsOrigin = `${WS_PROTOCOL}//${reactpyHostDomain}`;
} else {
wsOrigin = `${WS_PROTOCOL}//${window.location.host}`;
}
const websocketUrl = `${wsOrigin}/${reactpyUrlPrefix}/${reactpyComponentPath}`;

// Determine the HTTP route
let httpOrigin;
let webModulesPath;
if (reactpyHostDomain) {
httpOrigin = `${HTTP_PROTOCOL}//${reactpyHostDomain}`;
webModulesPath = `${reactpyUrlPrefix}/web_module`;
} else {
httpOrigin = `${HTTP_PROTOCOL}//${window.location.host}`;
if (reactpyResolvedWebModulesPath) {
webModulesPath = reactpyResolvedWebModulesPath;
} else {
webModulesPath = `${reactpyUrlPrefix}/web_module`;
}
}
const webModuleUrl = `${httpOrigin}/${webModulesPath}`;

// Function that loads the JavaScript web module, if needed
const loadImportSource = (source, sourceType) => {
return import(
sourceType == "NAME" ? `${WEB_MODULE_URL}${source}` : source
sourceType == "NAME" ? `${webModuleUrl}/${source}` : source
);
};

// Start rendering the component
mountLayoutWithWebSocket(
mountElement,
WS_URL,
websocketUrl,
loadImportSource,
maxReconnectTimeout
reactpyReconnectMax
);
}
6 changes: 5 additions & 1 deletion src/reactpy_django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import nest_asyncio

from reactpy_django import checks, components, decorators, hooks, types, utils
from reactpy_django.websocket.paths import REACTPY_WEBSOCKET_PATH
from reactpy_django.websocket.paths import (
REACTPY_WEBSOCKET_PATH,
REACTPY_WEBSOCKET_ROUTE,
)

__version__ = "3.3.2"
__all__ = [
"REACTPY_WEBSOCKET_PATH",
"REACTPY_WEBSOCKET_ROUTE",
"hooks",
"components",
"decorators",
Expand Down
Loading