lepus 0.2.2 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile.lock +3 -3
- data/README.md +104 -9
- data/bin/web-demo +592 -0
- data/docs/brand/BRAND.md +67 -0
- data/docs/brand/apple-touch-icon.png +0 -0
- data/docs/brand/favicon-16.png +0 -0
- data/docs/brand/favicon-32.png +0 -0
- data/docs/brand/favicon.ico +0 -0
- data/docs/brand/favicon.svg +1 -0
- data/docs/brand/icon-512.png +0 -0
- data/docs/brand/lockup-lepus-mono.svg +1 -0
- data/docs/brand/lockup-lepus-on-dark.svg +1 -0
- data/docs/brand/lockup-lepus-on-light.svg +1 -0
- data/docs/brand/lockup-lepus-saga-mono.svg +1 -0
- data/docs/brand/lockup-lepus-saga-on-dark.svg +1 -0
- data/docs/brand/lockup-lepus-saga-on-light.svg +1 -0
- data/docs/brand/lockup-lepus-saga.svg +1 -0
- data/docs/brand/lockup-lepus.svg +1 -0
- data/docs/brand/mark-mono.svg +1 -0
- data/docs/brand/mark-on-dark.svg +1 -0
- data/docs/brand/mark-on-light.svg +1 -0
- data/docs/brand/mark-small-mono.svg +1 -0
- data/docs/brand/mark-small-on-dark.svg +1 -0
- data/docs/brand/mark-small-on-light.svg +1 -0
- data/docs/brand/mark-small.svg +1 -0
- data/docs/brand/mark.svg +1 -0
- data/docs/configuration.md +1 -0
- data/docs/consumers.md +39 -5
- data/docs/getting-started.md +1 -1
- data/docs/images/lepus-web.png +0 -0
- data/docs/middleware.md +1 -1
- data/docs/producers.md +41 -0
- data/docs/testing.md +67 -48
- data/docs/web.md +39 -13
- data/gemfiles/Gemfile.rails-5.2.lock +2 -2
- data/gemfiles/Gemfile.rails-6.1.lock +4 -4
- data/gemfiles/Gemfile.rails-7.2.lock +1 -1
- data/gemfiles/Gemfile.rails-8.0.lock +1 -1
- data/lib/lepus/configuration.rb +6 -0
- data/lib/lepus/consumer.rb +34 -0
- data/lib/lepus/consumers/config.rb +8 -7
- data/lib/lepus/consumers/stats.rb +15 -2
- data/lib/lepus/delay.rb +180 -0
- data/lib/lepus/primitive/string.rb +2 -12
- data/lib/lepus/producer.rb +6 -1
- data/lib/lepus/producers/definition.rb +3 -1
- data/lib/lepus/publisher.rb +29 -3
- data/lib/lepus/testing/rspec_matchers.rb +20 -1
- data/lib/lepus/testing.rb +2 -0
- data/lib/lepus/version.rb +1 -1
- data/lib/lepus/web/api.rb +45 -0
- data/lib/lepus/web/app.rb +19 -16
- data/lib/lepus/web/management_api.rb +125 -2
- data/lib/lepus/web/topology.rb +341 -0
- data/lib/lepus/web.rb +17 -18
- data/lib/lepus.rb +1 -0
- data/web/assets/app.css +862 -0
- data/web/assets/apple-touch-icon.png +0 -0
- data/web/assets/favicon-32.png +0 -0
- data/web/assets/favicon.svg +1 -0
- data/web/assets/fonts/overpass/OFL.txt +93 -0
- data/web/assets/fonts/overpass/overpass-latin-wght.woff2 +0 -0
- data/web/assets/js/api.js +27 -0
- data/web/assets/js/dom.js +95 -0
- data/web/assets/js/format.js +63 -0
- data/web/assets/js/live.js +74 -0
- data/web/assets/js/main.js +156 -0
- data/web/assets/js/map.js +606 -0
- data/web/assets/js/model.js +262 -0
- data/web/assets/js/rates.js +161 -0
- data/web/assets/js/theme.js +8 -0
- data/web/assets/js/views/connections.js +61 -0
- data/web/assets/js/views/overview.js +229 -0
- data/web/assets/js/views/processes.js +101 -0
- data/web/assets/js/views/queues.js +456 -0
- data/web/index.html +58 -153
- data/web/sw.js +14 -143
- metadata +47 -12
- data/test_offline.html +0 -189
- data/web/assets/css/styles.css +0 -635
- data/web/assets/js/app.js +0 -6
- data/web/assets/js/bootstrap.js +0 -20
- data/web/assets/js/controllers/connection_controller.js +0 -44
- data/web/assets/js/controllers/dashboard_controller.js +0 -499
- data/web/assets/js/controllers/queue_controller.js +0 -17
- data/web/assets/js/controllers/theme_controller.js +0 -31
- data/web/assets/js/offline-manager.js +0 -233
- data/web/assets/js/service-worker-manager.js +0 -65
data/web/index.html
CHANGED
|
@@ -1,159 +1,64 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="color-scheme" content="light dark">
|
|
7
|
+
<base href="__BASE_PATH__" />
|
|
8
|
+
<title>Lepus</title>
|
|
9
|
+
<link rel="preload" href="assets/fonts/overpass/overpass-latin-wght.woff2" as="font" type="font/woff2" crossorigin>
|
|
10
|
+
<link rel="icon" href="assets/favicon-32.png" sizes="32x32" type="image/png">
|
|
11
|
+
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
|
|
12
|
+
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png">
|
|
13
|
+
<link rel="stylesheet" href="assets/app.css">
|
|
14
|
+
<script src="assets/js/theme.js"></script>
|
|
15
|
+
<script type="module" src="assets/js/main.js"></script>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<a class="skip" href="#view">Skip to content</a>
|
|
19
|
+
<header class="bar">
|
|
20
|
+
<a class="brand" href="./">
|
|
21
|
+
<svg class="brand-mark" viewBox="0 0 64 64" width="24" height="24" aria-hidden="true">
|
|
22
|
+
<path class="brand-l" d="M12 6V40A16 16 0 0 0 28 56H42A10 10 0 0 0 42 36H28V18"/>
|
|
23
|
+
<circle class="brand-dot" cx="40" cy="46" r="5"/>
|
|
24
|
+
</svg>
|
|
25
|
+
<span>Lepus</span>
|
|
26
|
+
</a>
|
|
27
|
+
<nav class="nav" aria-label="Sections">
|
|
28
|
+
<a href="#map" data-nav="map">Map</a>
|
|
29
|
+
<a href="#queues" data-nav="queues">Queues</a>
|
|
30
|
+
<a href="#processes" data-nav="processes">Processes</a>
|
|
31
|
+
<a href="#connections" data-nav="connections">Connections</a>
|
|
32
|
+
</nav>
|
|
33
|
+
<div class="bar-tools">
|
|
34
|
+
<div class="poll" id="poll">
|
|
35
|
+
<button type="button" class="live" id="live" aria-expanded="false" aria-controls="poll-menu">
|
|
36
|
+
<span class="live-dot" aria-hidden="true"></span><span class="live-text">Live</span><span class="live-rate"></span>
|
|
37
|
+
<svg class="live-caret" viewBox="0 0 10 10" width="10" height="10" aria-hidden="true"><path d="M2 3.5l3 3 3-3"/></svg>
|
|
38
|
+
</button>
|
|
39
|
+
<div class="poll-menu" id="poll-menu" hidden>
|
|
40
|
+
<fieldset>
|
|
41
|
+
<legend>Refresh</legend>
|
|
42
|
+
<div class="poll-options" id="poll-options"></div>
|
|
43
|
+
</fieldset>
|
|
44
|
+
<p class="poll-note">Updates also pause while this tab is in the background.</p>
|
|
31
45
|
</div>
|
|
32
46
|
</div>
|
|
33
|
-
<div class="
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
<div class="theme" role="group" aria-label="Color theme">
|
|
48
|
+
<button type="button" data-theme-choice="auto" aria-label="Match system theme" title="Match system">
|
|
49
|
+
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="5.5"/><path d="M8 2.5v11a5.5 5.5 0 0 0 0-11z" class="fill"/></svg>
|
|
50
|
+
</button>
|
|
51
|
+
<button type="button" data-theme-choice="light" aria-label="Light theme" title="Light">
|
|
52
|
+
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="3"/><path d="M8 1.5v1.8M8 12.7v1.8M1.5 8h1.8M12.7 8h1.8M3.4 3.4l1.3 1.3M11.3 11.3l1.3 1.3M3.4 12.6l1.3-1.3M11.3 4.7l1.3-1.3"/></svg>
|
|
53
|
+
</button>
|
|
54
|
+
<button type="button" data-theme-choice="dark" aria-label="Dark theme" title="Dark">
|
|
55
|
+
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M13 10.2A5.6 5.6 0 0 1 5.8 3a5.6 5.6 0 1 0 7.2 7.2z"/></svg>
|
|
40
56
|
</button>
|
|
41
57
|
</div>
|
|
42
|
-
</
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<h2>You're Offline</h2>
|
|
50
|
-
<p>The Lepus dashboard requires an internet connection to function properly.</p>
|
|
51
|
-
<p>Some features may be limited while offline:</p>
|
|
52
|
-
<ul class="offline-limitations">
|
|
53
|
-
<li>Real-time data updates won't work</li>
|
|
54
|
-
<li>External dependencies may not load</li>
|
|
55
|
-
<li>Charts may show fallback content</li>
|
|
56
|
-
</ul>
|
|
57
|
-
<div class="offline-actions">
|
|
58
|
-
<button data-offline-action="reload" class="btn btn-primary">Retry Connection</button>
|
|
59
|
-
<button data-offline-action="dismiss" class="btn btn-secondary">Continue Offline</button>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<section class="grid stats-grid">
|
|
65
|
-
<div class="card stat-card">
|
|
66
|
-
<div class="stat-label">Processes</div>
|
|
67
|
-
<div class="stat-value" data-dashboard-target="processCount">0</div>
|
|
68
|
-
<div class="stat-detail" data-dashboard-target="processDetail">Supervisors 0, Workers 0</div>
|
|
69
|
-
</div>
|
|
70
|
-
<div class="card stat-card">
|
|
71
|
-
<div class="stat-label">Active queues</div>
|
|
72
|
-
<div class="stat-value" data-dashboard-target="queueCount">0</div>
|
|
73
|
-
<div class="stat-detail" data-dashboard-target="queueDetail">0 running, 0 paused</div>
|
|
74
|
-
</div>
|
|
75
|
-
<div class="card stat-card">
|
|
76
|
-
<div class="stat-label">Messages</div>
|
|
77
|
-
<div class="stat-value" data-dashboard-target="totalMessages">0</div>
|
|
78
|
-
<div class="stat-detail" data-dashboard-target="messageDetail">0 ready, 0 unacked</div>
|
|
79
|
-
</div>
|
|
80
|
-
<div class="card stat-card">
|
|
81
|
-
<div class="stat-label">Memory usage</div>
|
|
82
|
-
<div class="stat-value" data-dashboard-target="memoryUsage">0 MB</div>
|
|
83
|
-
<div class="stat-detail" data-dashboard-target="memoryDetail">RSS 0 MB, Heap 0 MB</div>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="card stat-card">
|
|
86
|
-
<div class="stat-label">Connections</div>
|
|
87
|
-
<div class="stat-value" data-dashboard-target="connectionCount">0</div>
|
|
88
|
-
<div class="stat-detail" data-dashboard-target="connectionDetail">0 active, 0 idle</div>
|
|
89
|
-
</div>
|
|
90
|
-
<div class="card stat-card">
|
|
91
|
-
<div class="stat-label">Message rates</div>
|
|
92
|
-
<div class="stat-value"><span data-dashboard-target="publishRate">0</span>/<span data-dashboard-target="consumeRate">0</span> msg/s</div>
|
|
93
|
-
<div class="stat-detail" data-dashboard-target="rateDetail">Peak: 0/0 msg/s</div>
|
|
94
|
-
</div>
|
|
95
|
-
</section>
|
|
96
|
-
|
|
97
|
-
<section class="grid chart-grid">
|
|
98
|
-
<div class="card">
|
|
99
|
-
<div class="card-header">
|
|
100
|
-
<h3>Message Rates</h3>
|
|
101
|
-
</div>
|
|
102
|
-
<div class="card-body">
|
|
103
|
-
<canvas id="rateChart" height="240"></canvas>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
<div class="card">
|
|
107
|
-
<div class="card-header">
|
|
108
|
-
<h3>Queue Distribution</h3>
|
|
109
|
-
</div>
|
|
110
|
-
<div class="card-body">
|
|
111
|
-
<canvas id="queueChart" height="240"></canvas>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
</section>
|
|
115
|
-
|
|
116
|
-
<section class="grid processes-grid" data-dashboard-target="processesRoot">
|
|
117
|
-
<!-- Process hierarchy cards rendered here -->
|
|
118
|
-
</section>
|
|
119
|
-
|
|
120
|
-
<section class="grid queues-grid">
|
|
121
|
-
<div class="card">
|
|
122
|
-
<div class="card-header">
|
|
123
|
-
<h3>Queues</h3>
|
|
124
|
-
</div>
|
|
125
|
-
<div class="card-body">
|
|
126
|
-
<div class="table-responsive">
|
|
127
|
-
<table class="table" data-controller="queue">
|
|
128
|
-
<thead>
|
|
129
|
-
<tr>
|
|
130
|
-
<th>Queue</th>
|
|
131
|
-
<th>Type</th>
|
|
132
|
-
<th>Ready</th>
|
|
133
|
-
<th>Unacked</th>
|
|
134
|
-
<th>Total</th>
|
|
135
|
-
<th>Consumers</th>
|
|
136
|
-
<th>Memory</th>
|
|
137
|
-
<th></th>
|
|
138
|
-
</tr>
|
|
139
|
-
</thead>
|
|
140
|
-
<tbody data-dashboard-target="queuesRoot">
|
|
141
|
-
<!-- Queue rows populated here -->
|
|
142
|
-
</tbody>
|
|
143
|
-
</table>
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
</section>
|
|
148
|
-
|
|
149
|
-
</main>
|
|
150
|
-
|
|
151
|
-
<!-- Load offline support modules. Bootstrap is a separate file so it runs
|
|
152
|
-
under strict Content Security Policies that forbid inline scripts. -->
|
|
153
|
-
<script src="assets/js/offline-manager.js"></script>
|
|
154
|
-
<script src="assets/js/service-worker-manager.js"></script>
|
|
155
|
-
<script src="assets/js/bootstrap.js"></script>
|
|
156
|
-
</body>
|
|
157
|
-
</html>
|
|
158
|
-
|
|
159
|
-
|
|
58
|
+
</div>
|
|
59
|
+
</header>
|
|
60
|
+
<main id="view" class="view" tabindex="-1">
|
|
61
|
+
<p class="loading">Reading the broker</p>
|
|
62
|
+
</main>
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
data/web/sw.js
CHANGED
|
@@ -1,144 +1,15 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'assets/js/controllers/theme_controller.js',
|
|
16
|
-
'assets/js/controllers/connection_controller.js',
|
|
17
|
-
'assets/js/controllers/dashboard_controller.js',
|
|
18
|
-
'assets/js/controllers/queue_controller.js'
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
// Install event - cache static assets
|
|
22
|
-
self.addEventListener('install', (event) => {
|
|
23
|
-
console.log('Service Worker: Installing...');
|
|
24
|
-
event.waitUntil(
|
|
25
|
-
caches.open(CACHE_NAME)
|
|
26
|
-
.then((cache) => {
|
|
27
|
-
console.log('Service Worker: Caching static assets');
|
|
28
|
-
return cache.addAll(STATIC_ASSETS);
|
|
29
|
-
})
|
|
30
|
-
.then(() => {
|
|
31
|
-
console.log('Service Worker: Installation complete');
|
|
32
|
-
return self.skipWaiting();
|
|
33
|
-
})
|
|
34
|
-
.catch((error) => {
|
|
35
|
-
console.error('Service Worker: Installation failed', error);
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Activate event - clean up old caches
|
|
41
|
-
self.addEventListener('activate', (event) => {
|
|
42
|
-
console.log('Service Worker: Activating...');
|
|
43
|
-
event.waitUntil(
|
|
44
|
-
caches.keys().then((cacheNames) => {
|
|
45
|
-
return Promise.all(
|
|
46
|
-
cacheNames.map((cacheName) => {
|
|
47
|
-
if (cacheName !== CACHE_NAME) {
|
|
48
|
-
console.log('Service Worker: Deleting old cache', cacheName);
|
|
49
|
-
return caches.delete(cacheName);
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
}).then(() => {
|
|
54
|
-
console.log('Service Worker: Activation complete');
|
|
55
|
-
return self.clients.claim();
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Fetch event - serve from cache when offline
|
|
61
|
-
self.addEventListener('fetch', (event) => {
|
|
62
|
-
// Only handle GET requests
|
|
63
|
-
if (event.request.method !== 'GET') {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Skip cross-origin requests
|
|
68
|
-
if (!event.request.url.startsWith(self.location.origin)) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
event.respondWith(
|
|
73
|
-
caches.match(event.request)
|
|
74
|
-
.then((cachedResponse) => {
|
|
75
|
-
// Return cached version if available
|
|
76
|
-
if (cachedResponse) {
|
|
77
|
-
console.log('Service Worker: Serving from cache', event.request.url);
|
|
78
|
-
return cachedResponse;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Otherwise, fetch from network
|
|
82
|
-
console.log('Service Worker: Fetching from network', event.request.url);
|
|
83
|
-
return fetch(event.request)
|
|
84
|
-
.then((response) => {
|
|
85
|
-
// Don't cache non-successful responses
|
|
86
|
-
if (!response || response.status !== 200 || response.type !== 'basic') {
|
|
87
|
-
return response;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Clone the response for caching
|
|
91
|
-
const responseToCache = response.clone();
|
|
92
|
-
|
|
93
|
-
caches.open(CACHE_NAME)
|
|
94
|
-
.then((cache) => {
|
|
95
|
-
// Only cache static assets under our scope, not API responses.
|
|
96
|
-
// `self.registration.scope` is the absolute URL the worker controls
|
|
97
|
-
// (e.g. "https://host/lepus/"), so it transparently covers any mount path.
|
|
98
|
-
const scope = new URL(self.registration.scope);
|
|
99
|
-
const url = new URL(event.request.url);
|
|
100
|
-
const scopePath = scope.pathname;
|
|
101
|
-
const path = url.pathname;
|
|
102
|
-
const isUnderScope = path.startsWith(scopePath);
|
|
103
|
-
const relative = isUnderScope ? path.slice(scopePath.length) : path;
|
|
104
|
-
if (isUnderScope && (relative === '' || relative === 'index.html' || relative.startsWith('assets/'))) {
|
|
105
|
-
console.log('Service Worker: Caching response', event.request.url);
|
|
106
|
-
cache.put(event.request, responseToCache);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
return response;
|
|
111
|
-
})
|
|
112
|
-
.catch((error) => {
|
|
113
|
-
console.log('Service Worker: Network fetch failed', event.request.url, error);
|
|
114
|
-
|
|
115
|
-
// For navigation requests, return the cached index (scope root)
|
|
116
|
-
if (event.request.mode === 'navigate') {
|
|
117
|
-
return caches.match('./');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// For other requests, return a generic offline response
|
|
121
|
-
if (event.request.url.endsWith('.js') || event.request.url.endsWith('.css')) {
|
|
122
|
-
return new Response(
|
|
123
|
-
'/* Offline - Resource not available */',
|
|
124
|
-
{
|
|
125
|
-
status: 200,
|
|
126
|
-
headers: {
|
|
127
|
-
'Content-Type': event.request.url.endsWith('.js') ? 'application/javascript' : 'text/css'
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
throw error;
|
|
134
|
-
});
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Handle messages from the main thread
|
|
140
|
-
self.addEventListener('message', (event) => {
|
|
141
|
-
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
142
|
-
self.skipWaiting();
|
|
143
|
-
}
|
|
1
|
+
// The dashboard no longer works offline. This worker replaces the old caching
|
|
2
|
+
// one: it deletes the caches that one left behind, unregisters itself and
|
|
3
|
+
// reloads the open dashboard tabs once so they load straight from the server.
|
|
4
|
+
|
|
5
|
+
self.addEventListener("install", () => self.skipWaiting());
|
|
6
|
+
|
|
7
|
+
self.addEventListener("activate", (event) => {
|
|
8
|
+
event.waitUntil((async () => {
|
|
9
|
+
const keys = await caches.keys();
|
|
10
|
+
await Promise.all(keys.filter((key) => key.startsWith("lepus-dashboard")).map((key) => caches.delete(key)));
|
|
11
|
+
await self.registration.unregister();
|
|
12
|
+
const clients = await self.clients.matchAll({ type: "window" });
|
|
13
|
+
for (const client of clients) client.navigate(client.url);
|
|
14
|
+
})());
|
|
144
15
|
});
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lepus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marcos G. Zimmermann
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exec
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -301,9 +301,33 @@ files:
|
|
|
301
301
|
- Rakefile
|
|
302
302
|
- bin/console
|
|
303
303
|
- bin/setup
|
|
304
|
+
- bin/web-demo
|
|
304
305
|
- config.ru
|
|
305
306
|
- docker-compose.yml
|
|
306
307
|
- docs/README.md
|
|
308
|
+
- docs/brand/BRAND.md
|
|
309
|
+
- docs/brand/apple-touch-icon.png
|
|
310
|
+
- docs/brand/favicon-16.png
|
|
311
|
+
- docs/brand/favicon-32.png
|
|
312
|
+
- docs/brand/favicon.ico
|
|
313
|
+
- docs/brand/favicon.svg
|
|
314
|
+
- docs/brand/icon-512.png
|
|
315
|
+
- docs/brand/lockup-lepus-mono.svg
|
|
316
|
+
- docs/brand/lockup-lepus-on-dark.svg
|
|
317
|
+
- docs/brand/lockup-lepus-on-light.svg
|
|
318
|
+
- docs/brand/lockup-lepus-saga-mono.svg
|
|
319
|
+
- docs/brand/lockup-lepus-saga-on-dark.svg
|
|
320
|
+
- docs/brand/lockup-lepus-saga-on-light.svg
|
|
321
|
+
- docs/brand/lockup-lepus-saga.svg
|
|
322
|
+
- docs/brand/lockup-lepus.svg
|
|
323
|
+
- docs/brand/mark-mono.svg
|
|
324
|
+
- docs/brand/mark-on-dark.svg
|
|
325
|
+
- docs/brand/mark-on-light.svg
|
|
326
|
+
- docs/brand/mark-small-mono.svg
|
|
327
|
+
- docs/brand/mark-small-on-dark.svg
|
|
328
|
+
- docs/brand/mark-small-on-light.svg
|
|
329
|
+
- docs/brand/mark-small.svg
|
|
330
|
+
- docs/brand/mark.svg
|
|
307
331
|
- docs/cli.md
|
|
308
332
|
- docs/configuration.md
|
|
309
333
|
- docs/consumers.md
|
|
@@ -346,6 +370,7 @@ files:
|
|
|
346
370
|
- lib/lepus/consumers/stats_registry.rb
|
|
347
371
|
- lib/lepus/consumers/worker.rb
|
|
348
372
|
- lib/lepus/consumers/worker_factory.rb
|
|
373
|
+
- lib/lepus/delay.rb
|
|
349
374
|
- lib/lepus/message.rb
|
|
350
375
|
- lib/lepus/message/delivery_info.rb
|
|
351
376
|
- lib/lepus/message/metadata.rb
|
|
@@ -406,17 +431,27 @@ files:
|
|
|
406
431
|
- lib/lepus/web/app.rb
|
|
407
432
|
- lib/lepus/web/management_api.rb
|
|
408
433
|
- lib/lepus/web/respond_with.rb
|
|
434
|
+
- lib/lepus/web/topology.rb
|
|
409
435
|
- lib/puma/plugin/lepus.rb
|
|
410
|
-
-
|
|
411
|
-
- web/assets/
|
|
412
|
-
- web/assets/
|
|
413
|
-
- web/assets/
|
|
414
|
-
- web/assets/
|
|
415
|
-
- web/assets/
|
|
416
|
-
- web/assets/js/
|
|
417
|
-
- web/assets/js/
|
|
418
|
-
- web/assets/js/
|
|
419
|
-
- web/assets/js/
|
|
436
|
+
- web/assets/app.css
|
|
437
|
+
- web/assets/apple-touch-icon.png
|
|
438
|
+
- web/assets/favicon-32.png
|
|
439
|
+
- web/assets/favicon.svg
|
|
440
|
+
- web/assets/fonts/overpass/OFL.txt
|
|
441
|
+
- web/assets/fonts/overpass/overpass-latin-wght.woff2
|
|
442
|
+
- web/assets/js/api.js
|
|
443
|
+
- web/assets/js/dom.js
|
|
444
|
+
- web/assets/js/format.js
|
|
445
|
+
- web/assets/js/live.js
|
|
446
|
+
- web/assets/js/main.js
|
|
447
|
+
- web/assets/js/map.js
|
|
448
|
+
- web/assets/js/model.js
|
|
449
|
+
- web/assets/js/rates.js
|
|
450
|
+
- web/assets/js/theme.js
|
|
451
|
+
- web/assets/js/views/connections.js
|
|
452
|
+
- web/assets/js/views/overview.js
|
|
453
|
+
- web/assets/js/views/processes.js
|
|
454
|
+
- web/assets/js/views/queues.js
|
|
420
455
|
- web/index.html
|
|
421
456
|
- web/sw.js
|
|
422
457
|
homepage: https://github.com/marcosgz/lepus
|
data/test_offline.html
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Lepus Offline Test</title>
|
|
7
|
-
<style>
|
|
8
|
-
body { font-family: system-ui; padding: 20px; }
|
|
9
|
-
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ccc; border-radius: 5px; }
|
|
10
|
-
.success { background: #d4edda; border-color: #c3e6cb; }
|
|
11
|
-
.error { background: #f8d7da; border-color: #f5c6cb; }
|
|
12
|
-
.info { background: #d1ecf1; border-color: #bee5eb; }
|
|
13
|
-
button { padding: 10px 15px; margin: 5px; cursor: pointer; }
|
|
14
|
-
</style>
|
|
15
|
-
</head>
|
|
16
|
-
<body>
|
|
17
|
-
<h1>Lepus Offline Support Test</h1>
|
|
18
|
-
|
|
19
|
-
<div class="test-section info">
|
|
20
|
-
<h3>Current Status</h3>
|
|
21
|
-
<p>Online: <span id="online-status">Checking...</span></p>
|
|
22
|
-
<p>Service Worker: <span id="sw-status">Checking...</span></p>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<div class="test-section">
|
|
26
|
-
<h3>Test Controls</h3>
|
|
27
|
-
<button onclick="simulateOffline()">Simulate Offline</button>
|
|
28
|
-
<button onclick="simulateOnline()">Simulate Online</button>
|
|
29
|
-
<button onclick="testExternalDependencies()">Test External Dependencies</button>
|
|
30
|
-
<button onclick="testServiceWorker()">Test Service Worker</button>
|
|
31
|
-
<button onclick="testOfflineModules()">Test Offline Modules</button>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<div class="test-section">
|
|
35
|
-
<h3>Test Results</h3>
|
|
36
|
-
<div id="test-results"></div>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<script>
|
|
40
|
-
// Test offline functionality
|
|
41
|
-
function updateStatus() {
|
|
42
|
-
document.getElementById('online-status').textContent = navigator.onLine ? 'Yes' : 'No';
|
|
43
|
-
document.getElementById('online-status').style.color = navigator.onLine ? 'green' : 'red';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function addResult(message, type = 'info') {
|
|
47
|
-
const results = document.getElementById('test-results');
|
|
48
|
-
const div = document.createElement('div');
|
|
49
|
-
div.className = `test-section ${type}`;
|
|
50
|
-
div.innerHTML = `<p>${new Date().toLocaleTimeString()}: ${message}</p>`;
|
|
51
|
-
results.appendChild(div);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function simulateOffline() {
|
|
55
|
-
Object.defineProperty(navigator, 'onLine', {
|
|
56
|
-
writable: true,
|
|
57
|
-
value: false
|
|
58
|
-
});
|
|
59
|
-
window.dispatchEvent(new Event('offline'));
|
|
60
|
-
updateStatus();
|
|
61
|
-
addResult('Simulated offline state', 'info');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function simulateOnline() {
|
|
65
|
-
Object.defineProperty(navigator, 'onLine', {
|
|
66
|
-
writable: true,
|
|
67
|
-
value: true
|
|
68
|
-
});
|
|
69
|
-
window.dispatchEvent(new Event('online'));
|
|
70
|
-
updateStatus();
|
|
71
|
-
addResult('Simulated online state', 'success');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function testExternalDependencies() {
|
|
75
|
-
addResult('Testing external dependencies...', 'info');
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
// Test Stimulus CDN
|
|
79
|
-
const stimulusResponse = await fetch('https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js');
|
|
80
|
-
if (stimulusResponse.ok) {
|
|
81
|
-
addResult('✓ Stimulus CDN accessible', 'success');
|
|
82
|
-
} else {
|
|
83
|
-
addResult('✗ Stimulus CDN not accessible', 'error');
|
|
84
|
-
}
|
|
85
|
-
} catch (error) {
|
|
86
|
-
addResult('✗ Stimulus CDN failed: ' + error.message, 'error');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Test Chart.js CDN
|
|
91
|
-
const chartResponse = await fetch('https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js');
|
|
92
|
-
if (chartResponse.ok) {
|
|
93
|
-
addResult('✓ Chart.js CDN accessible', 'success');
|
|
94
|
-
} else {
|
|
95
|
-
addResult('✗ Chart.js CDN not accessible', 'error');
|
|
96
|
-
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
addResult('✗ Chart.js CDN failed: ' + error.message, 'error');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function testServiceWorker() {
|
|
103
|
-
if ('serviceWorker' in navigator) {
|
|
104
|
-
navigator.serviceWorker.register('/sw.js')
|
|
105
|
-
.then(registration => {
|
|
106
|
-
addResult('✓ Service Worker registered successfully', 'success');
|
|
107
|
-
return navigator.serviceWorker.ready;
|
|
108
|
-
})
|
|
109
|
-
.then(registration => {
|
|
110
|
-
addResult('✓ Service Worker ready', 'success');
|
|
111
|
-
document.getElementById('sw-status').textContent = 'Active';
|
|
112
|
-
document.getElementById('sw-status').style.color = 'green';
|
|
113
|
-
})
|
|
114
|
-
.catch(error => {
|
|
115
|
-
addResult('✗ Service Worker failed: ' + error.message, 'error');
|
|
116
|
-
document.getElementById('sw-status').textContent = 'Failed';
|
|
117
|
-
document.getElementById('sw-status').style.color = 'red';
|
|
118
|
-
});
|
|
119
|
-
} else {
|
|
120
|
-
addResult('✗ Service Worker not supported', 'error');
|
|
121
|
-
document.getElementById('sw-status').textContent = 'Not Supported';
|
|
122
|
-
document.getElementById('sw-status').style.color = 'red';
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function testOfflineModules() {
|
|
127
|
-
addResult('Testing offline modules...', 'info');
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
// Test offline-manager.js
|
|
131
|
-
const offlineResponse = await fetch('/assets/js/offline-manager.js');
|
|
132
|
-
if (offlineResponse.ok) {
|
|
133
|
-
addResult('✓ offline-manager.js accessible', 'success');
|
|
134
|
-
} else {
|
|
135
|
-
addResult('✗ offline-manager.js not accessible', 'error');
|
|
136
|
-
}
|
|
137
|
-
} catch (error) {
|
|
138
|
-
addResult('✗ offline-manager.js failed: ' + error.message, 'error');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
// Test service-worker-manager.js
|
|
143
|
-
const swResponse = await fetch('/assets/js/service-worker-manager.js');
|
|
144
|
-
if (swResponse.ok) {
|
|
145
|
-
addResult('✓ service-worker-manager.js accessible', 'success');
|
|
146
|
-
} else {
|
|
147
|
-
addResult('✗ service-worker-manager.js not accessible', 'error');
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
addResult('✗ service-worker-manager.js failed: ' + error.message, 'error');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Test if modules are loaded in window
|
|
154
|
-
if (window.OfflineManager) {
|
|
155
|
-
addResult('✓ OfflineManager class available', 'success');
|
|
156
|
-
} else {
|
|
157
|
-
addResult('✗ OfflineManager class not available', 'error');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (window.ServiceWorkerManager) {
|
|
161
|
-
addResult('✓ ServiceWorkerManager class available', 'success');
|
|
162
|
-
} else {
|
|
163
|
-
addResult('✗ ServiceWorkerManager class not available', 'error');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Initialize
|
|
168
|
-
updateStatus();
|
|
169
|
-
|
|
170
|
-
// Listen for online/offline events
|
|
171
|
-
window.addEventListener('online', () => {
|
|
172
|
-
updateStatus();
|
|
173
|
-
addResult('Browser went online', 'success');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
window.addEventListener('offline', () => {
|
|
177
|
-
updateStatus();
|
|
178
|
-
addResult('Browser went offline', 'error');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Auto-test on load
|
|
182
|
-
setTimeout(() => {
|
|
183
|
-
testExternalDependencies();
|
|
184
|
-
testServiceWorker();
|
|
185
|
-
testOfflineModules();
|
|
186
|
-
}, 1000);
|
|
187
|
-
</script>
|
|
188
|
-
</body>
|
|
189
|
-
</html>
|