openvibers/openvibe.studio Commits
Serve /release.json; LightCraft's optional Chinese font answers 204 (#4)
Changes
@@@ -60,6 +60,11 @@ server {# (STUDIO_BUNDLE_ROOT=/var/lib/openvibe-studio/apps, filled by scripts/fetch-bundles.sh) so a deploy's# git pull never touches them. The types are listed here because the module is only compiled by the# browser when it is served as application/wasm.++ # LightCraft asks for its Simplified Chinese face (Noto Sans CJK SC, 16 MB) on every open and runs without it++ # (Simplified-only characters show as boxes). Shipping it would nearly triple the download (the module is 6.4 MB++ # brotli), so the bundle is built without it and this answers "no content", which the app reads as no font,++ # instead of a 404 in every visitor's console.++ location = /apps/light/lightcraft_zh_hans.otf { add_header Cache-Control "public, max-age=86400" always; return 204; }location /apps/ {root /var/lib/openvibe-studio;types {@@@ -51,6 +51,9 @@ function createApp(opts = {}) {'# OpenVibe.Studio\n\nPhoto and Light: two open-source creative apps (PhotoCraft, LightCraft) built from pinned release tags and served inside an OpenVibe account.\n\n- [Home](/)\n- [Photo](/photo)\n- [Light](/light)\n- [Attribution](/attribution)\n- [API docs](/openapi.json)\n'));app.get('/openapi.json', (_req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'openapi.json')));app.use('/api/v1/studio', createApi({ sso, media, pics }));++ // GET /release.json (ADR-016): the navbar's release watch polls it so open tabs learn of a new release (the 11-09++ // browser sweep found it answering 404 here).++ require('openvibe-shared/release').createRelease({ service: 'studio', root: path.join(__dirname, '..') }).mount(app);app.use('/shared', require('openvibe-shared/serve').handler());app.use('/apps', createBundleFiles({ root: config.bundleRoot }));app.use(express.static(path.join(__dirname, '..', 'public'), { index: false, maxAge: '1h' }));@@@ -184,5 +184,6 @@ test('the app catalogue, the attribution page and the small endpoints answer', aassert.equal((await fetch(s.base + '/api/health')).status, 200);assert.equal((await fetch(s.base + '/llms.txt')).status, 200);assert.equal((await fetch(s.base + '/robots.txt')).status, 200);++ { const r = await fetch(s.base + '/release.json'); assert.equal(r.status, 200, 'the navbar release watch polls /release.json'); assert.equal((await r.json()).service, 'studio'); }assert.equal((await fetch(s.base + '/nope')).status, 404);});
@@ -60,6 +60,11 @@ server {
@@ -60,6 +60,11 @@ server {
# (STUDIO_BUNDLE_ROOT=/var/lib/openvibe-studio/apps, filled by scripts/fetch-bundles.sh) so a deploy's
# (STUDIO_BUNDLE_ROOT=/var/lib/openvibe-studio/apps, filled by scripts/fetch-bundles.sh) so a deploy's
# git pull never touches them. The types are listed here because the module is only compiled by the
# git pull never touches them. The types are listed here because the module is only compiled by the
# browser when it is served as application/wasm.
# browser when it is served as application/wasm.
+ # LightCraft asks for its Simplified Chinese face (Noto Sans CJK SC, 16 MB) on every open and runs without it
+ # (Simplified-only characters show as boxes). Shipping it would nearly triple the download (the module is 6.4 MB
+ # brotli), so the bundle is built without it and this answers "no content", which the app reads as no font,
+ # instead of a 404 in every visitor's console.
+ location = /apps/light/lightcraft_zh_hans.otf { add_header Cache-Control "public, max-age=86400" always; return 204; }
location /apps/ {
location /apps/ {
root /var/lib/openvibe-studio;
root /var/lib/openvibe-studio;
types {
types {
@@ -51,6 +51,9 @@ function createApp(opts = {}) {
@@ -51,6 +51,9 @@ function createApp(opts = {}) {
'# OpenVibe.Studio\n\nPhoto and Light: two open-source creative apps (PhotoCraft, LightCraft) built from pinned release tags and served inside an OpenVibe account.\n\n- [Home](/)\n- [Photo](/photo)\n- [Light](/light)\n- [Attribution](/attribution)\n- [API docs](/openapi.json)\n'));
'# OpenVibe.Studio\n\nPhoto and Light: two open-source creative apps (PhotoCraft, LightCraft) built from pinned release tags and served inside an OpenVibe account.\n\n- [Home](/)\n- [Photo](/photo)\n- [Light](/light)\n- [Attribution](/attribution)\n- [API docs](/openapi.json)\n'));
app.get('/openapi.json', (_req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'openapi.json')));
app.get('/openapi.json', (_req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'openapi.json')));
app.use('/api/v1/studio', createApi({ sso, media, pics }));
app.use('/api/v1/studio', createApi({ sso, media, pics }));
+ // GET /release.json (ADR-016): the navbar's release watch polls it so open tabs learn of a new release (the 11-09
+ // browser sweep found it answering 404 here).
+ require('openvibe-shared/release').createRelease({ service: 'studio', root: path.join(__dirname, '..') }).mount(app);
app.use('/shared', require('openvibe-shared/serve').handler());
app.use('/shared', require('openvibe-shared/serve').handler());
app.use('/apps', createBundleFiles({ root: config.bundleRoot }));
app.use('/apps', createBundleFiles({ root: config.bundleRoot }));
app.use(express.static(path.join(__dirname, '..', 'public'), { index: false, maxAge: '1h' }));
app.use(express.static(path.join(__dirname, '..', 'public'), { index: false, maxAge: '1h' }));
@@ -184,5 +184,6 @@ test('the app catalogue, the attribution page and the small endpoints answer', a
@@ -184,5 +184,6 @@ test('the app catalogue, the attribution page and the small endpoints answer', a
assert.equal((await fetch(s.base + '/api/health')).status, 200);
assert.equal((await fetch(s.base + '/api/health')).status, 200);
assert.equal((await fetch(s.base + '/llms.txt')).status, 200);
assert.equal((await fetch(s.base + '/llms.txt')).status, 200);
assert.equal((await fetch(s.base + '/robots.txt')).status, 200);
assert.equal((await fetch(s.base + '/robots.txt')).status, 200);
+ { const r = await fetch(s.base + '/release.json'); assert.equal(r.status, 200, 'the navbar release watch polls /release.json'); assert.equal((await r.json()).service, 'studio'); }
assert.equal((await fetch(s.base + '/nope')).status, 404);
assert.equal((await fetch(s.base + '/nope')).status, 404);
});
});