From e12bbcfc6ad642bb4cf5e774b9366b01760c010f Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Tue, 22 Jul 2025 15:26:54 +0530 Subject: [PATCH 01/25] Implement Code Challenge component with styling and WebSocket integration --- Frontend/src/App.jsx | 4 +- Frontend/src/components/CodeChallenge.css | 282 ++++++++++++++ Frontend/src/components/CodeChallenge.jsx | 451 ++++++++++++++++++++++ Frontend/src/index.css | 282 ++++++++++++++ 4 files changed, 1017 insertions(+), 2 deletions(-) create mode 100644 Frontend/src/components/CodeChallenge.css create mode 100644 Frontend/src/components/CodeChallenge.jsx diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx index 8f8699c..b19fb9e 100644 --- a/Frontend/src/App.jsx +++ b/Frontend/src/App.jsx @@ -1,10 +1,10 @@ -import VSCodeUI from "./components/VSCodeUI.jsx" +import CodeChallenge from "./components/CodeChallenge.jsx" import "./index.css" function App() { return (
- +
) } diff --git a/Frontend/src/components/CodeChallenge.css b/Frontend/src/components/CodeChallenge.css new file mode 100644 index 0000000..5eaf7e9 --- /dev/null +++ b/Frontend/src/components/CodeChallenge.css @@ -0,0 +1,282 @@ +/* Code Challenge Component Styles */ +.code-challenge-container { + display: flex; + flex-direction: column; + height: 100vh; + background-color: #1e1e1e; + color: #d4d4d4; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +.code-challenge-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + background-color: #1e1e1e; + border-bottom: 1px solid #333; +} + +.code-challenge-header h1 { + margin: 0; + font-size: 18px; + font-weight: 500; +} + +.sign-in-btn { + background-color: #333; + color: #fff; + border: none; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; +} + +.sign-in-btn:hover { + background-color: #444; +} + +.code-challenge-problem-nav { + padding: 10px 20px; + border-bottom: 1px solid #333; +} + +.problem-number { + margin: 0; + font-size: 16px; + font-weight: 500; +} + +.code-challenge-main { + display: flex; + flex-direction: row; + height: calc(70vh - 120px); + overflow: hidden; +} + +.left-panel { + display: flex; + flex-direction: column; + width: 50%; + border-right: 1px solid #333; +} + +.problem-tabs { + display: flex; + width: 100%; + height: 40px; + border-bottom: 1px solid #333; +} + +.problem-tabs button { + min-width: 120px; + padding: 0 20px; + background-color: #252526; + color: #d4d4d4; + border: none; + text-align: center; + cursor: pointer; + border-right: 1px solid #333; + font-size: 14px; +} + +.problem-tabs button.tab-active { + background-color: #1e1e1e; + border-left: 2px solid #0078d4; + color: #ffffff; +} + +.problem-tabs button:hover:not(.tab-active) { + background-color: #252526; +} + +.problem-content { + flex: 1; + overflow-y: auto; + padding: 20px; + border-right: 1px solid #333; +} + +.problem-container h1 { + margin-top: 0; + margin-bottom: 15px; + font-size: 24px; +} + +.problem-description { + margin-bottom: 20px; + line-height: 1.5; +} + +.problem-description code { + background-color: #2d2d2d; + padding: 2px 4px; + border-radius: 3px; + font-family: 'Consolas', 'Courier New', monospace; +} + +/* Removed problem examples styles */ + +.editor-section { + flex: 1; + display: flex; + flex-direction: column; + border-left: 1px solid #333; +} + +.editor-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background-color: #252526; + border-bottom: 1px solid #333; +} + +.editor-controls { + display: flex; + align-items: center; +} + +.language-selector { + background-color: #2d2d2d; + color: #d4d4d4; + border: 1px solid #3c3c3c; + padding: 5px 10px; + border-radius: 3px; + margin-right: 10px; +} + +.auto-btn { + background-color: #252526; + color: #d4d4d4; + border: 1px solid #3c3c3c; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; +} + +.auto-selected { + background-color: #0e639c; + border-color: #0e639c; + color: white; +} + +.editor-actions { + display: flex; + align-items: center; +} + +.run-btn { + display: flex; + align-items: center; + background-color: #252526; + color: #d4d4d4; + border: 1px solid #3c3c3c; + padding: 5px 10px; + border-radius: 3px; + margin-right: 10px; + cursor: pointer; +} + +.submit-btn { + display: flex; + align-items: center; + background-color: #0e8a16; + color: white; + border: none; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; +} + +.run-btn svg, .submit-btn svg { + margin-right: 5px; +} + +.run-btn:hover { + background-color: #2d2d2d; +} + +.submit-btn:hover { + background-color: #0ca01c; +} + +.editor-container { + flex: 1; + overflow: hidden; +} + +.terminal-section { + height: 30vh; + background-color: #1e1e1e; + border-top: 1px solid #333; +} + +.terminal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 10px; + background-color: #252526; + border-bottom: 1px solid #333; +} + +.terminal-controls { + display: flex; + align-items: center; +} + +.terminal-btn { + background: none; + border: none; + color: #d4d4d4; + font-size: 12px; + cursor: pointer; + margin-left: 5px; +} + +.terminal-content { + padding: 10px; + font-family: 'Consolas', 'Courier New', monospace; + height: calc(30vh - 40px); + overflow-y: auto; +} + +.terminal-line { + margin-bottom: 5px; + word-wrap: break-word; + white-space: pre-wrap; +} + +.terminal-line.system { + color: #569cd6; +} + +.terminal-line.output { + color: #d4d4d4; +} + +.terminal-line.error { + color: #f48771; +} + +.terminal-prompt { + display: flex; + align-items: center; + margin-top: 5px; +} + +.prompt-symbol { + margin-right: 5px; + color: #569cd6; +} + +.terminal-input { + flex: 1; + background: none; + border: none; + color: #d4d4d4; + font-family: 'Consolas', 'Courier New', monospace; + outline: none; +} diff --git a/Frontend/src/components/CodeChallenge.jsx b/Frontend/src/components/CodeChallenge.jsx new file mode 100644 index 0000000..9dae839 --- /dev/null +++ b/Frontend/src/components/CodeChallenge.jsx @@ -0,0 +1,451 @@ +import React, { useState, useEffect, useRef } from 'react'; +import Editor from "@monaco-editor/react"; +import { Play, Send } from 'lucide-react'; + +const CodeChallenge = () => { + const [activeQuestion, setActiveQuestion] = useState("Q.1"); + const [language, setLanguage] = useState("JavaScript"); + const [code, setCode] = useState(""); + const [isRunning, setIsRunning] = useState(false); + const [terminalOutput, setTerminalOutput] = useState([]); + const [autoSelected, setAutoSelected] = useState(true); + const [activeSocket, setActiveSocket] = useState(null); + const [submissionId, setSubmissionId] = useState(null); + const socketRef = useRef(null); + + // Example problem data + const problems = { + "Q.1": { + id: "two-sum", + title: "Two Sum", + description: "Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.", + constraints: "You may assume that each input would have exactly one solution, and you may not use the same element twice.", + examples: [ + { + input: "nums = [2,7,11,15], target = 9", + output: "[0,1]", + explanation: "Because nums[0] + nums[1] == 9, we return [0, 1]." + }, + { + input: "nums = [3,2,4], target = 6", + output: "[1,2]" + }, + { + input: "nums = [3,3], target = 6", + output: "[0,1]" + } + ], + starterCode: `/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + // Write your solution here + +};` + }, + "Q.2": { + id: "palindrome-number", + title: "Palindrome Number", + description: "Given an integer x, return true if x is a palindrome, and false otherwise.", + examples: [ + { + input: "x = 121", + output: "true" + }, + { + input: "x = -121", + output: "false", + explanation: "From left to right, it reads -121. From right to left, it reads 121-. Therefore it is not a palindrome." + } + ], + starterCode: `/** + * @param {number} x + * @return {boolean} + */ +var isPalindrome = function(x) { + // Write your solution here + +};` + }, + "Q.3": { + id: "valid-parentheses", + title: "Valid Parentheses", + description: "Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.", + constraints: "An input string is valid if: Open brackets must be closed by the same type of brackets. Open brackets must be closed in the correct order.", + examples: [ + { + input: 's = "()"', + output: "true" + }, + { + input: 's = "()[]{}"', + output: "true" + }, + { + input: 's = "(]"', + output: "false" + } + ], + starterCode: `/** + * @param {string} s + * @return {boolean} + */ +var isValid = function(s) { + // Write your solution here + +};` + } + }; + + // Set initial code based on active problem + useEffect(() => { + if (problems[activeQuestion]) { + setCode(problems[activeQuestion].starterCode); + } + }, [activeQuestion]); + + // Cleanup WebSocket connection on unmount + useEffect(() => { + return () => { + if (socketRef.current) { + socketRef.current.close(); + } + }; + }, []); + + // Connect to WebSocket + const connectToWebSocket = (id) => { + // Close existing connection if any + if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) { + socketRef.current.close(); + } + + const wsUrl = `ws://localhost:8080/api/ws/terminal/${id}`; + const socket = new WebSocket(wsUrl); + + socket.onopen = () => { + console.log('WebSocket connection established'); + setActiveSocket(socket); + }; + + socket.onmessage = (event) => { + try { + const message = JSON.parse(event.data); + + switch (message.type) { + case 'output': + setTerminalOutput(prev => [ + ...prev, + { + type: message.content.isError ? 'error' : 'output', + content: message.content.text + } + ]); + break; + + case 'status': + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: `Status: ${message.content.status}` } + ]); + + if (message.content.status === 'completed' || message.content.status === 'failed') { + setIsRunning(false); + } + break; + + case 'error': + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: message.content.message } + ]); + setIsRunning(false); + break; + + case 'system': + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: message.content } + ]); + break; + + default: + console.log('Unknown message type:', message); + } + } catch (error) { + console.error('Error parsing WebSocket message:', error); + } + }; + + socket.onerror = (error) => { + console.error('WebSocket error:', error); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: 'WebSocket connection error' } + ]); + setIsRunning(false); + }; + + socket.onclose = () => { + console.log('WebSocket connection closed'); + setActiveSocket(null); + }; + + socketRef.current = socket; + return socket; + }; + + // Handle code execution + const runCode = async () => { + setIsRunning(true); + setTerminalOutput([ + { type: 'system', content: `Running ${problems[activeQuestion].id}...` } + ]); + + try { + // Submit code to the backend + const response = await fetch('http://localhost:8080/api/submit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + code: code, + language: language.toLowerCase(), + input: '', // Add input if needed + }), + }); + + if (!response.ok) { + throw new Error(`Error: ${response.statusText}`); + } + + const data = await response.json(); + setSubmissionId(data.id); + + // Connect to WebSocket for real-time updates + connectToWebSocket(data.id); + + } catch (error) { + console.error('Error submitting code:', error); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Error: ${error.message}` } + ]); + setIsRunning(false); + } + }; + + // Handle code submission + const submitCode = async () => { + setIsRunning(true); + setTerminalOutput([ + { type: 'system', content: `Submitting solution for ${problems[activeQuestion].id}...` } + ]); + + try { + // Submit code to the backend + const response = await fetch('http://localhost:8080/api/submit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + code: code, + language: language.toLowerCase(), + input: '', + problemId: problems[activeQuestion].id + }), + }); + + if (!response.ok) { + throw new Error(`Error: ${response.statusText}`); + } + + const data = await response.json(); + setSubmissionId(data.id); + + // Connect to WebSocket for real-time updates + connectToWebSocket(data.id); + + } catch (error) { + console.error('Error submitting solution:', error); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Error: ${error.message}` } + ]); + setIsRunning(false); + } + }; + + // Render the current problem + const renderProblem = () => { + const problem = problems[activeQuestion]; + if (!problem) return null; + + return ( +
+

{problem.title}

+ +
+

{problem.description}

+ {problem.constraints &&

{problem.constraints}

} +
+ + {/* Test cases section removed */} +
+ ); + }; + + return ( +
+
+

OnScreen Test

+ +
+ +
+

1. {problems["Q.1"].title}

+
+ +
+
+ + + +
+ +
+ {renderProblem()} +
+ +
+
+
+ + + +
+ +
+ + + +
+
+ +
+ setCode(value)} + theme="vs-dark" + options={{ + fontSize: 14, + minimap: { enabled: false }, + scrollBeyondLastLine: false, + automaticLayout: true, + }} + /> +
+
+
+ +
+
+ Terminal +
+ + + +
+
+
+ {terminalOutput.map((line, index) => ( +
+ {line.content} +
+ ))} +
+ $ + { + if (e.key === 'Enter' && activeSocket) { + const input = e.target.value; + // Send input to WebSocket + activeSocket.send(JSON.stringify({ + type: 'input', + content: { text: input } + })); + + // Add input to terminal output + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: `$ ${input}` } + ]); + + // Clear the input field + e.target.value = ''; + } + }} + /> +
+
+
+
+ ); +}; + +export default CodeChallenge; diff --git a/Frontend/src/index.css b/Frontend/src/index.css index c762840..e714630 100644 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -1025,4 +1025,286 @@ body { .panel-close-btn:hover { opacity: 1; +} + +/* Code Challenge Component Styles */ +.code-challenge-container { + display: flex; + flex-direction: column; + height: 100vh; + background-color: var(--vscode-background); + color: var(--vscode-foreground); +} + +.code-challenge-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + background-color: var(--vscode-background); + border-bottom: 1px solid rgba(128, 128, 128, 0.35); +} + +.code-challenge-header h1 { + margin: 0; + font-size: 18px; + font-weight: 400; +} + +.sign-in-btn { + background-color: transparent; + color: var(--vscode-foreground); + border: 1px solid rgba(128, 128, 128, 0.5); + padding: 4px 12px; + border-radius: 4px; + font-size: 14px; + cursor: pointer; +} + +.sign-in-btn:hover { + background-color: rgba(255, 255, 255, 0.05); +} + +.code-challenge-problem-nav { + padding: 8px 16px; + border-bottom: 1px solid rgba(128, 128, 128, 0.35); +} + +.problem-number { + margin: 0; + font-size: 16px; + font-weight: 400; +} + +.code-challenge-main { + display: flex; + height: 60vh; + border-bottom: 1px solid rgba(128, 128, 128, 0.35); +} + +.problem-tabs { + display: flex; + flex-direction: column; + width: 130px; + border-right: 1px solid rgba(128, 128, 128, 0.35); +} + +.problem-tabs button { + padding: 16px; + background-color: transparent; + color: var(--vscode-foreground); + border: none; + text-align: left; + cursor: pointer; + border-bottom: 1px solid rgba(128, 128, 128, 0.2); +} + +.problem-tabs button.tab-active { + background-color: var(--vscode-background); + font-weight: 500; + border-left: 2px solid #007acc; +} + +.problem-tabs button:hover:not(.tab-active) { + background-color: rgba(255, 255, 255, 0.05); +} + +.problem-content { + flex: 1; + overflow-y: auto; + padding: 16px; + border-right: 1px solid rgba(128, 128, 128, 0.35); + width: 50%; +} + +.problem-container h1 { + margin-top: 0; + font-size: 22px; + margin-bottom: 16px; +} + +.problem-description { + margin-bottom: 20px; + font-size: 14px; + line-height: 1.5; +} + +.problem-examples h2 { + font-size: 16px; + margin-top: 24px; + margin-bottom: 12px; +} + +.example-box { + background-color: rgba(0, 0, 0, 0.3); + padding: 12px; + border-radius: 6px; + margin-bottom: 12px; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 14px; + line-height: 1.5; +} + +.editor-section { + width: 50%; + display: flex; + flex-direction: column; +} + +.editor-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + background-color: #252526; + border-bottom: 1px solid rgba(128, 128, 128, 0.35); +} + +.editor-controls { + display: flex; + align-items: center; +} + +.language-selector { + background-color: #3c3c3c; + color: #d4d4d4; + border: 1px solid #3c3c3c; + border-radius: 4px; + padding: 4px 8px; + font-size: 13px; + margin-right: 8px; +} + +.auto-btn { + background-color: #3c3c3c; + color: #d4d4d4; + border: none; + border-radius: 4px; + padding: 4px 12px; + font-size: 13px; + cursor: pointer; +} + +.auto-selected { + background-color: #4d4d4d; +} + +.editor-actions { + display: flex; + gap: 8px; +} + +.run-btn { + display: flex; + align-items: center; + gap: 4px; + background-color: #3c3c3c; + color: #d4d4d4; + border: none; + border-radius: 4px; + padding: 4px 12px; + font-size: 13px; + cursor: pointer; +} + +.submit-btn { + display: flex; + align-items: center; + gap: 4px; + background-color: #0e639c; + color: #ffffff; + border: none; + border-radius: 4px; + padding: 4px 12px; + font-size: 13px; + cursor: pointer; +} + +.run-btn:hover { + background-color: #4d4d4d; +} + +.submit-btn:hover { + background-color: #1177bb; +} + +.editor-container { + flex: 1; +} + +.terminal-section { + flex: 1; + display: flex; + flex-direction: column; + background-color: var(--vscode-panel-background); +} + +.terminal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 12px; + background-color: #252526; + font-size: 13px; +} + +.terminal-controls { + display: flex; + gap: 4px; +} + +.terminal-btn { + background-color: transparent; + color: #d4d4d4; + border: none; + cursor: pointer; + font-size: 12px; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; +} + +.terminal-content { + flex: 1; + padding: 8px 12px; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 14px; + overflow-y: auto; + white-space: pre-wrap; +} + +.terminal-line { + margin-bottom: 4px; + line-height: 1.4; +} + +.terminal-line.system { + color: #569cd6; +} + +.terminal-line.error { + color: #f48771; +} + +.terminal-prompt { + display: flex; + align-items: center; + margin-top: 8px; +} + +.prompt-symbol { + color: #569cd6; + margin-right: 8px; +} + +.terminal-input { + background-color: transparent; + color: #d4d4d4; + border: none; + outline: none; + flex: 1; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 14px; } \ No newline at end of file From 6964f370cb16d94b04d5e80724f94fe2dbed4f06 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Tue, 22 Jul 2025 15:32:59 +0530 Subject: [PATCH 02/25] Enhance WebSocket message handling to support new message types and improve error reporting --- Frontend/src/components/CodeChallenge.jsx | 53 +++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/Frontend/src/components/CodeChallenge.jsx b/Frontend/src/components/CodeChallenge.jsx index 9dae839..9098758 100644 --- a/Frontend/src/components/CodeChallenge.jsx +++ b/Frontend/src/components/CodeChallenge.jsx @@ -133,9 +133,11 @@ var isValid = function(s) { socket.onmessage = (event) => { try { const message = JSON.parse(event.data); + console.log('WebSocket message received:', message); switch (message.type) { case 'output': + // Handle output message based on the format seen in the screenshot setTerminalOutput(prev => [ ...prev, { @@ -144,22 +146,48 @@ var isValid = function(s) { } ]); break; - - case 'status': + + case 'input_prompt': + // Handle input prompt message (e.g., "Enter your name:") setTerminalOutput(prev => [ ...prev, - { type: 'system', content: `Status: ${message.content.status}` } + { type: 'output', content: message.content } + ]); + break; + + case 'status': + let statusText = ''; + if (typeof message.content === 'object') { + statusText = `Status: ${message.content.status}`; + } else { + statusText = `Status: ${message.content}`; + } + + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: statusText } ]); - if (message.content.status === 'completed' || message.content.status === 'failed') { + // If status contains "completed" or "failed", stop running + if ((typeof message.content === 'string' && + (message.content.includes('completed') || message.content.includes('failed'))) || + (message.content.status && + (message.content.status.includes('completed') || message.content.status.includes('failed')))) { setIsRunning(false); } break; case 'error': + let errorContent = ''; + if (typeof message.content === 'object' && message.content.message) { + errorContent = message.content.message; + } else { + errorContent = String(message.content); + } + setTerminalOutput(prev => [ ...prev, - { type: 'error', content: message.content.message } + { type: 'error', content: errorContent } ]); setIsRunning(false); break; @@ -167,12 +195,19 @@ var isValid = function(s) { case 'system': setTerminalOutput(prev => [ ...prev, - { type: 'system', content: message.content } + { type: 'system', content: String(message.content) } ]); break; default: + // Handle any other message types or direct string content console.log('Unknown message type:', message); + if (typeof message === 'object') { + setTerminalOutput(prev => [ + ...prev, + { type: 'output', content: JSON.stringify(message) } + ]); + } } } catch (error) { console.error('Error parsing WebSocket message:', error); @@ -424,10 +459,10 @@ var isValid = function(s) { onKeyPress={(e) => { if (e.key === 'Enter' && activeSocket) { const input = e.target.value; - // Send input to WebSocket + // Send input to WebSocket with the correct format activeSocket.send(JSON.stringify({ - type: 'input', - content: { text: input } + "type": "input", + "content": input })); // Add input to terminal output From 233be39b7ffb83eb31d02d174128fd0503154fb2 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Tue, 22 Jul 2025 15:47:24 +0530 Subject: [PATCH 03/25] Add language identifier mapping and starter code templates for multiple languages in CodeChallenge component --- Frontend/src/components/CodeChallenge.jsx | 85 +++++++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/Frontend/src/components/CodeChallenge.jsx b/Frontend/src/components/CodeChallenge.jsx index 9098758..0749247 100644 --- a/Frontend/src/components/CodeChallenge.jsx +++ b/Frontend/src/components/CodeChallenge.jsx @@ -12,6 +12,19 @@ const CodeChallenge = () => { const [activeSocket, setActiveSocket] = useState(null); const [submissionId, setSubmissionId] = useState(null); const socketRef = useRef(null); + + // Map frontend language names to backend language identifiers + const getLanguageIdentifier = (uiLanguage) => { + const languageMap = { + 'javascript': 'javascript', + 'python': 'python', + 'java': 'java', + 'c++': 'cpp', + 'c': 'c', + 'go': 'golang' + }; + return languageMap[uiLanguage.toLowerCase()] || uiLanguage.toLowerCase(); + }; // Example problem data const problems = { @@ -99,12 +112,72 @@ var isValid = function(s) { } }; + // Get appropriate starter code based on language + const getStarterCode = (problem, lang) => { + // Default JavaScript starter code is in the problem object + if (lang === 'JavaScript') { + return problem.starterCode; + } + + // Language-specific starter code templates + const templates = { + 'C': `#include +#include +#include + +// ${problem.title} solution + +int main() { + // Write your solution here + + return 0; +}`, + 'Go': `package main + +import ( + "fmt" +) + +// ${problem.title} solution +func main() { + // Write your solution here + +}`, + 'Python': `# ${problem.title} +def solution(): + # Write your solution here + pass + +if __name__ == "__main__": + solution()`, + 'Java': `public class Solution { + // ${problem.title} + public static void main(String[] args) { + // Write your solution here + + } +}`, + 'C++': `#include +#include +using namespace std; + +// ${problem.title} solution +int main() { + // Write your solution here + + return 0; +}` + }; + + return templates[lang] || problem.starterCode; + }; + // Set initial code based on active problem useEffect(() => { if (problems[activeQuestion]) { - setCode(problems[activeQuestion].starterCode); + setCode(getStarterCode(problems[activeQuestion], language)); } - }, [activeQuestion]); + }, [activeQuestion, language]); // Cleanup WebSocket connection on unmount useEffect(() => { @@ -248,7 +321,7 @@ var isValid = function(s) { }, body: JSON.stringify({ code: code, - language: language.toLowerCase(), + language: getLanguageIdentifier(language), input: '', // Add input if needed }), }); @@ -289,7 +362,7 @@ var isValid = function(s) { }, body: JSON.stringify({ code: code, - language: language.toLowerCase(), + language: getLanguageIdentifier(language), input: '', problemId: problems[activeQuestion].id }), @@ -383,6 +456,8 @@ var isValid = function(s) { + + @@ -475,8 +615,8 @@ int main() {
setCode(value)} theme="vs-dark" @@ -516,23 +656,92 @@ int main() { className="terminal-input" placeholder="Type here..." disabled={!isRunning} + // Update the ref callback + ref={(inputEl) => { + // Auto-focus input when isRunning changes to true + if (inputEl && isRunning) { + inputEl.focus(); + // Clear any previous input + inputEl.value = ''; + } + }} + onKeyDown={(e) => { // Change from onKeyPress to onKeyDown for better cross-browser support + if (e.key === 'Enter') { + e.preventDefault(); // Prevent default to avoid form submissions + const input = e.target.value.trim(); + + if (!input) return; // Skip empty input + + if (activeSocket && activeSocket.readyState === WebSocket.OPEN) { + try { + // Send input to server + activeSocket.send(JSON.stringify({ + "type": "input", + "content": input + })); + + // Add input to terminal output + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: `$ ${input}` } + ]); + + // Clear the input field + e.target.value = ''; + } catch (error) { + console.error("Error sending input:", error); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Failed to send input: ${error.message}` } + ]); + } + } else { + // Better error message with socket state information + const socketState = activeSocket ? + ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'][activeSocket.readyState] : + 'NO_SOCKET'; + + console.log(`Cannot send input: Socket state is ${socketState}`); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Cannot send input: connection not available (${socketState})` } + ]); + } + } + }} onKeyPress={(e) => { - if (e.key === 'Enter' && activeSocket) { + if (e.key === 'Enter' && activeSocket && activeSocket.readyState === WebSocket.OPEN) { const input = e.target.value; // Send input to WebSocket with the correct format - activeSocket.send(JSON.stringify({ - "type": "input", - "content": input - })); - - // Add input to terminal output - setTerminalOutput(prev => [ - ...prev, - { type: 'system', content: `$ ${input}` } - ]); - - // Clear the input field - e.target.value = ''; + try { + activeSocket.send(JSON.stringify({ + "type": "input", + "content": input + })); + + // Add input to terminal output + setTerminalOutput(prev => [ + ...prev, + { type: 'system', content: `$ ${input}` } + ]); + + // Clear the input field + e.target.value = ''; + } catch (error) { + console.error("Error sending input:", error); + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Failed to send input: ${error.message}` } + ]); + } + } else if (e.key === 'Enter') { + // Inform user if socket isn't available + if (!activeSocket || activeSocket.readyState !== WebSocket.OPEN) { + setTerminalOutput(prev => [ + ...prev, + { type: 'error', content: `Cannot send input: connection closed` } + ]); + } } }} /> From 4654b93b15a9858a4eb36261be2e5400c8e00864 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Wed, 23 Jul 2025 17:16:23 +0530 Subject: [PATCH 06/25] Enhance styling in CodeChallenge component for improved UI and readability --- Frontend/src/components/CodeChallenge.css | 14 +++++++------- Frontend/src/components/CodeChallenge.jsx | 17 ++++++----------- Frontend/src/index.css | 16 +++++++++------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Frontend/src/components/CodeChallenge.css b/Frontend/src/components/CodeChallenge.css index 15a1fad..eadd2f3 100644 --- a/Frontend/src/components/CodeChallenge.css +++ b/Frontend/src/components/CodeChallenge.css @@ -12,15 +12,15 @@ display: flex; justify-content: space-between; align-items: center; - padding: 10px 20px; + padding: 20px 20px; background-color: #1e1e1e; border-bottom: 1px solid #333; } .code-challenge-header h1 { margin: 0; - font-size: 18px; - font-weight: 500; + font-size: 25px; + font-weight: 600; } .sign-in-btn { @@ -43,7 +43,7 @@ .problem-number { margin: 0; - font-size: 16px; + font-size: 10px; font-weight: 500; } @@ -62,15 +62,14 @@ } .problem-tabs { - display: flex; width: 100%; height: 40px; border-bottom: 1px solid #333; } .problem-tabs button { - min-width: 120px; - padding: 0 20px; + min-width: 70px; + padding: 0 15px; background-color: #252526; color: #d4d4d4; border: none; @@ -219,6 +218,7 @@ .editor-container { flex: 1; overflow: hidden; + background-color: #000000; } .terminal-section { diff --git a/Frontend/src/components/CodeChallenge.jsx b/Frontend/src/components/CodeChallenge.jsx index bd01663..d56bed5 100644 --- a/Frontend/src/components/CodeChallenge.jsx +++ b/Frontend/src/components/CodeChallenge.jsx @@ -522,9 +522,9 @@ int main() { -
+ {/*

1. {problems["Q.1"].title}

-
+
*/}
@@ -567,12 +567,7 @@ int main() { - +
@@ -619,7 +614,7 @@ int main() { language={language.toLowerCase() === 'c++' ? 'cpp' : language.toLowerCase()} value={code} onChange={(value) => setCode(value)} - theme="vs-dark" + theme="hc-black" options={{ fontSize: 14, minimap: { enabled: false }, @@ -634,11 +629,11 @@ int main() {
Terminal -
+ {/*
-
+
*/}
{terminalOutput.map((line, index) => ( diff --git a/Frontend/src/index.css b/Frontend/src/index.css index e714630..17454e2 100644 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -1,17 +1,17 @@ :root { - --vscode-background: #1e1e1e; + --vscode-background: #000000; --vscode-foreground: #d4d4d4; --vscode-activityBar-background: #333333; --vscode-activityBar-foreground: #ffffff; --vscode-activityBar-inactiveForeground: #ffffff80; --vscode-sideBar-background: #252526; --vscode-sideBar-foreground: #cccccc; - --vscode-editor-background: #1e1e1e; + --vscode-editor-background: #000000; --vscode-statusBar-background: #007acc; --vscode-statusBar-foreground: #ffffff; - --vscode-panel-background: #1e1e1e; + --vscode-panel-background: #000000; --vscode-panel-border: #80808059; - --vscode-tab-activeBackground: #1e1e1e; + --vscode-tab-activeBackground: #000000; --vscode-tab-inactiveBackground: #2d2d2d; --vscode-tab-activeForeground: #ffffff; --vscode-tab-inactiveForeground: #ffffff80; @@ -1040,15 +1040,17 @@ body { display: flex; justify-content: space-between; align-items: center; - padding: 8px 16px; + padding: 12px 16px; background-color: var(--vscode-background); border-bottom: 1px solid rgba(128, 128, 128, 0.35); + } .code-challenge-header h1 { margin: 0; - font-size: 18px; + font-size: 25px; font-weight: 400; + font-weight: bold; } .sign-in-btn { @@ -1085,7 +1087,7 @@ body { .problem-tabs { display: flex; flex-direction: column; - width: 130px; + width: 80px; border-right: 1px solid rgba(128, 128, 128, 0.35); } From ac123608227126c9fe7b421e9545bf096664551d Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Wed, 23 Jul 2025 17:17:12 +0530 Subject: [PATCH 07/25] Remove CodeChallenge component CSS file to streamline styling management --- Frontend/src/components/CodeChallenge.css | 296 ---------------------- 1 file changed, 296 deletions(-) delete mode 100644 Frontend/src/components/CodeChallenge.css diff --git a/Frontend/src/components/CodeChallenge.css b/Frontend/src/components/CodeChallenge.css deleted file mode 100644 index eadd2f3..0000000 --- a/Frontend/src/components/CodeChallenge.css +++ /dev/null @@ -1,296 +0,0 @@ -/* Code Challenge Component Styles */ -.code-challenge-container { - display: flex; - flex-direction: column; - height: 100vh; - background-color: #1e1e1e; - color: #d4d4d4; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; -} - -.code-challenge-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 20px; - background-color: #1e1e1e; - border-bottom: 1px solid #333; -} - -.code-challenge-header h1 { - margin: 0; - font-size: 25px; - font-weight: 600; -} - -.sign-in-btn { - background-color: #333; - color: #fff; - border: none; - padding: 5px 10px; - border-radius: 3px; - cursor: pointer; -} - -.sign-in-btn:hover { - background-color: #444; -} - -.code-challenge-problem-nav { - padding: 10px 20px; - border-bottom: 1px solid #333; -} - -.problem-number { - margin: 0; - font-size: 10px; - font-weight: 500; -} - -.code-challenge-main { - display: flex; - flex-direction: row; - height: calc(70vh - 120px); - overflow: hidden; -} - -.left-panel { - display: flex; - flex-direction: column; - width: 50%; - border-right: 1px solid #333; -} - -.problem-tabs { - width: 100%; - height: 40px; - border-bottom: 1px solid #333; -} - -.problem-tabs button { - min-width: 70px; - padding: 0 15px; - background-color: #252526; - color: #d4d4d4; - border: none; - text-align: center; - cursor: pointer; - border-right: 1px solid #333; - font-size: 14px; -} - -.problem-tabs button.tab-active { - background-color: #1e1e1e; - border-left: 2px solid #0078d4; - color: #ffffff; -} - -.problem-tabs button:hover:not(.tab-active) { - background-color: #252526; -} - -.problem-content { - flex: 1; - overflow-y: auto; - padding: 20px; - border-right: 1px solid #333; -} - -.problem-container h1 { - margin-top: 0; - margin-bottom: 15px; - font-size: 24px; -} - -.problem-description { - margin-bottom: 20px; - line-height: 1.5; -} - -.problem-description code { - background-color: #2d2d2d; - padding: 2px 4px; - border-radius: 3px; - font-family: 'Consolas', 'Courier New', monospace; -} - -.problem-examples h2 { - font-size: 18px; - margin-top: 20px; - margin-bottom: 10px; -} - -.example-box { - background-color: #161616; - border-radius: 5px; - padding: 15px; - margin-bottom: 15px; - line-height: 1.5; - font-family: 'Consolas', 'Courier New', monospace; - border-left: 3px solid #444; -} - -.editor-section { - flex: 1; - display: flex; - flex-direction: column; - border-left: 1px solid #333; -} - -.editor-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px; - background-color: #252526; - border-bottom: 1px solid #333; -} - -.editor-controls { - display: flex; - align-items: center; -} - -.language-selector { - background-color: #2d2d2d; - color: #d4d4d4; - border: 1px solid #3c3c3c; - padding: 5px 10px; - border-radius: 3px; - margin-right: 10px; -} - -.auto-btn { - background-color: #252526; - color: #d4d4d4; - border: 1px solid #3c3c3c; - padding: 5px 10px; - border-radius: 3px; - cursor: pointer; -} - -.auto-selected { - background-color: #0e639c; - border-color: #0e639c; - color: white; -} - -.editor-actions { - display: flex; - align-items: center; -} - -.run-btn { - display: flex; - align-items: center; - background-color: #252526; - color: #d4d4d4; - border: 1px solid #3c3c3c; - padding: 5px 10px; - border-radius: 3px; - margin-right: 10px; - cursor: pointer; -} - -.submit-btn { - display: flex; - align-items: center; - background-color: #0e8a16; - color: white; - border: none; - padding: 5px 10px; - border-radius: 3px; - cursor: pointer; -} - -.run-btn svg, .submit-btn svg { - margin-right: 5px; -} - -.run-btn:hover { - background-color: #2d2d2d; -} - -.submit-btn:hover { - background-color: #0ca01c; -} - -.editor-container { - flex: 1; - overflow: hidden; - background-color: #000000; -} - -.terminal-section { - height: 30vh; - background-color: #1e1e1e; - border-top: 1px solid #333; -} - -.terminal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 5px 10px; - background-color: #252526; - border-bottom: 1px solid #333; -} - -.terminal-controls { - display: flex; - align-items: center; -} - -.terminal-btn { - background: none; - border: none; - color: #d4d4d4; - font-size: 12px; - cursor: pointer; - margin-left: 5px; -} - -.terminal-content { - padding: 10px; - font-family: 'Consolas', 'Courier New', monospace; - height: calc(30vh - 40px); - overflow-y: auto; -} - -.terminal-line { - margin-bottom: 5px; - word-wrap: break-word; - white-space: pre-wrap; -} - -.terminal-line.system { - color: #569cd6; -} - -.terminal-line.output { - color: #d4d4d4; -} - -.terminal-line.error { - color: #f48771; -} - -.terminal-prompt { - display: flex; - align-items: center; - margin-top: 5px; -} - -.prompt-symbol { - margin-right: 5px; - color: #569cd6; -} - -.terminal-input { - flex: 1; - background: none; - border: none; - color: #d4d4d4; - font-family: 'Consolas', 'Courier New', monospace; - outline: none; -} From d501b53ab68e2f3f388e49227afcd97e29390fc4 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Wed, 23 Jul 2025 17:25:21 +0530 Subject: [PATCH 08/25] Add favicon.ico and link it in index.html for improved branding --- Frontend/index.html | 1 + Frontend/public/favicon.ico | Bin 0 -> 1395 bytes 2 files changed, 1 insertion(+) create mode 100644 Frontend/public/favicon.ico diff --git a/Frontend/index.html b/Frontend/index.html index 238f04b..174b698 100644 --- a/Frontend/index.html +++ b/Frontend/index.html @@ -3,6 +3,7 @@ + VSCode diff --git a/Frontend/public/favicon.ico b/Frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b02b952c8922ffcb2f8eb52accfb47dad76329df GIT binary patch literal 1395 zcmV-(1&sQMP)A00iGjL_t(o!>v~dmg^`C1cZ>p7VdxA z5sTf9V<+j~!+VJn8_H-a6~??BzOb?8uY-UTg&-UMZvZK>F#!u(4xQ8 zKcZyvfyt#8^gJ#>-TaVuzGmaWN=S|qy;|Sb8;+61s>kn90MQch^^B5eTm>~~Hox;L zhWPwkYzfx&Ul?G!NAXQ6`lx&DS909H3oM{vPe|csA`rTpJdNN-GO)vxC^+a7Q>z07 zU97YP*-I{T2}5Wjt>0(^`#?RYx}T0BAVEDsTZ~B{^QAp?dzGeYVYR;YxlCd0URFtCV==Y;W zkTO;(vL%Ra#J`IepEtBxub(XN6lyEyXpr&l~vsL>D^2 zJ6Yrd_&7X`p3>qraZqKmZp;mbTvpF0TS(zqo)W?V#lrdnlAB+Xw!O5cSP*KV+4&Y9 z{LdS78#r%M%brg0!`6s`u)yqJNNuyjXCdR>F2DtDp2z>jV6z3Lk5o<;5M zo%W=?z%a~wsreNoPCoK6D(7p5b}P#EJlltxI_l_SjqRN*O({}mqwJjrezwDOAcl6} z*YRMcGm#@R!V-J3q@Ag?Iiad;f_54uJ%%ge0LM+6)!A;t$gM-y_-4TLt@bxr9Z{`` zTc7@NRPHg3!l|sBlmNQfbRv}Zqn|Pgbu0YlNspc?(4J(UhtADTo%}>Q996%}9wXnt z^i*NV%<-7-!^^N_e*Frh>v)CmmJVkTm8U>MzN?!)n==x`d3zE&(p|ko1jF0t{J!j>FkS2!=eV&3+*;DRm~-v@x54F21vJH43gK^|Q@ql&Q@13 Date: Wed, 23 Jul 2025 19:31:13 +0530 Subject: [PATCH 09/25] Add Dockerfile and Nginx configuration for multi-stage build setup --- Dockerfile | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 29 +++++++++++++++++++++++++ nginx/nginx.conf | 26 ++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 Dockerfile create mode 100644 nginx.conf create mode 100644 nginx/nginx.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fe5494b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,56 @@ +# Stage 1: Build the React frontend +FROM node:18-alpine AS frontend-builder +WORKDIR /app/frontend + +# Define a build-time argument for the API URL with a default value +ARG VITE_API_URL="" +# Set it as an environment variable for the build command +ENV VITE_API_URL=$VITE_API_URL + +# Copy package files and install dependencies +COPY Frontend/package.json Frontend/package-lock.json* Frontend/yarn.lock* ./ +RUN yarn install --frozen-lockfile + +# Copy the rest of the frontend source code +COPY Frontend/ ./ + +# Build the static files. Vite will use the VITE_API_URL env var. +RUN yarn build + +# Stage 2: Build the Go backend +FROM golang:1.19-alpine AS backend-builder +WORKDIR /app/backend + +# Install git for dependency fetching +RUN apk update && apk add --no-cache git + +# Copy go module files and download dependencies +COPY new-backend/go.mod new-backend/go.sum ./ +RUN go mod download + +# Copy the backend source code +COPY new-backend/ ./ + +# Build the Go binary +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o /monaco-backend . + +# Stage 3: Create the final image with Nginx +FROM nginx:1.25-alpine + +# Install Docker client for the backend +RUN apk update && apk add --no-cache docker-cli + +# Copy the Go backend binary +COPY --from=backend-builder /monaco-backend /usr/local/bin/monaco-backend + +# Copy the built frontend files to the Nginx html directory +COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html + +# Copy the Nginx configuration +COPY nginx.conf /etc/nginx/nginx.conf + +# Expose the public port for Nginx +EXPOSE 80 + +# Start both the backend and Nginx +CMD sh -c 'monaco-backend & nginx -g "daemon off;"' \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..e30e44b --- /dev/null +++ b/nginx.conf @@ -0,0 +1,29 @@ +events {} + +http { + include /etc/nginx/mime.types; # <-- ADD THIS LINE + + server { + listen 80; + server_name localhost; + + # Serve the static frontend files + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + # Proxy API requests to the Go backend + location /api/ { + proxy_pass http://localhost:8081; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..b0a7d44 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,26 @@ +events {} + +http { + include /etc/nginx/mime.types; # <-- ADD THIS LINE + + server { + listen 80; + server_name localhost; + + # Serve the static frontend files + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + # Proxy API requests to the Go backend + location /api/ { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file From 402235bdecdb8d80355493144ff7ca039475294c Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Fri, 8 Aug 2025 13:37:30 +0530 Subject: [PATCH 10/25] Remove unused vite.svg file to declutter the project --- Frontend/public/vite.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Frontend/public/vite.svg diff --git a/Frontend/public/vite.svg b/Frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/Frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 5104fcbde0e96df36eb3892fd569a220a894af8d Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Fri, 8 Aug 2025 13:37:52 +0530 Subject: [PATCH 11/25] java timeout --- new-backend/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/new-backend/config/config.go b/new-backend/config/config.go index a893c6b..412eaea 100644 --- a/new-backend/config/config.go +++ b/new-backend/config/config.go @@ -90,7 +90,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "eclipse-temurin:11-jdk", MemoryLimit: "400m", CPULimit: "0.5", - TimeoutSec: 60, + TimeoutSec: 100, CompileCmd: []string{"javac"}, RunCmd: []string{"java"}, FileExt: ".java", From 25900803c317404582630cdf1c1a2473e65e90eb Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Fri, 8 Aug 2025 13:39:49 +0530 Subject: [PATCH 12/25] Increase timeout settings for Python, C, C++, JavaScript, and Go language configurations to improve execution reliability --- new-backend/config/config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/new-backend/config/config.go b/new-backend/config/config.go index 412eaea..b74db9c 100644 --- a/new-backend/config/config.go +++ b/new-backend/config/config.go @@ -80,7 +80,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "python:3.9-slim", MemoryLimit: "100m", CPULimit: "0.1", - TimeoutSec: 30, + TimeoutSec: 90, RunCmd: []string{"python", "-c"}, FileExt: ".py", VersionCmd: []string{"python", "--version"}, @@ -101,7 +101,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "gcc:latest", MemoryLimit: "100m", CPULimit: "0.1", - TimeoutSec: 30, + TimeoutSec: 90, CompileCmd: []string{"gcc", "-o", "program"}, RunCmd: []string{"./program"}, FileExt: ".c", @@ -112,7 +112,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "gcc:latest", MemoryLimit: "100m", CPULimit: "0.1", - TimeoutSec: 30, + TimeoutSec: 90, CompileCmd: []string{"g++", "-o", "program"}, RunCmd: []string{"./program"}, FileExt: ".cpp", @@ -123,7 +123,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "node:16-alpine", MemoryLimit: "100m", CPULimit: "0.1", - TimeoutSec: 30, + TimeoutSec: 90, RunCmd: []string{"node", "-e"}, FileExt: ".js", VersionCmd: []string{"node", "--version"}, @@ -133,7 +133,7 @@ func getLanguageConfigs() map[string]LanguageConfig { Image: "golang:1.19-alpine", MemoryLimit: "100m", CPULimit: "0.1", - TimeoutSec: 30, + TimeoutSec: 90, CompileCmd: []string{"go", "build", "-o", "program"}, RunCmd: []string{"./program"}, FileExt: ".go", From eb2873a3b987e469e590b35c64fd8acd6005d1bf Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Thu, 14 Aug 2025 21:15:53 +0530 Subject: [PATCH 13/25] Add Docker setup for Monaco backend with Cloudflare tunnel support --- new-backend/Dockerfile.tunnel | 56 +++++++++++++++++++++++++++ new-backend/README.tunnel.md | 46 ++++++++++++++++++++++ new-backend/config.json | 15 +++++++ new-backend/docker-compose.tunnel.yml | 27 +++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 new-backend/Dockerfile.tunnel create mode 100644 new-backend/README.tunnel.md create mode 100644 new-backend/config.json create mode 100644 new-backend/docker-compose.tunnel.yml diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel new file mode 100644 index 0000000..e677375 --- /dev/null +++ b/new-backend/Dockerfile.tunnel @@ -0,0 +1,56 @@ +FROM golang:1.19-alpine AS builder + +# Install git and required dependencies +RUN apk update && apk add --no-cache git + +# Set working directory +WORKDIR /app + +# Copy go mod and sum files +COPY go.mod go.sum* ./ + +# Download dependencies +RUN go mod download + +# Copy source code +COPY . . + +# Build the application with optimizations +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o monaco-backend . + +# Use a smaller image for the final container +FROM alpine:latest + +# Install Docker client and cloudflared +RUN apk update && apk add --no-cache docker-cli curl && \ + curl -L --output cloudflared.tgz https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.tgz && \ + tar -xzf cloudflared.tgz && \ + chmod +x cloudflared && \ + mv cloudflared /usr/local/bin/ && \ + rm cloudflared.tgz + +# Create directories for cloudflared +RUN mkdir -p /etc/cloudflared + +# Copy the certificate file and config +COPY cert.pem /etc/cloudflared/cert.pem +COPY config.json /etc/cloudflared/config.json + +# Copy the binary from builder +COPY --from=builder /app/monaco-backend /monaco-backend + +# Add startup script +RUN echo '#!/bin/sh\n\ +# Start the backend\n\ +/monaco-backend & \n\ +# Wait for backend to start\n\ +sleep 5\n\ +# Start cloudflared tunnel using config file\n\ +cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json\n\ +' > /start.sh && chmod +x /start.sh + +# Expose port for local access +EXPOSE 8080 + +# Run the startup script +ENTRYPOINT ["/start.sh"] diff --git a/new-backend/README.tunnel.md b/new-backend/README.tunnel.md new file mode 100644 index 0000000..2a12762 --- /dev/null +++ b/new-backend/README.tunnel.md @@ -0,0 +1,46 @@ +# Backend with Cloudflare Tunnel + +This setup runs the Monaco backend service and establishes a Cloudflare tunnel, exposing the service to the internet securely via api.ishikabhoyar.tech. + +## Prerequisites + +- Docker and Docker Compose installed +- The Cloudflare tunnel certificate (cert.pem) in the same directory as the Dockerfile.tunnel + +## Files + +- `Dockerfile.tunnel`: Dockerfile that builds the backend and sets up Cloudflare tunnel +- `cert.pem`: Cloudflare tunnel certificate +- `config.json`: Cloudflare tunnel configuration that routes traffic to api.ishikabhoyar.tech +- `docker-compose.tunnel.yml`: Docker Compose configuration for easy deployment + +## How to Run + +```bash +# Build and start the container +docker-compose -f docker-compose.tunnel.yml up -d + +# Check logs +docker-compose -f docker-compose.tunnel.yml logs -f +``` + +## How it Works + +1. The Dockerfile builds the Go backend application +2. It installs the Cloudflare tunnel client (cloudflared) +3. On container start: + - The backend server starts on port 8080 + - The Cloudflare tunnel connects to Cloudflare's edge network using the config.json + - External traffic to api.ishikabhoyar.tech is routed through the tunnel to the backend + - The cloudflared runs entirely within the container, isolated from any host cloudflared instance + +## Environment Variables + +You can customize the behavior by modifying the environment variables in the docker-compose.tunnel.yml file: + +- `PORT`: The port the backend server listens on (default: 8080) +- `CONCURRENT_EXECUTIONS`: Number of concurrent code executions (default: 5) +- `QUEUE_CAPACITY`: Maximum queue capacity for code executions (default: 100) +- `DEFAULT_TIMEOUT`: Default timeout for code execution in seconds (default: 30) +- `SANDBOX_NETWORK_DISABLED`: Whether to disable network in sandbox containers (default: true) +- `SANDBOX_PIDS_LIMIT`: Process ID limit for sandbox containers (default: 50) diff --git a/new-backend/config.json b/new-backend/config.json new file mode 100644 index 0000000..fbc1e34 --- /dev/null +++ b/new-backend/config.json @@ -0,0 +1,15 @@ +{ + "tunnel": "monaco-backend-tunnel", + "credentials-file": "/etc/cloudflared/cert.pem", + "ingress": [ + { + "hostname": "api.ishikabhoyar.tech", + "service": "http://localhost:8080" + }, + { + "service": "http_status:404" + } + ], + "protocol": "http2", + "loglevel": "info" +} diff --git a/new-backend/docker-compose.tunnel.yml b/new-backend/docker-compose.tunnel.yml new file mode 100644 index 0000000..2537db6 --- /dev/null +++ b/new-backend/docker-compose.tunnel.yml @@ -0,0 +1,27 @@ +version: '3.8' + +services: + backend: + build: + context: . + dockerfile: Dockerfile.tunnel + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + # Port is only exposed locally, traffic comes through the tunnel + ports: + - "127.0.0.1:8080:8080" + environment: + - PORT=8080 + - CONCURRENT_EXECUTIONS=5 + - QUEUE_CAPACITY=100 + - DEFAULT_TIMEOUT=30 + - SANDBOX_NETWORK_DISABLED=true + - SANDBOX_PIDS_LIMIT=50 + # Isolated network to prevent conflicts with host cloudflared + networks: + - monaco-backend-network + +networks: + monaco-backend-network: + driver: bridge From 5902300c95e7549bf12121fc20e6676459c57699 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Thu, 14 Aug 2025 23:42:45 +0530 Subject: [PATCH 14/25] Refactor Dockerfile to streamline cloudflared installation and improve image layering --- new-backend/Dockerfile.tunnel | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index e677375..94e763e 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -18,20 +18,21 @@ COPY . . # Build the application with optimizations RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o monaco-backend . +# Get cloudflared from official image +FROM cloudflare/cloudflared:latest AS cloudflared + # Use a smaller image for the final container FROM alpine:latest -# Install Docker client and cloudflared -RUN apk update && apk add --no-cache docker-cli curl && \ - curl -L --output cloudflared.tgz https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.tgz && \ - tar -xzf cloudflared.tgz && \ - chmod +x cloudflared && \ - mv cloudflared /usr/local/bin/ && \ - rm cloudflared.tgz +# Install Docker client +RUN apk update && apk add --no-cache docker-cli # Create directories for cloudflared RUN mkdir -p /etc/cloudflared +# Copy cloudflared from official image +COPY --from=cloudflared /usr/local/bin/cloudflared /usr/local/bin/cloudflared + # Copy the certificate file and config COPY cert.pem /etc/cloudflared/cert.pem COPY config.json /etc/cloudflared/config.json From 9474d2f6330bee5774cbaa2ace42fc8a08a83d10 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Thu, 14 Aug 2025 23:49:19 +0530 Subject: [PATCH 15/25] Add Dockerfile and Makefile for cloudflared tunnel setup; include startup script --- new-backend/Dockerfile.tunnel | 12 +++--------- new-backend/Makefile | 21 +++++++++++++++++++++ new-backend/start.sh | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 new-backend/Makefile create mode 100644 new-backend/start.sh diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index 94e763e..c0f4b16 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -40,15 +40,9 @@ COPY config.json /etc/cloudflared/config.json # Copy the binary from builder COPY --from=builder /app/monaco-backend /monaco-backend -# Add startup script -RUN echo '#!/bin/sh\n\ -# Start the backend\n\ -/monaco-backend & \n\ -# Wait for backend to start\n\ -sleep 5\n\ -# Start cloudflared tunnel using config file\n\ -cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json\n\ -' > /start.sh && chmod +x /start.sh +# Copy the startup script +COPY start.sh /start.sh +RUN chmod +x /start.sh # Expose port for local access EXPOSE 8080 diff --git a/new-backend/Makefile b/new-backend/Makefile new file mode 100644 index 0000000..b39dec5 --- /dev/null +++ b/new-backend/Makefile @@ -0,0 +1,21 @@ +.PHONY: build run run-detached stop logs + +# Build the image +build: + docker-compose -f docker-compose.tunnel.yml build + +# Run the container +run: build + docker-compose -f docker-compose.tunnel.yml up + +# Run in detached mode +run-detached: build + docker-compose -f docker-compose.tunnel.yml up -d + +# Stop the container +stop: + docker-compose -f docker-compose.tunnel.yml down + +# View logs +logs: + docker-compose -f docker-compose.tunnel.yml logs -f diff --git a/new-backend/start.sh b/new-backend/start.sh new file mode 100644 index 0000000..6090f69 --- /dev/null +++ b/new-backend/start.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Start the backend +/monaco-backend & +BACKEND_PID=$! +echo "Backend started with PID: $BACKEND_PID" + +# Wait for backend to start +echo "Waiting for backend to initialize..." +sleep 5 + +# Start cloudflared tunnel using config file +echo "Starting Cloudflare tunnel to api.ishikabhoyar.tech..." +cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json + +# If cloudflared exits, kill the backend too +kill $BACKEND_PID From 720a37fa821a8268d38f2f128fd48581f321eda2 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Thu, 14 Aug 2025 23:52:05 +0530 Subject: [PATCH 16/25] Refactor Dockerfile to create startup script dynamically, ensuring correct line endings and improved backend initialization --- new-backend/Dockerfile.tunnel | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index c0f4b16..c879479 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -40,9 +40,21 @@ COPY config.json /etc/cloudflared/config.json # Copy the binary from builder COPY --from=builder /app/monaco-backend /monaco-backend -# Copy the startup script -COPY start.sh /start.sh -RUN chmod +x /start.sh +# Create startup script inside the container to ensure correct line endings +RUN printf '#!/bin/sh\\n' > /start.sh && \ + printf '# Start the backend\\n' >> /start.sh && \ + printf '/monaco-backend &\\n' >> /start.sh && \ + printf 'BACKEND_PID=$!\\n' >> /start.sh && \ + printf 'echo "Backend started with PID: $BACKEND_PID"\\n\\n' >> /start.sh && \ + printf '# Wait for backend to start\\n' >> /start.sh && \ + printf 'echo "Waiting for backend to initialize..."\\n' >> /start.sh && \ + printf 'sleep 5\\n\\n' >> /start.sh && \ + printf '# Start cloudflared tunnel using config file\\n' >> /start.sh && \ + printf 'echo "Starting Cloudflare tunnel to api.ishikabhoyar.tech..."\\n' >> /start.sh && \ + printf 'cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json\\n\\n' >> /start.sh && \ + printf '# If cloudflared exits, kill the backend too\\n' >> /start.sh && \ + printf 'kill $BACKEND_PID\\n' >> /start.sh && \ + chmod +x /start.sh # Expose port for local access EXPOSE 8080 From e5827cfa422f7fa390c70d3de3b2581611c1f5cc Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Thu, 14 Aug 2025 23:55:57 +0530 Subject: [PATCH 17/25] Add supervisord configuration for backend and cloudflared services --- new-backend/supervisord.conf | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 new-backend/supervisord.conf diff --git a/new-backend/supervisord.conf b/new-backend/supervisord.conf new file mode 100644 index 0000000..434c549 --- /dev/null +++ b/new-backend/supervisord.conf @@ -0,0 +1,21 @@ +[supervisord] +nodaemon=true +user=root + +[program:backend] +command=/monaco-backend +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:cloudflared] +command=cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 From c7d65c612f86e18e1a8d134469bc2de2a9ebc09e Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:05:00 +0530 Subject: [PATCH 18/25] Refactor Dockerfile and docker-compose to use supervisord for process management and update cloudflared installation method --- new-backend/Dockerfile.tunnel | 59 +++++++++++++++------------ new-backend/docker-compose.tunnel.yml | 4 +- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index c879479..1d0a252 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -18,21 +18,20 @@ COPY . . # Build the application with optimizations RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o monaco-backend . -# Get cloudflared from official image -FROM cloudflare/cloudflared:latest AS cloudflared - -# Use a smaller image for the final container +# Final stage FROM alpine:latest -# Install Docker client -RUN apk update && apk add --no-cache docker-cli +# Install Docker client and supervisor +RUN apk update && apk add --no-cache docker-cli supervisor wget + +# Get cloudflared directly from GitHub (more reliable than the tarball) +RUN wget -O cloudflared https://github.com/cloudflare/cloudflared/releases/download/2023.5.0/cloudflared-linux-amd64 && \ + chmod +x cloudflared && \ + mv cloudflared /usr/local/bin/ # Create directories for cloudflared RUN mkdir -p /etc/cloudflared -# Copy cloudflared from official image -COPY --from=cloudflared /usr/local/bin/cloudflared /usr/local/bin/cloudflared - # Copy the certificate file and config COPY cert.pem /etc/cloudflared/cert.pem COPY config.json /etc/cloudflared/config.json @@ -40,24 +39,32 @@ COPY config.json /etc/cloudflared/config.json # Copy the binary from builder COPY --from=builder /app/monaco-backend /monaco-backend -# Create startup script inside the container to ensure correct line endings -RUN printf '#!/bin/sh\\n' > /start.sh && \ - printf '# Start the backend\\n' >> /start.sh && \ - printf '/monaco-backend &\\n' >> /start.sh && \ - printf 'BACKEND_PID=$!\\n' >> /start.sh && \ - printf 'echo "Backend started with PID: $BACKEND_PID"\\n\\n' >> /start.sh && \ - printf '# Wait for backend to start\\n' >> /start.sh && \ - printf 'echo "Waiting for backend to initialize..."\\n' >> /start.sh && \ - printf 'sleep 5\\n\\n' >> /start.sh && \ - printf '# Start cloudflared tunnel using config file\\n' >> /start.sh && \ - printf 'echo "Starting Cloudflare tunnel to api.ishikabhoyar.tech..."\\n' >> /start.sh && \ - printf 'cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json\\n\\n' >> /start.sh && \ - printf '# If cloudflared exits, kill the backend too\\n' >> /start.sh && \ - printf 'kill $BACKEND_PID\\n' >> /start.sh && \ - chmod +x /start.sh +# Create supervisord config +RUN mkdir -p /etc/supervisor/conf.d/ +RUN echo "[supervisord]" > /etc/supervisor/conf.d/supervisord.conf && \ + echo "nodaemon=true" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "user=root" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "[program:backend]" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "command=/monaco-backend" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "autostart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "autorestart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stdout_logfile=/dev/stdout" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stdout_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stderr_logfile=/dev/stderr" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stderr_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "[program:cloudflared]" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "command=cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "autostart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "autorestart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stdout_logfile=/dev/stdout" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stdout_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stderr_logfile=/dev/stderr" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "stderr_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf # Expose port for local access EXPOSE 8080 -# Run the startup script -ENTRYPOINT ["/start.sh"] +# Use supervisord to manage processes +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/new-backend/docker-compose.tunnel.yml b/new-backend/docker-compose.tunnel.yml index 2537db6..8b2c065 100644 --- a/new-backend/docker-compose.tunnel.yml +++ b/new-backend/docker-compose.tunnel.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: backend: build: @@ -7,7 +5,7 @@ services: dockerfile: Dockerfile.tunnel restart: unless-stopped volumes: - - /var/run/docker.sock:/var/run/docker.sock + - //var/run/docker.sock:/var/run/docker.sock # Port is only exposed locally, traffic comes through the tunnel ports: - "127.0.0.1:8080:8080" From 20680719f54b5e32bc1b4c7f845178809402b0f8 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:09:36 +0530 Subject: [PATCH 19/25] Update Dockerfile and docker-compose to enhance cloudflared configuration and startup process --- new-backend/Dockerfile.tunnel | 2 +- new-backend/Dockerfile.tunnel.new | 66 +++++++++++++++++++++++++++ new-backend/config.json.new | 13 ++++++ new-backend/docker-compose.tunnel.yml | 3 ++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 new-backend/Dockerfile.tunnel.new create mode 100644 new-backend/config.json.new diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index 1d0a252..2f12a16 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -55,7 +55,7 @@ RUN echo "[supervisord]" > /etc/supervisor/conf.d/supervisord.conf && \ echo "stderr_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "[program:cloudflared]" >> /etc/supervisor/conf.d/supervisord.conf && \ - echo "command=cloudflared tunnel --no-autoupdate run --config /etc/cloudflared/config.json" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "command=cloudflared tunnel --no-autoupdate --config /etc/cloudflared/config.json run" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autostart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autorestart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "stdout_logfile=/dev/stdout" >> /etc/supervisor/conf.d/supervisord.conf && \ diff --git a/new-backend/Dockerfile.tunnel.new b/new-backend/Dockerfile.tunnel.new new file mode 100644 index 0000000..f9a7aee --- /dev/null +++ b/new-backend/Dockerfile.tunnel.new @@ -0,0 +1,66 @@ +FROM golang:1.19-alpine AS builder + +# Install git and required dependencies +RUN apk update && apk add --no-cache git + +# Set working directory +WORKDIR /app + +# Copy go mod and sum files +COPY go.mod go.sum* ./ + +# Download dependencies +RUN go mod download + +# Copy source code +COPY . . + +# Build the application with optimizations +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o monaco-backend . + +# Final stage +FROM alpine:latest + +# Install Docker client and tools +RUN apk update && apk add --no-cache docker-cli bash wget + +# Get cloudflared directly +RUN wget -O cloudflared https://github.com/cloudflare/cloudflared/releases/download/2023.7.3/cloudflared-linux-amd64 && \ + chmod +x cloudflared && \ + mv cloudflared /usr/local/bin/ + +# Create directories for cloudflared +RUN mkdir -p /etc/cloudflared + +# Copy the certificate file and config +COPY cert.pem /etc/cloudflared/cert.pem +COPY config.json /etc/cloudflared/config.json + +# Copy the binary from builder +COPY --from=builder /app/monaco-backend /monaco-backend + +# Create a simple bash script to run both services +RUN echo '#!/bin/bash\n\ +# Start the backend in the background\n\ +/monaco-backend &\n\ +BACKEND_PID=$!\n\ +echo "Backend started with PID: $BACKEND_PID"\n\ +\n\ +# Wait for backend to start\n\ +echo "Waiting for backend to initialize..."\n\ +sleep 5\n\ +\n\ +# Start cloudflared with proper arguments\n\ +echo "Starting Cloudflare tunnel to api.ishikabhoyar.tech..."\n\ +# Use the specific tunnel name from config.json\n\ +cloudflared tunnel run monaco-backend-tunnel\n\ +\n\ +# If cloudflared exits, kill the backend\n\ +kill $BACKEND_PID\n\ +' > /start.sh && chmod +x /start.sh + +# Expose port for local access +EXPOSE 8080 + +# Run the startup script +CMD ["/start.sh"] diff --git a/new-backend/config.json.new b/new-backend/config.json.new new file mode 100644 index 0000000..7846252 --- /dev/null +++ b/new-backend/config.json.new @@ -0,0 +1,13 @@ +{ + "tunnel": "monaco-backend-tunnel", + "credentials-file": "/etc/cloudflared/cert.pem", + "ingress": [ + { + "hostname": "api.ishikabhoyar.tech", + "service": "http://localhost:8080" + }, + { + "service": "http_status:404" + } + ] +} diff --git a/new-backend/docker-compose.tunnel.yml b/new-backend/docker-compose.tunnel.yml index 8b2c065..45a180c 100644 --- a/new-backend/docker-compose.tunnel.yml +++ b/new-backend/docker-compose.tunnel.yml @@ -16,6 +16,9 @@ services: - DEFAULT_TIMEOUT=30 - SANDBOX_NETWORK_DISABLED=true - SANDBOX_PIDS_LIMIT=50 + # Define cloudflared environment variables + - TUNNEL_ORIGIN_CERT=/etc/cloudflared/cert.pem + - NO_AUTOUPDATE=true # Isolated network to prevent conflicts with host cloudflared networks: - monaco-backend-network From 232c6ec530ab7b8dab6c661b75d47316883e6780 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:17:18 +0530 Subject: [PATCH 20/25] Update supervisord configuration for cloudflared to use tunnel URL and modify tunnel ID in config.json --- new-backend/Dockerfile.tunnel | 2 +- new-backend/config.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index 2f12a16..0eb09e5 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -55,7 +55,7 @@ RUN echo "[supervisord]" > /etc/supervisor/conf.d/supervisord.conf && \ echo "stderr_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "[program:cloudflared]" >> /etc/supervisor/conf.d/supervisord.conf && \ - echo "command=cloudflared tunnel --no-autoupdate --config /etc/cloudflared/config.json run" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "command=cloudflared tunnel --url http://localhost:8080 run" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autostart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autorestart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "stdout_logfile=/dev/stdout" >> /etc/supervisor/conf.d/supervisord.conf && \ diff --git a/new-backend/config.json b/new-backend/config.json index fbc1e34..75eb97b 100644 --- a/new-backend/config.json +++ b/new-backend/config.json @@ -1,5 +1,5 @@ { - "tunnel": "monaco-backend-tunnel", + "tunnel": "YOUR_TUNNEL_ID_HERE", "credentials-file": "/etc/cloudflared/cert.pem", "ingress": [ { From 458b2ca06fd389f1ff9a22e3445f2685d9f0427c Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:19:58 +0530 Subject: [PATCH 21/25] Add credentials.json and update cloudflared configuration in Dockerfile --- new-backend/Dockerfile.tunnel | 3 ++- new-backend/config.json | 4 ++-- new-backend/credentials.json | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 new-backend/credentials.json diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index 0eb09e5..13395a3 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -34,6 +34,7 @@ RUN mkdir -p /etc/cloudflared # Copy the certificate file and config COPY cert.pem /etc/cloudflared/cert.pem +COPY credentials.json /etc/cloudflared/credentials.json COPY config.json /etc/cloudflared/config.json # Copy the binary from builder @@ -55,7 +56,7 @@ RUN echo "[supervisord]" > /etc/supervisor/conf.d/supervisord.conf && \ echo "stderr_logfile_maxbytes=0" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "[program:cloudflared]" >> /etc/supervisor/conf.d/supervisord.conf && \ - echo "command=cloudflared tunnel --url http://localhost:8080 run" >> /etc/supervisor/conf.d/supervisord.conf && \ + echo "command=cloudflared tunnel --config /etc/cloudflared/config.json run" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autostart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "autorestart=true" >> /etc/supervisor/conf.d/supervisord.conf && \ echo "stdout_logfile=/dev/stdout" >> /etc/supervisor/conf.d/supervisord.conf && \ diff --git a/new-backend/config.json b/new-backend/config.json index 75eb97b..814bcd6 100644 --- a/new-backend/config.json +++ b/new-backend/config.json @@ -1,6 +1,6 @@ { - "tunnel": "YOUR_TUNNEL_ID_HERE", - "credentials-file": "/etc/cloudflared/cert.pem", + "tunnel": "5d2682ef-0b5b-47e5-b0fa-ad48968ce016", + "credentials-file": "/etc/cloudflared/credentials.json", "ingress": [ { "hostname": "api.ishikabhoyar.tech", diff --git a/new-backend/credentials.json b/new-backend/credentials.json new file mode 100644 index 0000000..922f3fd --- /dev/null +++ b/new-backend/credentials.json @@ -0,0 +1 @@ +{"AccountTag":"453afb9373a00a55876e6127cf0efd97","TunnelSecret":"02VClcBt+1nxxu8ioUzw/UizXtKKh4X7UUpneVbfB/Y=","TunnelID":"5d2682ef-0b5b-47e5-b0fa-ad48968ce016"} From e9553dd3af9f2fefecc859d4eeb61fa4bd33a8c9 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:23:29 +0530 Subject: [PATCH 22/25] Add DNS routing setup for cloudflared tunnel in Dockerfile --- new-backend/Dockerfile.tunnel | 3 +++ 1 file changed, 3 insertions(+) diff --git a/new-backend/Dockerfile.tunnel b/new-backend/Dockerfile.tunnel index 13395a3..fabbb01 100644 --- a/new-backend/Dockerfile.tunnel +++ b/new-backend/Dockerfile.tunnel @@ -37,6 +37,9 @@ COPY cert.pem /etc/cloudflared/cert.pem COPY credentials.json /etc/cloudflared/credentials.json COPY config.json /etc/cloudflared/config.json +# Setup DNS routing for the tunnel (only needs to be done once) +RUN cloudflared tunnel route dns 5d2682ef-0b5b-47e5-b0fa-ad48968ce016 api.ishikabhoyar.tech || echo "DNS routing already set up or failed - continuing anyway" + # Copy the binary from builder COPY --from=builder /app/monaco-backend /monaco-backend From 210f5319903ff7cb33b37958fffc055c5c795bd2 Mon Sep 17 00:00:00 2001 From: Arnab-Afk Date: Fri, 15 Aug 2025 14:53:57 +0530 Subject: [PATCH 23/25] Update configuration defaults for server and executor settings --- new-backend/config/config.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/new-backend/config/config.go b/new-backend/config/config.go index b74db9c..ae46dbe 100644 --- a/new-backend/config/config.go +++ b/new-backend/config/config.go @@ -8,10 +8,10 @@ import ( // Config holds all configuration for the application type Config struct { - Server ServerConfig - Executor ExecutorConfig - Languages map[string]LanguageConfig - Sandbox SandboxConfig + Server ServerConfig + Executor ExecutorConfig + Languages map[string]LanguageConfig + Sandbox SandboxConfig } // ServerConfig holds server-related configurations @@ -31,15 +31,15 @@ type ExecutorConfig struct { // LanguageConfig holds language-specific configurations type LanguageConfig struct { - Name string - Image string - MemoryLimit string - CPULimit string - TimeoutSec int - CompileCmd []string - RunCmd []string - FileExt string - VersionCmd []string + Name string + Image string + MemoryLimit string + CPULimit string + TimeoutSec int + CompileCmd []string + RunCmd []string + FileExt string + VersionCmd []string } // SandboxConfig holds sandbox-related configurations @@ -56,11 +56,11 @@ func GetConfig() *Config { Port: getEnv("PORT", "8080"), ReadTimeout: time.Duration(getEnvAsInt("READ_TIMEOUT", 15)) * time.Second, WriteTimeout: time.Duration(getEnvAsInt("WRITE_TIMEOUT", 15)) * time.Second, - IdleTimeout: time.Duration(getEnvAsInt("IDLE_TIMEOUT", 60)) * time.Second, + IdleTimeout: time.Duration(getEnvAsInt("IDLE_TIMEOUT", 90)) * time.Second, }, Executor: ExecutorConfig{ - ConcurrentExecutions: getEnvAsInt("CONCURRENT_EXECUTIONS", 5), - QueueCapacity: getEnvAsInt("QUEUE_CAPACITY", 100), + ConcurrentExecutions: getEnvAsInt("CONCURRENT_EXECUTIONS", 100), + QueueCapacity: getEnvAsInt("QUEUE_CAPACITY", 1000), DefaultTimeout: time.Duration(getEnvAsInt("DEFAULT_TIMEOUT", 30)) * time.Second, }, Languages: getLanguageConfigs(), From e4f193613a61ea47a7b128bb218a89b52120e7f3 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Sun, 17 Aug 2025 17:03:16 +0530 Subject: [PATCH 24/25] Add footer component and styles to prevent overlap with content --- Frontend/src/App.jsx | 7 ++ Frontend/src/components/EditorArea.jsx | 5 -- Frontend/src/index.css | 100 +++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx index b19fb9e..53584d1 100644 --- a/Frontend/src/App.jsx +++ b/Frontend/src/App.jsx @@ -5,6 +5,13 @@ function App() { return (
+
+
+ + Copyright © 2025. Made with by Ishika and Arnab. + +
+
) } diff --git a/Frontend/src/components/EditorArea.jsx b/Frontend/src/components/EditorArea.jsx index cc88414..e0c7a50 100644 --- a/Frontend/src/components/EditorArea.jsx +++ b/Frontend/src/components/EditorArea.jsx @@ -136,11 +136,6 @@ const EditorArea = ({ useEffect(() => { localStorage.setItem("vscode-clone-files", JSON.stringify(files)); }, [files]); - - useEffect(() => { - localStorage.setItem("vscode-clone-structure", JSON.stringify(fileStructure)); - }, [fileStructure]); - // Add this effect to handle editor resize when sidebar changes useEffect(() => { // Force editor to readjust layout when sidebar visibility changes diff --git a/Frontend/src/index.css b/Frontend/src/index.css index 17454e2..95c8403 100644 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -1239,6 +1239,7 @@ body { display: flex; flex-direction: column; background-color: var(--vscode-panel-background); + padding-bottom: 50px; /* Add padding at the bottom to prevent footer overlap */ } .terminal-header { @@ -1309,4 +1310,103 @@ body { flex: 1; font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; +} + +/* Footer Styles */ +.fixed { + position: fixed; +} + +.bottom-0 { + bottom: 0; +} + +.left-0 { + left: 0; +} + +.right-0 { + right: 0; +} + +.border-t { + border-top-width: 1px; + border-top-style: solid; +} + +.border-slate-200\/40 { + border-color: rgba(226, 232, 240, 0.4); +} + +.dark\:border-gray-800\/20 { + border-color: rgba(31, 41, 55, 0.2); +} + +.bg-white { + background-color: #ffffff; +} + +.dark\:bg-\[\#070c1f\] { + background-color: #000000; +} + +.footer-bar { + background-color: #000000; + z-index: 1000; + box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); +} + +.flex { + display: flex; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.h-7 { + height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.text-slate-500 { + color: #64748b; +} + +.dark\:text-gray-500 { + color: #6b7280; +} + +.text-slate-400 { + color: #94a3b8; +} + +.dark\:text-gray-400 { + color: #9ca3af; +} + +.text-red-400 { + color: #f87171; +} + +.dark\:text-red-500 { + color: #ef4444; +} + +.mx-0\.5 { + margin-left: 0.125rem; + margin-right: 0.125rem; +} + +/* Make sure the footer appears on top of other elements */ +footer { + z-index: 1000; } \ No newline at end of file From eabfbf806e06be834bac94b15cb2f715d276cba1 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Sun, 17 Aug 2025 17:07:38 +0530 Subject: [PATCH 25/25] Refactor footer padding and enhance scrollbar styles for terminal and content areas --- Frontend/src/App.jsx | 3 ++- Frontend/src/index.css | 44 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx index 53584d1..8002bc3 100644 --- a/Frontend/src/App.jsx +++ b/Frontend/src/App.jsx @@ -8,7 +8,8 @@ function App() { diff --git a/Frontend/src/index.css b/Frontend/src/index.css index 95c8403..edf7378 100644 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -422,15 +422,36 @@ body { padding: 8px; font-family: monospace; overflow-y: auto; + overflow-x: hidden; height: calc(100% - 36px); /* Adjust based on your header height */ background-color: #1e1e1e; color: #ddd; outline: none; /* Remove focus outline */ + scrollbar-width: thin; + scrollbar-color: #555555 #1e1e1e; +} + +.panel-terminal::-webkit-scrollbar { + width: 8px; +} + +.panel-terminal::-webkit-scrollbar-track { + background: #1e1e1e; +} + +.panel-terminal::-webkit-scrollbar-thumb { + background: #555555; + border-radius: 4px; +} + +.panel-terminal::-webkit-scrollbar-thumb:hover { + background: #666666; } .panel-terminal .terminal-line { white-space: pre-wrap; margin-bottom: 3px; + word-wrap: break-word; /* Ensure long words don't cause horizontal overflow */ } .terminal-line { @@ -1239,7 +1260,7 @@ body { display: flex; flex-direction: column; background-color: var(--vscode-panel-background); - padding-bottom: 50px; /* Add padding at the bottom to prevent footer overlap */ + padding-bottom: 30px; /* Reduced padding to keep buttons just above footer */ } .terminal-header { @@ -1275,7 +1296,28 @@ body { font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; overflow-y: auto; + overflow-x: hidden; white-space: pre-wrap; + max-height: calc(100% - 10px); /* Ensure content doesn't expand beyond container */ + scrollbar-width: thin; + scrollbar-color: #555555 #1e1e1e; +} + +.terminal-content::-webkit-scrollbar { + width: 8px; +} + +.terminal-content::-webkit-scrollbar-track { + background: #1e1e1e; +} + +.terminal-content::-webkit-scrollbar-thumb { + background: #555555; + border-radius: 4px; +} + +.terminal-content::-webkit-scrollbar-thumb:hover { + background: #666666; } .terminal-line {