composer-interface-mapper.ts•10.7 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, Browser, Page } from 'playwright';
import fs from 'fs/promises';
import path from 'path';
export interface ComposerInterface {
loginUrl?: string;
dashboardUrl?: string;
editorUrl?: string;
createUrl?: string;
viewUrl?: string;
accessPattern: 'direct' | 'dashboard' | 'redirect';
authenticationRequired: boolean;
workingPaths: string[];
failedPaths: string[];
}
export class ComposerInterfaceMapper {
private browser: Browser | null = null;
private page: Page | null = null;
private baseUrl = 'https://composer.euconquisto.com';
private jwtToken: string | null = null;
async initialize(): Promise<void> {
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(): Promise<ComposerInterface> {
if (!this.page) throw new Error('Page not initialized');
console.log('🔐 Starting authenticated exploration...');
const result: ComposerInterface = {
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
};
});
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);
// Test discovered navigation links
for (const link of navigationLinks.slice(0, 3)) { // Limit to first 3
if (link.href && !link.href.startsWith('javascript:')) {
try {
const fullUrl = link.href.startsWith('http') ? link.href : `${this.baseUrl}${link.href}`;
console.log(`🎯 Testing discovered link: ${fullUrl}`);
await this.page.goto(fullUrl, { waitUntil: 'networkidle', timeout: 10000 });
const isValid = !(await this.page.textContent('body'))?.includes('404');
if (isValid) {
result.workingPaths.push(link.href);
if (link.text.toLowerCase().includes('editor')) {
result.editorUrl = fullUrl;
}
if (link.text.toLowerCase().includes('create')) {
result.createUrl = fullUrl;
}
}
} catch (error) {
console.log(` ❌ Link test failed: ${link.href}`);
}
}
}
}
return result;
}
async testCompositionLoading(interfaceUrl: string): Promise<boolean> {
if (!this.page) return false;
try {
console.log('🧪 Testing composition loading at:', interfaceUrl);
await this.page.goto(interfaceUrl, { waitUntil: 'networkidle' });
// Check if localStorage has composition data
const hasComposition = await this.page.evaluate(() => {
const data = localStorage.getItem('rdp-composer-data');
return !!data;
});
if (hasComposition) {
console.log('✅ Found composition data in localStorage');
// 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 bodyText.includes('fotossíntese') || bodyText.includes('plantas');
});
await this.page.screenshot({
path: 'logs/screenshots/discovery/composition-test.png',
fullPage: true
});
return compositionVisible;
}
return false;
} catch (error) {
console.log('❌ Composition loading test failed:', error.message);
return false;
}
}
async generateReport(): Promise<ComposerInterface> {
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'}`);
}
// 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}`);
return result;
}
async cleanup(): Promise<void> {
if (this.browser) {
await this.browser.close();
}
}
}
// Standalone execution
if (require.main === module) {
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();
});
}