openvibers/openvibe.tools Commits
Paste gateway talks to OpenVibe.Community directly (it owns pastes since the Wave 5 cutover); the visitor's JWT and address go along
Changes
@@@ -37,4 +37,6 @@ module.exports = {// Live owns the accounts these pastes belong to, so it is the only service that can// turn a signed-in visitor into the user id Media files the write under.liveUrl: process.env.LIVE_URL || 'http://127.0.0.1:3000',++ // OpenVibe.Community owns pastes (roadmap Wave 5); the paste front-end's API goes straight there.++ communityUrl: (process.env.OV_COMMUNITY_INTERNAL_URL || 'http://127.0.0.1:4200').replace(/\/$/, ''),};@@@ -131,26 +131,19 @@ app.get('/api/health', (_req, res) => {app.get('/api/brand', (_req, res) => res.json(BRAND));--// ── Paste.OpenVibe → OpenVibe.Live proxy ─────────────────────--// The paste front-end talks to /api/pastes/* on this gateway; we proxy server-side to--// the same API the streaming site's own paste pages use. Keeping the proxy avoids CORS--// headaches and hides the upstream from the browser.--//--// This used to call Media's tenant API directly, forwarding the visitor's JWT for Media--// to identify them by. That was wrong: the JWT names the account by its NETWORK id,--// while the user_id Media stores for these pastes is Live's own — different numbers for--// the same person, so a comment posted here was filed under whichever unrelated Live--// account happened to hold that number. Live resolves the visitor against its own--// accounts before writing, which is a thing only Live can do, and this inherits its--// author names and permission checks rather than reimplementing them.++// ── Paste.OpenVibe → OpenVibe.Community proxy ────────────────++// The paste front-end talks to /api/pastes/* on this gateway; we proxy server-side to OpenVibe.Community,++// which owns pastes (roadmap Wave 5). The visitor's Network JWT goes along: Community verifies it and++// files writes under the person's canonical subject, so no site has to translate ids any more. The++// client address goes along too, for Community's anonymous-write limits.app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, res) => {try {-- const target = `${config.liveUrl}/api/pastes${req.url}`;++ const target = `${config.communityUrl}/api/pastes${req.url}`;const fetchOpts = { method: req.method, headers: {} };-- // Forward the visitor's JWT — Live verifies it and maps it to a local account.++ // Forward the visitor's JWT — Community verifies it and maps it to their subject.const userToken = extractToken(req);if (userToken) fetchOpts.headers['Authorization'] = `Bearer ${userToken}`;-- // Live rate-limits, bans and records by client address. Without this every++ // Community rate-limits anonymous writes by client address. Without this every// visitor arriving through this gateway would share one bucket and one identity.if (req.ip) fetchOpts.headers['X-Forwarded-For'] = req.ip;if (req.method !== 'GET' && req.method !== 'HEAD' && req.body && Object.keys(req.body).length) {@@@ -165,7 +158,7 @@ app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, rereturn res.send(raw);} catch (err) {console.error('[PasteProxy]', err.message);-- return res.status(502).json({ error: 'Could not reach the media service' });++ return res.status(502).json({ error: 'Could not reach the paste service' });}});
@@ -37,4 +37,6 @@ module.exports = {
@@ -37,4 +37,6 @@ module.exports = {
// Live owns the accounts these pastes belong to, so it is the only service that can
// Live owns the accounts these pastes belong to, so it is the only service that can
// turn a signed-in visitor into the user id Media files the write under.
// turn a signed-in visitor into the user id Media files the write under.
liveUrl: process.env.LIVE_URL || 'http://127.0.0.1:3000',
liveUrl: process.env.LIVE_URL || 'http://127.0.0.1:3000',
+ // OpenVibe.Community owns pastes (roadmap Wave 5); the paste front-end's API goes straight there.
+ communityUrl: (process.env.OV_COMMUNITY_INTERNAL_URL || 'http://127.0.0.1:4200').replace(/\/$/, ''),
};
};
@@ -131,26 +131,19 @@ app.get('/api/health', (_req, res) => {
@@ -131,26 +131,19 @@ app.get('/api/health', (_req, res) => {
app.get('/api/brand', (_req, res) => res.json(BRAND));
app.get('/api/brand', (_req, res) => res.json(BRAND));
-// ── Paste.OpenVibe → OpenVibe.Live proxy ─────────────────────
-// The paste front-end talks to /api/pastes/* on this gateway; we proxy server-side to
-// the same API the streaming site's own paste pages use. Keeping the proxy avoids CORS
-// headaches and hides the upstream from the browser.
-//
-// This used to call Media's tenant API directly, forwarding the visitor's JWT for Media
-// to identify them by. That was wrong: the JWT names the account by its NETWORK id,
-// while the user_id Media stores for these pastes is Live's own — different numbers for
-// the same person, so a comment posted here was filed under whichever unrelated Live
-// account happened to hold that number. Live resolves the visitor against its own
-// accounts before writing, which is a thing only Live can do, and this inherits its
-// author names and permission checks rather than reimplementing them.
+// ── Paste.OpenVibe → OpenVibe.Community proxy ────────────────
+// The paste front-end talks to /api/pastes/* on this gateway; we proxy server-side to OpenVibe.Community,
+// which owns pastes (roadmap Wave 5). The visitor's Network JWT goes along: Community verifies it and
+// files writes under the person's canonical subject, so no site has to translate ids any more. The
+// client address goes along too, for Community's anonymous-write limits.
app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, res) => {
app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, res) => {
try {
try {
- const target = `${config.liveUrl}/api/pastes${req.url}`;
+ const target = `${config.communityUrl}/api/pastes${req.url}`;
const fetchOpts = { method: req.method, headers: {} };
const fetchOpts = { method: req.method, headers: {} };
- // Forward the visitor's JWT — Live verifies it and maps it to a local account.
+ // Forward the visitor's JWT — Community verifies it and maps it to their subject.
const userToken = extractToken(req);
const userToken = extractToken(req);
if (userToken) fetchOpts.headers['Authorization'] = `Bearer ${userToken}`;
if (userToken) fetchOpts.headers['Authorization'] = `Bearer ${userToken}`;
- // Live rate-limits, bans and records by client address. Without this every
+ // Community rate-limits anonymous writes by client address. Without this every
// visitor arriving through this gateway would share one bucket and one identity.
// visitor arriving through this gateway would share one bucket and one identity.
if (req.ip) fetchOpts.headers['X-Forwarded-For'] = req.ip;
if (req.ip) fetchOpts.headers['X-Forwarded-For'] = req.ip;
if (req.method !== 'GET' && req.method !== 'HEAD' && req.body && Object.keys(req.body).length) {
if (req.method !== 'GET' && req.method !== 'HEAD' && req.body && Object.keys(req.body).length) {
@@ -165,7 +158,7 @@ app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, re
@@ -165,7 +158,7 @@ app.use('/api/pastes', rateLimit({ windowMs: 60_000, max: 120 }), async (req, re
return res.send(raw);
return res.send(raw);
} catch (err) {
} catch (err) {
console.error('[PasteProxy]', err.message);
console.error('[PasteProxy]', err.message);
- return res.status(502).json({ error: 'Could not reach the media service' });
+ return res.status(502).json({ error: 'Could not reach the paste service' });
}
}
});
});