// ========================================== // Switchboard Core — SDK: API Domain Namespaces // ========================================== // Namespaced domain clients wrapping restClient. // Each domain is a plain object with typed methods. // // Factory: createDomains(restClient) // ========================================== /** * Build query string from an options object. Skips null/undefined values. * @param {object} [opts] * @returns {string} — '' or '?key=val&...' */ function _qs(opts) { if (!opts) return ''; const p = new URLSearchParams(); for (const [k, v] of Object.entries(opts)) { if (v != null) p.set(k, String(v)); } const s = p.toString(); return s ? '?' + s : ''; } /** * Standard CRUD factory. * @param {object} rc — restClient * @param {string} base — e.g. '/api/v1/channels' * @returns {object} — { list, get, create, update, del } */ function crud(rc, base) { return { list: (opts) => rc.get(base + _qs(opts)), get: (id) => rc.get(`${base}/${id}`), create: (data) => rc.post(base, data), update: (id, data) => rc.put(`${base}/${id}`, data), del: (id) => rc.del(`${base}/${id}`), }; } /** * Create all 18 domain namespaces. * * @param {object} restClient * @returns {object} api — the full sw.api object */ export function createDomains(restClient) { const rc = restClient; return { // ── Escape hatches (raw restClient) ───── get: rc.get.bind(rc), post: rc.post.bind(rc), put: rc.put.bind(rc), patch: rc.patch.bind(rc), del: rc.del.bind(rc), stream: rc.stream.bind(rc), upload: rc.upload.bind(rc), // ── 1. Auth ───────────────────────────── auth: { login: (login, password) => rc.post('/api/v1/auth/login', { login, password }), register: (username, email, password) => rc.post('/api/v1/auth/register', { username, email, password }), refresh: (refreshToken) => rc.post('/api/v1/auth/refresh', { refresh_token: refreshToken }), logout: (refreshToken) => rc.post('/api/v1/auth/logout', { refresh_token: refreshToken }), }, // ── 2. Connections ──────────────────── connections: { ...crud(rc, '/api/v1/connections'), resolve: (type, name) => rc.get('/api/v1/connections/resolve' + _qs({ type, name })), }, // ── 3. Connection Types ──────────────── connectionTypes: { list: () => rc.get('/api/v1/connection-types'), }, // ── 11. Notifications ────────────────── notifications: { list: (opts) => rc.get('/api/v1/notifications' + _qs(opts)), unreadCount: () => rc.get('/api/v1/notifications/unread-count'), markRead: (id) => rc.patch(`/api/v1/notifications/${id}/read`, {}), markAllRead: () => rc.post('/api/v1/notifications/mark-all-read', {}), prefs: () => rc.get('/api/v1/notifications/preferences'), setPref: (type, data) => rc.put(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`, data), delPref: (type) => rc.del(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`), del: (id) => rc.del(`/api/v1/notifications/${id}`), }, // ── 12. Extensions ───────────────────── extensions: { list: (opts) => rc.get('/api/v1/extensions' + _qs(opts)), get: (id) => rc.get(`/api/v1/extensions/${id}`), settings: (id) => rc.get(`/api/v1/extensions/${id}/settings`), }, // ── 13. Profile ──────────────────────── profile: { get: () => rc.get('/api/v1/profile'), update: (data) => rc.put('/api/v1/profile', data), avatar: (base64) => rc.post('/api/v1/profile/avatar', { image: base64 }), deleteAvatar:() => rc.del('/api/v1/profile/avatar'), password: (current, pw) => rc.post('/api/v1/profile/password', { current_password: current, new_password: pw }), settings: () => rc.get('/api/v1/settings'), updateSettings: (data) => rc.put('/api/v1/settings', data), permissions: () => rc.get('/api/v1/profile/permissions'), }, // ── 14. Teams ────────────────────────── teams: { mine: () => rc.get('/api/v1/teams/mine'), get: (id) => rc.get(`/api/v1/teams/${id}`), update: (id, data) => rc.put(`/api/v1/teams/${id}`, data), members: (id) => rc.get(`/api/v1/teams/${id}/members`), addMember: (id, userId, role) => rc.post(`/api/v1/teams/${id}/members`, { user_id: userId, role }), updateMember:(id, memberId, role) => rc.put(`/api/v1/teams/${id}/members/${memberId}`, { role }), removeMember:(id, memberId) => rc.del(`/api/v1/teams/${id}/members/${memberId}`), groups: (id) => rc.get(`/api/v1/teams/${id}/groups`), // Team connections connections: (id) => rc.get(`/api/v1/teams/${id}/connections`), createConnection: (id, data) => rc.post(`/api/v1/teams/${id}/connections`, data), updateConnection: (id, cid, data) => rc.put(`/api/v1/teams/${id}/connections/${cid}`, data), deleteConnection: (id, cid) => rc.del(`/api/v1/teams/${id}/connections/${cid}`), audit: (id, opts) => rc.get(`/api/v1/teams/${id}/audit` + _qs(opts)), auditActions:(id) => rc.get(`/api/v1/teams/${id}/audit/actions`), // Team workflows workflows: (id, opts) => rc.get(`/api/v1/teams/${id}/workflows` + _qs(opts)), createWorkflow: (id, data) => rc.post(`/api/v1/teams/${id}/workflows`, data), getWorkflow: (id, wfId) => rc.get(`/api/v1/teams/${id}/workflows/${wfId}`), updateWorkflow: (id, wfId, data) => rc.put(`/api/v1/teams/${id}/workflows/${wfId}`, data), deleteWorkflow: (id, wfId) => rc.del(`/api/v1/teams/${id}/workflows/${wfId}`), publishWorkflow: (id, wfId) => rc.post(`/api/v1/teams/${id}/workflows/${wfId}/publish`, {}), // Team workflow stages (v0.37.15 — FE wiring for existing BE routes) workflowStages: (id, wfId) => rc.get(`/api/v1/teams/${id}/workflows/${wfId}/stages`), createWorkflowStage: (id, wfId, data) => rc.post(`/api/v1/teams/${id}/workflows/${wfId}/stages`, data), updateWorkflowStage: (id, wfId, sid, data) => rc.put(`/api/v1/teams/${id}/workflows/${wfId}/stages/${sid}`, data), deleteWorkflowStage: (id, wfId, sid) => rc.del(`/api/v1/teams/${id}/workflows/${wfId}/stages/${sid}`), reorderWorkflowStages: (id, wfId, ids) => rc.patch(`/api/v1/teams/${id}/workflows/${wfId}/stages/reorder`, { ordered_ids: ids }), // Adopt global workflows (v0.3.6) availableWorkflows: (id) => rc.get(`/api/v1/teams/${id}/workflows/available`), adoptWorkflow: (id, wfId) => rc.post(`/api/v1/teams/${id}/workflows/${wfId}/adopt`, {}), }, // ── 9. Workflows ─────────────────────── workflows: { ...crud(rc, '/api/v1/workflows'), update: (id, data) => rc.patch(`/api/v1/workflows/${id}`, data), stages: (id) => rc.get(`/api/v1/workflows/${id}/stages`), instances: (id, opts) => rc.get(`/api/v1/workflows/${id}/instances` + _qs(opts)), advance: (id, data) => rc.post(`/api/v1/workflows/${id}/advance`, data), reject: (id, data) => rc.post(`/api/v1/workflows/${id}/reject`, data), cancel: (channelId) => rc.post(`/api/v1/channels/${channelId}/workflow/cancel`, {}), }, // ── 10. Surfaces ────────────────────── surfaces: { list: (opts) => rc.get('/api/v1/surfaces' + _qs(opts)), get: (id) => rc.get(`/api/v1/surfaces/${id}`), install: (id) => rc.post(`/api/v1/surfaces/${id}/install`, {}), }, // ── 18. Admin ────────────────────────── admin: { stats: () => rc.get('/api/v1/admin/stats'), users: { list: (opts) => rc.get('/api/v1/admin/users' + _qs(opts)), create: (data) => rc.post('/api/v1/admin/users', data), del: (id) => rc.del(`/api/v1/admin/users/${id}`), resetPassword: (id, pw) => rc.post(`/api/v1/admin/users/${id}/reset-password`, { password: pw }), updateRole: (id, isAdmin) => rc.put(`/api/v1/admin/users/${id}/role`, { is_admin: isAdmin }), toggleActive: (id, active, opts) => rc.put(`/api/v1/admin/users/${id}/active`, { is_active: active, ...opts }), permissions: (id) => rc.get(`/api/v1/admin/users/${id}/permissions`), resetVault: (id) => rc.post(`/api/v1/admin/users/${id}/vault/reset`, {}), }, settings: { get: () => rc.get('/api/v1/admin/settings'), update: (key, val) => rc.put(`/api/v1/admin/settings/${key}`, val), public: () => rc.get('/api/v1/settings/public'), }, teams: { list: () => rc.get('/api/v1/admin/teams'), get: (id) => rc.get(`/api/v1/admin/teams/${id}`), create: (data) => rc.post('/api/v1/admin/teams', data), update: (id, data) => rc.put(`/api/v1/admin/teams/${id}`, data), del: (id) => rc.del(`/api/v1/admin/teams/${id}`), members: (id) => rc.get(`/api/v1/admin/teams/${id}/members`), addMember: (id, userId, role) => rc.post(`/api/v1/admin/teams/${id}/members`, { user_id: userId, role }), updateMember: (id, mid, role) => rc.put(`/api/v1/admin/teams/${id}/members/${mid}`, { role }), removeMember: (id, mid) => rc.del(`/api/v1/admin/teams/${id}/members/${mid}`), }, groups: { list: () => rc.get('/api/v1/admin/groups'), get: (id) => rc.get(`/api/v1/admin/groups/${id}`), create: (data) => rc.post('/api/v1/admin/groups', data), update: (id, data) => rc.put(`/api/v1/admin/groups/${id}`, data), del: (id) => rc.del(`/api/v1/admin/groups/${id}`), members: (id) => rc.get(`/api/v1/admin/groups/${id}/members`), addMember: (id, userId) => rc.post(`/api/v1/admin/groups/${id}/members`, { user_id: userId }), removeMember: (id, userId) => rc.del(`/api/v1/admin/groups/${id}/members/${userId}`), }, grants: { get: (type, id) => rc.get(`/api/v1/admin/grants/${type}/${id}`), set: (type, id, scope, groups) => rc.put(`/api/v1/admin/grants/${type}/${id}`, { grant_scope: scope, granted_groups: groups || [] }), del: (type, id) => rc.del(`/api/v1/admin/grants/${type}/${id}`), }, permissions: { list: () => rc.get('/api/v1/admin/permissions'), }, audit: { list: (opts) => rc.get('/api/v1/admin/audit' + _qs(opts)), actions: () => rc.get('/api/v1/admin/audit/actions'), }, storage: { status: () => rc.get('/api/v1/admin/storage/status'), }, vault: { status: () => rc.get('/api/v1/admin/vault/status'), }, notifications: { testEmail: () => rc.post('/api/v1/admin/notifications/test-email', {}), broadcast: (data) => rc.post('/api/v1/admin/notifications/broadcast', data), }, extensions: { list: () => rc.get('/api/v1/admin/extensions'), create: (data) => rc.post('/api/v1/admin/extensions', data), update: (id, data) => rc.put(`/api/v1/admin/extensions/${id}`, data), del: (id) => rc.del(`/api/v1/admin/extensions/${id}`), }, // v0.38.1: Global connections connections: crud(rc, '/api/v1/admin/connections'), packages: { list: (opts) => rc.get('/api/v1/admin/packages' + _qs(opts)), get: (id) => rc.get(`/api/v1/admin/packages/${id}`), install: (file) => rc.upload('/api/v1/admin/packages/install', file), enable: (id) => rc.put(`/api/v1/admin/packages/${id}/enable`, {}), disable: (id) => rc.put(`/api/v1/admin/packages/${id}/disable`, {}), del: (id) => rc.del(`/api/v1/admin/packages/${id}`), settings: (id) => rc.get(`/api/v1/admin/packages/${id}/settings`), updateSettings: (id, data) => rc.put(`/api/v1/admin/packages/${id}/settings`, data), dependencies: (id) => rc.get(`/api/v1/admin/packages/${id}/dependencies`), // v0.38.2 consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`), // v0.38.2 update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file), // v0.5.4 exportPkg: (id) => `/api/v1/admin/packages/${id}/export`, // v0.5.4 (URL for window.open) registry: () => rc.get('/api/v1/admin/packages/registry'), registryInstall: (url) => rc.post('/api/v1/admin/packages/registry/install', { url }), // v0.5.0: Extension permissions permissions: (id) => rc.get(`/api/v1/admin/extensions/${id}/permissions`), grantPerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/grant`, {}), revokePerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/revoke`, {}), grantAllPerms: (id) => rc.post(`/api/v1/admin/extensions/${id}/permissions/grant-all`, {}), }, // v0.38.2: Full dependency graph dependencies: { list: () => rc.get('/api/v1/admin/dependencies'), }, surfaces: { list: () => rc.get('/api/v1/admin/surfaces'), get: (id) => rc.get(`/api/v1/admin/surfaces/${id}`), install: (file) => rc.upload('/api/v1/admin/surfaces/install', file), enable: (id) => rc.put(`/api/v1/admin/surfaces/${id}/enable`, {}), disable: (id) => rc.put(`/api/v1/admin/surfaces/${id}/disable`, {}), del: (id) => rc.del(`/api/v1/admin/surfaces/${id}`), }, // v0.6.1: Backup/Restore backup: { create: (opts) => rc.post('/api/v1/admin/backup' + _qs(opts)), list: () => rc.get('/api/v1/admin/backups'), download: (name) => `/api/v1/admin/backups/${name}`, del: (name) => rc.del(`/api/v1/admin/backups/${name}`), restore: (file) => rc.upload('/api/v1/admin/restore', file), }, }, // ── Users ────────────────────────────── users: { search: (q) => rc.get('/api/v1/users/search' + _qs({ q })), }, presence: { heartbeat: () => rc.post('/api/v1/presence/heartbeat', {}), }, groups: { mine: () => rc.get('/api/v1/groups/mine'), }, health: () => rc.get('/api/v1/health'), // ── Extension-scoped client ────────── /** * Scoped API client for an extension's surface routes. * @param {string} packageId — package slug (e.g. 'tasks') * @returns {object} — { get, post, put, del, upload } */ ext(packageId) { const base = '/s/' + packageId + '/api'; return { get: (path, opts) => rc.get(base + path + _qs(opts)), post: (path, data) => rc.post(base + path, data), put: (path, data) => rc.put(base + path, data), del: (path) => rc.del(base + path), upload: (path, file) => rc.upload(base + path, file), }; }, }; }