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
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
// Offline Manager - Handles offline detection and external dependency loading
|
|
2
|
-
class OfflineManager {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.isOnline = navigator.onLine;
|
|
5
|
-
this.stimulusLoaded = false;
|
|
6
|
-
this.chartLoaded = false;
|
|
7
|
-
this.initialized = false;
|
|
8
|
-
|
|
9
|
-
this.setupEventListeners();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
setupEventListeners() {
|
|
13
|
-
// Set up online/offline event listeners
|
|
14
|
-
window.addEventListener('online', () => {
|
|
15
|
-
this.isOnline = true;
|
|
16
|
-
this.hideOfflineBanner();
|
|
17
|
-
this.hideOfflineContent();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
window.addEventListener('offline', () => {
|
|
21
|
-
this.isOnline = false;
|
|
22
|
-
this.showOfflineBanner();
|
|
23
|
-
this.showOfflineContent();
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Function to show offline banner
|
|
28
|
-
showOfflineBanner() {
|
|
29
|
-
const banner = document.getElementById('offline-banner');
|
|
30
|
-
if (banner) {
|
|
31
|
-
banner.style.display = 'block';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Function to hide offline banner
|
|
36
|
-
hideOfflineBanner() {
|
|
37
|
-
const banner = document.getElementById('offline-banner');
|
|
38
|
-
if (banner) {
|
|
39
|
-
banner.style.display = 'none';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Function to show offline content
|
|
44
|
-
showOfflineContent() {
|
|
45
|
-
const content = document.getElementById('offline-content');
|
|
46
|
-
const mainContent = document.querySelector('.stats-grid');
|
|
47
|
-
if (content && mainContent) {
|
|
48
|
-
content.style.display = 'flex';
|
|
49
|
-
mainContent.style.display = 'none';
|
|
50
|
-
// Hide other main sections
|
|
51
|
-
document.querySelectorAll('.chart-grid, .processes-grid, .queues-grid').forEach(section => {
|
|
52
|
-
section.style.display = 'none';
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Function to hide offline content
|
|
58
|
-
hideOfflineContent() {
|
|
59
|
-
const content = document.getElementById('offline-content');
|
|
60
|
-
const mainContent = document.querySelector('.stats-grid');
|
|
61
|
-
if (content && mainContent) {
|
|
62
|
-
content.style.display = 'none';
|
|
63
|
-
mainContent.style.display = 'grid';
|
|
64
|
-
// Show other main sections
|
|
65
|
-
document.querySelectorAll('.chart-grid, .processes-grid, .queues-grid').forEach(section => {
|
|
66
|
-
section.style.display = 'grid';
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Function to load external scripts with fallback
|
|
72
|
-
loadExternalScript(src, fallbackCode, onLoad) {
|
|
73
|
-
const script = document.createElement('script');
|
|
74
|
-
script.src = src;
|
|
75
|
-
script.onload = function() {
|
|
76
|
-
onLoad(true);
|
|
77
|
-
};
|
|
78
|
-
script.onerror = function() {
|
|
79
|
-
console.warn('Failed to load external script:', src);
|
|
80
|
-
if (fallbackCode) {
|
|
81
|
-
// Execute fallback code
|
|
82
|
-
eval(fallbackCode);
|
|
83
|
-
onLoad(false);
|
|
84
|
-
} else {
|
|
85
|
-
onLoad(false);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
document.head.appendChild(script);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Load Stimulus with fallback
|
|
92
|
-
loadStimulus() {
|
|
93
|
-
return new Promise((resolve) => {
|
|
94
|
-
this.loadExternalScript(
|
|
95
|
-
'https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js',
|
|
96
|
-
`
|
|
97
|
-
// Minimal Stimulus fallback
|
|
98
|
-
window.Stimulus = {
|
|
99
|
-
Application: class {
|
|
100
|
-
constructor() { this.controllers = new Map(); }
|
|
101
|
-
register(name, controller) { this.controllers.set(name, controller); }
|
|
102
|
-
start() {
|
|
103
|
-
console.warn('Using offline Stimulus fallback');
|
|
104
|
-
// Basic controller initialization
|
|
105
|
-
document.querySelectorAll('[data-controller]').forEach(element => {
|
|
106
|
-
const controllers = element.dataset.controller.split(' ');
|
|
107
|
-
controllers.forEach(controllerName => {
|
|
108
|
-
const ControllerClass = this.controllers.get(controllerName);
|
|
109
|
-
if (ControllerClass) {
|
|
110
|
-
new ControllerClass(element);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
Controller: class {
|
|
117
|
-
constructor(element) { this.element = element; }
|
|
118
|
-
get targets() {
|
|
119
|
-
return {
|
|
120
|
-
find: (name) => this.element.querySelector('[data-' + this.constructor.name.toLowerCase().replace('controller', '') + '-target="' + name + '"]'),
|
|
121
|
-
findAll: (name) => this.element.querySelectorAll('[data-' + this.constructor.name.toLowerCase().replace('controller', '') + '-target="' + name + '"]')
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
`,
|
|
127
|
-
(loaded) => {
|
|
128
|
-
this.stimulusLoaded = loaded;
|
|
129
|
-
if (!loaded) this.showOfflineBanner();
|
|
130
|
-
resolve(loaded);
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Load Chart.js with fallback
|
|
137
|
-
loadChartJs() {
|
|
138
|
-
return new Promise((resolve) => {
|
|
139
|
-
this.loadExternalScript(
|
|
140
|
-
'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js',
|
|
141
|
-
`
|
|
142
|
-
// Chart.js fallback - simple canvas drawing
|
|
143
|
-
window.Chart = class {
|
|
144
|
-
constructor(ctx, config) {
|
|
145
|
-
this.ctx = ctx;
|
|
146
|
-
this.config = config;
|
|
147
|
-
this.drawFallbackChart();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
drawFallbackChart() {
|
|
151
|
-
const canvas = this.ctx.canvas;
|
|
152
|
-
const width = canvas.width;
|
|
153
|
-
const height = canvas.height;
|
|
154
|
-
|
|
155
|
-
// Clear canvas
|
|
156
|
-
this.ctx.clearRect(0, 0, width, height);
|
|
157
|
-
|
|
158
|
-
// Draw fallback message
|
|
159
|
-
this.ctx.fillStyle = '#666';
|
|
160
|
-
this.ctx.font = '14px system-ui';
|
|
161
|
-
this.ctx.textAlign = 'center';
|
|
162
|
-
this.ctx.fillText('Charts unavailable offline', width/2, height/2);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
update() { this.drawFallbackChart(); }
|
|
166
|
-
destroy() {}
|
|
167
|
-
};
|
|
168
|
-
`,
|
|
169
|
-
(loaded) => {
|
|
170
|
-
this.chartLoaded = loaded;
|
|
171
|
-
if (!loaded) this.showOfflineBanner();
|
|
172
|
-
resolve(loaded);
|
|
173
|
-
}
|
|
174
|
-
);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Load local scripts serially. Controllers reference `StimulusApp`
|
|
179
|
-
// defined by app.js, so they must not execute before app.js.
|
|
180
|
-
loadLocalScripts() {
|
|
181
|
-
const scripts = [
|
|
182
|
-
'assets/js/app.js',
|
|
183
|
-
'assets/js/controllers/theme_controller.js',
|
|
184
|
-
'assets/js/controllers/connection_controller.js',
|
|
185
|
-
'assets/js/controllers/dashboard_controller.js',
|
|
186
|
-
'assets/js/controllers/queue_controller.js'
|
|
187
|
-
];
|
|
188
|
-
|
|
189
|
-
const loadOne = (src) => new Promise((resolve) => {
|
|
190
|
-
const script = document.createElement('script');
|
|
191
|
-
script.src = src;
|
|
192
|
-
script.onload = () => resolve();
|
|
193
|
-
script.onerror = () => {
|
|
194
|
-
console.warn('Failed to load local script:', src);
|
|
195
|
-
resolve();
|
|
196
|
-
};
|
|
197
|
-
document.head.appendChild(script);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
return scripts.reduce((chain, src) => chain.then(() => loadOne(src)), Promise.resolve());
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Initialize the application
|
|
204
|
-
async initialize() {
|
|
205
|
-
if (this.initialized) return;
|
|
206
|
-
this.initialized = true;
|
|
207
|
-
|
|
208
|
-
// Set up dismiss button
|
|
209
|
-
const dismissBtn = document.getElementById('dismiss-offline');
|
|
210
|
-
if (dismissBtn) {
|
|
211
|
-
dismissBtn.addEventListener('click', () => this.hideOfflineBanner());
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Check initial online status
|
|
215
|
-
if (!this.isOnline) {
|
|
216
|
-
this.showOfflineBanner();
|
|
217
|
-
// Show offline content if we're offline and external dependencies failed to load
|
|
218
|
-
setTimeout(() => {
|
|
219
|
-
if (!this.stimulusLoaded || !this.chartLoaded) {
|
|
220
|
-
this.showOfflineContent();
|
|
221
|
-
}
|
|
222
|
-
}, 2000); // Wait 2 seconds to see if dependencies load
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Load dependencies in sequence
|
|
226
|
-
await this.loadStimulus();
|
|
227
|
-
await this.loadChartJs();
|
|
228
|
-
await this.loadLocalScripts();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Export for use in other modules
|
|
233
|
-
window.OfflineManager = OfflineManager;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// Service Worker Manager - Handles service worker registration and updates
|
|
2
|
-
class ServiceWorkerManager {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.registration = null;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// Register service worker. The SW is purely an offline-cache enhancement,
|
|
8
|
-
// so we never await `.ready` — if the SW install fails (e.g. one cached
|
|
9
|
-
// asset 401s behind auth) we would otherwise hang the whole dashboard
|
|
10
|
-
// bootstrap waiting for a worker that will never activate.
|
|
11
|
-
async register() {
|
|
12
|
-
if (!('serviceWorker' in navigator)) {
|
|
13
|
-
console.warn('Service Worker not supported');
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
this.registration = await navigator.serviceWorker.register('sw.js');
|
|
19
|
-
console.log('Service Worker registered:', this.registration.scope);
|
|
20
|
-
|
|
21
|
-
this.registration.addEventListener('updatefound', () => {
|
|
22
|
-
const newWorker = this.registration.installing;
|
|
23
|
-
newWorker.addEventListener('statechange', () => {
|
|
24
|
-
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
|
25
|
-
console.log('New service worker available. Reload to update.');
|
|
26
|
-
this.showUpdateNotification();
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return true;
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.warn('Service Worker registration failed:', error);
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Show update notification (optional enhancement)
|
|
39
|
-
showUpdateNotification() {
|
|
40
|
-
// You could implement a notification here to prompt users to reload
|
|
41
|
-
// For now, we'll just log it
|
|
42
|
-
console.log('Update available - consider implementing user notification');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Unregister service worker (for development/testing)
|
|
46
|
-
async unregister() {
|
|
47
|
-
if (this.registration) {
|
|
48
|
-
await this.registration.unregister();
|
|
49
|
-
console.log('Service Worker unregistered');
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check if service worker is active
|
|
54
|
-
isActive() {
|
|
55
|
-
return navigator.serviceWorker.controller !== null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Get service worker registration
|
|
59
|
-
getRegistration() {
|
|
60
|
-
return this.registration;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Export for use in other modules
|
|
65
|
-
window.ServiceWorkerManager = ServiceWorkerManager;
|