composer-interface-mapper.js•10.1 kB
/**
* @document Composer Interface Discovery and Mapping
* @version 1.0.0
* @status active
* @author Claude Code
* @created 2025-06-30
* @description Automated discovery of Composer interface URLs and access patterns
*/
import { chromium } from 'playwright';
import fs from 'fs/promises';
class ComposerInterfaceMapper {
constructor() {
this.browser = null;
this.page = null;
this.baseUrl = 'https://composer.euconquisto.com';
this.jwtToken = null;
}
async initialize() {
this.browser = await chromium.launch({
headless: false,
slowMo: 500
});
const context = await this.browser.newContext({
viewport: { width: 1920, height: 1080 }
});
this.page = await context.newPage();
// Load JWT token
try {
this.jwtToken = await fs.readFile('archive/authentication/correct-jwt-new.txt', 'utf-8');
this.jwtToken = this.jwtToken.trim();
console.log('✅ JWT token loaded for authenticated discovery');
} catch (error) {
console.log('⚠️ No JWT token found, using unauthenticated discovery');
}
}
async authenticateAndExplore() {
console.log('🔐 Starting authenticated exploration...');
const result = {
accessPattern: 'direct',
authenticationRequired: true,
workingPaths: [],
failedPaths: []
};
// Step 1: Authenticate if JWT available
if (this.jwtToken) {
console.log('🎫 Authenticating with JWT token...');
const loginUrl = `${this.baseUrl}/auth/login?token=${this.jwtToken}`;
result.loginUrl = loginUrl;
await this.page.goto(loginUrl, { waitUntil: 'networkidle' });
await this.page.waitForTimeout(3000);
// Take screenshot of authenticated state
await this.page.screenshot({
path: 'logs/screenshots/discovery/01-authenticated-state.png',
fullPage: true
});
} else {
// Try direct access
await this.page.goto(this.baseUrl, { waitUntil: 'networkidle' });
}
// Step 2: Discover dashboard/main interface
console.log('🏠 Discovering main interface...');
const currentUrl = this.page.url();
console.log(`📍 Current URL after auth: ${currentUrl}`);
if (!currentUrl.includes('404') && !currentUrl.includes('not exist')) {
result.dashboardUrl = currentUrl;
result.workingPaths.push(currentUrl.replace(this.baseUrl, ''));
}
// Step 3: Test common composer paths
const pathsToTest = [
'/',
'/dashboard',
'/home',
'/workspace',
'/composer',
'/editor',
'/create',
'/new',
'/compositions',
'/app',
'/portal',
'/studio'
];
for (const path of pathsToTest) {
try {
console.log(`🧭 Testing path: ${path}`);
await this.page.goto(`${this.baseUrl}${path}`, {
waitUntil: 'networkidle',
timeout: 10000
});
const content = await this.page.textContent('body');
const isValid = content &&
!content.includes('404') &&
!content.includes('not exist') &&
!content.includes('WebContentNotFound');
if (isValid) {
result.workingPaths.push(path);
console.log(` ✅ Working: ${path}`);
// Take screenshot of working interfaces
await this.page.screenshot({
path: `logs/screenshots/discovery/working-${path.replace('/', 'root')}.png`,
fullPage: true
});
// Check for composer-specific indicators
const indicators = await this.page.evaluate(() => {
const bodyText = document.body.innerText.toLowerCase();
return {
hasComposer: bodyText.includes('composer') || bodyText.includes('composition'),
hasEditor: bodyText.includes('editor') || bodyText.includes('edit'),
hasCreate: bodyText.includes('create') || bodyText.includes('new'),
hasWidgets: document.querySelectorAll('[class*="widget"], [class*="element"]').length > 0,
hasNavigation: document.querySelectorAll('nav, [class*="nav"], [class*="menu"]').length > 0,
textSnippet: bodyText.substring(0, 200)
};
});
console.log(` 📊 Indicators:`, indicators);
// Identify specific interface types
if (indicators.hasEditor || indicators.hasWidgets) {
result.editorUrl = `${this.baseUrl}${path}`;
}
if (indicators.hasCreate) {
result.createUrl = `${this.baseUrl}${path}`;
}
} else {
result.failedPaths.push(path);
console.log(` ❌ Failed: ${path}`);
}
} catch (error) {
result.failedPaths.push(path);
console.log(` ❌ Error on ${path}: ${error.message}`);
}
}
// Step 4: Look for navigation links and buttons
console.log('🔗 Searching for navigation elements...');
if (result.dashboardUrl) {
await this.page.goto(result.dashboardUrl, { waitUntil: 'networkidle' });
const navigationLinks = await this.page.evaluate(() => {
const links = Array.from(document.querySelectorAll('a, button'));
return links
.map(el => ({
text: el.textContent?.trim() || '',
href: el.getAttribute('href') || '',
onclick: el.getAttribute('onclick') || ''
}))
.filter(link =>
link.text.toLowerCase().includes('composer') ||
link.text.toLowerCase().includes('editor') ||
link.text.toLowerCase().includes('create') ||
link.text.toLowerCase().includes('new') ||
link.href.includes('composer') ||
link.href.includes('editor')
);
});
console.log('🔗 Found navigation elements:', navigationLinks);
}
return result;
}
async testCompositionLoading(interfaceUrl) {
try {
console.log('🧪 Testing composition loading at:', interfaceUrl);
await this.page.goto(interfaceUrl, { waitUntil: 'networkidle' });
// Inject our test composition first
const injectionResult = await this.page.evaluate(() => {
// Clear existing data
localStorage.removeItem('rdp-composer-data');
// Inject test data
const testComposition = {
composition: {
id: 'test-fotossintese',
title: 'Fotossíntese Test',
elements: [
{ id: '1', type: 'head-1', content_title: 'Test Header' },
{ id: '2', type: 'text-1', content: 'Test content about fotossíntese' }
]
}
};
localStorage.setItem('rdp-composer-data', JSON.stringify(testComposition));
return !!localStorage.getItem('rdp-composer-data');
});
console.log('💉 Composition injection result:', injectionResult);
// Wait for potential auto-loading
await this.page.waitForTimeout(5000);
// Check if composition appears
const compositionVisible = await this.page.evaluate(() => {
const bodyText = document.body.innerText.toLowerCase();
return {
hasData: !!localStorage.getItem('rdp-composer-data'),
hasPhotosynthesis: bodyText.includes('fotossíntese'),
hasTest: bodyText.includes('test'),
textSnippet: bodyText.substring(0, 300)
};
});
await this.page.screenshot({
path: 'logs/screenshots/discovery/composition-test.png',
fullPage: true
});
console.log('🧪 Composition visibility test:', compositionVisible);
return compositionVisible.hasPhotosynthesis || compositionVisible.hasTest;
} catch (error) {
console.log('❌ Composition loading test failed:', error.message);
return false;
}
}
async generateReport() {
const result = await this.authenticateAndExplore();
// Test composition loading on working interfaces
if (result.editorUrl) {
const compositionWorks = await this.testCompositionLoading(result.editorUrl);
console.log(`🎨 Composition loading test: ${compositionWorks ? 'SUCCESS' : 'FAILED'}`);
result.compositionLoadingWorks = compositionWorks;
}
// Determine access pattern
if (result.editorUrl || result.createUrl) {
result.accessPattern = 'direct';
} else if (result.dashboardUrl && result.workingPaths.length > 0) {
result.accessPattern = 'dashboard';
} else {
result.accessPattern = 'redirect';
}
console.log('\n📊 Discovery Report:');
console.log('==================');
console.log(`🔐 Authentication: ${this.jwtToken ? 'JWT Used' : 'Unauthenticated'}`);
console.log(`🏠 Dashboard URL: ${result.dashboardUrl || 'Not found'}`);
console.log(`🎨 Editor URL: ${result.editorUrl || 'Not found'}`);
console.log(`➕ Create URL: ${result.createUrl || 'Not found'}`);
console.log(`✅ Working paths: ${result.workingPaths.length}`);
console.log(`❌ Failed paths: ${result.failedPaths.length}`);
console.log(`🔄 Access pattern: ${result.accessPattern}`);
console.log(`🧪 Composition loading: ${result.compositionLoadingWorks ? 'WORKS' : 'NEEDS INVESTIGATION'}`);
return result;
}
async cleanup() {
if (this.browser) {
console.log('\n⏸️ Keeping browser open for 10 seconds...');
await new Promise(resolve => setTimeout(resolve, 10000));
await this.browser.close();
}
}
}
// Run the discovery
const mapper = new ComposerInterfaceMapper();
mapper.initialize()
.then(() => mapper.generateReport())
.then(result => {
console.log('\n✅ Interface discovery completed!');
console.log('📸 Screenshots saved in logs/screenshots/discovery/');
return mapper.cleanup();
})
.catch(error => {
console.error('❌ Discovery failed:', error);
return mapper.cleanup();
});