import { tap, expect } from '@git.zone/tstest/tapbundle'; import { WorkHosterHandler } from '../ts/opsserver/handlers/workhoster.handler.js'; import * as plugins from '../ts/plugins.js'; import * as interfaces from '../ts_interfaces/index.js'; type TScope = interfaces.data.TApiTokenScope; const fireTypedRequest = async ( router: plugins.typedrequest.TypedRouter, method: string, request: Record, ) => { return await router.routeAndAddResponse({ method, request, response: {}, correlation: { id: `${method}-${Date.now()}-${Math.random().toString(16).slice(2)}`, phase: 'request', }, } as any, { localRequest: true, skipHooks: true }) as any; }; const makeApiTokenManager = (scopes: TScope[]) => { const token = { id: 'token-1', name: 'workhoster-test-token', scopes, createdBy: 'token-user', createdAt: Date.now(), expiresAt: null, lastUsedAt: null, enabled: true, } as interfaces.data.IStoredApiToken; return { validateToken: async (rawToken: string) => rawToken === 'valid-token' ? token : null, hasScope: (storedToken: interfaces.data.IStoredApiToken, scope: TScope) => storedToken.scopes.includes(scope), }; }; const makeRouteConfigManager = () => { const routes = new Map(); let nextRouteNumber = 1; return { routes, manager: { findApiRouteByExternalKey: (externalKey: string) => { return Array.from(routes.values()).find((route) => route.origin === 'api' && route.metadata?.externalKey === externalKey, ); }, createRoute: async ( route: interfaces.data.IDcRouterRouteConfig, createdBy: string, enabled = true, metadata?: interfaces.data.IRouteMetadata, ) => { const id = `route-${nextRouteNumber++}`; routes.set(id, { id, route, enabled, createdBy, createdAt: Date.now(), updatedAt: Date.now(), origin: 'api', metadata, }); return id; }, updateRoute: async ( id: string, patch: { route?: Partial; enabled?: boolean; metadata?: Partial; }, ) => { const storedRoute = routes.get(id); if (!storedRoute) return { success: false, message: 'Route not found' }; if (patch.route) { storedRoute.route = { ...storedRoute.route, ...patch.route } as interfaces.data.IDcRouterRouteConfig; } if (patch.enabled !== undefined) { storedRoute.enabled = patch.enabled; } if (patch.metadata) { storedRoute.metadata = { ...storedRoute.metadata, ...patch.metadata }; } storedRoute.updatedAt = Date.now(); return { success: true }; }, deleteRoute: async (id: string) => { const deleted = routes.delete(id); return deleted ? { success: true } : { success: false, message: 'Route not found' }; }, }, }; }; const setupHandler = (options: { scopes: TScope[]; dcRouterRef?: Record; }) => { const typedrouter = new plugins.typedrequest.TypedRouter(); const opsServerRef: any = { typedrouter, adminHandler: { adminIdentityGuard: { exec: async () => false, }, }, dcRouterRef: { options: {}, apiTokenManager: makeApiTokenManager(options.scopes), ...options.dcRouterRef, }, }; new WorkHosterHandler(opsServerRef); return { typedrouter, opsServerRef }; }; tap.test('WorkHosterHandler exposes capabilities and managed domains with workhosters:read', async () => { const { typedrouter } = setupHandler({ scopes: ['workhosters:read'], dcRouterRef: { options: { remoteIngressConfig: { enabled: true }, dnsScopes: ['example.com'], http3: { enabled: false }, }, routeConfigManager: {}, smartProxy: {}, emailDomainManager: {}, emailServer: {}, dnsManager: { listDomains: async () => [ { id: 'domain-1', name: 'example.com', source: 'dcrouter', authoritative: true }, { id: 'domain-2', name: 'provider.example', source: 'provider', providerId: 'cloudflare-1', authoritative: false }, ], toPublicDomain: (domainDoc: any) => ({ ...domainDoc, createdAt: 1, updatedAt: 1, createdBy: 'test', }), }, }, }); const capabilitiesResult = await fireTypedRequest(typedrouter, 'getGatewayCapabilities', { apiToken: 'valid-token', }); expect(capabilitiesResult.error).toBeUndefined(); expect(capabilitiesResult.response.capabilities.routes.idempotentSync).toEqual(true); expect(capabilitiesResult.response.capabilities.domains.read).toEqual(true); expect(capabilitiesResult.response.capabilities.certificates.export).toEqual(true); expect(capabilitiesResult.response.capabilities.email.inbound).toEqual(true); expect(capabilitiesResult.response.capabilities.remoteIngress.enabled).toEqual(true); expect(capabilitiesResult.response.capabilities.dns.authoritative).toEqual(true); expect(capabilitiesResult.response.capabilities.http3.enabled).toEqual(false); const domainsResult = await fireTypedRequest(typedrouter, 'getWorkHosterDomains', { apiToken: 'valid-token', }); expect(domainsResult.error).toBeUndefined(); expect(domainsResult.response.domains.length).toEqual(2); expect(domainsResult.response.domains[0].capabilities.canCreateSubdomains).toEqual(true); expect(domainsResult.response.domains[1].capabilities.canManageDnsRecords).toEqual(true); expect(domainsResult.response.domains[1].capabilities.canIssueCertificates).toEqual(true); expect(domainsResult.response.domains[1].capabilities.canHostEmail).toEqual(true); }); tap.test('WorkHosterHandler syncs WorkApp routes idempotently with workhosters:write', async () => { const routeConfig = makeRouteConfigManager(); const { typedrouter } = setupHandler({ scopes: ['workhosters:write'], dcRouterRef: { options: {}, routeConfigManager: routeConfig.manager, }, }); const ownership: interfaces.data.IWorkAppRouteOwnership = { workHosterType: 'onebox', workHosterId: 'box-1', workAppId: 'app-1', hostname: 'app.example.com', }; const createResult = await fireTypedRequest(typedrouter, 'syncWorkAppRoute', { apiToken: 'valid-token', ownership, route: { match: { ports: [443], domains: ['app.example.com'] }, action: { type: 'forward', targets: [{ host: '10.0.0.2', port: 8080 }], tls: { mode: 'terminate', certificate: 'auto' }, }, }, }); expect(createResult.error).toBeUndefined(); expect(createResult.response).toEqual({ success: true, action: 'created', routeId: 'route-1' }); expect(routeConfig.routes.size).toEqual(1); const createdRoute = routeConfig.routes.get('route-1')!; expect(createdRoute.createdBy).toEqual('token-user'); expect(createdRoute.route.name?.startsWith('workapp-onebox-box-1-app-1-app-example-com')).toEqual(true); expect(createdRoute.metadata).toEqual({ ownerType: 'workhoster', workHosterType: 'onebox', workHosterId: 'box-1', workAppId: 'app-1', externalKey: 'onebox:box-1:app-1:app.example.com', }); const updateResult = await fireTypedRequest(typedrouter, 'syncWorkAppRoute', { apiToken: 'valid-token', ownership, enabled: false, route: { name: 'updated-workapp-route', match: { ports: [443], domains: ['app.example.com'] }, action: { type: 'forward', targets: [{ host: '10.0.0.3', port: 3000 }], tls: { mode: 'terminate', certificate: 'auto' }, }, }, }); expect(updateResult.error).toBeUndefined(); expect(updateResult.response).toEqual({ success: true, action: 'updated', routeId: 'route-1' }); expect(routeConfig.routes.size).toEqual(1); expect(routeConfig.routes.get('route-1')?.enabled).toEqual(false); expect(routeConfig.routes.get('route-1')?.route.name).toEqual('updated-workapp-route'); expect(routeConfig.routes.get('route-1')?.route.action.targets?.[0].host).toEqual('10.0.0.3'); const deleteResult = await fireTypedRequest(typedrouter, 'syncWorkAppRoute', { apiToken: 'valid-token', ownership, delete: true, }); expect(deleteResult.error).toBeUndefined(); expect(deleteResult.response).toEqual({ success: true, action: 'deleted', routeId: 'route-1' }); expect(routeConfig.routes.size).toEqual(0); const unchangedResult = await fireTypedRequest(typedrouter, 'syncWorkAppRoute', { apiToken: 'valid-token', ownership, delete: true, }); expect(unchangedResult.error).toBeUndefined(); expect(unchangedResult.response).toEqual({ success: true, action: 'unchanged' }); }); tap.test('WorkHosterHandler rejects WorkApp route sync without workhosters:write', async () => { const routeConfig = makeRouteConfigManager(); const { typedrouter } = setupHandler({ scopes: ['workhosters:read'], dcRouterRef: { options: {}, routeConfigManager: routeConfig.manager, }, }); const result = await fireTypedRequest(typedrouter, 'syncWorkAppRoute', { apiToken: 'valid-token', ownership: { workHosterType: 'onebox', workHosterId: 'box-1', workAppId: 'app-1', hostname: 'app.example.com', }, delete: true, }); expect(result.error?.text).toEqual('insufficient scope'); expect(routeConfig.routes.size).toEqual(0); }); tap.test('WorkHosterHandler exposes and syncs WorkApp mail identities', async () => { const syncedRequests: Array<{ data: any; userId: string }> = []; const identity: interfaces.data.IWorkAppMailIdentity = { id: 'mail-1', externalKey: 'onebox:box-1:app-1:hello@example.com', ownership: { workHosterType: 'onebox', workHosterId: 'box-1', workAppId: 'app-1', }, address: 'hello@example.com', localPart: 'hello', domain: 'example.com', enabled: true, inbound: { enabled: true, targetHost: '10.0.0.2', targetPort: 2525, }, smtp: { enabled: true, username: 'workapp-user', }, createdAt: 1, updatedAt: 1, createdBy: 'token-user', }; const { typedrouter } = setupHandler({ scopes: ['workhosters:read', 'workhosters:write'], dcRouterRef: { options: {}, workAppMailManager: { listMailIdentities: async (filter: any) => filter.workAppId === 'app-1' ? [identity] : [], syncMailIdentity: async (data: any, userId: string) => { syncedRequests.push({ data, userId }); return { success: true, action: 'created', identity, smtpCredentials: { username: 'workapp-user', password: 'generated-password', }, }; }, }, }, }); const listResult = await fireTypedRequest(typedrouter, 'getWorkAppMailIdentities', { apiToken: 'valid-token', ownership: { workAppId: 'app-1' }, }); expect(listResult.error).toBeUndefined(); expect(listResult.response.identities).toEqual([identity]); const syncResult = await fireTypedRequest(typedrouter, 'syncWorkAppMailIdentity', { apiToken: 'valid-token', ownership: identity.ownership, localPart: 'hello', domain: 'example.com', inbound: identity.inbound, }); expect(syncResult.error).toBeUndefined(); expect(syncResult.response.success).toEqual(true); expect(syncResult.response.smtpCredentials.password).toEqual('generated-password'); expect(syncedRequests[0].userId).toEqual('token-user'); }); tap.test('WorkHosterHandler rejects WorkApp mail sync without workhosters:write', async () => { const { typedrouter } = setupHandler({ scopes: ['workhosters:read'], dcRouterRef: { options: {}, workAppMailManager: { syncMailIdentity: async () => ({ success: true }), }, }, }); const result = await fireTypedRequest(typedrouter, 'syncWorkAppMailIdentity', { apiToken: 'valid-token', ownership: { workHosterType: 'onebox', workHosterId: 'box-1', workAppId: 'app-1', }, localPart: 'hello', domain: 'example.com', }); expect(result.error?.text).toEqual('insufficient scope'); }); export default tap.start();