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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +104 -9
  5. data/bin/web-demo +592 -0
  6. data/docs/brand/BRAND.md +67 -0
  7. data/docs/brand/apple-touch-icon.png +0 -0
  8. data/docs/brand/favicon-16.png +0 -0
  9. data/docs/brand/favicon-32.png +0 -0
  10. data/docs/brand/favicon.ico +0 -0
  11. data/docs/brand/favicon.svg +1 -0
  12. data/docs/brand/icon-512.png +0 -0
  13. data/docs/brand/lockup-lepus-mono.svg +1 -0
  14. data/docs/brand/lockup-lepus-on-dark.svg +1 -0
  15. data/docs/brand/lockup-lepus-on-light.svg +1 -0
  16. data/docs/brand/lockup-lepus-saga-mono.svg +1 -0
  17. data/docs/brand/lockup-lepus-saga-on-dark.svg +1 -0
  18. data/docs/brand/lockup-lepus-saga-on-light.svg +1 -0
  19. data/docs/brand/lockup-lepus-saga.svg +1 -0
  20. data/docs/brand/lockup-lepus.svg +1 -0
  21. data/docs/brand/mark-mono.svg +1 -0
  22. data/docs/brand/mark-on-dark.svg +1 -0
  23. data/docs/brand/mark-on-light.svg +1 -0
  24. data/docs/brand/mark-small-mono.svg +1 -0
  25. data/docs/brand/mark-small-on-dark.svg +1 -0
  26. data/docs/brand/mark-small-on-light.svg +1 -0
  27. data/docs/brand/mark-small.svg +1 -0
  28. data/docs/brand/mark.svg +1 -0
  29. data/docs/configuration.md +1 -0
  30. data/docs/consumers.md +39 -5
  31. data/docs/getting-started.md +1 -1
  32. data/docs/images/lepus-web.png +0 -0
  33. data/docs/middleware.md +1 -1
  34. data/docs/producers.md +41 -0
  35. data/docs/testing.md +67 -48
  36. data/docs/web.md +39 -13
  37. data/gemfiles/Gemfile.rails-5.2.lock +2 -2
  38. data/gemfiles/Gemfile.rails-6.1.lock +4 -4
  39. data/gemfiles/Gemfile.rails-7.2.lock +1 -1
  40. data/gemfiles/Gemfile.rails-8.0.lock +1 -1
  41. data/lib/lepus/configuration.rb +6 -0
  42. data/lib/lepus/consumer.rb +34 -0
  43. data/lib/lepus/consumers/config.rb +8 -7
  44. data/lib/lepus/consumers/stats.rb +15 -2
  45. data/lib/lepus/delay.rb +180 -0
  46. data/lib/lepus/primitive/string.rb +2 -12
  47. data/lib/lepus/producer.rb +6 -1
  48. data/lib/lepus/producers/definition.rb +3 -1
  49. data/lib/lepus/publisher.rb +29 -3
  50. data/lib/lepus/testing/rspec_matchers.rb +20 -1
  51. data/lib/lepus/testing.rb +2 -0
  52. data/lib/lepus/version.rb +1 -1
  53. data/lib/lepus/web/api.rb +45 -0
  54. data/lib/lepus/web/app.rb +19 -16
  55. data/lib/lepus/web/management_api.rb +125 -2
  56. data/lib/lepus/web/topology.rb +341 -0
  57. data/lib/lepus/web.rb +17 -18
  58. data/lib/lepus.rb +1 -0
  59. data/web/assets/app.css +862 -0
  60. data/web/assets/apple-touch-icon.png +0 -0
  61. data/web/assets/favicon-32.png +0 -0
  62. data/web/assets/favicon.svg +1 -0
  63. data/web/assets/fonts/overpass/OFL.txt +93 -0
  64. data/web/assets/fonts/overpass/overpass-latin-wght.woff2 +0 -0
  65. data/web/assets/js/api.js +27 -0
  66. data/web/assets/js/dom.js +95 -0
  67. data/web/assets/js/format.js +63 -0
  68. data/web/assets/js/live.js +74 -0
  69. data/web/assets/js/main.js +156 -0
  70. data/web/assets/js/map.js +606 -0
  71. data/web/assets/js/model.js +262 -0
  72. data/web/assets/js/rates.js +161 -0
  73. data/web/assets/js/theme.js +8 -0
  74. data/web/assets/js/views/connections.js +61 -0
  75. data/web/assets/js/views/overview.js +229 -0
  76. data/web/assets/js/views/processes.js +101 -0
  77. data/web/assets/js/views/queues.js +456 -0
  78. data/web/index.html +58 -153
  79. data/web/sw.js +14 -143
  80. metadata +47 -12
  81. data/test_offline.html +0 -189
  82. data/web/assets/css/styles.css +0 -635
  83. data/web/assets/js/app.js +0 -6
  84. data/web/assets/js/bootstrap.js +0 -20
  85. data/web/assets/js/controllers/connection_controller.js +0 -44
  86. data/web/assets/js/controllers/dashboard_controller.js +0 -499
  87. data/web/assets/js/controllers/queue_controller.js +0 -17
  88. data/web/assets/js/controllers/theme_controller.js +0 -31
  89. data/web/assets/js/offline-manager.js +0 -233
  90. 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
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <base href="__BASE_PATH__" />
7
- <title>Lepus Dashboard</title>
8
- <link rel="stylesheet" href="assets/css/styles.css" />
9
- <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%236ea8fe'/%3E%3Ctext x='16' y='22' font-family='system-ui, -apple-system, sans-serif' font-size='18' font-weight='700' text-anchor='middle' fill='white'%3EL%3C/text%3E%3C/svg%3E" />
10
- </head>
11
- <body data-controller="theme connection dashboard">
12
- <!-- Offline notification banner -->
13
- <div id="offline-banner" class="offline-banner" style="display: none;">
14
- <div class="offline-content">
15
- <span class="offline-icon">⚠️</span>
16
- <span class="offline-message">You're offline. Some features may not work properly.</span>
17
- <button id="dismiss-offline" class="offline-dismiss">×</button>
18
- </div>
19
- </div>
20
-
21
- <header class="navbar">
22
- <div class="brand">
23
- <span class="logo-badge">L</span>
24
- <span class="title">Lepus</span>
25
- </div>
26
- <div class="nav-controls">
27
- <div class="refresh-control">
28
- <label for="refreshRange">Refresh:</label>
29
- <input id="refreshRange" type="range" min="5" max="60" step="5" value="15" data-dashboard-target="refreshRange" data-action="input->dashboard#updateRefresh" />
30
- <span class="refresh-value"><span data-dashboard-target="refreshLabel">15</span>s</span>
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="nav-actions">
34
- <div class="connection-status" data-connection-target="indicator" title="Connection status">
35
- <span class="status-dot"></span>
36
- <span class="status-text" data-connection-target="text">Connected</span>
37
- </div>
38
- <button class="btn" data-action="theme#toggle" aria-label="Toggle theme" title="Toggle theme">
39
- <span data-theme-target="icon">🌙</span>
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
- </header>
43
-
44
- <main class="container">
45
- <!-- Offline content placeholder -->
46
- <div id="offline-content" class="offline-content-placeholder" style="display: none;">
47
- <div class="offline-card">
48
- <div class="offline-icon-large">📡</div>
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
- // Service Worker for Lepus Web Dashboard
2
- // Provides offline caching for static assets
3
-
4
- const CACHE_NAME = 'lepus-dashboard-v1';
5
-
6
- // Paths are relative to the service worker script URL, so they resolve
7
- // correctly whether the dashboard is mounted at `/` or a sub-path like `/lepus`.
8
- const STATIC_ASSETS = [
9
- './',
10
- 'assets/css/styles.css',
11
- 'assets/js/app.js',
12
- 'assets/js/bootstrap.js',
13
- 'assets/js/offline-manager.js',
14
- 'assets/js/service-worker-manager.js',
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.2.2
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-10-30 00:00:00.000000000 Z
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
- - test_offline.html
411
- - web/assets/css/styles.css
412
- - web/assets/js/app.js
413
- - web/assets/js/bootstrap.js
414
- - web/assets/js/controllers/connection_controller.js
415
- - web/assets/js/controllers/dashboard_controller.js
416
- - web/assets/js/controllers/queue_controller.js
417
- - web/assets/js/controllers/theme_controller.js
418
- - web/assets/js/offline-manager.js
419
- - web/assets/js/service-worker-manager.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>