openvibers/openvibe.services Commits
Services: OpenVibe.Zone's zones in the console, with delete for project admins (contracts 0.173.0) (#60)
Changes
@@@ -7,11 +7,11 @@"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)","port": 4930,"domain": "openvibe.services",-- "contracts": "openvibe-contracts v0.154.0",++ "contracts": "openvibe-contracts v0.173.0","sdk": "openvibe-sdk v0.44.0","shared": "openvibe-shared v4.1.3","packages": [-- "openvibe-contracts v0.154.0",++ "openvibe-contracts v0.173.0","openvibe-sdk v0.44.0","openvibe-shared v4.1.3"],@@@ -16,7 +16,7 @@"express-rate-limit": "^7.5.0","helmet": "^8.1.0","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3","pg": "^8.23.0"@@@ -890,9 +890,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.170.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",-- "integrity": "sha512-EpOUiqFSYBZ1p7aQTek1IYyHcbD9QT0pdr492+teWvZf425xXSMu7+DQsnzgMP8JdTmdS2VMxoirrBwMUGxjrw==",++ "version": "0.173.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0",++ "integrity": "sha512-6zHbU9zghIYDd3PPGtgMTsIgxrWwsoBHO/rvuLW1rghr6kRK6VwCmSMZRcjQbcPODPH1XrUAFuLg/T1K4nOCMQ==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -27,7 +27,7 @@"express-rate-limit": "^7.5.0","helmet": "^8.1.0","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3","pg": "^8.23.0"@@@ -51,6 +51,9 @@ const KIND_ACTIONS = {'space.site': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },'space.deploy': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },'space.domain': { admins: true, actions: () => [['delete', 'Delete…']] },++ // A zone (OpenVibe.Zone, contracts 0.173.0): delete it and every object in it, for a project admin. The default zone++ // goes only with its environment, so it offers nothing.++ 'zone.object-zone': { admins: true, actions: (x) => (['deleting', 'deleted'].includes(x.state) || /^default \(/.test(String(x.name || '')) ? [] : [['delete', 'Delete…']]) },default: { actions: (x) => [x.state === 'paused' ? ['resume', 'Resume'] : ['suspend', 'Suspend'], ['delete', 'Delete…']] },};@@@ -66,6 +69,7 @@ const RESOURCES_PER_PAGE = 50;const STATE_KIND = {ready: 'ok', active: 'ok', published: 'ok', live: 'ok', enabled: 'ok', online: 'ok', open: 'ok',draft: 'warn', queued: 'warn', pending: 'warn', paused: 'warn', processing: 'warn', deprecated: 'warn', offline: 'warn',++ provisioning: 'warn', deleting: 'warn',failed: 'bad', revoked: 'bad', deleted: 'bad', disabled: 'bad', archived: 'bad', suspended: 'bad',};const stateBadge = (s) => badge(s, STATE_KIND[s] || '');@@@ -17,14 +17,15 @@ const { createAuthorities } = require('../server/authorities');const { check, done } = require('./helpers/app');const quiet = { warn() {}, log() {}, error() {} };--// Static hosting's index is Space's since contracts 0.150.0 (the engine moved from Host, plan T20).--const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'media', 'network', 'openre', 'space', 'watch'];++// Static hosting's index is Space's since contracts 0.150.0 (the engine moved from Host, plan T20); Zone's zones since 0.173.0.++const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'media', 'network', 'openre', 'space', 'watch', 'zone'];async function main() {-- await check('the pin lists eleven active <id>.resource.read capabilities, ten of them authorities', () => {++ await check('the pin lists twelve active <id>.resource.read capabilities, eleven of them authorities', () => {const ids = contracts.capabilities.manifests.filter((c) => /^([a-z][a-z0-9-]{0,31})\.resource\.read$/.test(c.id) && c.status === 'active').map((c) => c.id).sort();assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read',-- 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'space.resource.read', 'watch.resource.read']);++ 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'space.resource.read', 'watch.resource.read',++ 'zone.resource.read']);assert.strictEqual(contracts.capabilities.get('host.resource.read').status, 'retired', 'Host serves no hosting index since 0.150.0');assert.strictEqual(contracts.capabilities.get('codes.resource.read').status, 'retired', 'Codes owns no resources since 0.113.0');// services.resource.read is Services' own id; Services is never its own authority (the next check).@@@ -45,6 +46,7 @@ async function main() {chat: 'http://127.0.0.1:4400',openre: 'http://127.0.0.1:4500',community: 'http://127.0.0.1:4200',++ zone: 'http://127.0.0.1:5050',};for (const [id, origin] of Object.entries(expected)) {const a = registry.get(id);@@@ -88,10 +88,10 @@ function registryOf(list) {const prj = ids.newId('project');const object = resources.format({ service: 'media', project_id: prj, type: 'object', id: ids.newId('media') });-- await check('only an authority with an active <id>.resource.control takes control requests (Watch 0.165, Events 0.168, Actor and Media 0.169, Space 0.170)', async () => {++ await check('only an authority with an active <id>.resource.control takes control requests (Watch 0.165, Events 0.168, Actor and Media 0.169, Space 0.170, Zone 0.173)', async () => {const real = createAuthorities(t.config, { log: { warn() {} } });assert.ok(real.size() > 0);-- const TAKES = { watch: 'watch.resource.control', events: 'events.resource.control', actor: 'actor.resource.control', media: 'media.resource.control', space: 'space.resource.control' };++ const TAKES = { watch: 'watch.resource.control', events: 'events.resource.control', actor: 'actor.resource.control', media: 'media.resource.control', space: 'space.resource.control', zone: 'zone.resource.control' };for (const a of real.list()) assert.strictEqual(a.control, TAKES[a.id] || null, `${a.id}: the control capability in the pinned contracts`);});
@@ -7,11 +7,11 @@
@@ -7,11 +7,11 @@
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"port": 4930,
"port": 4930,
"domain": "openvibe.services",
"domain": "openvibe.services",
- "contracts": "openvibe-contracts v0.154.0",
+ "contracts": "openvibe-contracts v0.173.0",
"sdk": "openvibe-sdk v0.44.0",
"sdk": "openvibe-sdk v0.44.0",
"shared": "openvibe-shared v4.1.3",
"shared": "openvibe-shared v4.1.3",
"packages": [
"packages": [
- "openvibe-contracts v0.154.0",
+ "openvibe-contracts v0.173.0",
"openvibe-sdk v0.44.0",
"openvibe-sdk v0.44.0",
"openvibe-shared v4.1.3"
"openvibe-shared v4.1.3"
],
],
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3",
"pg": "^8.23.0"
"pg": "^8.23.0"
@@ -890,9 +890,9 @@
@@ -890,9 +890,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.170.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",
- "integrity": "sha512-EpOUiqFSYBZ1p7aQTek1IYyHcbD9QT0pdr492+teWvZf425xXSMu7+DQsnzgMP8JdTmdS2VMxoirrBwMUGxjrw==",
+ "version": "0.173.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0",
+ "integrity": "sha512-6zHbU9zghIYDd3PPGtgMTsIgxrWwsoBHO/rvuLW1rghr6kRK6VwCmSMZRcjQbcPODPH1XrUAFuLg/T1K4nOCMQ==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -27,7 +27,7 @@
@@ -27,7 +27,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.170.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.173.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.1.3",
"pg": "^8.23.0"
"pg": "^8.23.0"
@@ -51,6 +51,9 @@ const KIND_ACTIONS = {
@@ -51,6 +51,9 @@ const KIND_ACTIONS = {
'space.site': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },
'space.site': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },
'space.deploy': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },
'space.deploy': { admins: true, actions: (x) => (x.state === 'deleted' ? [] : [['delete', 'Delete…']]) },
'space.domain': { admins: true, actions: () => [['delete', 'Delete…']] },
'space.domain': { admins: true, actions: () => [['delete', 'Delete…']] },
+ // A zone (OpenVibe.Zone, contracts 0.173.0): delete it and every object in it, for a project admin. The default zone
+ // goes only with its environment, so it offers nothing.
+ 'zone.object-zone': { admins: true, actions: (x) => (['deleting', 'deleted'].includes(x.state) || /^default \(/.test(String(x.name || '')) ? [] : [['delete', 'Delete…']]) },
default: { actions: (x) => [x.state === 'paused' ? ['resume', 'Resume'] : ['suspend', 'Suspend'], ['delete', 'Delete…']] },
default: { actions: (x) => [x.state === 'paused' ? ['resume', 'Resume'] : ['suspend', 'Suspend'], ['delete', 'Delete…']] },
};
};
@@ -66,6 +69,7 @@ const RESOURCES_PER_PAGE = 50;
@@ -66,6 +69,7 @@ const RESOURCES_PER_PAGE = 50;
const STATE_KIND = {
const STATE_KIND = {
ready: 'ok', active: 'ok', published: 'ok', live: 'ok', enabled: 'ok', online: 'ok', open: 'ok',
ready: 'ok', active: 'ok', published: 'ok', live: 'ok', enabled: 'ok', online: 'ok', open: 'ok',
draft: 'warn', queued: 'warn', pending: 'warn', paused: 'warn', processing: 'warn', deprecated: 'warn', offline: 'warn',
draft: 'warn', queued: 'warn', pending: 'warn', paused: 'warn', processing: 'warn', deprecated: 'warn', offline: 'warn',
+ provisioning: 'warn', deleting: 'warn',
failed: 'bad', revoked: 'bad', deleted: 'bad', disabled: 'bad', archived: 'bad', suspended: 'bad',
failed: 'bad', revoked: 'bad', deleted: 'bad', disabled: 'bad', archived: 'bad', suspended: 'bad',
};
};
const stateBadge = (s) => badge(s, STATE_KIND[s] || '');
const stateBadge = (s) => badge(s, STATE_KIND[s] || '');
@@ -17,14 +17,15 @@ const { createAuthorities } = require('../server/authorities');
@@ -17,14 +17,15 @@ const { createAuthorities } = require('../server/authorities');
const { check, done } = require('./helpers/app');
const { check, done } = require('./helpers/app');
const quiet = { warn() {}, log() {}, error() {} };
const quiet = { warn() {}, log() {}, error() {} };
-// Static hosting's index is Space's since contracts 0.150.0 (the engine moved from Host, plan T20).
-const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'media', 'network', 'openre', 'space', 'watch'];
+// Static hosting's index is Space's since contracts 0.150.0 (the engine moved from Host, plan T20); Zone's zones since 0.173.0.
+const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'media', 'network', 'openre', 'space', 'watch', 'zone'];
async function main() {
async function main() {
- await check('the pin lists eleven active <id>.resource.read capabilities, ten of them authorities', () => {
+ await check('the pin lists twelve active <id>.resource.read capabilities, eleven of them authorities', () => {
const ids = contracts.capabilities.manifests.filter((c) => /^([a-z][a-z0-9-]{0,31})\.resource\.read$/.test(c.id) && c.status === 'active').map((c) => c.id).sort();
const ids = contracts.capabilities.manifests.filter((c) => /^([a-z][a-z0-9-]{0,31})\.resource\.read$/.test(c.id) && c.status === 'active').map((c) => c.id).sort();
assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read',
assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read',
- 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'space.resource.read', 'watch.resource.read']);
+ 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'space.resource.read', 'watch.resource.read',
+ 'zone.resource.read']);
assert.strictEqual(contracts.capabilities.get('host.resource.read').status, 'retired', 'Host serves no hosting index since 0.150.0');
assert.strictEqual(contracts.capabilities.get('host.resource.read').status, 'retired', 'Host serves no hosting index since 0.150.0');
assert.strictEqual(contracts.capabilities.get('codes.resource.read').status, 'retired', 'Codes owns no resources since 0.113.0');
assert.strictEqual(contracts.capabilities.get('codes.resource.read').status, 'retired', 'Codes owns no resources since 0.113.0');
// services.resource.read is Services' own id; Services is never its own authority (the next check).
// services.resource.read is Services' own id; Services is never its own authority (the next check).
@@ -45,6 +46,7 @@ async function main() {
@@ -45,6 +46,7 @@ async function main() {
chat: 'http://127.0.0.1:4400',
chat: 'http://127.0.0.1:4400',
openre: 'http://127.0.0.1:4500',
openre: 'http://127.0.0.1:4500',
community: 'http://127.0.0.1:4200',
community: 'http://127.0.0.1:4200',
+ zone: 'http://127.0.0.1:5050',
};
};
for (const [id, origin] of Object.entries(expected)) {
for (const [id, origin] of Object.entries(expected)) {
const a = registry.get(id);
const a = registry.get(id);
@@ -88,10 +88,10 @@ function registryOf(list) {
@@ -88,10 +88,10 @@ function registryOf(list) {
const prj = ids.newId('project');
const prj = ids.newId('project');
const object = resources.format({ service: 'media', project_id: prj, type: 'object', id: ids.newId('media') });
const object = resources.format({ service: 'media', project_id: prj, type: 'object', id: ids.newId('media') });
- await check('only an authority with an active <id>.resource.control takes control requests (Watch 0.165, Events 0.168, Actor and Media 0.169, Space 0.170)', async () => {
+ await check('only an authority with an active <id>.resource.control takes control requests (Watch 0.165, Events 0.168, Actor and Media 0.169, Space 0.170, Zone 0.173)', async () => {
const real = createAuthorities(t.config, { log: { warn() {} } });
const real = createAuthorities(t.config, { log: { warn() {} } });
assert.ok(real.size() > 0);
assert.ok(real.size() > 0);
- const TAKES = { watch: 'watch.resource.control', events: 'events.resource.control', actor: 'actor.resource.control', media: 'media.resource.control', space: 'space.resource.control' };
+ const TAKES = { watch: 'watch.resource.control', events: 'events.resource.control', actor: 'actor.resource.control', media: 'media.resource.control', space: 'space.resource.control', zone: 'zone.resource.control' };
for (const a of real.list()) assert.strictEqual(a.control, TAKES[a.id] || null, `${a.id}: the control capability in the pinned contracts`);
for (const a of real.list()) assert.strictEqual(a.control, TAKES[a.id] || null, `${a.id}: the control capability in the pinned contracts`);
});
});