const APP_A_ID = 'af1b9e91-9bec-4d6a-b76c-92d7273b82ce'; const APP_B_ID = 'fcbd895c-2370-46db-bd6a-057f6d887b0f'; const PROJECT_ID = '3f55ceef-78ae-40c8-a41b-0505db8c59ee'; async function main() { const authRes = await fetch('https://auth.ghaymah.systems/signin/email-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'ziadalex2003@gmail.com', password: '2062003Mz*' }) }); const { session } = await authRes.json(); const token = session.accessToken; const userId = session.user.id; // Helper to generate domain hash like ghaymah does async function genDomainHash(name, id) { const input = `${name}-${id}`; const data = new TextEncoder().encode(input); const hash = await crypto.subtle.digest('SHA-256', data); const hex = Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join(''); return hex.substring(0, 12); } const hashA = await genDomainHash('tcp-echo-server', APP_A_ID); const hashB = await genDomainHash('tcp-test-client', APP_B_ID); const domainA = `tcp-echo-server-${hashA}.hosted.ghaymah.systems`; const domainB = `tcp-test-client-${hashB}.hosted.ghaymah.systems`; console.log('App A domain would be:', domainA); console.log('App B domain would be:', domainB); // Update App A with proper values including publicAccess domain const updateA = await fetch('https://graphql.ghaymah.systems/v1/graphql', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `mutation UpdateResourceValues($id: uuid!, $values: json!) { update_ghaymah_cloud_resources_by_pk( pk_columns: { id: $id }, _set: { values: $values } ) { id status } }`, variables: { id: APP_A_ID, values: { name: 'tcp-echo-server', projectId: PROJECT_ID, container: { image: 'node:22-alpine' }, resourceTier: 't1', ports: [{ number: 8080, expose: true }], publicAccess: { enabled: true, domain: domainA }, source: { enabled: false }, command: ['node', '-e', ` const net = require('net'); const http = require('http'); const dns = require('dns'); const os = require('os'); const tcpServer = net.createServer(socket => { console.log('TCP connection from', socket.remoteAddress); socket.on('data', data => { socket.write('ECHO:' + data.toString()); }); socket.on('error', () => {}); }); tcpServer.listen(9000, () => console.log('TCP echo server on 9000')); http.createServer(async (req, res) => { const url = new URL(req.url, 'http://localhost'); res.setHeader('Content-Type', 'application/json'); if (url.pathname === '/health' || url.pathname === '/') { res.end(JSON.stringify({ status: 'ok', service: 'tcp-echo-server', hostname: os.hostname() })); } else if (url.pathname === '/dns-lookup') { const name = url.searchParams.get('name'); try { const addrs = await dns.promises.resolve4(name); res.end(JSON.stringify({ name, addresses: addrs })); } catch(e) { try { const r = await dns.promises.lookup(name); res.end(JSON.stringify({ name, address: r.address, source: 'lookup' })); } catch(e2) { res.end(JSON.stringify({ name, error: e.code, error2: e2.code })); } } } else if (url.pathname === '/resolv-conf') { try { res.end(JSON.stringify({ resolv_conf: require('fs').readFileSync('/etc/resolv.conf','utf8') })); } catch(e) { res.end(JSON.stringify({ error: e.message })); } } else if (url.pathname === '/ip-addr') { res.end(JSON.stringify({ interfaces: os.networkInterfaces() })); } else if (url.pathname === '/tcp-connect') { const host = url.searchParams.get('host'), port = parseInt(url.searchParams.get('port')); const sock = new net.Socket(); sock.setTimeout(5000); sock.connect(port, host, () => { sock.write('HELLO'); sock.on('data', d => { res.end(JSON.stringify({ success: true, host, port, response: d.toString() })); sock.destroy(); }); }); sock.on('error', e => { res.end(JSON.stringify({ success: false, host, port, error: e.message })); }); sock.on('timeout', () => { res.end(JSON.stringify({ success: false, host, port, error: 'timeout' })); sock.destroy(); }); } else if (url.pathname === '/external-test') { try { const r = await fetch('https://httpbin.org/ip',{signal:AbortSignal.timeout(5000)}); res.end(JSON.stringify({ external: true, data: await r.json() })); } catch(e) { res.end(JSON.stringify({ external: false, error: e.message })); } } else { res.end(JSON.stringify({ error: 'not found' })); } }).listen(8080, () => console.log('HTTP diagnostics on 8080')); `] } } }) }); console.log('Update A:', JSON.stringify(await updateA.json())); // Update App B const updateB = await fetch('https://graphql.ghaymah.systems/v1/graphql', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `mutation UpdateResourceValues($id: uuid!, $values: json!) { update_ghaymah_cloud_resources_by_pk( pk_columns: { id: $id }, _set: { values: $values } ) { id status } }`, variables: { id: APP_B_ID, values: { name: 'tcp-test-client', projectId: PROJECT_ID, container: { image: 'node:22-alpine' }, resourceTier: 't1', ports: [{ number: 8080, expose: true }], publicAccess: { enabled: true, domain: domainB }, source: { enabled: false }, command: ['node', '-e', ` const net = require('net'); const http = require('http'); const dns = require('dns'); const os = require('os'); http.createServer(async (req, res) => { const url = new URL(req.url, 'http://localhost'); res.setHeader('Content-Type', 'application/json'); if (url.pathname === '/health' || url.pathname === '/') { res.end(JSON.stringify({ status: 'ok', service: 'tcp-test-client', hostname: os.hostname() })); } else if (url.pathname === '/dns-lookup') { const name = url.searchParams.get('name'); try { const addrs = await dns.promises.resolve4(name); res.end(JSON.stringify({ name, addresses: addrs, source: 'resolve4' })); } catch(e) { try { const r = await dns.promises.lookup(name); res.end(JSON.stringify({ name, address: r.address, source: 'lookup' })); } catch(e2) { res.end(JSON.stringify({ name, error: e.code, error2: e2.code })); } } } else if (url.pathname === '/resolv-conf') { try { res.end(JSON.stringify({ resolv_conf: require('fs').readFileSync('/etc/resolv.conf','utf8') })); } catch(e) { res.end(JSON.stringify({ error: e.message })); } } else if (url.pathname === '/ip-addr') { res.end(JSON.stringify({ interfaces: os.networkInterfaces() })); } else if (url.pathname === '/tcp-connect') { const host = url.searchParams.get('host'), port = parseInt(url.searchParams.get('port')); const sock = new net.Socket(); sock.setTimeout(5000); sock.connect(port, host, () => { sock.write('HELLO_B'); sock.on('data', d => { res.end(JSON.stringify({ success: true, host, port, response: d.toString() })); sock.destroy(); }); }); sock.on('error', e => { res.end(JSON.stringify({ success: false, host, port, error: e.message })); }); sock.on('timeout', () => { res.end(JSON.stringify({ success: false, host, port, error: 'timeout' })); sock.destroy(); }); } else if (url.pathname === '/external-test') { try { const r = await fetch('https://httpbin.org/ip',{signal:AbortSignal.timeout(5000)}); res.end(JSON.stringify({ external: true, data: await r.json() })); } catch(e) { res.end(JSON.stringify({ external: false, error: e.message })); } } else if (url.pathname === '/port-scan') { const host = url.searchParams.get('host'); const portsStr = url.searchParams.get('ports') || '22,53,80,443,3000,4000,5432,6379,8080,9000'; const ports = portsStr.split(',').map(Number); const results = []; for (const p of ports) { await new Promise((resolve) => { const s = new net.Socket(); s.setTimeout(2000); s.connect(p, host, () => { results.push({ port: p, status: 'open' }); s.destroy(); resolve(); }); s.on('error', e => { results.push({ port: p, status: 'closed', error: e.code }); resolve(); }); s.on('timeout', () => { results.push({ port: p, status: 'filtered' }); s.destroy(); resolve(); }); }); } res.end(JSON.stringify({ host, scan: results })); } else { res.end(JSON.stringify({ error: 'not found' })); } }).listen(8080, () => console.log('TCP test client on 8080')); `] } } }) }); console.log('Update B:', JSON.stringify(await updateB.json())); // Wait for re-deployment console.log('Waiting 30s for re-deployment...'); await new Promise(r => setTimeout(r, 30000)); // Check status again const finalRes = await fetch('https://graphql.ghaymah.systems/v1/graphql', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `query GetResources($userId: uuid) { ghaymah_cloud_resources(where: { user_id: { _eq: $userId }, deleted: { _eq: false }, chart_name: { _eq: "ghaymah-app" } }) { id chart_name status values message } }`, variables: { userId } }) }); const finalData = await finalRes.json(); finalData.data.ghaymah_cloud_resources.forEach(r => { if (r.values.name === 'tcp-echo-server' || r.values.name === 'tcp-test-client') { console.log('FINAL:', r.values.name, '| status:', r.status, '| domain:', r.values.publicAccess?.domain, '| msg:', r.message); } }); // Test the domains for (const domain of [domainA, domainB]) { try { const testRes = await fetch(`https://${domain}/health`, { signal: AbortSignal.timeout(5000) }); console.log(`${domain} /health:`, testRes.status, await testRes.text()); } catch(e) { console.log(`${domain} /health: ERROR`, e.message); } } } main().catch(e => console.error('FATAL:', e));