Made some MASSIVE improvements

This commit is contained in:
2025-06-02 14:34:03 -06:00
parent 7362e7a350
commit 608e47b2fa
4 changed files with 334 additions and 58 deletions

View File

@ -5,4 +5,7 @@ S3_SECRET_KEY=your_secret_key_here
S3_BUCKET_NAME=your_bucket_name
SECRET_CODE=super_secret_code
ADMIN_CODE=super_secret_code
JWT_SECRET=your_jwt_secret_key_here_make_it_long_and_random
JWT_SECRET=your_jwt_secret_key_here_make_it_long_and_random
SOLUTION_1=29
SOLUTION_2=31
SOLUTION_3=5

View File

@ -15,4 +15,8 @@ services:
SECRET_CODE: ${SECRET_CODE}
ADMIN_CODE: ${ADMIN_CODE}
JWT_SECRET: ${JWT_SECRET}
# Puzzle Solutions (private)
SOLUTION_1: ${SOLUTION_1}
SOLUTION_2: ${SOLUTION_2}
SOLUTION_3: ${SOLUTION_3}
restart: unless-stopped

View File

@ -9,12 +9,16 @@ type FileSystemStructure = {
};
interface TerminalProps {
solution1: number;
solution2: number;
solution3: number;
puzzle1Content: string;
puzzle2Content: string;
puzzle3Content: string;
}
const TerminalComponent: React.FC<TerminalProps> = ({ solution1 = 42, solution2 = 87, solution3 = 5 }) => {
const TerminalComponent: React.FC<TerminalProps> = ({
puzzle1Content,
puzzle2Content,
puzzle3Content
}) => {
const terminalRef = useRef<HTMLDivElement>(null);
const xtermRef = useRef<Terminal | null>(null);
const fitAddonRef = useRef<FitAddon | null>(null);
@ -57,35 +61,13 @@ const TerminalComponent: React.FC<TerminalProps> = ({ solution1 = 42, solution2
"- /usr: System-wide shared resources and executables (read-only for you!).\n",
// Puzzle 1: Number Base
'/etc/sys_data/core_access.bin': `System memory address reference:\n` +
`Register B1: ${solution1.toString(2).padStart(8, '0')}\n` +
`Note: Always decode binary values when transferring between subsystems.`,
'/etc/sys_data/core_access.bin': puzzle1Content,
// Puzzle 2: Hex Code (Color)
'/etc/config/color_palette.config': `# Display Color Settings (RGB hex format)
PRIMARY_COLOR=#FF${solution2.toString(16).padStart(2, '0')}33
SECONDARY_COLOR=#33FF57
ALERT_COLOR=#FF0000
# System Note:
# Color channels must be isolated when importing to legacy devices.
# Remember middle values carry essential security data.`,
'/etc/config/color_palette.config': puzzle2Content,
// Puzzle 3: Log File Analysis
'/var/log/critical_errors.log': "[ERROR 001] System boot failure sequence A.\n" +
"[INFO 002] Retrying boot sequence B.\n" +
"[ERROR 003] Disk read error on /dev/sda1.\n" +
"[WARN 004] Network interface eth0 flapping.\n" +
"[INFO 005] System recovered.\n" +
"[DEBUG 006] User 'admin' login attempt.\n" +
"[ERROR 007] Authentication failure for user 'admin'.\n" +
"[INFO 008] User 'hacker' login successful.\n" +
"[WARN 009] Unusual activity detected from IP 192.168.1.101.\n" +
"[ERROR 010] Critical process 'security_daemon' terminated unexpectedly.\n" +
"[INFO 011] Attempting to restart 'security_daemon'.\n" +
`[ERROR 012] Failed to restart 'security_daemon'. Max retries (${solution3}) reached.\n` +
"[ERROR 014] System shutting down due to critical errors.\n" +
"[RECOVER] Backup facility requires retry count for authentication sequence.",
'/var/log/critical_errors.log': puzzle3Content,
// Home directory files with hints
'/home/user/puzzle_progress.txt': "My notes on the combination lock:\n- First number: __ (Check core system data)\n- Second number: __ (UI color settings have relevant info)\n- Third number: __ (Error logs contain critical values)\n\nReminder: Always convert between number formats as needed!",
@ -103,7 +85,7 @@ ALERT_COLOR=#FF0000
'/usr/bin/common_commands.txt': "Available commands: ls, cd, cat, clear, help (for README)\n\nImportant system paths:\n- Main config: /etc/config/\n- System data: /etc/sys_data/\n- Error logs: /var/log/",
'/usr/share/misc/old_data.bak': "ARCHIVE FILE: Contains outdated research notes from Project Chimera. Access restricted.\n\nPossible combination values tested:\n- 12-45-78 (Failed)\n- 24-68-99 (Failed)\n- 11-22-33 (Failed)\n\nNOTE: New secure generation algorithm implemented. Old combinations no longer valid.\n\nCurrent security relies on: core_access biometrics, color_palette UI safeguards, and error logging thresholds."
}), [solution1, solution2, solution3]);
}), [puzzle1Content, puzzle2Content, puzzle3Content]);
const currentPath = useRef<string>('/');
const prompt = () => {
@ -237,7 +219,10 @@ ALERT_COLOR=#FF0000
background: '#1e1e1e',
foreground: '#d4d4d4',
cursor: '#d4d4d4',
}
},
allowTransparency: true,
scrollback: 1000,
rightClickSelectsWord: true
});
const fit = new FitAddon();
const webLinks = new WebLinksAddon();
@ -250,11 +235,164 @@ ALERT_COLOR=#FF0000
term.open(terminalRef.current);
fit.fit();
// Enhanced paste handler for Ctrl+V and Cmd+V
term.attachCustomKeyEventHandler((e) => {
// Handle paste with Ctrl+V (Windows/Linux) or Cmd+V (Mac)
if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
e.preventDefault();
navigator.clipboard.readText().then(text => {
// Process the pasted text character by character to update currentLine
const cleanText = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
for (const char of cleanText) {
if (char === '\n') {
// Handle newline as Enter key
if (currentLine.current.trim()) {
commandHistory.current.push(currentLine.current);
}
historyIndex.current = commandHistory.current.length;
processCommand(currentLine.current);
currentLine.current = '';
} else {
// Add printable characters to current line and display
term.write(char);
currentLine.current += char;
}
}
}).catch(err => {
console.warn('Failed to read clipboard:', err);
});
return false;
}
// Handle copy with Ctrl+C or Cmd+C (when text is selected)
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
const selection = term.getSelection();
if (selection) {
navigator.clipboard.writeText(selection).catch(err => {
console.warn('Failed to write to clipboard:', err);
});
}
return true; // Don't prevent default for copy
}
return true;
});
// Add right-click context menu support
term.element?.addEventListener('contextmenu', (e) => {
e.preventDefault();
// Create context menu
const existingMenu = document.getElementById('terminal-context-menu');
if (existingMenu) {
existingMenu.remove();
}
const menu = document.createElement('div');
menu.id = 'terminal-context-menu';
menu.style.cssText = `
position: fixed;
top: ${e.clientY}px;
left: ${e.clientX}px;
background: #2d2d2d;
border: 1px solid #555;
border-radius: 4px;
padding: 4px 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
z-index: 1000;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
min-width: 120px;
`;
const selection = term.getSelection();
// Copy option (only if text is selected)
if (selection) {
const copyItem = document.createElement('div');
copyItem.textContent = 'Copy';
copyItem.style.cssText = `
padding: 8px 16px;
color: #fff;
cursor: pointer;
transition: background-color 0.1s;
`;
copyItem.addEventListener('mouseenter', () => {
copyItem.style.backgroundColor = '#404040';
});
copyItem.addEventListener('mouseleave', () => {
copyItem.style.backgroundColor = 'transparent';
});
copyItem.addEventListener('click', () => {
navigator.clipboard.writeText(selection).catch(err => {
console.warn('Failed to write to clipboard:', err);
});
menu.remove();
});
menu.appendChild(copyItem);
}
// Paste option
const pasteItem = document.createElement('div');
pasteItem.textContent = 'Paste';
pasteItem.style.cssText = `
padding: 8px 16px;
color: #fff;
cursor: pointer;
transition: background-color 0.1s;
`;
pasteItem.addEventListener('mouseenter', () => {
pasteItem.style.backgroundColor = '#404040';
});
pasteItem.addEventListener('mouseleave', () => {
pasteItem.style.backgroundColor = 'transparent';
});
pasteItem.addEventListener('click', () => {
navigator.clipboard.readText().then(text => {
// Process the pasted text character by character to update currentLine
const cleanText = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
for (const char of cleanText) {
if (char === '\n') {
// Handle newline as Enter key
if (currentLine.current.trim()) {
commandHistory.current.push(currentLine.current);
}
historyIndex.current = commandHistory.current.length;
processCommand(currentLine.current);
currentLine.current = '';
} else {
// Add printable characters to current line and display
term.write(char);
currentLine.current += char;
}
}
}).catch(err => {
console.warn('Failed to read clipboard:', err);
});
menu.remove();
});
menu.appendChild(pasteItem);
document.body.appendChild(menu);
// Remove menu when clicking elsewhere
const removeMenu = (event: MouseEvent) => {
if (!menu.contains(event.target as Node)) {
menu.remove();
document.removeEventListener('click', removeMenu);
}
};
// Use setTimeout to avoid immediate removal
setTimeout(() => {
document.addEventListener('click', removeMenu);
}, 0);
});
term.write('Welcome to Ixabata-shell!\r\n');
term.write('Type a command to begin. (Try ls, cd, cat)\r\n');
prompt();
term.onKey(({ key, domEvent }) => {
if (!xtermRef.current) return;
@ -305,40 +443,142 @@ ALERT_COLOR=#FF0000
let suggestions: string[] = [];
const availableCommands = ['ls', 'cd', 'cat', 'clear', 'help'];
// If we're completing a command (first word)
if (inputParts.length === 0 && !currentLine.current.includes(' ')) {
suggestions = availableCommands.filter(c => c.startsWith(currentArgument));
} else {
const currentDirItems = Array.isArray(fileSystem[currentPath.current]) ? fileSystem[currentPath.current] as string[] : [];
suggestions = currentDirItems.filter(item => item.startsWith(currentArgument));
} else {
// For path completion (after a command)
const command = inputParts[0];
// Only complete paths for commands that accept file paths
if (['ls', 'cd', 'cat'].includes(command)) {
// Handle absolute paths
if (currentArgument.startsWith('/')) {
const pathParts = currentArgument.split('/');
const lastPart = pathParts.pop() || '';
const basePath = pathParts.join('/') || '/';
if (fileSystem[basePath] && Array.isArray(fileSystem[basePath])) {
suggestions = (fileSystem[basePath] as string[])
.filter(item => item.startsWith(lastPart))
.map(item => {
// Return the full path for absolute completions
return basePath === '/' ? '/' + item : basePath + '/' + item;
});
}
} else {
// Handle relative paths
const currentDirItems = Array.isArray(fileSystem[currentPath.current])
? fileSystem[currentPath.current] as string[]
: [];
suggestions = currentDirItems.filter(item => item.startsWith(currentArgument));
}
}
}
if (suggestions.length === 1) {
const suggestion = suggestions[0];
let resolvedSuggestionPath = suggestion;
if (!suggestion.startsWith('/')) {
resolvedSuggestionPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + suggestion;
// Handle path resolution for the suggestion
if (currentArgument.startsWith('/')) {
// For absolute paths, the suggestion is already the full path
resolvedSuggestionPath = suggestion;
} else {
// For relative paths, use the current directory
resolvedSuggestionPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + suggestion;
}
// Normalize path
if (resolvedSuggestionPath !== '/' && resolvedSuggestionPath.endsWith('/')) {
resolvedSuggestionPath = resolvedSuggestionPath.slice(0, -1);
resolvedSuggestionPath = resolvedSuggestionPath.slice(0, -1);
}
const isDirectory = Array.isArray(fileSystem[resolvedSuggestionPath]);
const completion = suggestion + (isDirectory ? '/' : ' ');
xtermRef.current.write(completion.substring(currentArgument.length));
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
let completion: string;
if (currentArgument.startsWith('/')) {
// For absolute paths, replace the entire argument
completion = suggestion + (isDirectory ? '/' : ' ');
xtermRef.current.write('\x1b[2K\r'); // Clear the line
xtermRef.current.write(`root@ixabatasha.life:${currentPath.current}# `);
xtermRef.current.write((commandPart ? commandPart + ' ' : '') + completion);
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
} else {
// For relative paths, append to the existing argument
completion = suggestion + (isDirectory ? '/' : ' ');
xtermRef.current.write(completion.substring(currentArgument.length));
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
}
if (!isDirectory || !completion.endsWith('/')) {
currentLine.current = currentLine.current.trimEnd();
currentLine.current = currentLine.current.trimEnd();
}
} else if (suggestions.length > 1) {
// Show all possible completions in a bash-like format
xtermRef.current.write('\r\n');
// For display purposes, show just the item names, not full paths
const displaySuggestions = suggestions.map(s => {
if (currentArgument.startsWith('/')) {
return s.split('/').pop() || s;
}
return s;
});
// Calculate the longest common prefix
let commonPrefix = '';
if (displaySuggestions.length > 0) {
const first = displaySuggestions[0];
for (let i = 0; i < first.length; i++) {
if (displaySuggestions.every(s => s[i] === first[i])) {
commonPrefix += first[i];
} else {
break;
}
}
}
} else if (suggestions.length > 1) {
xtermRef.current.write('\r\n' + suggestions.join('\t') + '\r\n');
prompt();
xtermRef.current.write(currentLine.current);
// If there's a common prefix, complete up to that point
if (commonPrefix.length > (currentArgument.startsWith('/') ? currentArgument.split('/').pop()?.length || 0 : currentArgument.length)) {
if (currentArgument.startsWith('/')) {
const pathParts = currentArgument.split('/');
pathParts.pop();
const newPath = pathParts.join('/') + '/' + commonPrefix;
xtermRef.current.write('\x1b[2K\r'); // Clear the line
xtermRef.current.write(`root@ixabatasha.life:${currentPath.current}# `);
xtermRef.current.write((commandPart ? commandPart + ' ' : '') + newPath);
currentLine.current = (commandPart ? commandPart + ' ' : '') + newPath;
} else {
xtermRef.current.write(commonPrefix.substring(currentArgument.length));
currentLine.current = (commandPart ? commandPart + ' ' : '') + commonPrefix;
}
} else {
// Display all options in columns
const maxLength = Math.max(...displaySuggestions.map(s => s.length)) + 2;
const columns = Math.floor(term.cols / maxLength);
const rows = Math.ceil(displaySuggestions.length / columns);
for (let row = 0; row < rows; row++) {
let line = '';
for (let col = 0; col < columns; col++) {
const index = row + (col * rows);
if (index < displaySuggestions.length) {
const item = displaySuggestions[index];
const fullPath = suggestions[index];
const isDir = Array.isArray(fileSystem[fullPath.startsWith('/') ? fullPath : (currentPath.current === '/' ? '' : currentPath.current) + '/' + fullPath]);
line += item + (isDir ? '/' : '') + ' '.repeat(maxLength - item.length - (isDir ? 1 : 0));
}
}
xtermRef.current.write(line + '\r\n');
}
// Reprint the prompt and current input
prompt();
xtermRef.current.write(currentLine.current);
}
}
// If no suggestions, do nothing (or beep: xtermRef.current.write('\x07');)
// If no suggestions, do nothing
} else if (printable && key.length === 1) { // Ensure it's a single character, not a special key like 'Shift'
xtermRef.current.write(key);
currentLine.current += key;

View File

@ -26,10 +26,39 @@ const title = "root@ixabatasha.life";
- How to find: Look for the "max retries" count in the security_daemon error
*/
// Define the solutions for the combination lock
const solution1 = 29; // Binary puzzle solution
const solution2 = 31; // Hex color code puzzle solution
const solution3 = 5; // Log file analysis puzzle solution
// Get solutions from environment variables (server-side only)
const solution1 = parseInt(process.env.SOLUTION_1 || import.meta.env.SOLUTION_1 || '29');
const solution2 = parseInt(process.env.SOLUTION_2 || import.meta.env.SOLUTION_2 || '31');
const solution3 = parseInt(process.env.SOLUTION_3 || import.meta.env.SOLUTION_3 || '5');
// Generate puzzle content server-side
const puzzle1Content = `System memory address reference:
Register B1: ${solution1.toString(2).padStart(8, '0')}
Note: Always decode binary values when transferring between subsystems.`;
const puzzle2Content = `# Display Color Settings (RGB hex format)
PRIMARY_COLOR=#FF${solution2.toString(16).padStart(2, '0')}33
SECONDARY_COLOR=#33FF57
ALERT_COLOR=#FF0000
# System Note:
# Color channels must be isolated when importing to legacy devices.
# Remember middle values carry essential security data.`;
const puzzle3Content = `[ERROR 001] System boot failure sequence A.
[INFO 002] Retrying boot sequence B.
[ERROR 003] Disk read error on /dev/sda1.
[WARN 004] Network interface eth0 flapping.
[INFO 005] System recovered.
[DEBUG 006] User 'admin' login attempt.
[ERROR 007] Authentication failure for user 'admin'.
[INFO 008] User 'hacker' login successful.
[WARN 009] Unusual activity detected from IP 192.168.1.101.
[ERROR 010] Critical process 'security_daemon' terminated unexpectedly.
[INFO 011] Attempting to restart 'security_daemon'.
[ERROR 012] Failed to restart 'security_daemon'. Max retries (${solution3}) reached.
[ERROR 014] System shutting down due to critical errors.
[RECOVER] Backup facility requires retry count for authentication sequence.`;
---
<!doctype html>
@ -48,9 +77,9 @@ const solution3 = 5; // Log file analysis puzzle solution
<div id="terminal-container" class="w-full h-full">
<TerminalComponent
client:only="react"
solution1={solution1}
solution2={solution2}
solution3={solution3}
puzzle1Content={puzzle1Content}
puzzle2Content={puzzle2Content}
puzzle3Content={puzzle3Content}
/>
</div>
</body>