@data-fair/dev-server 2.6.3 → 2.6.4
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/README.md +3 -4
- package/package.json +1 -1
- package/src/app.js +52 -44
package/README.md
CHANGED
|
@@ -50,10 +50,9 @@ développement, dans la même version mineure. La recherche part du nom lu dans
|
|
|
50
50
|
balise `<meta name="application-name">` de l'`index.html` local et de la version
|
|
51
51
|
du `package.json`.
|
|
52
52
|
|
|
53
|
-
Une application de base réservée à une organisation (`privateAccess`)
|
|
54
|
-
invisible
|
|
55
|
-
|
|
56
|
-
`DATAFAIR_API_KEY` est renseignée.
|
|
53
|
+
Une application de base réservée à une organisation (`privateAccess`) reste
|
|
54
|
+
invisible : data-fair ne lit pas la clé d'API sur `/base-applications` et répond
|
|
55
|
+
401 au paramètre `privateAccess`, que le dev-server n'envoie donc pas.
|
|
57
56
|
|
|
58
57
|
Ne pas la trouver n'arrête pas la recherche pour autant : les applications qui
|
|
59
58
|
tournent dessus sont souvent publiques, et il suffit de connaître son URL pour
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -30,42 +30,62 @@ let devServerWS = [];
|
|
|
30
30
|
let dataFairWS = [];
|
|
31
31
|
const dataFairWSChannels = new Map();
|
|
32
32
|
const dfWsUrl = config.dataFair.url.replace('http://', 'ws://').replace('https://', 'wss://') + '/';
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
// the connection to data-fair is reopened whenever it closes (network change, data-fair restart),
|
|
34
|
+
// and the channels of the connected apps are subscribed again on it
|
|
35
|
+
let dataFairOutputWS;
|
|
36
|
+
const subscribe = (channel) => {
|
|
37
|
+
const message = { type: 'subscribe', channel };
|
|
38
|
+
if (config.dataFair.apiKey)
|
|
39
|
+
message.apiKey = config.dataFair.apiKey;
|
|
40
|
+
dataFairOutputWS.send(JSON.stringify(message));
|
|
41
|
+
};
|
|
42
|
+
const connectDataFairWS = () => {
|
|
43
|
+
dataFairOutputWS = new WebSocket(dfWsUrl);
|
|
44
|
+
dataFairOutputWS.on('open', () => {
|
|
45
|
+
debug('connect to external websocket server ' + dfWsUrl);
|
|
46
|
+
for (const channel of new Set([...dataFairWSChannels.values()].flat()))
|
|
47
|
+
subscribe(channel);
|
|
48
|
+
});
|
|
49
|
+
dataFairOutputWS.on('message', (data) => {
|
|
50
|
+
const dataStr = data.toString();
|
|
51
|
+
const message = JSON.parse(dataStr);
|
|
52
|
+
debug('incoming message from data-fair socket', message);
|
|
53
|
+
for (const ws of dataFairWS) {
|
|
54
|
+
if (dataFairWSChannels.get(ws)?.includes(message.channel)) {
|
|
55
|
+
ws.send(dataStr);
|
|
56
|
+
}
|
|
44
57
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
dataFairOutputWS.on('
|
|
48
|
-
|
|
58
|
+
});
|
|
59
|
+
dataFairOutputWS.on('error', (err) => console.error('failed to connect to external websocket server', dfWsUrl, err));
|
|
60
|
+
dataFairOutputWS.on('close', () => {
|
|
61
|
+
console.error('external websocket was closed, reconnecting in 2s', dfWsUrl);
|
|
62
|
+
setTimeout(connectDataFairWS, 2000);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
connectDataFairWS();
|
|
49
66
|
server.on('upgrade', function upgrade(req, socket, head) {
|
|
50
67
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
51
68
|
debug('Opening websocket ' + req.url);
|
|
52
69
|
if (req.url === '/data-fair' || req.url === '/data-fair/') {
|
|
53
70
|
dataFairWS.push(ws);
|
|
54
|
-
ws.on('close', () => {
|
|
71
|
+
ws.on('close', () => {
|
|
72
|
+
dataFairWS = dataFairWS.filter(_ws => _ws !== ws);
|
|
73
|
+
dataFairWSChannels.delete(ws);
|
|
74
|
+
});
|
|
55
75
|
ws.on('message', (data) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).filter(c => c !== message.channel));
|
|
66
|
-
}
|
|
67
|
-
dataFairOutputWS.send(JSON.stringify(message));
|
|
76
|
+
const message = JSON.parse(data);
|
|
77
|
+
debug('outgoing message to data-fair socket', message);
|
|
78
|
+
if (message.type === 'subscribe') {
|
|
79
|
+
dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).concat([message.channel]));
|
|
80
|
+
if (config.dataFair.apiKey)
|
|
81
|
+
message.apiKey = config.dataFair.apiKey;
|
|
82
|
+
}
|
|
83
|
+
if (message.type === 'unsubscribe') {
|
|
84
|
+
dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).filter(c => c !== message.channel));
|
|
68
85
|
}
|
|
86
|
+
// while data-fair is unreachable the subscriptions are only recorded, the open handler sends them
|
|
87
|
+
if (dataFairOutputWS.readyState === WebSocket.OPEN)
|
|
88
|
+
dataFairOutputWS.send(JSON.stringify(message));
|
|
69
89
|
});
|
|
70
90
|
}
|
|
71
91
|
else if (req.url === '/') {
|
|
@@ -164,15 +184,6 @@ const localAppInfo = async () => {
|
|
|
164
184
|
}
|
|
165
185
|
return { name, version, packageName };
|
|
166
186
|
};
|
|
167
|
-
// The owner the dev-server works on behalf of, in the "type:id[:department]" form the
|
|
168
|
-
// data-fair api expects for its owner and privateAccess filters.
|
|
169
|
-
const ownerFilter = () => {
|
|
170
|
-
const owner = config.dataFair.owner;
|
|
171
|
-
let filter = `${owner.type}:${owner.id}`;
|
|
172
|
-
if (owner.department)
|
|
173
|
-
filter += ':' + owner.department;
|
|
174
|
-
return filter;
|
|
175
|
-
};
|
|
176
187
|
// Enrich the datasets from the remote data-fair and rewrite every remote origin to ours
|
|
177
188
|
// (pure helpers in enrich.ts, wired here to our remote api and local origins).
|
|
178
189
|
const prepareConfig = (configuration) => prepareRemoteConfig(configuration, {
|
|
@@ -323,15 +334,12 @@ app.get('/configurations', async (req, res) => {
|
|
|
323
334
|
return;
|
|
324
335
|
}
|
|
325
336
|
response.minorVersion = minorVersion(version);
|
|
326
|
-
// a base application restricted to an organization is invisible to an anonymous request.
|
|
327
|
-
// privateAccess is what makes it visible, and data-fair answers 401 to it without
|
|
328
|
-
// credentials, so it is only sent when an api key is configured.
|
|
329
337
|
response.authenticated = !!config.dataFair.apiKey;
|
|
330
338
|
try {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const baseApps = await remoteFetch(
|
|
339
|
+
// no privateAccess filter: data-fair reads no api key on /base-applications and answers 401
|
|
340
|
+
// to it, so a base application restricted to an organization stays invisible here and is
|
|
341
|
+
// reached by guessing its url below
|
|
342
|
+
const baseApps = await remoteFetch('/base-applications?applicationName=' + encodeURIComponent(name) + '&size=1000&count=false');
|
|
335
343
|
const matchingBaseApps = (baseApps.results ?? []).filter((b) => typeof b.version === 'string' && minorVersion(b.version) === response.minorVersion);
|
|
336
344
|
// Not finding the base application is not the end of the search: it may simply be private
|
|
337
345
|
// while the applications running on it are public, and those are reachable as soon as its
|