index.htmlā¢19.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AWS Infrastructure Chat</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacMacSystemFont, 'Segoe UI', Roboto, sans-serif;
/* Dark theme background */
background: linear-gradient(135deg, #121212 0%, #1a1a1a 100%); /* Darker gradient */
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #e0e0e0; /* Light text color for dark background */
}
.container {
width: 100%;
max-width: 1200px;
display: flex;
gap: 20px;
height: 80vh;
}
.chat-panel, .sidebar {
flex: 1;
background: #1e1e1e; /* Dark panel background */
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); /* More prominent shadow for dark theme */
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid #333; /* Subtle border for definition */
}
.sidebar {
width: 300px;
padding: 20px;
overflow-y: auto;
}
.chat-header {
background: linear-gradient(135deg, #3a005a 0%, #0a0a0a 100%); /* Darker, subtle gradient */
color: white;
padding: 20px;
text-align: center;
}
.chat-header h1 {
font-size: 24px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
background: #242424; /* Slightly lighter dark background for messages */
}
.message {
margin-bottom: 20px;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message.user {
text-align: right;
}
.message.assistant {
text-align: left;
}
.message-content {
display: inline-block;
padding: 12px 18px;
border-radius: 18px;
max-width: 70%;
word-wrap: break-word;
}
.message.user .message-content {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); /* Keep user messages distinct */
color: white;
}
.message.assistant .message-content {
background: #333333; /* Darker background for assistant messages */
color: #e0e0e0; /* Light text color */
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); /* Adjusted shadow for dark theme */
}
.chat-input-container {
padding: 20px;
background: #1e1e1e; /* Match panel background */
border-top: 1px solid #333; /* Darker border */
}
.chat-input-wrapper {
display: flex;
gap: 10px;
}
.chat-input {
flex: 1;
padding: 12px 20px;
border: 2px solid #555; /* Darker border for input */
border-radius: 25px;
font-size: 16px;
outline: none;
transition: border-color 0.3s;
background: #2b2b2b; /* Dark input background */
color: #e0e0e0; /* Light text color */
}
.chat-input:focus {
border-color: #667eea; /* Highlight color remains */
}
.send-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); /* Keep send button distinct */
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
transition: transform 0.2s;
}
.send-button:hover {
transform: scale(1.05);
}
.send-button:active {
transform: scale(0.95);
}
.resource-card {
background: #2b2b2b; /* Darker card background */
border-radius: 12px;
padding: 15px;
margin-bottom: 15px;
border: 1px solid #444; /* Darker border for cards */
transition: all 0.3s;
}
.resource-card:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2); /* Adjusted shadow for dark theme */
}
.resource-type {
font-weight: 600;
color: #92b0ff; /* Lighter color for type in dark theme */
margin-bottom: 5px;
}
.resource-id {
font-size: 14px;
color: #c0c0c0; /* Lighter grey for IDs */
font-family: 'Courier New', monospace;
}
.resource-date {
font-size: 12px;
color: #888; /* Slightly darker grey for dates */
margin-top: 5px;
}
.delete-button {
background: #a00000; /* Darker red for delete button */
color: white;
border: none;
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
cursor: pointer;
margin-top: 10px;
transition: background 0.2s;
}
.delete-button:hover {
background: #c82333; /* Standard red on hover */
}
.examples {
margin-top: 30px;
}
.examples h3 {
color: #e0e0e0; /* Light text for headings */
margin-bottom: 15px;
font-size: 18px;
}
.example-item {
background: #333333; /* Darker example item background */
padding: 10px 15px;
border-radius: 8px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
color: #e0e0e0; /* Light text for example items */
}
.example-item:hover {
background: #444444; /* Slightly lighter on hover */
transform: translateX(5px);
}
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #555; /* Darker loading spinner border */
border-top: 3px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-message {
background: #4a0000; /* Darker red background for errors */
color: #ffcccc; /* Lighter red text */
padding: 10px;
border-radius: 8px;
margin-top: 10px;
font-size: 14px;
}
.success-message {
background: #0a4a1a; /* Darker green background for success */
color: #ccffcc; /* Lighter green text */
padding: 10px;
border-radius: 8px;
margin-top: 10px;
font-size: 14px;
}
.status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.status-indicator.online {
background: #28a745; /* Keep online indicator visible */
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(40, 167, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0);
}
}
@media (max-width: 768px) {
.container {
flex-direction: column;
height: 90vh;
}
.sidebar {
width: 100%;
height: 200px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="chat-panel">
<div class="chat-header">
<h1>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 3h18v18H3zM12 8v8m-4-4h8"/>
</svg>
AWS Infrastructure Chat
</h1>
<p style="margin-top: 5px; opacity: 0.9; font-size: 14px;">
<span class="status-indicator online"></span>
Deploy infrastructure with natural language
</p>
</div>
<div class="chat-messages" id="chatMessages">
<div class="message assistant">
<div class="message-content">
Hello! š I'm your AWS infrastructure assistant. I can help you:
<ul style="margin-top: 10px; margin-left: 20px;">
<li>Create EC2 instances</li>
<li>Create S3 buckets</li>
<li>Configure DynamoDB tables</li>
<li>Deploy Lambda functions</li>
<li>Create IAM roles</li>
</ul>
<br>
Just tell me what you need, for example: "Create an S3 bucket named my-project"
</div>
</div>
</div>
<div class="chat-input-container">
<div class="chat-input-wrapper">
<input
type="text"
class="chat-input"
id="chatInput"
placeholder="Type your command here..."
onkeypress="handleKeyPress(event)"
>
<button class="send-button" onclick="sendMessage()">
Send
</button>
</div>
</div>
</div>
<div class="sidebar">
<h2 style="margin-bottom: 20px; color: #e0e0e0;">Deployed Resources</h2>
<div id="resourcesList">
<p style="color: #999; text-align: center; padding: 20px;">
No resources deployed yet
</p>
</div>
<div class="examples">
<h3>Quick examples:</h3>
<div class="example-item" onclick="setExample('Create an S3 bucket for my application')">
š¾ Create S3 bucket
</div>
<div class="example-item" onclick="setExample('Deploy a t2.micro EC2 instance')">
š„ļø Create EC2 instance
</div>
<div class="example-item" onclick="setExample('Create a DynamoDB table for users')">
šļø Create DynamoDB table
</div>
<div class="example-item" onclick="setExample('Deploy a Lambda function')">
ā” Deploy Lambda function
</div>
<div class="example-item" onclick="setExample('Show all resources')">
š List all resources
</div>
</div>
</div>
</div>
<script>
// API configuration
const API_URL = window.location.origin + '/api';
// Application state
let resources = [];
let isProcessing = false;
// Send message using chat endpoint
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message || isProcessing) return;
// Add user message
addMessage(message, 'user');
input.value = '';
// Process command
isProcessing = true;
try {
addMessage('Processing your request...', 'assistant');
const response = await fetch(`${API_URL}/chat/message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message })
});
const data = await response.json();
// Remove processing message
const messages = document.getElementById('chatMessages');
messages.removeChild(messages.lastChild);
if (data.success) {
if (data.intent === 'list') {
displayResourcesList(data.data.resources);
} else {
const successMsg = data.intent === 'create'
? `ā
Resource created successfully!\n\nType: ${data.resourceType}\nID: ${data.data.resourceId}`
: `ā
${data.message}`;
addMessage(successMsg, 'assistant', 'success');
}
// Update resources list
await updateResourcesList();
} else {
addMessage(data.message || 'Could not process your request', 'assistant', data.intent === 'help' ? '' : 'error');
}
} catch (error) {
console.error('Error:', error);
// Remove processing message if it exists
const messages = document.getElementById('chatMessages');
if (messages.lastChild.textContent.includes('Processing')) {
messages.removeChild(messages.lastChild);
}
addMessage(`ā Error: ${error.message}`, 'assistant', 'error');
} finally {
isProcessing = false;
}
}
// Display resources list in chat
function displayResourcesList(resources) {
if (resources.length > 0) {
let message = 'š Deployed Resources:\n\n';
resources.forEach(resource => {
message += `⢠${resource.type.toUpperCase()}: ${resource.id}\n`;
if (resource.metadata?.awsResourceId) {
message += ` AWS ID: ${resource.metadata.awsResourceId}\n`;
}
message += ` Created: ${new Date(resource.createdAt).toLocaleString()}\n\n`;
});
addMessage(message, 'assistant');
} else {
addMessage('No resources currently deployed.', 'assistant');
}
}
// Update resources list in sidebar
async function updateResourcesList() {
try {
const response = await fetch(`${API_URL}/infrastructure/resources`);
const data = await response.json();
const resourcesDiv = document.getElementById('resourcesList');
if (data.success && data.resources.length > 0) {
resources = data.resources;
resourcesDiv.innerHTML = resources.map(resource => `
<div class="resource-card">
<div class="resource-type">${resource.type.toUpperCase()}</div>
<div class="resource-id">${resource.id}</div>
${resource.metadata?.awsResourceId ?
`<div class="resource-id">AWS ID: ${resource.metadata.awsResourceId}</div>` : ''}
<div class="resource-date">${new Date(resource.createdAt).toLocaleString()}</div>
<button class="delete-button" onclick="deleteResource('${resource.id}', '${resource.type}')">
Delete
</button>
</div>
`).join('');
} else {
resourcesDiv.innerHTML = '<p style="color: #999; text-align: center; padding: 20px;">No resources deployed yet</p>';
}
} catch (error) {
console.error('Error updating resources:', error);
}
}
// Delete resource
async function deleteResource(resourceId, resourceType) {
if (!confirm(`Are you sure you want to delete resource ${resourceId}?`)) {
return;
}
const message = `Delete resource ${resourceId}`;
// Add user message
addMessage(message, 'user');
// Send delete command
isProcessing = true;
try {
const response = await fetch(`${API_URL}/chat/message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: `Delete resource ${resourceId}`
})
});
const data = await response.json();
if (data.success) {
addMessage('ā
Resource deleted successfully', 'assistant', 'success');
await updateResourcesList();
} else {
addMessage(`ā Error: ${data.message}`, 'assistant', 'error');
}
} catch (error) {
addMessage(`ā Error: ${error.message}`, 'assistant', 'error');
} finally {
isProcessing = false;
}
}
// Add message to chat
function addMessage(content, sender, type = '') {
const messagesDiv = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
let messageContent = content;
if (type === 'error') {
messageContent = `<div class="error-message">${content}</div>`;
} else if (type === 'success') {
messageContent = `<div class="success-message">${content}</div>`;
}
messageDiv.innerHTML = `
<div class="message-content">
${messageContent.replace(/\n/g, '<br>')}
</div>
`;
messagesDiv.appendChild(messageDiv);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
// Handle Enter key
function handleKeyPress(event) {
if (event.key === 'Enter') {
sendMessage();
}
}
// Set example
function setExample(text) {
document.getElementById('chatInput').value = text;
document.getElementById('chatInput').focus();
}
// Initialize
window.onload = function() {
updateResourcesList();
document.getElementById('chatInput').focus();
// Update resources every 30 seconds
setInterval(updateResourcesList, 30000);
};
</script>
</body>
</html>