mrmd-server 0.1.3 → 0.1.6
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.
- package/package.json +2 -2
- package/src/server.js +71 -7
- package/static/http-shim.js +27 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mrmd-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "HTTP server for mrmd - run mrmd in any browser, access from anywhere",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"chokidar": "^3.6.0",
|
|
47
47
|
"fzf": "^0.5.2",
|
|
48
48
|
"mrmd-project": "^0.1.1",
|
|
49
|
-
"mrmd-electron": "^0.3.
|
|
49
|
+
"mrmd-electron": "^0.3.3",
|
|
50
50
|
"mrmd-sync": "^0.3.2"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/server.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import express from 'express';
|
|
8
8
|
import cors from 'cors';
|
|
9
9
|
import { createServer as createHttpServer } from 'http';
|
|
10
|
-
import { WebSocketServer } from 'ws';
|
|
10
|
+
import { WebSocketServer, WebSocket as WsClient } from 'ws';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import fs from 'fs/promises';
|
|
13
13
|
import { existsSync } from 'fs';
|
|
@@ -200,13 +200,32 @@ export async function createServer(config) {
|
|
|
200
200
|
// Serve mrmd-electron assets (fonts, icons)
|
|
201
201
|
app.use('/assets', express.static(path.join(electronPath, 'assets')));
|
|
202
202
|
|
|
203
|
-
// Serve mrmd-editor dist
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
// Serve mrmd-editor dist - check multiple locations
|
|
204
|
+
const editorDistCandidates = [
|
|
205
|
+
path.join(electronPath, 'editor'), // Bundled in mrmd-electron (npm)
|
|
206
|
+
path.join(electronPath, '../mrmd-editor/dist'), // Sibling (dev mode)
|
|
207
|
+
];
|
|
208
|
+
for (const distPath of editorDistCandidates) {
|
|
209
|
+
if (existsSync(path.join(distPath, 'mrmd.iife.js'))) {
|
|
210
|
+
app.use('/mrmd-editor/dist', express.static(distPath));
|
|
211
|
+
app.use('/dist', express.static(distPath));
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
207
215
|
|
|
208
|
-
// Serve node_modules
|
|
209
|
-
|
|
216
|
+
// Serve node_modules for xterm, etc. - check multiple locations (npm hoisting)
|
|
217
|
+
const nodeModulesCandidates = [
|
|
218
|
+
path.join(electronPath, 'node_modules'), // Direct dependency
|
|
219
|
+
path.join(electronPath, '..'), // Hoisted to parent (npm installed)
|
|
220
|
+
path.join(__dirname, '../node_modules'), // mrmd-server's node_modules
|
|
221
|
+
path.join(__dirname, '../../node_modules'), // Hoisted further up
|
|
222
|
+
];
|
|
223
|
+
for (const nmPath of nodeModulesCandidates) {
|
|
224
|
+
if (existsSync(path.join(nmPath, 'xterm'))) {
|
|
225
|
+
app.use('/node_modules', express.static(nmPath));
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
210
229
|
|
|
211
230
|
// Serve transformed index.html at root
|
|
212
231
|
app.get('/', async (req, res) => {
|
|
@@ -241,6 +260,51 @@ export async function createServer(config) {
|
|
|
241
260
|
const wss = new WebSocketServer({ server, path: '/events' });
|
|
242
261
|
setupWebSocket(wss, eventBus, token, noAuth);
|
|
243
262
|
|
|
263
|
+
// WebSocket proxy for sync connections (remote browsers can't reach localhost)
|
|
264
|
+
const syncWss = new WebSocketServer({ noServer: true });
|
|
265
|
+
server.on('upgrade', (request, socket, head) => {
|
|
266
|
+
const url = new URL(request.url, 'http://localhost');
|
|
267
|
+
|
|
268
|
+
// Handle /events normally
|
|
269
|
+
if (url.pathname === '/events') {
|
|
270
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
271
|
+
wss.emit('connection', ws, request);
|
|
272
|
+
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Handle /sync/:port/:doc - proxy to local sync server
|
|
277
|
+
const syncMatch = url.pathname.match(/^\/sync\/(\d+)\/(.+)$/);
|
|
278
|
+
if (syncMatch) {
|
|
279
|
+
const [, syncPort, docName] = syncMatch;
|
|
280
|
+
const targetUrl = `ws://127.0.0.1:${syncPort}/${docName}`;
|
|
281
|
+
|
|
282
|
+
// Create connection to local sync server
|
|
283
|
+
const upstream = new WsClient(targetUrl);
|
|
284
|
+
|
|
285
|
+
upstream.on('open', () => {
|
|
286
|
+
syncWss.handleUpgrade(request, socket, head, (clientWs) => {
|
|
287
|
+
// Bidirectional proxy
|
|
288
|
+
clientWs.on('message', (data) => upstream.send(data));
|
|
289
|
+
upstream.on('message', (data) => clientWs.send(data));
|
|
290
|
+
clientWs.on('close', () => upstream.close());
|
|
291
|
+
upstream.on('close', () => clientWs.close());
|
|
292
|
+
clientWs.on('error', () => upstream.close());
|
|
293
|
+
upstream.on('error', () => clientWs.close());
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
upstream.on('error', (err) => {
|
|
298
|
+
console.error(`[sync-proxy] Failed to connect to ${targetUrl}:`, err.message);
|
|
299
|
+
socket.destroy();
|
|
300
|
+
});
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Unknown upgrade request
|
|
305
|
+
socket.destroy();
|
|
306
|
+
});
|
|
307
|
+
|
|
244
308
|
return {
|
|
245
309
|
app,
|
|
246
310
|
server,
|
package/static/http-shim.js
CHANGED
|
@@ -17,6 +17,33 @@
|
|
|
17
17
|
const TOKEN = params.get('token') || '';
|
|
18
18
|
const BASE_URL = window.MRMD_SERVER_URL || window.location.origin;
|
|
19
19
|
|
|
20
|
+
// ==========================================================================
|
|
21
|
+
// WebSocket Proxy Interceptor
|
|
22
|
+
// ==========================================================================
|
|
23
|
+
// Intercept WebSocket connections to localhost sync servers and route through proxy
|
|
24
|
+
const OriginalWebSocket = window.WebSocket;
|
|
25
|
+
window.WebSocket = function(url, protocols) {
|
|
26
|
+
let targetUrl = url;
|
|
27
|
+
|
|
28
|
+
// Check if this is a sync connection to localhost
|
|
29
|
+
const match = url.match(/^wss?:\/\/127\.0\.0\.1:(\d+)\/(.+)$/);
|
|
30
|
+
if (match) {
|
|
31
|
+
const [, port, docPath] = match;
|
|
32
|
+
// Route through server proxy
|
|
33
|
+
const proxyUrl = new URL(`/sync/${port}/${docPath}`, BASE_URL);
|
|
34
|
+
proxyUrl.protocol = proxyUrl.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
35
|
+
targetUrl = proxyUrl.toString();
|
|
36
|
+
console.log(`[http-shim] Proxying sync WebSocket: ${url} -> ${targetUrl}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new OriginalWebSocket(targetUrl, protocols);
|
|
40
|
+
};
|
|
41
|
+
window.WebSocket.prototype = OriginalWebSocket.prototype;
|
|
42
|
+
window.WebSocket.CONNECTING = OriginalWebSocket.CONNECTING;
|
|
43
|
+
window.WebSocket.OPEN = OriginalWebSocket.OPEN;
|
|
44
|
+
window.WebSocket.CLOSING = OriginalWebSocket.CLOSING;
|
|
45
|
+
window.WebSocket.CLOSED = OriginalWebSocket.CLOSED;
|
|
46
|
+
|
|
20
47
|
// ==========================================================================
|
|
21
48
|
// HTTP Client
|
|
22
49
|
// ==========================================================================
|