Skip to content

Commit dd647d5

Browse files
committed
client-replica: hide the debug widget
1 parent 4aa99f2 commit dd647d5

File tree

7 files changed

+129
-46
lines changed

7 files changed

+129
-46
lines changed

doc/RFCS/20250821_client_replica_file_system.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ TODO
316316
- `puter.fs.replica.debug` - toggle debug widget and logs, may be merged with `puter.debugMode` in the future
317317
- `puter.fs.replica.fs_tree` - the in-memory FS Tree, should only be used by internal code
318318

319+
### Websocket Lifecycle & Events
320+
321+
TODO
322+
319323
### Code Location
320324

321325
- `src/puter-js/src/modules/FileSystem/replica` - puter-js client

src/backend/src/routers/filesystem_api/fs_tree_manager/common.js

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,60 @@ const {
2222
const { Struct } = require('google-protobuf/google/protobuf/struct_pb.js');
2323

2424
const config = require('../../../config');
25-
const fsTreeManagerUrl = config.services?.['client-replica']?.fs_tree_manager_url;
26-
27-
// Create gRPC client
28-
const client = new FSTreeManagerClient(fsTreeManagerUrl, grpc.credentials.createInsecure(), {
29-
// Reconnect backoff (defaults can be slow: ~20s→120s)
30-
//
31-
// ref:
32-
// - https://grpc.github.io/grpc/core/group__grpc__arg__keys.html
33-
// - https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
34-
'grpc.initial_reconnect_backoff_ms': 500,
35-
'grpc.min_reconnect_backoff_ms': 500,
36-
'grpc.max_reconnect_backoff_ms': 5000,
37-
38-
// // Keepalive so dead TCPs are detected quickly
39-
// 'grpc.keepalive_time_ms': 15000, // send PING every 15s
40-
// 'grpc.keepalive_timeout_ms': 5000, // wait 5s for PING ack
41-
// 'grpc.keepalive_permit_without_calls': 1, // allow pings when idle
42-
43-
// // (Optional) be polite about PING cadence
44-
// 'grpc.http2.min_time_between_pings_ms': 10000,
45-
});
25+
26+
// Singleton client instance
27+
let clientInstance = null;
28+
29+
/**
30+
* Get the gRPC client for the FS Tree Manager service
31+
* Returns null if the client-replica service is not configured or disabled
32+
* Creates and caches the client instance on first call
33+
* @returns {FSTreeManagerClient|null} The gRPC client or null if not available
34+
*/
35+
function getClient() {
36+
// Return cached instance if available
37+
if ( clientInstance !== null ) {
38+
return clientInstance;
39+
}
40+
41+
const clientReplicaConfig = config.services?.['client-replica'];
42+
43+
// Return null if config is missing or disabled
44+
if ( !clientReplicaConfig || !clientReplicaConfig.enabled ) {
45+
clientInstance = null;
46+
return null;
47+
}
48+
49+
const fsTreeManagerUrl = clientReplicaConfig.fs_tree_manager_url;
50+
51+
// Return null if URL is not configured
52+
if ( !fsTreeManagerUrl ) {
53+
clientInstance = null;
54+
return null;
55+
}
56+
57+
// Create and cache gRPC client
58+
clientInstance = new FSTreeManagerClient(fsTreeManagerUrl, grpc.credentials.createInsecure(), {
59+
// Reconnect backoff (defaults can be slow: ~20s→120s)
60+
//
61+
// ref:
62+
// - https://grpc.github.io/grpc/core/group__grpc__arg__keys.html
63+
// - https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
64+
'grpc.initial_reconnect_backoff_ms': 500,
65+
'grpc.min_reconnect_backoff_ms': 500,
66+
'grpc.max_reconnect_backoff_ms': 5000,
67+
68+
// // Keepalive so dead TCPs are detected quickly
69+
// 'grpc.keepalive_time_ms': 15000, // send PING every 15s
70+
// 'grpc.keepalive_timeout_ms': 5000, // wait 5s for PING ack
71+
// 'grpc.keepalive_permit_without_calls': 1, // allow pings when idle
72+
73+
// // (Optional) be polite about PING cadence
74+
// 'grpc.http2.min_time_between_pings_ms': 10000,
75+
});
76+
77+
return clientInstance;
78+
}
4679

4780
/**
4881
* Sends a new filesystem entry to the gRPC service
@@ -62,6 +95,13 @@ async function sendFSNew(userId, metadata) {
6295
return;
6396
}
6497

98+
const client = getClient();
99+
if ( !client ) {
100+
// Client-replica service is not available, silently resolve
101+
resolve();
102+
return;
103+
}
104+
65105
const fsEntry = buildFsEntry(metadata);
66106
const request = new NewFSEntryRequest();
67107
request.setUserId(userId);
@@ -96,6 +136,13 @@ async function sendFSRemove(userId, uuid) {
96136
return;
97137
}
98138

139+
const client = getClient();
140+
if ( !client ) {
141+
// Client-replica service is not available, silently resolve
142+
resolve();
143+
return;
144+
}
145+
99146
const request = new RemoveFSEntryRequest();
100147
request.setUserId(userId);
101148
request.setUuid(uuid);
@@ -118,6 +165,13 @@ async function sendFSPurge(userId) {
118165
return;
119166
}
120167

168+
const client = getClient();
169+
if ( !client ) {
170+
// Client-replica service is not available, silently resolve
171+
resolve();
172+
return;
173+
}
174+
121175
const request = new PurgeReplicaRequest();
122176
request.setUserId(userId);
123177

@@ -216,8 +270,8 @@ function buildFsEntry(metadataObj) {
216270
}
217271

218272
module.exports = {
219-
// gRPC client and protobuf classes
220-
client,
273+
// gRPC client function and protobuf classes
274+
getClient,
221275
FSEntry,
222276
NewFSEntryRequest,
223277
RemoveFSEntryRequest,

src/backend/src/routers/filesystem_api/fs_tree_manager/fetch_replica.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,19 @@ module.exports = {
2727
handler: async (socket, _data) => {
2828
// Import gRPC client and protobuf classes from common
2929
const {
30-
client,
30+
getClient,
3131
FetchReplicaRequest,
3232
} = require('./common');
3333

34+
const client = getClient();
35+
if ( !client ) {
36+
// Client-replica service is not available
37+
return socket.emit('replica/fetch/error', {
38+
success: false,
39+
error: { message: 'client-replica service is not available' },
40+
});
41+
}
42+
3443
// Build the request message
3544
const requestMsg = new FetchReplicaRequest();
3645
requestMsg.setUserId(socket.user.id);

src/backend/src/routers/filesystem_api/fs_tree_manager/pull_diff.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,20 @@ module.exports = {
2727
handler: async (socket, data) => {
2828
// Import gRPC client and protobuf classes from common
2929
const {
30-
client,
30+
getClient,
3131
PullRequest,
3232
PullRequestItem,
3333
} = require('./common');
3434

35+
const client = getClient();
36+
if (!client) {
37+
// Client-replica service is not available
38+
return socket.emit('replica/pull_diff/error', {
39+
success: false,
40+
error: { message: 'client-replica service is not available' },
41+
});
42+
}
43+
3544
try {
3645
// Build the PullRequest message
3746
const requestMsg = new PullRequest();

src/gui/src/UI/UIDesktop.js

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -715,11 +715,10 @@ async function UIDesktop(options) {
715715
window.update_user_preferences(user_preferences);
716716
}
717717

718-
// Add replica status widget
719-
h += `<div id="replica-status-widget" class="replica-status-widget">
718+
// Add replica status widget (always create, but conditionally show)
719+
h += `<div id="replica-status-widget" class="replica-status-widget" style="display: ${puter.fs.replica.debug ? 'flex' : 'none'};">
720720
<div class="replica-status-label">client-replica:</div>
721721
<div class="replica-status-value" id="replica-status-value">false</div>
722-
<input type="checkbox" id="replica-status-toggle" class="replica-status-toggle">
723722
</div>`;
724723

725724
// Append to <body>
@@ -728,33 +727,25 @@ async function UIDesktop(options) {
728727
// Initialize replica status widget function
729728
window.updateReplicaStatusWidget = function() {
730729
const statusElement = document.getElementById('replica-status-value');
731-
const toggleElement = document.getElementById('replica-status-toggle');
732-
if (statusElement && toggleElement) {
730+
if (statusElement) {
733731
const isAvailable = puter.fs.replica.available === true;
734732
statusElement.textContent = isAvailable.toString();
735733
statusElement.className = `replica-status-value ${isAvailable}`;
736-
toggleElement.checked = isAvailable;
737734
}
738735
};
739736

740-
// Handle toggle change
741-
window.handleReplicaToggle = function() {
742-
const toggleElement = document.getElementById('replica-status-toggle');
743-
const statusElement = document.getElementById('replica-status-value');
744-
if (toggleElement && statusElement) {
745-
const newValue = toggleElement.checked;
746-
statusElement.textContent = newValue.toString();
747-
statusElement.className = `replica-status-value ${newValue}`;
748-
puter.fs.replica.available = newValue;
737+
738+
// Function to show/hide widget based on debug flag
739+
window.updateReplicaWidgetVisibility = function() {
740+
const widget = document.getElementById('replica-status-widget');
741+
if (widget) {
742+
widget.style.display = puter.fs.replica.debug ? 'flex' : 'none';
749743
}
750744
};
751745

752746
// Initialize widget and start polling
753747
setTimeout(() => {
754-
const toggleElement = document.getElementById('replica-status-toggle');
755-
if (toggleElement) {
756-
toggleElement.addEventListener('change', window.handleReplicaToggle);
757-
}
748+
// Start polling immediately - the visibility is controlled by CSS
758749
setInterval(window.updateReplicaStatusWidget, 100);
759750
}, 100);
760751

src/gui/src/css/style.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5312,7 +5312,7 @@ fieldset[name=number-code] {
53125312
.replica-status-widget {
53135313
position: fixed;
53145314
top: 10px;
5315-
right: 10px;
5315+
right: 50px;
53165316
background: rgba(0, 0, 0, 0.8);
53175317
color: white;
53185318
padding: 8px 12px;
@@ -5328,6 +5328,10 @@ fieldset[name=number-code] {
53285328
border: 1px solid rgba(255, 255, 255, 0.1);
53295329
}
53305330

5331+
.replica-status-widget.hidden {
5332+
display: none;
5333+
}
5334+
53315335
.replica-status-label {
53325336
font-weight: 500;
53335337
opacity: 0.8;

src/puter-js/src/modules/FileSystem/replica/manager.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ReplicaManager {
3131
this.fs_tree = null;
3232
this.last_local_update = 0; // milliseconds since epoch
3333

34-
this.debug = true;
34+
this.debug = false;
3535
}
3636

3737
/**
@@ -385,6 +385,18 @@ class ReplicaManager {
385385

386386
this.available = false;
387387
}
388+
389+
/**
390+
* Set the debug flag
391+
*/
392+
setDebug(enabled) {
393+
this.debug = enabled;
394+
395+
// Update widget visibility if the function exists (in GUI environment)
396+
if (typeof window !== 'undefined' && window.updateReplicaWidgetVisibility) {
397+
window.updateReplicaWidgetVisibility();
398+
}
399+
}
388400
}
389401

390402
// Create singleton instance

0 commit comments

Comments
 (0)