openvibers/openvibe.ai Commits

A page that is not here goes to the same path on Exchange (#508)

OP OpenVibers committed 15 h ago · fd11770e55 · 2 files, +10 −7

A browser asking AI for a page it does not have was sent to the Exchange home, so a missing page answered 200 (a soft 404) and the release browser check failed AI every run. It now goes to the same path on Exchange: a stale link lands on the page of that name, and a page that never existed is Exchange's honest 404. API and session paths answer as before.

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 7cb5c05..6ddb28b 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -361,12 +361,14 @@ function createApp({ choices = null, searchFetch = null, config, db, registry, p
res.type('text/plain').send(`User-agent: *\nAllow: /llms.txt\nAllow: /api/v1/creations\nAllow: /api/v1/creations.md\nDisallow: /\nDisallow: /runs\nDisallow: /console\nDisallow: /auth/\nDisallow: /api/\n\n# The human site is OpenVibe.Exchange; the API stays here.\nSitemap: ${EXCHANGE}/sitemap.xml\n`);
});
-- // A browser asking for a page that is not here goes to the Exchange home: the human site moved, and a
-- // stale link should land on the product, not a 404. An API or session path still answers as it did.
++ // A browser asking for a page that is not here goes to the same path on Exchange: the human site moved, so a
++ // stale link lands on the page of that name, and a page that never existed is Exchange's honest 404 (11-12: sending
++ // every miss to the home was a soft 404 the release browser check failed on). An API or session path still answers
++ // as it did.
const MACHINE_PATH = /^\/(?:api|v1|internal|metrics|shared|release\.json|release-metrics|auth|console|runs|favicon\.ico|robots\.txt)(?:\/|$)/;
app.use((req, res) => {
if (req.method === 'GET' && !MACHINE_PATH.test(req.path) && req.accepts(['application/json', 'text/html']) === 'text/html') {
-- return res.redirect(301, `${EXCHANGE}/`);
++ return res.redirect(301, `${EXCHANGE}${req.originalUrl.startsWith('/') ? req.originalUrl : '/'}`);
}
return http.sendProblem(res, 404, 'ai.not_found', { detail: `no route ${req.method} ${req.path}`, ctx: req.ov });
});
ddiff --git a/test/home.test.js b/test/home.test.js
iindex b03110a..e53f0e3 100644
---- a/test/home.test.js
++++ b/test/home.test.js
@@@ -1,7 +1,7 @@
'use strict';
// The public pages of ai.openvibe.services moved to openvibe.exchange on 2026-11-07. This origin keeps the API,
// a run's receipt and the operator console; every human page answers 301 on an exact map (an unknown human path
--// goes to the Exchange home), and no API path is ever redirected. The text/plain API index for curl stays.
++// goes to the same path on Exchange), and no API path is ever redirected. The text/plain API index for curl stays.
const assert = require('assert');
const fs = require('fs');
const path = require('path');
@@@ -69,9 +69,10 @@ t.test('/llms.txt stays on AI and carries the creation catalogue', async () => {
assert.ok(r.text.includes('Write a blog post'), 'lists a live creation');
});
--t.test('an unknown human page goes to the Exchange home; an unknown API path stays a problem document', async () => {
-- await moved('/prices-and-latency', '/');
-- await moved('/some-old-post', '/');
++t.test('an unknown human page goes to the same path on Exchange (its page, or its honest 404); an unknown API path stays a problem document', async () => {
++ await moved('/prices-and-latency', '/prices-and-latency');
++ await moved('/some-old-post?ref=x', '/some-old-post?ref=x');
++ await moved('/__ovcheck-404', '/__ovcheck-404');
for (const p of ['/api/nope', '/v1/nope', '/api/v1/nope']) {
const r = await request(h.base, 'GET', p, { headers: { Accept: BROWSER }, redirect: 'manual' });
assert.strictEqual(r.status, 404, p);