Adding ClientId; Adding Teacher Panel to start/end event recording

main
Amēlija I 2025-04-29 20:09:41 +03:00
parent fb3d497aa3
commit 53d1767a2d
4 changed files with 266 additions and 136 deletions

3
.gitignore vendored
View File

@ -12,9 +12,6 @@ dist
# misc
.DS_Store
# config
config.json
# local env files
.env.local
.env.development.local

View File

@ -6,11 +6,13 @@
"author": "Your Name",
"permissions": [
"storage"
"storage",
"alarms"
],
"host_permissions": [
"http://localhost:5000/*"
"http://localhost:5000/*",
"https://zpdai.rkg.lv/*"
],
"content_security_policy": {

View File

@ -1,3 +1,5 @@
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
function saveOptions(event) {
event.preventDefault();
const serverUrl = document.querySelector("#server-url").value;
@ -7,21 +9,63 @@ function saveOptions(event) {
alert("URL сервера и пароль не могут быть пустыми!");
return;
}
browser.storage.sync.set({ serverUrl, password }).then(() => {
const status = document.querySelector("#status");
status.textContent = "Настройки сохранены!";
setTimeout(() => (status.textContent = ""), 2000);
});
if (typeof browser !== 'undefined') {
browserAPI.storage.sync.set({ serverUrl, password })
.then(() => {
showStatus("Настройки сохранены!");
})
.catch((error) => {
console.error("Ошибка сохранения настроек:", error);
showStatus("Ошибка сохранения!", true);
});
} else {
browserAPI.storage.sync.set({ serverUrl, password }, () => {
if (chrome.runtime.lastError) {
console.error("Ошибка сохранения настроек:", chrome.runtime.lastError);
showStatus("Ошибка сохранения!", true);
} else {
showStatus("Настройки сохранены!");
}
});
}
}
function restoreOptions() {
browser.storage.sync.get(["serverUrl", "password"]).then((result) => {
const serverUrl = result.serverUrl || "http://localhost:5000/submit";
const password = result.password || "";
document.querySelector("#server-url").value = serverUrl;
document.querySelector("#password").value = password;
});
if (typeof browser !== 'undefined') {
browserAPI.storage.sync.get(["serverUrl", "password"])
.then((result) => {
updateFormFields(result);
})
.catch((error) => {
console.error("Ошибка загрузки настроек:", error);
updateFormFields({});
});
} else {
browserAPI.storage.sync.get(["serverUrl", "password"], (result) => {
if (chrome.runtime.lastError) {
console.error("Ошибка загрузки настроек:", chrome.runtime.lastError);
updateFormFields({});
} else {
updateFormFields(result);
}
});
}
}
function updateFormFields(result) {
document.querySelector("#server-url").value = result.serverUrl || "http://localhost:5000/submit";
document.querySelector("#password").value = result.password || "";
}
function showStatus(message, isError = false) {
const status = document.querySelector("#status");
status.textContent = message;
status.style.color = isError ? "red" : "green";
setTimeout(() => {
status.textContent = "";
status.style.color = "";
}, 2000);
}
document.addEventListener("DOMContentLoaded", restoreOptions);

View File

@ -1,38 +1,74 @@
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
let serverUrl = "http://localhost:5000/submit";
let password = "";
let clientId = null;
let isRecording = false;
let checkInterval = null;
let currentSessionId = null;
function loadSettings() {
return browser.storage.sync.get(["serverUrl", "password"]).then((result) => {
if (result.serverUrl) {
serverUrl = result.serverUrl;
console.log("URL сервера загружен из настроек:", serverUrl);
}
if (result.password) {
password = result.password;
console.log("Пароль загружен из настроек.");
} else {
return fetch(browser.runtime.getURL("config.json"))
.then(response => {
if (!response.ok) {
throw new Error(`Ошибка загрузки config.json: ${response.statusText}`);
}
return response.json();
return new Promise((resolve) => {
if (typeof browser !== 'undefined') {
browserAPI.storage.sync.get(["serverUrl", "password"])
.then((result) => {
processSettings(result);
resolve();
})
.then(config => {
serverUrl = config.FLASK_SERVER_URL;
password = config.PASSWORD || password;
console.log("URL сервера успешно загружен:", serverUrl);
.catch((error) => {
console.error("Ошибка загрузки настроек:", error);
resolve();
});
} else {
browserAPI.storage.sync.get(["serverUrl", "password"], (result) => {
if (chrome.runtime.lastError) {
console.error("Ошибка загрузки настроек:", chrome.runtime.lastError);
} else {
processSettings(result);
}
resolve();
});
}
}).catch((error) => {
console.error("Ошибка загрузки URL сервера:", error);
});
}
loadSettings();
function processSettings(result) {
if (result.serverUrl) {
serverUrl = result.serverUrl;
console.log("URL сервера загружен из настроек:", serverUrl);
}
if (result.password) {
password = result.password;
console.log("Пароль загружен из настроек.");
}
}
let clientId = null;
function handleClientId() {
if (typeof browser !== 'undefined') {
browserAPI.storage.sync.get("clientId")
.then((result) => {
if (result.clientId) {
clientId = result.clientId;
console.log("ID клиента загружен:", clientId);
} else {
generateAndSaveId();
}
})
.catch(console.error);
} else {
browserAPI.storage.sync.get("clientId", (result) => {
if (chrome.runtime.lastError) {
console.error("Ошибка загрузки ID клиента:", chrome.runtime.lastError);
} else if (result.clientId) {
clientId = result.clientId;
console.log("ID клиента загружен:", clientId);
} else {
generateAndSaveId();
}
});
}
}
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
@ -42,19 +78,54 @@ function generateUUID() {
});
}
browser.storage.sync.get("clientId").then((result) => {
if (result.clientId) {
clientId = result.clientId;
console.log("ID клиента загружен:", clientId);
function generateAndSaveId() {
clientId = generateUUID();
if (typeof browser !== 'undefined') {
browserAPI.storage.sync.set({ clientId })
.then(() => console.log("ID клиента создан и сохранен:", clientId))
.catch(console.error);
} else {
clientId = generateUUID();
browser.storage.sync.set({ clientId }).then(() => {
console.log("ID клиента создан и сохранен:", clientId);
browserAPI.storage.sync.set({ clientId }, () => {
if (chrome.runtime.lastError) {
console.error("Ошибка сохранения ID клиента:", chrome.runtime.lastError);
} else {
console.log("ID клиента создан и сохранен:", clientId);
}
});
}
}).catch((error) => {
console.error("Ошибка загрузки ID клиента:", error);
});
}
async function checkSessionStatus() {
try {
const statusUrl = 'http://127.0.0.1:5000/api/session_status';
const response = await fetch(statusUrl, {
headers: { 'X-Password': password }
});
const { active, session_id } = await response.json();
if (active && !isRecording) {
startRecording(session_id);
} else if (!active && isRecording) {
stopRecording();
}
} catch (error) {
console.error('Ошибка проверки сессии:', error);
}
}
function startRecording(sessionId) {
isRecording = true;
currentSessionId = sessionId;
console.log('Запись действий начата для сессии:', sessionId);
}
function stopRecording() {
isRecording = false;
currentSessionId = null;
console.log('Запись действий остановлена');
}
function createEventJSON(eventType, data) {
@ -71,10 +142,8 @@ function createEventJSON(eventType, data) {
async function sendDataToServer(eventData) {
if (!serverUrl || !password) {
console.error("URL сервера или пароль не заданы. Проверьте настройки.");
return;
}
if (!isRecording || !serverUrl || !password) return;
try {
const response = await fetch(serverUrl, {
method: 'POST',
@ -82,7 +151,10 @@ async function sendDataToServer(eventData) {
'Content-Type': 'application/json',
'X-Password': password,
},
body: JSON.stringify(eventData),
body: JSON.stringify({
...eventData,
session_id: currentSessionId
})
});
const responseData = await response.json();
@ -96,104 +168,119 @@ async function sendDataToServer(eventData) {
}
}
document.addEventListener("keydown", (event) => {
const key = event.key;
const code = event.code;
let description;
if (key.match(/^\p{Number}$/u)) {
description = "number";
} else if (key.match(/^\p{Letter}$/u)) {
description = "letter";
} else if (key.match(/^\p{Punctuation}$|^\p{Symbol}$/u)) {
description = "symbol";
} else {
description = `Клавиша: ${key} Код: ${code}`;
}
const eventData = createEventJSON('keydown', { description });
console.log("Событие keydown:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
document.addEventListener("mousedown", (event) => {
const eventData = createEventJSON('mousedown', { button: event.button, description: `Клик мышью: ${event.button}` });
console.log("Событие mousedown:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
function debounce(func, wait) {
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
timeout = setTimeout(() => func(...args), delay);
};
}
let accumulatedPixels = 0;
async function initialize() {
await loadSettings();
handleClientId();
const handleWheel = debounce((event) => {
const direction = accumulatedPixels > 0 ? "вниз" : "вверх";
const pixels = Math.abs(accumulatedPixels);
checkInterval = setInterval(checkSessionStatus, 5000);
checkSessionStatus();
const eventData = createEventJSON('wheel', { direction, pixels: `${pixels}px`, description: "Прокрутка колёсика мыши"});
console.log("Событие wheel:", JSON.stringify(eventData));
sendDataToServer(eventData);
accumulatedPixels = 0;
}, 200);
document.addEventListener("keydown", (event) => {
if (!isRecording) return;
const key = event.key;
const code = event.code;
let description;
document.addEventListener("wheel", (event) => { accumulatedPixels += event.deltaY; handleWheel(event);});
if (key.match(/^\p{Number}$/u)) {
description = "number";
} else if (key.match(/^\p{Letter}$/u)) {
description = "letter";
} else if (key.match(/^\p{Punctuation}$|^\p{Symbol}$/u)) {
description = "symbol";
} else {
description = `Клавиша: ${key} Код: ${code}`;
}
document.addEventListener("click", (event) => {
const eventType = 'click';
const clickedElement = event.target;
const tag = clickedElement.tagName;
const id = clickedElement.id || "отсутствует";
const className = clickedElement.className || "отсутствует";
const eventData = createEventJSON('keydown', { description });
console.log("Событие keydown:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
let description = "Клик по элементу";
let data = { tag, id, className };
if (tag === 'A') {
const href = clickedElement.href;
data.href = href;
description = "Клик по ссылке";
document.addEventListener("mousedown", (event) => {
if (!isRecording) return;
const eventData = createEventJSON('mousedown', { button: event.button, description: `Клик мышью: ${event.button}` });
console.log("Событие mousedown:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
localStorage.setItem('clickedElementInfo', JSON.stringify(data));
navigateWithPromise(href);
} else if (tag === 'INPUT') {
const placeholder = clickedElement.placeholder || "отсутствует";
data.placeholder = placeholder;
description = "Клик по полю ввода";
let accumulatedPixels = 0;
const handleWheel = debounce((event) => {
if (!isRecording) return;
const direction = accumulatedPixels > 0 ? "вниз" : "вверх";
const pixels = Math.abs(accumulatedPixels);
const eventData = createEventJSON('wheel', { direction, pixels: `${pixels}px`, description: "Прокрутка колёсика мыши"});
console.log("Событие wheel:", JSON.stringify(eventData));
sendDataToServer(eventData);
accumulatedPixels = 0;
}, 200);
document.addEventListener("wheel", (event) => { if (!isRecording) return; accumulatedPixels += event.deltaY; handleWheel(event);});
document.addEventListener("click", (event) => {
if (!isRecording) return;
const eventType = 'click';
const clickedElement = event.target;
const tag = clickedElement.tagName;
const id = clickedElement.id || "отсутствует";
const className = clickedElement.className || "отсутствует";
let description = "Клик по элементу";
let data = { tag, id, className };
if (tag === 'A') {
const href = clickedElement.href;
data.href = href;
description = "Клик по ссылке";
localStorage.setItem('clickedElementInfo', JSON.stringify(data));
navigateWithPromise(href);
} else if (tag === 'INPUT') {
const placeholder = clickedElement.placeholder || "отсутствует";
data.placeholder = placeholder;
description = "Клик по полю ввода";
}
data.description = description;
const eventData = createEventJSON(eventType, data);
console.log("Событие click:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
function navigateWithPromise(href) {
return new Promise((resolve) => {
window.location.href = href;
window.addEventListener('load', resolve);
});
}
data.description = description;
const eventData = createEventJSON(eventType, data);
console.log("Событие click:", JSON.stringify(eventData));
sendDataToServer(eventData);
});
document.addEventListener("copy", (event) => {
if (!isRecording) return;
const selectedText = window.getSelection().toString();
let outputText = selectedText.length > 50 ? selectedText.substring(0, 49) + '…' : selectedText;
function navigateWithPromise(href) {
return new Promise((resolve) => {
window.location.href = href;
window.addEventListener('load', resolve);
if (selectedText.length > 0) {
const eventData = createEventJSON('copy', { selectedText: outputText, description: "Скопирован текст" });
console.log("Событие copy:", JSON.stringify(eventData));
sendDataToServer(eventData);
}
});
}
document.addEventListener("copy", (event) => {
const selectedText = window.getSelection().toString();
let outputText = selectedText.length > 50 ? selectedText.substring(0, 49) + '…' : selectedText;
if (selectedText.length > 0) {
const eventData = createEventJSON('copy', { selectedText: outputText, description: "Скопирован текст" });
console.log("Событие copy:", JSON.stringify(eventData));
sendDataToServer(eventData);
}
});
initialize();