Files

306 lines
12 KiB
JavaScript

// Comprehensive Ghaymah Platform Test Script
// Tests: Project creation, App deployment, DNS resolution, TCP, and Security
async function auth() {
const res = 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 data = await res.json();
return { token: data.session.accessToken, userId: data.session.user.id };
}
async function gql(token, query, variables = {}) {
const res = await fetch('https://graphql.ghaymah.systems/v1/graphql', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables })
});
return res.json();
}
async function gwApi(path, token, opts = {}) {
const res = await fetch(`https://gateway.ghaymah.systems${path}`, {
...opts,
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token, ...(opts.headers || {}) }
});
return res.json();
}
function log(phase, msg) {
const ts = new Date().toISOString();
console.log(`[${ts}] [${phase}] ${msg}`);
}
async function main() {
log('AUTH', 'Authenticating...');
const { token, userId } = await auth();
log('AUTH', `Authenticated as ${userId}`);
// 1. List current resources
log('RECON', 'Listing existing resources...');
const resources = await gql(token, `query GetResources($userId: uuid) {
ghaymah_cloud_resources(where: { user_id: { _eq: $userId }, deleted: { _eq: false } }) {
id chart_name chart_version status values
}
}`, { userId });
const resList = resources.data?.ghaymah_cloud_resources || [];
resList.forEach(r => log('RECON', ` Resource: ${r.chart_name} | ${r.values?.name || r.id} | status: ${r.status}`));
// 2. List existing apps via ListApps mutation
log('RECON', 'Listing existing apps...');
const apps = await gql(token, `mutation ListApps {
listApps {
id name image status alias url
ports { name number hostname health { path } }
env { name value }
tags { key value }
}
}`);
const appList = apps.data?.listApps || [];
appList.forEach(a => log('RECON', ` App: ${a.name} | status: ${a.status} | alias: ${a.alias}`));
log('RECON', ` Total apps: ${appList.length}`);
// List full details of the apps
appList.forEach(a => {
log('RECON', ` App ${a.name} ports: ${JSON.stringify(a.ports)}`);
log('RECON', ` App ${a.name} tags: ${JSON.stringify(a.tags)}`);
log('RECON', ` App ${a.name} env: ${JSON.stringify(a.env?.map(e => e.name))}`);
});
// 3. Create a test project for DNS/TCP testing
log('SETUP', 'Creating test project: dns-tcp-test...');
const projectId = crypto.randomUUID();
const createProject = await gql(token, `mutation CreateResourceWithId($id: uuid!, $userId: uuid!, $chartName: String!, $chartVersion: String!, $values: json!) {
insert_ghaymah_cloud_resources_one(object: { id: $id, user_id: $userId, chart_name: $chartName, chart_version: $chartVersion, values: $values }) {
id
}
}`, {
id: projectId,
userId,
chartName: 'ghaymah-project',
chartVersion: '0.0',
values: { name: 'dns-tcp-test', userId }
});
log('SETUP', `Project creation result: ${JSON.stringify(createProject)}`);
// Wait for project to sync
log('SETUP', 'Waiting 10s for project sync...');
await new Promise(r => setTimeout(r, 10000));
// 4. Deploy App A (TCP echo server)
log('DEPLOY', 'Creating App A (tcp-echo-server)...');
const appAId = crypto.randomUUID();
const createAppA = await gql(token, `mutation CreateResourceWithId($id: uuid!, $userId: uuid!, $chartName: String!, $chartVersion: String!, $values: json!) {
insert_ghaymah_cloud_resources_one(object: { id: $id, user_id: $userId, chart_name: $chartName, chart_version: $chartVersion, values: $values }) {
id
}
}`, {
id: appAId,
userId,
chartName: 'ghaymah-app',
chartVersion: '0.1',
values: {
name: 'tcp-echo-server',
projectId,
container: {
image: 'node:22-alpine'
},
resourceTier: 't1',
ports: [{ number: 9000, expose: true }],
publicAccess: { enabled: true },
source: { enabled: false },
command: ['node', '-e', `
const net = require('net');
const http = require('http');
const dns = require('dns');
const os = require('os');
// TCP Echo Server on port 9000
const tcpServer = net.createServer(socket => {
console.log('TCP connection from', socket.remoteAddress);
socket.on('data', data => {
console.log('TCP received:', data.toString());
socket.write('ECHO:' + data.toString());
});
socket.on('error', () => {});
});
tcpServer.listen(9000, () => console.log('TCP echo server on 9000'));
// HTTP Server on port 8080 for health + diagnostics
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');
if (!name) { res.end(JSON.stringify({ error: 'need ?name=' })); return; }
try {
const addrs = await dns.promises.resolve4(name);
res.end(JSON.stringify({ name, addresses: addrs }));
} catch(e) {
res.end(JSON.stringify({ name, error: e.code || e.message }));
}
} else if (url.pathname === '/resolv-conf') {
const fs = require('fs');
try {
const rc = fs.readFileSync('/etc/resolv.conf', 'utf8');
res.end(JSON.stringify({ resolv_conf: rc }));
} catch(e) {
res.end(JSON.stringify({ error: e.message }));
}
} else if (url.pathname === '/ip-addr') {
const ifaces = os.networkInterfaces();
res.end(JSON.stringify({ interfaces: ifaces }));
} else if (url.pathname === '/tcp-connect') {
const host = url.searchParams.get('host');
const port = parseInt(url.searchParams.get('port'));
if (!host || !port) { res.end(JSON.stringify({ error: 'need ?host=&port=' })); return; }
const sock = new net.Socket();
sock.setTimeout(5000);
sock.connect(port, host, () => {
sock.write('HELLO_FROM_APP_A');
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) });
const d = await r.json();
res.end(JSON.stringify({ external_access: true, data: d }));
} catch(e) {
res.end(JSON.stringify({ external_access: false, error: e.message }));
}
} else {
res.end(JSON.stringify({ error: 'not found' }));
}
}).listen(8080, () => console.log('HTTP diagnostics on 8080'));
`]
}
});
log('DEPLOY', `App A result: ${JSON.stringify(createAppA)}`);
// 5. Deploy App B (tcp-test-client)
log('DEPLOY', 'Creating App B (tcp-test-client)...');
const appBId = crypto.randomUUID();
const createAppB = await gql(token, `mutation CreateResourceWithId($id: uuid!, $userId: uuid!, $chartName: String!, $chartVersion: String!, $values: json!) {
insert_ghaymah_cloud_resources_one(object: { id: $id, user_id: $userId, chart_name: $chartName, chart_version: $chartVersion, values: $values }) {
id
}
}`, {
id: appBId,
userId,
chartName: 'ghaymah-app',
chartVersion: '0.1',
values: {
name: 'tcp-test-client',
projectId,
container: {
image: 'node:22-alpine'
},
resourceTier: 't1',
ports: [{ number: 8080, expose: true }],
publicAccess: { enabled: true },
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');
if (!name) { res.end(JSON.stringify({ error: 'need ?name=' })); return; }
try {
const addrs = await dns.promises.resolve4(name);
res.end(JSON.stringify({ name, addresses: addrs, source: 'resolve4' }));
} catch(e) {
try {
const result = await dns.promises.lookup(name);
res.end(JSON.stringify({ name, address: result.address, family: result.family, source: 'lookup' }));
} catch(e2) {
res.end(JSON.stringify({ name, error: e.code || e.message, error2: e2.code || e2.message }));
}
}
} else if (url.pathname === '/resolv-conf') {
const fs = require('fs');
try {
const rc = fs.readFileSync('/etc/resolv.conf', 'utf8');
res.end(JSON.stringify({ resolv_conf: rc }));
} catch(e) {
res.end(JSON.stringify({ error: e.message }));
}
} else if (url.pathname === '/ip-addr') {
const ifaces = os.networkInterfaces();
res.end(JSON.stringify({ interfaces: ifaces }));
} else if (url.pathname === '/tcp-connect') {
const host = url.searchParams.get('host');
const port = parseInt(url.searchParams.get('port'));
if (!host || !port) { res.end(JSON.stringify({ error: 'need ?host=&port=' })); return; }
const sock = new net.Socket();
sock.setTimeout(5000);
sock.connect(port, host, () => {
sock.write('HELLO_FROM_APP_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) });
const d = await r.json();
res.end(JSON.stringify({ external_access: true, data: d }));
} catch(e) {
res.end(JSON.stringify({ external_access: 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) {
try {
await new Promise((resolve, reject) => {
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(); });
});
} catch(e) { results.push({ port: p, status: 'error', error: e.message }); }
}
res.end(JSON.stringify({ host, scan: results }));
} else {
res.end(JSON.stringify({ error: 'not found' }));
}
}).listen(8080, () => console.log('TCP test client HTTP on 8080'));
`]
}
});
log('DEPLOY', `App B result: ${JSON.stringify(createAppB)}`);
// Save IDs for later
console.log('\n=== DEPLOYMENT IDS ===');
console.log('Project ID:', projectId);
console.log('App A (tcp-echo-server) ID:', appAId);
console.log('App B (tcp-test-client) ID:', appBId);
}
main().catch(e => { console.error('FATAL:', e); process.exit(1); });