Wire up events for creating new public reports
This involved moving a lot of stuff to the platform layer since I don't want event interfaces leaking out. Also this includes a fix to the user authentication which I had previously broken by making a platform-layer user object independent of the database layer.
This commit is contained in:
parent
9a5cc4cf97
commit
e8d865d0ab
24 changed files with 915 additions and 541 deletions
120
html/static/js/events.js
Normal file
120
html/static/js/events.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// sse-manager.js - Include this in your common template
|
||||
window.SSEManager = (function () {
|
||||
let eventSource = null;
|
||||
let subscribers = new Map();
|
||||
let isConnected = false;
|
||||
let connectionPromise = null;
|
||||
|
||||
function subscribe(eventType, handler) {
|
||||
if (!subscribers.has(eventType)) {
|
||||
subscribers.set(eventType, []);
|
||||
}
|
||||
subscribers.get(eventType).push(handler);
|
||||
|
||||
// If already connected, attach the listener immediately
|
||||
if (isConnected && eventSource) {
|
||||
eventSource.addEventListener(eventType, handler);
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribe(eventType, handler) {
|
||||
if (subscribers.has(eventType)) {
|
||||
const handlers = subscribers.get(eventType);
|
||||
const index = handlers.indexOf(handler);
|
||||
if (index > -1) {
|
||||
handlers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
if (eventSource) {
|
||||
eventSource.removeEventListener(eventType, handler);
|
||||
}
|
||||
}
|
||||
|
||||
function connect(url) {
|
||||
if (connectionPromise) {
|
||||
return connectionPromise;
|
||||
}
|
||||
|
||||
connectionPromise = new Promise((resolve, reject) => {
|
||||
eventSource = new EventSource(url);
|
||||
|
||||
eventSource.onopen = function () {
|
||||
isConnected = true;
|
||||
|
||||
// Attach all pre-registered handlers
|
||||
subscribers.forEach((handlers, eventType) => {
|
||||
handlers.forEach((handler) => {
|
||||
eventSource.addEventListener("message", (message) => {
|
||||
const data = JSON.parse(message.data);
|
||||
handler(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
console.log("SSE connected");
|
||||
resolve(eventSource);
|
||||
};
|
||||
|
||||
eventSource.onerror = function (err) {
|
||||
console.error("SSE error:", err);
|
||||
isConnected = false;
|
||||
|
||||
// Reconnect after delay
|
||||
setTimeout(() => {
|
||||
connectionPromise = null;
|
||||
connect(url);
|
||||
}, 5000);
|
||||
|
||||
if (!isConnected) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return connectionPromise;
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
isConnected = false;
|
||||
connectionPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
function ready(callback) {
|
||||
if (connectionPromise) {
|
||||
connectionPromise.then(callback);
|
||||
} else {
|
||||
// If connect hasn't been called yet, queue it
|
||||
const checkInterval = setInterval(() => {
|
||||
if (connectionPromise) {
|
||||
clearInterval(checkInterval);
|
||||
connectionPromise.then(callback);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect,
|
||||
disconnect,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
ready,
|
||||
};
|
||||
})();
|
||||
|
||||
// Initialize SSE for navigation notifications
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
SSEManager.connect("/api/events");
|
||||
});
|
||||
|
||||
function updateNotificationBadge(data) {
|
||||
const badge = document.querySelector(".notification-badge");
|
||||
if (badge) {
|
||||
badge.textContent = data.count;
|
||||
badge.style.display = data.count > 0 ? "block" : "none";
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,12 @@
|
|||
<link rel="stylesheet" href="/static/vendor/css/bootstrap-icons.min.css" />
|
||||
<!-- favicon -->
|
||||
<link rel="icon" href="/static/favicon-sync.ico" type="image/x-icon" />
|
||||
<script src="/static/js/events.js"></script>
|
||||
{{ block "extraheader" . }}{{ end }}
|
||||
<script>
|
||||
SSEManager.subscribe("*", function (e) {
|
||||
console.log("event", e);
|
||||
});
|
||||
function restoreLocalStorage() {
|
||||
const expanded = localStorage.getItem("sidebar.expanded");
|
||||
if (expanded == "false") {
|
||||
|
|
@ -60,7 +64,6 @@
|
|||
setTooltipsForSidebar();
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/events.js"></script>
|
||||
{{ if not .Config.IsProductionEnvironment }}
|
||||
<script src="/.flogo/injector.js"></script>
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -196,6 +196,50 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSE Testing -->
|
||||
<div class="card mb-5">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<i class="bi bi-bell"></i> Server-sent event testing
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/sudo/sse" method="POST">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="organizationID" class="form-label"
|
||||
>Organization ID</label
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="organization-id"
|
||||
name="organizationID"
|
||||
placeholder="Organization ID"
|
||||
type="text"
|
||||
value="{{ .Organization.ID }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">Content</label>
|
||||
<input
|
||||
class="form-control"
|
||||
id="content"
|
||||
name="content"
|
||||
placeholder="Message content"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Send SSE<i class="bi bi-send"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-5" />
|
||||
<!-- Push Notification Testing -->
|
||||
<div class="card mb-5">
|
||||
<div class="card-header bg-danger text-white">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue