mirror of
https://github.com/sissbruecker/linkding.git
synced 2026-02-28 06:53:12 +08:00
Live reload for dev mode
This commit is contained in:
44
bookmarks/static/live-reload.js
Normal file
44
bookmarks/static/live-reload.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const RELOAD_URL = "/live_reload";
|
||||||
|
|
||||||
|
let eventSource = null;
|
||||||
|
let serverId = null;
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
console.debug("[live-reload] Connecting to", RELOAD_URL);
|
||||||
|
|
||||||
|
eventSource = new EventSource(RELOAD_URL);
|
||||||
|
|
||||||
|
eventSource.addEventListener("connected", (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (serverId && serverId !== data.server_id) {
|
||||||
|
console.log("[live-reload] Server restarted, reloading page");
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug("[live-reload] Connected, server ID:", data.server_id);
|
||||||
|
serverId = data.server_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
eventSource.addEventListener("file_change", (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
console.log("[live-reload] File changed:", data);
|
||||||
|
|
||||||
|
if (data.file_path.endsWith(".css") || data.file_path.endsWith(".js")) {
|
||||||
|
console.log("[live-reload] Asset changed, reloading page");
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventSource.onerror = (error) => {
|
||||||
|
console.debug("[live-reload] Disconnected", error);
|
||||||
|
eventSource.close();
|
||||||
|
eventSource = null;
|
||||||
|
|
||||||
|
// Reconnect after a delay
|
||||||
|
setTimeout(connect, 1000);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
connect();
|
||||||
@@ -61,4 +61,5 @@
|
|||||||
{% if not request.global_settings.enable_link_prefetch %}<meta name="turbo-prefetch" content="false">{% endif %}
|
{% if not request.global_settings.enable_link_prefetch %}<meta name="turbo-prefetch" content="false">{% endif %}
|
||||||
{% if rss_feed_url %}<link rel="alternate" type="application/rss+xml" href="{{ rss_feed_url }}" />{% endif %}
|
{% if rss_feed_url %}<link rel="alternate" type="application/rss+xml" href="{{ rss_feed_url }}" />{% endif %}
|
||||||
<script src="{% static "bundle.js" %}?v={{ app_version }}"></script>
|
<script src="{% static "bundle.js" %}?v={{ app_version }}"></script>
|
||||||
|
{% if debug %}<script src="{% static "live-reload.js" %}"></script>{% endif %}
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -106,6 +106,12 @@ urlpatterns = [
|
|||||||
path("opensearch.xml", views.opensearch, name="opensearch"),
|
path("opensearch.xml", views.opensearch, name="opensearch"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Live reload (debug only)
|
||||||
|
if settings.DEBUG:
|
||||||
|
from bookmarks.views import reload
|
||||||
|
|
||||||
|
urlpatterns.append(path("live_reload", reload.live_reload, name="live_reload"))
|
||||||
|
|
||||||
# Put all linkding URLs into a linkding namespace
|
# Put all linkding URLs into a linkding namespace
|
||||||
urlpatterns = [path("", include((urlpatterns, "linkding")))]
|
urlpatterns = [path("", include((urlpatterns, "linkding")))]
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ from .manifest import manifest
|
|||||||
from .custom_css import custom_css
|
from .custom_css import custom_css
|
||||||
from .root import root
|
from .root import root
|
||||||
from .opensearch import opensearch
|
from .opensearch import opensearch
|
||||||
|
|
||||||
|
|||||||
63
bookmarks/views/reload.py
Normal file
63
bookmarks/views/reload.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
|
from queue import Empty, Queue
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.http import StreamingHttpResponse
|
||||||
|
from django.utils.autoreload import autoreload_started, file_changed
|
||||||
|
|
||||||
|
_styles_dir = Path(__file__).resolve().parent.parent / "styles"
|
||||||
|
_static_dir = Path(__file__).resolve().parent.parent / "static"
|
||||||
|
|
||||||
|
_server_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
_active_connections = set()
|
||||||
|
_connections_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
def _event_stream():
|
||||||
|
client_queue = Queue()
|
||||||
|
|
||||||
|
with _connections_lock:
|
||||||
|
_active_connections.add(client_queue)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.dumps({"server_id": _server_id})
|
||||||
|
yield f"event: connected\ndata: {data}\n\n"
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data = client_queue.get(timeout=30)
|
||||||
|
yield f"event: file_change\ndata: {data}\n\n"
|
||||||
|
except Empty:
|
||||||
|
yield ": keepalive\n\n"
|
||||||
|
finally:
|
||||||
|
with _connections_lock:
|
||||||
|
_active_connections.discard(client_queue)
|
||||||
|
|
||||||
|
|
||||||
|
def live_reload(request):
|
||||||
|
response = StreamingHttpResponse(_event_stream(), content_type="text/event-stream")
|
||||||
|
response["Cache-Control"] = "no-cache"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(autoreload_started)
|
||||||
|
def handle_auto_reload(sender, **kwargs):
|
||||||
|
sender.watch_dir(_styles_dir, "**/*.css")
|
||||||
|
sender.watch_dir(_static_dir, "bundle.js")
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(file_changed)
|
||||||
|
def handle_file_changed(sender, file_path, **kwargs):
|
||||||
|
print(f"File changed: {file_path}")
|
||||||
|
data = json.dumps({"file_path": str(file_path)})
|
||||||
|
with _connections_lock:
|
||||||
|
for queue in _active_connections:
|
||||||
|
queue.put(data)
|
||||||
|
|
||||||
|
# Return True for CSS/JS files to prevent Django server restart
|
||||||
|
if file_path.suffix in (".css", ".js"):
|
||||||
|
return True
|
||||||
Reference in New Issue
Block a user