cms / index.html
Kast-KIIT's picture
undefined - Follow Up Deployment
f31e7bc verified
Raw
History Blame Contribute Delete
26.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secure TASK Submission</title>
<!-- UI libraries -->
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.15.3/ace.js"></script>
<style>
.editor{height:400px;width:100%;font-size:16px}
.typewriter-cursor{display:inline-block;width:10px;height:20px;background:#000;animation:blink 1s step-end infinite}
@keyframes blink{from,to{background:transparent}50%{background:#000}}
.hidden{display:none}
.shake{animation:shake .5s}
@keyframes shake{0%,100%{transform:translateX(0)}20%,60%{transform:translateX(-5px)}40%,80%{transform:translateX(5px)}}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- ───────────────────────── HEADER ───────────────────────── -->
<header class="bg-blue-600 text-white p-4 rounded-lg shadow-md mb-8">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold">Secure TASK Submission</h1>
<div id="appMode" class="bg-blue-800 px-3 py-1 rounded-full text-sm">Student Mode</div>
</div>
</header>
<!-- ───────────────────────── MODE TOGGLE ──────────────────── -->
<div class="bg-white p-4 rounded-lg shadow-md mb-8">
<div class="flex space-x-4">
<button id="studentModeBtn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">Student Mode</button>
<button id="teacherModeBtn" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 transition">Teacher Mode</button>
</div>
</div>
<!-- ───────────────────────── STUDENT VIEW ─────────────────── -->
<div id="studentInterface" class="bg-white p-6 rounded-lg shadow-md">
<!-- Student form -->
<div id="studentInfoForm" class="mb-8">
<h2 class="text-xl font-semibold mb-4">Student Information &amp; Assignment Details</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div>
<label for="rollNumber" class="block text-sm font-medium text-gray-700 mb-1">Roll Number</label>
<input id="rollNumber" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="studentName" class="block text-sm font-medium text-gray-700 mb-1">Full Name</label>
<input id="studentName" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label for="courseName" class="block text-sm font-medium text-gray-700 mb-1">Course Name</label>
<input id="courseName" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="assignmentNumber" class="block text-sm font-medium text-gray-700 mb-1">Assignment #</label>
<input id="assignmentNumber" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
</div>
</div>
</div>
<button id="startAssignmentBtn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">Start Assignment</button>
</div>
<!-- Assignment editor -->
<div id="assignmentArea" class="hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">TASK Editor</h2>
<div class="flex items-center space-x-4">
<div>
<label for="languageSelect" class="block text-sm font-medium text-gray-700 mb-1">Language</label>
<select id="languageSelect" class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
<option value="plaintext">Plain Text</option>
<option value="python">Python</option><option value="java">Java</option>
<option value="c_cpp">C/C++</option><option value="javascript">JavaScript</option>
<option value="html">HTML</option><option value="css">CSS</option>
</select>
</div>
<div class="text-sm bg-gray-200 px-3 py-1 rounded-full">
<span id="charCount">0</span> characters typed
</div>
</div>
</div>
<div class="mb-4 p-4 bg-gray-100 rounded-md">
<h3 class="font-medium mb-2">Assignment Instructions:</h3>
<p id="assignmentPrompt" class="text-gray-700">Type your assignment character by character. Copy-paste is not allowed.
</p>
</div>
<div class="relative">
<div id="editor" class="editor border border-gray-300 rounded-md"></div>
<div id="typewriterOverlay" class="absolute inset-0 bg-white bg-opacity-90 flex items-center justify-center hidden">
<div class="text-center p-8 max-w-lg">
<h3 class="text-xl font-semibold mb-4">Typewriter Mode Active</h3>
<p class="mb-6">You must type your code character by character. Copy-paste is disabled to ensure academic integrity.</p>
<div class="bg-gray-200 p-4 rounded-md mb-6">
<p class="font-mono text-lg"><span id="typedContent"></span><span class="typewriter-cursor"></span></p>
</div>
<button id="resumeTypingBtn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">Continue Typing</button>
</div>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<div class="text-sm text-gray-600"><span id="lastSaved"></span></div>
<div class="space-x-2">
<button id="submitAssignmentBtn" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition">Download Assignment</button>
<button id="saveDraftBtn" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 transition">Save Draft</button>
</div>
</div>
<!-- Locked summary -->
<div id="studentInfoDisplay" class="mt-6 p-4 bg-gray-100 rounded-md">
<h3 class="font-medium mb-2">Submission Information</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div><p class="text-sm text-gray-600">Roll Number</p><p id="displayRollNumber" class="font-medium"></p></div>
<div><p class="text-sm text-gray-600">Full Name</p><p id="displayStudentName" class="font-medium"></p></div>
<div class="grid grid-cols-2 gap-4">
<div><p class="text-sm text-gray-600">Course Name</p><p id="displayCourseName" class="font-medium"></p></div>
<div><p class="text-sm text-gray-600">Assignment #</p><p id="displayAssignmentNumber" class="font-medium"></p></div>
</div>
</div>
</div>
</div>
</div>
<!-- ───────────────────────── TEACHER VIEW ──────────────────── -->
<div id="teacherInterface" class="bg-white p-6 rounded-lg shadow-md hidden">
<h2 class="text-xl font-semibold mb-4">Teacher Dashboard</h2>
<!-- Upload -->
<div class="mb-6">
<label for="assignmentFile" class="block text-sm font-medium text-gray-700 mb-2">Upload Student Assignment</label>
<div class="flex items-center space-x-4">
<input id="assignmentFile" type="file" accept=".json,.task" class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
<button id="uploadAssignmentBtn" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition">Upload</button>
</div>
<p class="text-sm text-gray-500 mt-2">Only assignments created with this app can be viewed.</p>
</div>
<!-- Verification banner -->
<div id="assignmentVerification" class="hidden p-4 border rounded-md mb-6">
<div class="flex items-center mb-2">
<span id="verificationIcon" class="text-2xl mr-2"></span>
<h3 id="verificationResult" class="font-semibold"></h3>
</div>
<p id="verificationDetails" class="text-sm"></p>
</div>
<!-- Viewer -->
<div id="assignmentViewer" class="hidden">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium">Assignment Submission</h3>
<div class="text-sm bg-gray-200 px-3 py-1 rounded-full"><span id="submissionCharCount">0</span> characters</div>
</div>
<div class="mb-4 p-4 bg-gray-100 rounded-md">
<h4 class="font-medium mb-2">Student Information:</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div><p class="text-sm text-gray-600">Roll Number</p><p id="submissionRollNumber" class="font-medium"></p></div>
<div><p class="text-sm text-gray-600">Full Name</p><p id="submissionStudentName" class="font-medium"></p></div>
<div><p class="text-sm text-gray-600">Course Name</p><p id="submissionCourseName" class="font-medium"></p></div>
<div><p class="text-sm text-gray-600">Assignment #</p><p id="submissionAssignmentNumber" class="font-medium"></p></div>
</div>
</div>
<div class="mb-4 p-4 bg-gray-100 rounded-md">
<h4 class="font-medium mb-2">Assignment Instructions:</h4>
<p id="submissionPrompt" class="text-gray-700"></p>
</div>
<div class="border border-gray-300 rounded-md p-4 bg-white">
<pre id="submissionContent" class="whitespace-pre-wrap font-mono text-sm"></pre>
</div>
<div class="mt-4 flex justify-end">
<button id="downloadAssignmentBtn" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition">Download Submission</button>
</div>
</div>
</div>
</div><!-- container -->
<!-- ───────────────────────── SCRIPT ────────────────────────── -->
<script>
/* ---------- DOM ELEMENTS ---------- */
const studentModeBtn = document.getElementById('studentModeBtn');
const teacherModeBtn = document.getElementById('teacherModeBtn');
const appMode = document.getElementById('appMode');
const studentInterface= document.getElementById('studentInterface');
const teacherInterface= document.getElementById('teacherInterface');
/* Student */
const studentInfoForm = document.getElementById('studentInfoForm');
const assignmentArea = document.getElementById('assignmentArea');
const rollNumberInput = document.getElementById('rollNumber');
const studentNameInput= document.getElementById('studentName');
const courseNameInput = document.getElementById('courseName');
const assignmentNumberInput = document.getElementById('assignmentNumber');
const startAssignmentBtn = document.getElementById('startAssignmentBtn');
const displayRollNumber = document.getElementById('displayRollNumber');
const displayStudentName = document.getElementById('displayStudentName');
const displayCourseName = document.getElementById('displayCourseName');
const displayAssignmentNumber = document.getElementById('displayAssignmentNumber');
const languageSelect = document.getElementById('languageSelect');
const charCount = document.getElementById('charCount');
const submitAssignmentBtn = document.getElementById('submitAssignmentBtn');
const saveDraftBtn = document.getElementById('saveDraftBtn');
const lastSaved = document.getElementById('lastSaved');
const typewriterOverlay= document.getElementById('typewriterOverlay');
const typedContent = document.getElementById('typedContent');
const resumeTypingBtn = document.getElementById('resumeTypingBtn');
/* Teacher */
const assignmentFile = document.getElementById('assignmentFile');
const uploadAssignmentBtn = document.getElementById('uploadAssignmentBtn');
const assignmentVerification = document.getElementById('assignmentVerification');
const verificationIcon = document.getElementById('verificationIcon');
const verificationResult = document.getElementById('verificationResult');
const verificationDetails = document.getElementById('verificationDetails');
const assignmentViewer = document.getElementById('assignmentViewer');
const submissionRollNumber = document.getElementById('submissionRollNumber');
const submissionStudentName = document.getElementById('submissionStudentName');
const submissionCourseName = document.getElementById('submissionCourseName');
const submissionAssignmentNumber= document.getElementById('submissionAssignmentNumber');
const submissionPrompt = document.getElementById('submissionPrompt');
const submissionContent = document.getElementById('submissionContent');
const submissionCharCount = document.getElementById('submissionCharCount');
const downloadAssignmentBtn = document.getElementById('downloadAssignmentBtn');
/* ---------- APP STATE ---------- */
let studentInfoLocked = false;
let editor;
let lastSaveTime = null;
let currentContent='', typedPosition=0, typingTimeout;
/* ---------- HELPERS ---------- */
async function sha256(text){
const data = new TextEncoder().encode(text);
const digest = await crypto.subtle.digest('SHA-256', data);
return [...new Uint8Array(digest)].map(b=>b.toString(16).padStart(2,'0')).join('');
}
/* --- Authenticated-Encryption helpers (AES-GCM) --- */
async function deriveKey(roll, name, ts){
const seed = new TextEncoder().encode(`${roll}:${name}:${ts}`);
const hash = await crypto.subtle.digest('SHA-256', seed);
return crypto.subtle.importKey('raw', hash, 'AES-GCM', false, ['encrypt','decrypt']);
}
function strToUtf8(str){ return new TextEncoder().encode(str);}
function u8ToB64(u8){
return btoa(String.fromCharCode(...u8));
}
function b64ToU8(b64){
return Uint8Array.from(atob(b64), c=>c.charCodeAt(0));
}
/* Encrypt full assignment object */
async function encryptAssignment(obj){
const key = await deriveKey(obj.rollNumber, obj.studentName, obj.timestamp);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ad = strToUtf8(`${obj.rollNumber}:${obj.timestamp}`);
const plaintext = strToUtf8(JSON.stringify(obj));
const cipherBuf = await crypto.subtle.encrypt({name:'AES-GCM',iv,additionalData:ad}, key, plaintext);
return {
iv: u8ToB64(iv),
ad: u8ToB64(ad),
ct: u8ToB64(new Uint8Array(cipherBuf)),
meta:{
rollNumber: obj.rollNumber,
studentName: obj.studentName,
timestamp: obj.timestamp,
alg:'AES-GCM-256'
}
};
}
/* Decrypt on Teacher side */
async function decryptAssignment(pkg){
const iv = b64ToU8(pkg.iv);
const ad = b64ToU8(pkg.ad);
const ct = b64ToU8(pkg.ct);
const {rollNumber,studentName,timestamp}=pkg.meta;
const key = await deriveKey(rollNumber, studentName, timestamp);
const plainBuf = await crypto.subtle.decrypt({name:'AES-GCM',iv,additionalData:ad}, key, ct);
return JSON.parse(new TextDecoder().decode(plainBuf));
}
/* ---------- ACE INITIALISATION ---------- */
function initEditor(){
editor = ace.edit('editor');
editor.setTheme('ace/theme/chrome');
editor.session.setMode('ace/mode/plain_text');
editor.setOptions({fontSize:'14px',showPrintMargin:false,behavioursEnabled:true});
editor.commands.addCommand({
name:'insertBraces',bindKey:{win:'{',mac:'{'},exec(ed){ed.insert('{}');ed.navigateLeft(1);}
});
editor.commands.bindKeys({'Ctrl-V':null,'Command-V':null,'Ctrl-C':null,'Command-C':null,'Ctrl-X':null,'Command-X':null});
editor.session.on('change',()=>{
if(!studentInfoLocked) return;
updateCharCount(); lastSaveTime=new Date(); updateLastSaved();
});
}
/* ---------- INITIALISE APP ---------- */
function init(){
initEditor();
editor.container.addEventListener('keydown',e=>{
if(!studentInfoLocked) return;
if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='v'){e.preventDefault();showTypewriterOverlay();}
});
studentModeBtn.onclick = ()=>{studentInterface.classList.remove('hidden');teacherInterface.classList.add('hidden');appMode.textContent='Student Mode';};
teacherModeBtn.onclick = ()=>{studentInterface.classList.add('hidden');teacherInterface.classList.remove('hidden');appMode.textContent='Teacher Mode';};
startAssignmentBtn.onclick = ()=>{
const roll=rollNumberInput.value.trim(), name=studentNameInput.value.trim(),
course=courseNameInput.value.trim(), num=assignmentNumberInput.value.trim();
if(!roll||!name||!course||!num){alert('Please fill in all required fields');return;}
studentInfoLocked=true;
displayRollNumber.textContent=roll;
displayStudentName.textContent=name;
displayCourseName.textContent=course;
displayAssignmentNumber.textContent=num;
studentInfoForm.classList.add('hidden'); assignmentArea.classList.remove('hidden'); editor.focus();
};
languageSelect.onchange = ()=>{
const map={python:'python',java:'java',c_cpp:'c_cpp',javascript:'javascript',html:'html',css:'css'};
editor.session.setMode(`ace/mode/${map[languageSelect.value]||'plain_text'}`);
};
saveDraftBtn.onclick = ()=>{saveDraft();};
submitAssignmentBtn.onclick = ()=>{submitAssignment();};
resumeTypingBtn.onclick = ()=>{typewriterOverlay.classList.add('hidden');editor.focus();};
uploadAssignmentBtn.onclick = ()=>{verifyAssignment();};
downloadAssignmentBtn.onclick = ()=>{downloadSubmission();};
setInterval(()=>{if(studentInfoLocked&&lastSaveTime){saveDraft();}},30000);
updateCharCount(); updateLastSaved();
}
/* ---------- UI HELPERS ---------- */
function updateCharCount(){charCount.textContent=editor.getValue().length;}
function updateLastSaved(){
lastSaved.textContent=lastSaveTime ? `Last saved: ${lastSaveTime.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:true})}`:'Not saved yet';
}
/* ---------- TYPEWRITER OVERLAY ---------- */
function showTypewriterOverlay(){
currentContent=editor.getValue();typedPosition=0;typedContent.textContent='';
typewriterOverlay.classList.remove('hidden');
const warn=document.createElement('div');warn.className='text-red-500 font-medium mb-4';warn.textContent='You must type one character at a time!';
typewriterOverlay.querySelector('.text-center').prepend(warn); typeNextCharacter();
}
function typeNextCharacter(){if(typedPosition<currentContent.length){typedContent.textContent+=currentContent.charAt(typedPosition++);typingTimeout=setTimeout(typeNextCharacter,Math.random()*100+50);}}
/* ---------- SAVE DRAFT ---------- */
async function saveDraft(){
if(!studentInfoLocked) return;
const data = {
rollNumber:displayRollNumber.textContent, studentName:displayStudentName.textContent,
courseName:displayCourseName.textContent, assignmentNumber:displayAssignmentNumber.textContent,
language:languageSelect.value, content:editor.getValue(),
prompt:document.getElementById('assignmentPrompt').textContent,
timestamp:new Date().toISOString(),
hash:await sha256(editor.getValue())
};
localStorage.setItem('assignmentDraft',JSON.stringify(data));
lastSaveTime=new Date();updateLastSaved();
saveDraftBtn.textContent='Saved!';setTimeout(()=>{saveDraftBtn.textContent='Save Draft';},2000);
}
/* ---------- SUBMIT / DOWNLOAD (Student) ---------- */
async function submitAssignment(){
if(!studentInfoLocked) return;
const payload = {
rollNumber:displayRollNumber.textContent, studentName:displayStudentName.textContent,
courseName:displayCourseName.textContent, assignmentNumber:displayAssignmentNumber.textContent,
language:languageSelect.value, content:editor.getValue(),
prompt:document.getElementById('assignmentPrompt').textContent,
timestamp:new Date().toISOString(),
hash:await sha256(editor.getValue())
};
/* ---- Authenticated encryption ---- */
const encryptedPkg = await encryptAssignment(payload);
const fileText = JSON.stringify(encryptedPkg); // still JSON but unreadable without key
const blob = new Blob([fileText], {type:'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href=url; a.download=`assignment_${payload.rollNumber}.task`; document.body.appendChild(a); a.click();
document.body.removeChild(a); URL.revokeObjectURL(url);
localStorage.removeItem('assignmentDraft'); editor.setValue(''); updateCharCount();
}
/* ---------- VERIFY / DECRYPT (Teacher) ---------- */
async function verifyAssignment(){
const file=assignmentFile.files[0];
if(!file){alert('Please select a file');return;}
// locks obvious mistakes to enforce the extension
if (!file.name.toLowerCase().endsWith('.task')) {
showVerificationResult(false,
'Wrong file type – please upload a ".task" assignment file.');
return;
}
const reader=new FileReader();
reader.onload=async e=>{
try{
const encryptedPkg=JSON.parse(e.target.result);
const data = await decryptAssignment(encryptedPkg); // throws if tampered
/* quick sanity fields */
const required=['rollNumber','studentName','content','hash'];
if(required.some(k=>!data[k])){showVerificationResult(false,'Missing required fields');return;}
const expectedHash=await sha256(data.content);
if(expectedHash!==data.hash){showVerificationResult(false,'Content hash mismatch');return;}
showVerificationResult(true,'Valid encrypted submission'); displayAssignment(data);
}catch(err){
showVerificationResult(false,'Decryption failed – file was modified or wrong key');
}
};
reader.readAsText(file);
}
/* ---------- DISPLAY ASSIGNMENT ---------- */
function displayAssignment(d){
submissionRollNumber.textContent=d.rollNumber;
submissionStudentName.textContent=d.studentName;
submissionCourseName.textContent=d.courseName||'N/A';
submissionAssignmentNumber.textContent=d.assignmentNumber||'N/A';
submissionPrompt.textContent=d.prompt;
submissionContent.textContent=d.content;
submissionCharCount.textContent=d.content.length;
const map={python:'python',java:'java',c_cpp:'cpp',javascript:'javascript',html:'html',css:'css'};
submissionContent.className=`whitespace-pre-wrap font-mono text-sm language-${map[d.language]||''}`;
assignmentViewer.classList.remove('hidden');
}
/* ---------- VERIFICATION BANNER ---------- */
function showVerificationResult(ok,msg){
assignmentVerification.classList.remove('hidden');
if(ok){
verificationIcon.innerHTML='<i class="fas fa-check-circle text-green-500"></i>';
verificationResult.textContent='Valid Submission';
verificationResult.className='font-semibold text-green-600';
}else{
verificationIcon.innerHTML='<i class="fas fa-times-circle text-red-500"></i>';
verificationResult.textContent='Invalid Submission';
verificationResult.className='font-semibold text-red-600';
assignmentViewer.classList.add('hidden');
}
verificationDetails.textContent=msg;
}
/* ---------- DOWNLOAD COPY (Teacher) ---------- */
async function downloadSubmission(){
if(assignmentViewer.classList.contains('hidden')) return;
/* Collect the data currently displayed */
const data = {
rollNumber:submissionRollNumber.textContent,
studentName:submissionStudentName.textContent,
courseName:submissionCourseName.textContent,
assignmentNumber:submissionAssignmentNumber.textContent,
language:languageSelect.value,
content:submissionContent.textContent,
prompt:submissionPrompt.textContent,
timestamp:new Date().toISOString(),
hash:await sha256(submissionContent.textContent)
};
const encryptedPkg = await encryptAssignment(data);
const blob = new Blob([JSON.stringify(encryptedPkg)],{type:'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a=document.createElement('a');
a.href=url; a.download=`assignment_${data.rollNumber}_checked.task`;
document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
}
/* ---------- INITIALISE ---------- */
document.addEventListener('DOMContentLoaded',init);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Kast-KIIT/cms" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>