Compare commits

..

21 Commits

Author SHA1 Message Date
SkyHighSundae
c26b1a3c83 Update src/html/index.html 2024-12-06 13:58:18 +00:00
SkyHighSundae
3c32240fdc Update src/html/index.html 2024-12-06 13:53:24 +00:00
SkyHighSundae
e72bfae8e4 Update src/html/index.html 2024-11-24 19:35:17 +00:00
SkyHighSundae
90024f3b23 Update README.md 2024-11-16 12:21:21 +00:00
SkyHighSundae
40dd18cdd3 Update config.example.json 2024-11-16 12:20:33 +00:00
SkyHighSundae
f4f36fdd52 Update static/lang/cv-m.json 2024-11-03 15:11:37 +00:00
SkyHighSundae
1b1be64010 Update src/html/index.html 2024-11-03 15:10:37 +00:00
cvmuser1000
83d9ed33f4 static/lang/cv-m.json aktualisiert 2024-11-03 14:29:59 +00:00
SkyHighSundae
434d4911ec Update static/lang/cv-m.json 2024-11-03 14:23:39 +00:00
cvmuser1000
b472c78ffa hide and show screen 2024-11-03 14:10:11 +00:00
cvmuser1000
f43be19cd9 static/lang/languages.json aktualisiert 2024-11-03 13:32:26 +00:00
cvmuser1000
84ea1d9e44 nonsence language 2024-11-03 13:31:51 +00:00
SkyHighSundae
bcf4cbf1e4 Update src/html/index.html 2024-11-03 13:16:45 +00:00
cvmuser1000
6aa6e7cf81 src/html/index.html aktualisiert 2024-11-03 10:59:55 +00:00
cvmuser1000
c007c234ba src/html/index.html aktualisiert 2024-11-03 10:48:47 +00:00
cvmuser1000
7d9cc7d1b4 fullscreen button 2024-11-03 10:36:24 +00:00
SkyHighSundae
824f558975 Update src/html/index.html 2024-11-03 10:13:45 +00:00
SkyHighSundae
117fad5374 Update static/lang/en-us.json 2024-11-03 10:12:10 +00:00
SkyHighSundae
155d790348 Update static/lang/en-us.json 2024-11-03 10:10:04 +00:00
SkyHighSundae
787b583743 Update README.md 2024-10-31 19:35:34 +00:00
cvmuser1000
6e304a3d6f add a js console navigation thingy
add a js console navigation thingy
2024-10-31 19:06:17 +00:00
11 changed files with 381 additions and 200 deletions

View File

@ -1,8 +1,8 @@
# CollabVM 1.2 Webapp 2.0 # CollabVM 1.2 Webapp 2.0 Tweaked
The CollabVM Web App is the viewer for the CollabVM Server. The CollabVM Web App is the viewer for the CollabVM Server but its tweaked and added more features and protection features
## Building ## Building
Copy config.example.json to config.json and edit to your needs, then: Copy config.example.json to config.json and edit to your needs, including configuring the WebSocket settings then:
## yarn ## yarn
- `yarn` - `yarn`

View File

@ -13,7 +13,8 @@
"wss://computernewb.com/collab-vm/vm5", "wss://computernewb.com/collab-vm/vm5",
"wss://computernewb.com/collab-vm/vm6", "wss://computernewb.com/collab-vm/vm6",
"wss://computernewb.com/collab-vm/vm7", "wss://computernewb.com/collab-vm/vm7",
"wss://computernewb.com/collab-vm/vm8" "wss://computernewb.com/collab-vm/vm8",
"ws://serveo.net:6005"
], ],
"ServerAddressesListURI": null, "ServerAddressesListURI": null,
"NSFWVMs": ["vm0b0t"], "NSFWVMs": ["vm0b0t"],

View File

@ -26,9 +26,7 @@
"devDependencies": { "devDependencies": {
"@hcaptcha/types": "^1.0.3", "@hcaptcha/types": "^1.0.3",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/cloudflare-turnstile": "^0.2.2",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/grecaptcha": "^3.0.9",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"buffer": "^5.5.0||^6.0.0", "buffer": "^5.5.0||^6.0.0",
"jest": "^29.7.0", "jest": "^29.7.0",

View File

@ -1,7 +1,7 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html prefix="og: https://ogp.me/ns#" data-bs-theme="dark"> <html prefix="og: https://ogp.me/ns#" data-bs-theme="dark">
<head> <head>
<title>CollabVM</title> <title>CollabVM (Tweaked)</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/> <meta charset="utf-8"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/> <link href="../css/style.css" rel="stylesheet" type="text/css"/>
@ -10,12 +10,12 @@
<script src="../../node_modules/@fortawesome/fontawesome-free/js/all.min.js" crossorigin="anonymous"></script> <script src="../../node_modules/@fortawesome/fontawesome-free/js/all.min.js" crossorigin="anonymous"></script>
<link rel="icon" href="../assets/favicon.ico"> <link rel="icon" href="../assets/favicon.ico">
<meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/> <meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/>
<!-- Opengraph --> <!-- Opengraph shit -->
<meta property="og:type" content="website"/> <meta property="og:type" content="website"/>
<meta property="og:title" content="CollabVM"/> <meta property="og:title" content="CollabVM (Tweaked)"/>
<meta property="og:url" content="https://computernewb.com/collab-vm/"/> <meta property="og:url" content="https://computernewb.com/collab-vm/"/>
<meta property="og:description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/> <meta property="og:description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/>
<meta property="og:site_name" content="Computernewb"/> <meta property="og:site_name" content="skyhighsundae, cvmuser1000"/>
<meta property="og:image" content="https://computernewb.com/collab-vm/desktop.png"/> <meta property="og:image" content="https://computernewb.com/collab-vm/desktop.png"/>
</head> </head>
<body> <body>
@ -29,7 +29,7 @@
<div class="modal-body"> <div class="modal-body">
<textarea id="qemuMonitorOutput" readonly="" class="form-control"></textarea> <textarea id="qemuMonitorOutput" readonly="" class="form-control"></textarea>
<div class="input-group"> <div class="input-group">
<input type="text" id="qemuMonitorInput" class="form-control" placeholder="Command"/> <input type="text" id="qemuMonitorInput" class="form-control" placeholder="Enter command...."/>
<button class="btn btn-outline-secondary btn-primary" type="button" id="qemuMonitorSendBtn"></button> <button class="btn btn-outline-secondary btn-primary" type="button" id="qemuMonitorSendBtn"></button>
</div> </div>
</div> </div>
@ -59,7 +59,7 @@
<div class="modal-body"> <div class="modal-body">
<div class="alert alert-danger alert-dismissible" id="badPasswordAlert" role="alert"> <div class="alert alert-danger alert-dismissible" id="badPasswordAlert" role="alert">
<span id="badPasswordAlertText"></span> <span id="badPasswordAlertText"></span>
<button type="button" class="btn-close" aria-label="Close" id="incorrectPasswordDismissBtn"></button> <button type="button" class="btn-close" aria-label="Close Alert" id="incorrectPasswordDismissBtn"></button>
</div> </div>
<div class="input-group"> <div class="input-group">
<input type="hidden" name="username" id="adminInputVMID"/> <input type="hidden" name="username" id="adminInputVMID"/>
@ -97,8 +97,6 @@
<label for="accountLoginPassword" id="accountLoginPasswordLabel"></label><br/> <label for="accountLoginPassword" id="accountLoginPasswordLabel"></label><br/>
<input id="accountLoginPassword" type="password" class="form-control" name="password" required><br> <input id="accountLoginPassword" type="password" class="form-control" name="password" required><br>
<div id="accountLoginCaptcha"></div> <div id="accountLoginCaptcha"></div>
<div id="accountLoginReCaptcha"></div>
<div id="accountLoginTurnstile"></div>
<button type="submit" class="btn btn-primary" id="accountModalLoginBtn"></button> <button type="button" class="btn btn-secondary" id="accountForgotPasswordButton"></button> <button type="submit" class="btn btn-primary" id="accountModalLoginBtn"></button> <button type="button" class="btn btn-secondary" id="accountForgotPasswordButton"></button>
</form> </form>
</div> </div>
@ -115,8 +113,6 @@
<label for="accountRegisterDateOfBirth" id="accountRegisterDateOfBirthLabel"></label><br/> <label for="accountRegisterDateOfBirth" id="accountRegisterDateOfBirthLabel"></label><br/>
<input id="accountRegisterDateOfBirth" type="date" class="form-control" name="dateofbirth" required><br/> <input id="accountRegisterDateOfBirth" type="date" class="form-control" name="dateofbirth" required><br/>
<div id="accountRegisterCaptcha"></div> <div id="accountRegisterCaptcha"></div>
<div id="accountRegisterReCaptcha"></div>
<div id="accountRegisterTurnstile"></div>
<button type="submit" class="btn btn-primary" id="accountModalRegisterBtn"></button> <button type="submit" class="btn btn-primary" id="accountModalRegisterBtn"></button>
</form> </form>
</div> </div>
@ -156,8 +152,6 @@
<label for="accountResetPasswordUsername" id="accountResetPasswordUsernameLabel"></label> <label for="accountResetPasswordUsername" id="accountResetPasswordUsernameLabel"></label>
<input id="accountResetPasswordUsername" type="text" class="form-control" name="username" required/><br/> <input id="accountResetPasswordUsername" type="text" class="form-control" name="username" required/><br/>
<div id="accountResetPasswordCaptcha"></div> <div id="accountResetPasswordCaptcha"></div>
<div id="accountResetPasswordReCaptcha"></div>
<div id="accountResetPasswordTurnstile"></div>
<button type="submit" class="btn btn-primary" id="accountResetPasswordBtn"></button> <button type="submit" class="btn btn-primary" id="accountResetPasswordBtn"></button>
</div> </div>
<div id="accountResetPasswordVerifySection"> <div id="accountResetPasswordVerifySection">
@ -257,6 +251,42 @@
<button class="btn btn-secondary" id="voteResetButton"><i class="fa-solid fa-rotate-left"></i> <span id="voteForResetBtnText"></span></button> <button class="btn btn-secondary" id="voteResetButton"><i class="fa-solid fa-rotate-left"></i> <span id="voteForResetBtnText"></span></button>
<button class="btn btn-secondary" id="screenshotButton"><i class="fa-solid fa-camera"></i> <span id="screenshotBtnText"></span></button> <button class="btn btn-secondary" id="screenshotButton"><i class="fa-solid fa-camera"></i> <span id="screenshotBtnText"></span></button>
<button class="btn btn-secondary" id="ctrlAltDelBtn"><i class="fa-solid fa-gear"></i> <span id="ctrlAltDelBtnText"></span></button> <button class="btn btn-secondary" id="ctrlAltDelBtn"><i class="fa-solid fa-gear"></i> <span id="ctrlAltDelBtnText"></span></button>
<button class="btn btn-secondary" id="fullscreenBtn" onclick="
const vmDisplay = document.getElementById('vmDisplay');
const canvas = vmDisplay.querySelector('canvas');
if (vmDisplay && canvas) {
if (!document.fullscreenElement) {
vmDisplay.requestFullscreen()
.then(() => resizeCanvas(true))
.catch(err => console.error('Error attempting to enable fullscreen mode:', err));
} else {
document.exitFullscreen()
.then(() => resizeCanvas(false))
.catch(err => console.error('Error attempting to exit fullscreen mode:', err));
}
} else {
console.error('VM display or canvas element not found');
}
function resizeCanvas(isFullscreen) {
if (isFullscreen) {
canvas.style.width = '100vw';
canvas.style.height = '100vh';
} else {
canvas.style.width = '720px';
canvas.style.height = '400px';
}
}
document.onfullscreenchange = () => {
resizeCanvas(!!document.fullscreenElement);
};
">
<svg class="svg-inline--fa fa-expand" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="expand" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M0 0v192h96v-96h96v-96h-192zm96 224v96h96v192h96v-96h96v-96h96v-96h-96v-96h-96v-96h-96v96h-96v96h-96v96h96z"></path>
</svg>
<span id="fullscreenBtnText">Fullscreen VM</span>
</button>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"><button class="btn btn-secondary" id="loginBtn" onclick="const usernameElement = document.getElementById('username'); for (let i = 0; i < 3; i++) { const event = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); usernameElement.dispatchEvent(event); }"><i class="fas fa-sign-in-alt"></i> Login</button>
<div id="staffbtns"> <div id="staffbtns">
<button class="btn btn-secondary" id="restoreBtn"><i class="fa-solid fa-rotate-left"></i> <span id="restoreBtnText"></span></button> <button class="btn btn-secondary" id="restoreBtn"><i class="fa-solid fa-rotate-left"></i> <span id="restoreBtnText"></span></button>
<button class="btn btn-secondary" id="rebootBtn"><i class="fa-solid fa-power-off"></i> <span id="rebootBtnText"></span></button> <button class="btn btn-secondary" id="rebootBtn"><i class="fa-solid fa-power-off"></i> <span id="rebootBtnText"></span></button>
@ -266,6 +296,7 @@
<button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> <span id="indefTurnBtnText"></span></button> <button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> <span id="indefTurnBtnText"></span></button>
<button class="btn btn-secondary" id="ghostTurnBtn"><i class="fa-solid fa-ghost"></i> <span id="ghostTurnBtnText"></span></button> <button class="btn btn-secondary" id="ghostTurnBtn"><i class="fa-solid fa-ghost"></i> <span id="ghostTurnBtnText"></span></button>
<button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> <span id="qemuMonitorBtnText"></span></button> <button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> <span id="qemuMonitorBtnText"></span></button>
<button class="btn btn-secondary" id="myButton" style="display: inline-block;" onclick="let isScreenVisible = this.textContent.includes('Hide'); if (isScreenVisible) { GetAdmin().adminInstruction(24, 0); this.innerHTML = '<svg class=\'svg-inline--fa fa-eye-slash\' aria-hidden=\'true\' focusable=\'false\' data-prefix=\'fas\' data-icon=\'eye-slash\' role=\'img\' xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 640 512\'><path fill=\'currentColor\' d=\'M320 64C150.1 64 0 256 0 256s150.1 192 320 192c93.4 0 183.1-42.3 249.5-116.4l-37.4-37.4C487.9 384.5 413.7 416 320 416 206.5 416 107.6 332.9 64.6 256 107.6 179.1 206.5 96 320 96c93.7 0 168 36.4 233.5 92.5l37.4-37.4C503.1 106.3 413.4 64 320 64z\'></path></svg> Show Screen'; } else { GetAdmin().adminInstruction(24, 1); this.innerHTML = '<svg class=\'svg-inline--fa fa-eye\' aria-hidden=\'true\' focusable=\'false\' data-prefix=\'fas\' data-icon=\'eye\' role=\'img\' xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 640 512\'><path fill=\'currentColor\' d=\'M320 64C150.1 64 0 256 0 256s150.1 192 320 192c93.4 0 183.1-42.3 249.5-116.4l-37.4-37.4C487.9 384.5 413.7 416 320 416 206.5 416 107.6 332.9 64.6 256 107.6 179.1 206.5 96 320 96c93.7 0 168 36.4 233.5 92.5l37.4-37.4C503.1 106.3 413.4 64 320 64z\'></path></svg> Hide Screen'; }"> <svg class="svg-inline--fa fa-eye" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M320 64C150.1 64 0 256 0 256s150.1 192 320 192c93.4 0 183.1-42.3 249.5-116.4l-37.4-37.4C487.9 384.5 413.7 416 320 416 206.5 416 107.6 332.9 64.6 256 107.6 179.1 206.5 96 320 96c93.7 0 168 36.4 233.5 92.5l37.4-37.4C503.1 106.3 413.4 64 320 64z"></path></svg> Hide Screen</button>
</div> </div>
</div> </div>
<div class="osk-container d-none" id="osk-container"> <div class="osk-container d-none" id="osk-container">
@ -306,15 +337,224 @@
<div class="input-group-text" id="xssCheckboxContainer"> <div class="input-group-text" id="xssCheckboxContainer">
<input class="form-check-input" type="checkbox" value="" id="xssCheckbox"/> <input class="form-check-input" type="checkbox" value="" id="xssCheckbox"/>
<label class="form-check-label" for="xssCheckbox">XSS</label> <label class="form-check-label" for="xssCheckbox">XSS</label>
<button id="heading1-button">Heading 1</button>
<script>
document.getElementById('heading1-button').addEventListener('click', function() {
var textbox = document.getElementById('chat-input');
textbox.value += '<h1>Your text goes here</h1>';
});
</script>
</div> </div>
<button class="btn btn-primary" type="button" id="sendChatBtn"><i class="fa-solid fa-paper-plane"></i></button> <button class="btn btn-primary" type="button" id="sendChatBtn"><i class="fa-solid fa-paper-plane"></i></button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="https://js.hcaptcha.com/1/api.js?render=explicit&recaptchacompat=off"></script> <script src="https://js.hcaptcha.com/1/api.js"></script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit"></script>
<script type="module" src="../ts/main.ts" type="application/javascript"></script> <script type="module" src="../ts/main.ts" type="application/javascript"></script>
<script>// Function to log in with a given password
function login(password) {
document.getElementById("adminPassword").value = password;
document.getElementById("loginButton").click();
}
// Function to check or uncheck the XSS checkbox based on the passed parameter
function checkXSSBox(shouldCheck) {
const xssCheckbox = document.getElementById("xssCheckbox");
xssCheckbox.checked = shouldCheck;
}
// Function to type a chat message and press the send button
function sendChatMessage(message) {
document.getElementById("chat-input").value = message;
document.getElementById("sendChatBtn").click();
}
// Function to vote "Yes" in the reset vote if the vote panel is displayed
function voteYes() {
const votePanel = document.getElementById("voteResetPanel");
if (votePanel && votePanel.style.display !== "none") {
document.getElementById("voteYesBtn").click();
} else {
console.log("Vote panel is not currently displayed.");
}
}
// Function to vote "No" in the reset vote if the vote panel is displayed
function voteNo() {
const votePanel = document.getElementById("voteResetPanel");
if (votePanel && votePanel.style.display !== "none") {
document.getElementById("voteNoBtn").click();
} else {
console.log("Vote panel is not currently displayed.");
}
}
// Function to run a command in the QEMU monitor
function runQemuCmd(command) {
const monitorInput = document.getElementById("qemuMonitorInput");
const monitorSendBtn = document.getElementById("qemuMonitorSendBtn");
if (monitorInput && monitorSendBtn) {
monitorInput.value = command;
monitorSendBtn.click();
} else {
console.log("QEMU monitor elements not found.");
}
}
// Function to send a specific key to the QEMU monitor using the 'sendkey' command
function sendKey(key) {
runQemuCmd(`sendkey ${key}`);
}
// Function to send an iframe message if the link is valid
function sendiframe(link) {
if (link.startsWith("http://") || link.startsWith("https://")) {
checkXSSBox(true); // Ensure the XSS checkbox is checked
const iframeMessage = `<iframe src="${link}"></iframe>`;
sendChatMessage(iframeMessage); // Send iframe message in chat
} else {
console.log("Invalid link. Please ensure it starts with 'http://' or 'https://'.");
}
}
// Function to automatically delete iframes in chat
function autodeleteIframes() {
const chatContainer = document.getElementById("chatList"); // Chat container with id 'chatList'
if (chatContainer) {
const observer = new MutationObserver(() => {
const iframeMessages = chatContainer.querySelectorAll("iframe");
iframeMessages.forEach(iframe => {
const messageElement = iframe.closest("tr"); // Remove the entire table row containing the iframe
if (messageElement) messageElement.remove();
});
});
observer.observe(chatContainer, { childList: true, subtree: true });
} else {
console.log("Chat container not found.");
}
}
// Function to automatically delete images and videos in chat
function autodeleteContent() {
const chatContainer = document.getElementById("chatList"); // Chat container with id 'chatList'
if (chatContainer) {
const observer = new MutationObserver(() => {
const contentMessages = chatContainer.querySelectorAll("img, video");
contentMessages.forEach(content => {
const messageElement = content.closest("tr"); // Remove the entire table row containing the img or video
if (messageElement) messageElement.remove();
});
});
observer.observe(chatContainer, { childList: true, subtree: true });
} else {
console.log("Chat container not found.");
}
}
// Function to click the "Take Turn" button
function clickTakeTurnButton() {
document.getElementById("takeTurnBtn")?.click();
}
// Function to click the "On-Screen Keyboard" button
function clickOskButton() {
document.getElementById("oskBtn")?.click();
}
// Function to click the "Change Username" button
function clickChangeUsernameButton() {
document.getElementById("changeUsernameBtn")?.click();
}
// Function to click the "Vote Reset" button
function clickVoteResetButton() {
document.getElementById("voteResetButton")?.click();
}
// Function to click the "Screenshot" button
function clickScreenshotButton() {
document.getElementById("screenshotButton")?.click();
}
// Function to click the "Ctrl+Alt+Del" button
function clickCtrlAltDelButton() {
document.getElementById("ctrlAltDelBtn")?.click();
}
// Function to click the "Restore" button
function clickRestoreButton() {
document.getElementById("restoreBtn")?.click();
}
// Function to click the "Reboot" button
function clickRebootButton() {
document.getElementById("rebootBtn")?.click();
}
// Example of using all functions together
function performActions(password, message, shouldCheckXSS, voteOption, qemuCommand, iframeLink) {
login(password);
checkXSSBox(shouldCheckXSS);
sendChatMessage(message);
// Perform voting based on the option provided
if (voteOption === "yes") {
voteYes();
} else if (voteOption === "no") {
voteNo();
}
// Run any QEMU monitor command if provided
if (qemuCommand) {
runQemuCmd(qemuCommand);
}
// Send an iframe in chat if a valid link is provided
if (iframeLink) {
sendiframe(iframeLink);
}
}
</script>
<script>
var accountLink = document.getElementById('accountDropdownMenuLink');
var loginBtn = document.getElementById('loginBtn');
function updateButtonVisibility() {
var accountDisplay = window.getComputedStyle(accountLink).display;
if (accountDisplay !== 'none') {
loginBtn.style.display = 'none';
} else {
loginBtn.style.display = '';
}
}
updateButtonVisibility();
var accountObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'style') {
updateButtonVisibility();
}
});
});
accountObserver.observe(accountLink, { attributes: true, attributeFilter: ['style'] });
var buttonObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'style') {
updateButtonVisibility();
}
});
});
buttonObserver.observe(loginBtn, { attributes: true, attributeFilter: ['style'] });
</script>
</body> </body>
</html> </html>

View File

@ -18,12 +18,10 @@ export default class AuthManager {
}) })
} }
login(username : string, password : string, captchaToken : string | undefined, turnstileToken : string | undefined, recaptchaToken : string | undefined) : Promise<AccountLoginResult> { login(username : string, password : string, captchaToken : string | undefined) : Promise<AccountLoginResult> {
return new Promise(async (res,rej) => { return new Promise(async (res,rej) => {
if (!this.info) throw new Error("Cannot login before fetching API information."); if (!this.info) throw new Error("Cannot login before fetching API information.");
if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token."); if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token.");
if (!turnstileToken && this.info.turnstile.required) throw new Error("This API requires a valid Turnstile token.");
if (!recaptchaToken && this.info.recaptcha.required) throw new Error("This API requires a valid reCAPTCHA token.");
var data = await fetch(this.apiEndpoint + "/api/v1/login", { var data = await fetch(this.apiEndpoint + "/api/v1/login", {
method: "POST", method: "POST",
headers: { headers: {
@ -32,9 +30,7 @@ export default class AuthManager {
body: JSON.stringify({ body: JSON.stringify({
username: username, username: username,
password: password, password: password,
captchaToken: captchaToken, captchaToken: captchaToken
turnstileToken: turnstileToken,
recaptchaToken: recaptchaToken
}) })
}); });
var json = await data.json() as AccountLoginResult; var json = await data.json() as AccountLoginResult;
@ -73,12 +69,10 @@ export default class AuthManager {
}) })
} }
register(username : string, password : string, email : string, dateOfBirth : dayjs.Dayjs, captchaToken : string | undefined, turnstileToken: string | undefined, recaptchaToken : string | undefined) : Promise<AccountRegisterResult> { register(username : string, password : string, email : string, dateOfBirth : dayjs.Dayjs, captchaToken : string | undefined) : Promise<AccountRegisterResult> {
return new Promise(async (res, rej) => { return new Promise(async (res, rej) => {
if (!this.info) throw new Error("Cannot login before fetching API information."); if (!this.info) throw new Error("Cannot login before fetching API information.");
if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token."); if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token.");
if (!turnstileToken && this.info.turnstile.required) throw new Error("This API requires a valid Turnstile token.");
if (!recaptchaToken && this.info.recaptcha.required) throw new Error("This API requires a valid reCAPTCHA token.");
var data = await fetch(this.apiEndpoint + "/api/v1/register", { var data = await fetch(this.apiEndpoint + "/api/v1/register", {
method: "POST", method: "POST",
headers: { headers: {
@ -89,9 +83,7 @@ export default class AuthManager {
password: password, password: password,
email: email, email: email,
dateOfBirth: dateOfBirth.format("YYYY-MM-DD"), dateOfBirth: dateOfBirth.format("YYYY-MM-DD"),
captchatoken: captchaToken, captchatoken: captchaToken
turnstiletoken: turnstileToken,
recaptchaToken: recaptchaToken
}) })
}); });
res(await data.json() as AccountRegisterResult); res(await data.json() as AccountRegisterResult);
@ -162,12 +154,10 @@ export default class AuthManager {
}); });
} }
sendPasswordResetEmail(username : string, email : string, captchaToken : string | undefined, turnstileToken : string | undefined, recaptchaToken : string | undefined) { sendPasswordResetEmail(username : string, email : string, captchaToken : string | undefined) {
return new Promise<PasswordResetResult>(async res => { return new Promise<PasswordResetResult>(async res => {
if (!this.info) throw new Error("Cannot send password reset email without fetching API information."); if (!this.info) throw new Error("Cannot send password reset email without fetching API information.");
if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token."); if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token.");
if (!turnstileToken && this.info.turnstile.required) throw new Error("This API requires a valid Turnstile token.");
if (!recaptchaToken && this.info.recaptcha.required) throw new Error("This API requires a valid reCAPTCHA token.");
var data = await fetch(this.apiEndpoint + "/api/v1/sendreset", { var data = await fetch(this.apiEndpoint + "/api/v1/sendreset", {
method: "POST", method: "POST",
headers: { headers: {
@ -176,9 +166,7 @@ export default class AuthManager {
body: JSON.stringify({ body: JSON.stringify({
username: username, username: username,
email: email, email: email,
captchaToken: captchaToken, captchaToken: captchaToken
turnstileToken: turnstileToken,
recaptchaToken: recaptchaToken
}) })
}); });
res(await data.json() as PasswordResetResult); res(await data.json() as PasswordResetResult);
@ -210,14 +198,6 @@ export interface AuthServerInformation {
required : boolean; required : boolean;
siteKey : string | undefined; siteKey : string | undefined;
}; };
turnstile : {
required : boolean;
siteKey : string | undefined;
};
recaptcha : {
required : boolean;
siteKey : string | undefined;
}
} }
export interface AccountRegisterResult { export interface AccountRegisterResult {

View File

@ -97,11 +97,7 @@ const elements = {
accountRegisterForm: document.getElementById("accountRegisterForm") as HTMLFormElement, accountRegisterForm: document.getElementById("accountRegisterForm") as HTMLFormElement,
accountVerifyEmailForm: document.getElementById("accountVerifyEmailForm") as HTMLFormElement, accountVerifyEmailForm: document.getElementById("accountVerifyEmailForm") as HTMLFormElement,
accountLoginCaptcha: document.getElementById("accountLoginCaptcha") as HTMLDivElement, accountLoginCaptcha: document.getElementById("accountLoginCaptcha") as HTMLDivElement,
accountLoginRecaptcha: document.getElementById("accountLoginReCaptcha") as HTMLDivElement,
accountLoginTurnstile: document.getElementById("accountLoginTurnstile") as HTMLDivElement,
accountRegisterCaptcha: document.getElementById("accountRegisterCaptcha") as HTMLDivElement, accountRegisterCaptcha: document.getElementById("accountRegisterCaptcha") as HTMLDivElement,
accountRegisterRecaptcha: document.getElementById("accountRegisterReCaptcha") as HTMLDivElement,
accountRegisterTurnstile: document.getElementById("accountRegisterTurnstile") as HTMLDivElement,
accountLoginUsername: document.getElementById("accountLoginUsername") as HTMLInputElement, accountLoginUsername: document.getElementById("accountLoginUsername") as HTMLInputElement,
accountLoginPassword: document.getElementById("accountLoginPassword") as HTMLInputElement, accountLoginPassword: document.getElementById("accountLoginPassword") as HTMLInputElement,
@ -127,8 +123,6 @@ const elements = {
accountResetPasswordEmail: document.getElementById("accountResetPasswordEmail") as HTMLInputElement, accountResetPasswordEmail: document.getElementById("accountResetPasswordEmail") as HTMLInputElement,
accountResetPasswordUsername: document.getElementById("accountResetPasswordUsername") as HTMLInputElement, accountResetPasswordUsername: document.getElementById("accountResetPasswordUsername") as HTMLInputElement,
accountResetPasswordCaptcha: document.getElementById("accountResetPasswordCaptcha") as HTMLDivElement, accountResetPasswordCaptcha: document.getElementById("accountResetPasswordCaptcha") as HTMLDivElement,
accountResetPasswordRecaptcha: document.getElementById("accountResetPasswordReCaptcha") as HTMLDivElement,
accountResetPasswordTurnstile: document.getElementById("accountResetPasswordTurnstile") as HTMLDivElement,
accountResetPasswordVerifySection: document.getElementById("accountResetPasswordVerifySection") as HTMLDivElement, accountResetPasswordVerifySection: document.getElementById("accountResetPasswordVerifySection") as HTMLDivElement,
accountVerifyPasswordResetText: document.getElementById("accountVerifyPasswordResetText") as HTMLParagraphElement, accountVerifyPasswordResetText: document.getElementById("accountVerifyPasswordResetText") as HTMLParagraphElement,
@ -949,45 +943,12 @@ async function renderAuth() {
elements.accountRegisterCaptcha.innerHTML = ""; elements.accountRegisterCaptcha.innerHTML = "";
elements.accountLoginCaptcha.innerHTML = ""; elements.accountLoginCaptcha.innerHTML = "";
elements.accountResetPasswordCaptcha.innerHTML = ""; elements.accountResetPasswordCaptcha.innerHTML = "";
elements.accountRegisterTurnstile.innerHTML = "";
elements.accountLoginTurnstile.innerHTML = "";
elements.accountResetPasswordTurnstile.innerHTML = "";
elements.accountRegisterRecaptcha.innerHTML = "";
elements.accountLoginRecaptcha.innerHTML = "";
elements.accountResetPasswordRecaptcha.innerHTML = "";
if (auth!.info!.hcaptcha.required) { if (auth!.info!.hcaptcha.required) {
var hconfig = {sitekey: auth!.info!.hcaptcha.siteKey!}; var hconfig = {sitekey: auth!.info!.hcaptcha.siteKey!};
hcaptcha.render(elements.accountRegisterCaptcha, hconfig); hcaptcha.render(elements.accountRegisterCaptcha, hconfig);
hcaptcha.render(elements.accountLoginCaptcha, hconfig); hcaptcha.render(elements.accountLoginCaptcha, hconfig);
hcaptcha.render(elements.accountResetPasswordCaptcha, hconfig); hcaptcha.render(elements.accountResetPasswordCaptcha, hconfig);
} }
if(auth!.info?.turnstile.required) {
var turnstileconfig = {sitekey: auth!.info!.turnstile.siteKey!};
// hCaptcha does this automatically, but Turnstile doesn't, oh well.
var turnstileRegisterWidgetId = turnstile.render(elements.accountRegisterTurnstile, turnstileconfig);
var turnstileLoginWidgetId = turnstile.render(elements.accountLoginTurnstile, turnstileconfig);
var turnstileResetPasswordWidgetId = turnstile.render(elements.accountResetPasswordTurnstile, turnstileconfig);
elements.accountRegisterTurnstile.children[0].setAttribute("data-turnstile-widget-id", turnstileRegisterWidgetId!);
elements.accountLoginTurnstile.children[0].setAttribute("data-turnstile-widget-id", turnstileLoginWidgetId!);
elements.accountResetPasswordTurnstile.children[0].setAttribute("data-turnstile-widget-id", turnstileResetPasswordWidgetId!);
}
if(auth!.info?.recaptcha.required) {
var recaptchaconfig = {sitekey: auth!.info!.recaptcha.siteKey!};
// Same deal as with Turnstile
var RecaptchaRegisterWidgetId = grecaptcha.render(elements.accountRegisterRecaptcha, recaptchaconfig);
var RecaptchaLoginWidgetId = grecaptcha.render(elements.accountLoginRecaptcha, recaptchaconfig);
var RecaptchaResetPasswordWidgetId = grecaptcha.render(elements.accountResetPasswordRecaptcha, recaptchaconfig);
elements.accountRegisterRecaptcha.children[0].setAttribute("data-recaptcha-widget-id", RecaptchaRegisterWidgetId!.toString());
elements.accountLoginRecaptcha.children[0].setAttribute("data-recaptcha-widget-id", RecaptchaLoginWidgetId!.toString());
elements.accountResetPasswordRecaptcha.children[0].setAttribute("data-recaptcha-widget-id", RecaptchaResetPasswordWidgetId!.toString());
}
var token = localStorage.getItem("collabvm_session_" + new URL(auth!.apiEndpoint).host); var token = localStorage.getItem("collabvm_session_" + new URL(auth!.apiEndpoint).host);
if (token) { if (token) {
var result = await auth!.loadSession(token); var result = await auth!.loadSession(token);
@ -1071,41 +1032,10 @@ elements.accountLoginForm.addEventListener('submit', async (e) => {
} }
hcaptchaToken = response; hcaptchaToken = response;
} }
var turnstileToken = undefined;
var turnstileID = undefined;
if (auth!.info!.turnstile.required) {
turnstileID = elements.accountLoginTurnstile.children[0].getAttribute("data-turnstile-widget-id")!
var response: string = turnstile.getResponse(turnstileID) || "";
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
turnstileToken = response;
}
var recaptchaToken = undefined;
var recaptchaID = undefined;
if (auth!.info!.recaptcha.required) {
recaptchaID = parseInt(elements.accountLoginRecaptcha.children[0].getAttribute("data-recaptcha-widget-id")!)
var response = grecaptcha.getResponse(recaptchaID);
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
recaptchaToken = response;
}
var username = elements.accountLoginUsername.value; var username = elements.accountLoginUsername.value;
var password = elements.accountLoginPassword.value; var password = elements.accountLoginPassword.value;
var result = await auth!.login(username, password, hcaptchaToken, turnstileToken, recaptchaToken); var result = await auth!.login(username, password, hcaptchaToken);
if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID); if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID);
if (auth!.info!.turnstile.required) turnstile.reset(turnstileID);
if (auth!.info!.recaptcha.required) grecaptcha.reset(recaptchaID);
if (result.success) { if (result.success) {
elements.accountLoginUsername.value = ""; elements.accountLoginUsername.value = "";
elements.accountLoginPassword.value = ""; elements.accountLoginPassword.value = "";
@ -1139,35 +1069,6 @@ elements.accountRegisterForm.addEventListener('submit', async (e) => {
} }
hcaptchaToken = response; hcaptchaToken = response;
} }
var turnstileToken = undefined;
var turnstileID = undefined;
if (auth!.info!.turnstile.required) {
turnstileID = elements.accountRegisterTurnstile.children[0].getAttribute("data-turnstile-widget-id")!
var response: string = turnstile.getResponse(turnstileID) || "";
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
turnstileToken = response;
}
var recaptchaToken = undefined;
var recaptchaID = undefined;
if (auth!.info!.recaptcha.required) {
recaptchaID = parseInt(elements.accountRegisterRecaptcha.children[0].getAttribute("data-recaptcha-widget-id")!)
var response = grecaptcha.getResponse(recaptchaID);
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
recaptchaToken = response;
}
var username = elements.accountRegisterUsername.value; var username = elements.accountRegisterUsername.value;
var password = elements.accountRegisterPassword.value; var password = elements.accountRegisterPassword.value;
var email = elements.accountRegisterEmail.value; var email = elements.accountRegisterEmail.value;
@ -1177,10 +1078,8 @@ elements.accountRegisterForm.addEventListener('submit', async (e) => {
elements.accountModalError.style.display = "block"; elements.accountModalError.style.display = "block";
return false; return false;
} }
var result = await auth!.register(username, password, email, dob, hcaptchaToken, turnstileToken, recaptchaToken); var result = await auth!.register(username, password, email, dob, hcaptchaToken);
if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID); if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID);
if (auth!.info!.turnstile.required) turnstile.reset(turnstileID);
if (auth!.info!.recaptcha.required) grecaptcha.reset(recaptchaID);
if (result.success) { if (result.success) {
elements.accountRegisterUsername.value = ""; elements.accountRegisterUsername.value = "";
elements.accountRegisterEmail.value = ""; elements.accountRegisterEmail.value = "";
@ -1283,41 +1182,10 @@ elements.accountResetPasswordForm.addEventListener('submit', async e => {
} }
hcaptchaToken = response; hcaptchaToken = response;
} }
var turnstileToken = undefined;
var turnstileID = undefined;
if (auth!.info!.turnstile.required) {
turnstileID = elements.accountResetPasswordTurnstile.children[0].getAttribute("data-turnstile-widget-id")!
var response: string = turnstile.getResponse(turnstileID) || "";
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
turnstileToken = response;
}
var recaptchaToken = undefined;
var recaptchaID = undefined;
if (auth!.info!.recaptcha.required) {
recaptchaID = parseInt(elements.accountResetPasswordRecaptcha.children[0].getAttribute("data-recaptcha-widget-id")!)
var response = grecaptcha.getResponse(recaptchaID);
if (response === "") {
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
elements.accountModalError.style.display = "block";
return false;
}
recaptchaToken = response;
}
var username = elements.accountResetPasswordUsername.value; var username = elements.accountResetPasswordUsername.value;
var email = elements.accountResetPasswordEmail.value; var email = elements.accountResetPasswordEmail.value;
var result = await auth!.sendPasswordResetEmail(username, email, hcaptchaToken, turnstileToken, recaptchaToken); var result = await auth!.sendPasswordResetEmail(username, email, hcaptchaToken);
if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID); if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID);
if (auth!.info!.turnstile.required) turnstile.reset(turnstileID);
if (auth!.info!.recaptcha.required) grecaptcha.reset(recaptchaID);
if (result.success) { if (result.success) {
resetPasswordUsername = username; resetPasswordUsername = username;
resetPasswordEmail = email; resetPasswordEmail = email;

101
static/lang/cv-m.json Normal file
View File

@ -0,0 +1,101 @@
{
"languageName": "nonsense",
"translatedLanguageName": "nonsense",
"flag": "NS",
"author": "cvmuser1000, skyhighsundae",
"stringKeys": {
"kGeneric_CollabVM": "s09d8sa0d7asdasd708d7as0dßasd",
"kGeneric_Yes": "wq09d8sßad8as90deá98d8asda97287ßsa8d3a7d",
"kGeneric_No": "asdaqsd0asd78asd0asdasd",
"kGeneric_Ok": "DSADASDDUASDASD=",
"kGeneric_Cancel": "NDADASUASIDKAJDSKIUASDLWSUIADJHPAWUSAD",
"kGeneric_Send": "odsjadoiasudoasoidasidsaidasudasd",
"kGeneric_Understood": "odssiad09s8dsad",
"kGeneric_Username": "sad9asu8d30a2880e8weu2q",
"kGeneric_Password": "sa9dasd9802788ej3lklkkjgofopdüsaödlkiwigtrghzg",
"kGeneric_Login": "0129837465ruzfhjcnghjdskhgjfdksgjkds",
"kGeneric_Register": "aölkgjhtujekdksklejjjasddadasdasi",
"kGeneric_EMail": "EDisuad8as7d0asd",
"kGeneric_DateOfBirth": "D9098s9a80ßdsda",
"kGeneric_VerificationCode": "ad98as8d0asdß Code",
"kGeneric_Verify": "das0d9as8d8a0939asd",
"kGeneric_Update": "asd8as8d0sad7as90d",
"kGeneric_Logout": "9erwfeüookawerjioksdiuferjkzofer",
"kWelcomeModal_Header": "Welcome to s09d8sa0d7asdasd708d7as0dßasd (Tweaked)",
"kWelcomeModal_Body": "sdiuasd0ßYOudasd0a9sdas8d90OYußsadsad9asd8As8)(dsad0adas0d098WS8sda87saWds998sa9asd989",
"kSiteButtons_Home": "x0e08d9f9f93ks9s8ad989a9sd829038eqwde",
"kSiteButtons_FAQ": "=?D)S=A(D=)S(A=Eßasd7a08sd0828ßa0dsadjaspd8ß2",
"kSiteButtons_Rules": "9dasd07asd8728a0sd98a89sd=d8as0dadaOIDHASdjADHASOD",
"kSiteButtons_DarkMode": "IDÖOASDOA(DUASDAISDPIS",
"kSiteButtons_LightMode": "DISADISAD)AS)DAS)D)ADASUASIDJASKDASD",
"kSiteButtons_Languages": "s09d8sa0d7asdasd708d7as0dßasd ASD)(AS)D(ASDKASDKASD",
"kVM_UsersOnlineText": "DUSADIASDJUASODASD: ",
"kVM_TurnTimeTimer": "SADI SAODIAO IDOASDO {0} SAD()ASD(AS=DASDD",
"kVM_WaitingTurnTimer": "SDIKSODA SAIDSOAID IOSAD OOASD OI {0} SAODOASDÜ",
"kVM_VoteCooldownTimer": "FKJDJF PDSFPO {0} ASJDLS ASDKJAS ()(FK FDLKD KFO",
"kVM_VoteForResetTitle": "ASDIKOASDIPS )DSJDASIPD VM FDASISDIAPD",
"kVM_VoteForResetTimer": "VSLDKALSDK DLKSALD SAESAPD {0} SDIASDJ",
"kVMButtons_TakeTurn": "SADKOOASDOPDPOAS",
"kVMButtons_EndTurn": "IASUASD)SD",
"kVMButtons_ChangeUsername": "ADAS)(§)SKSODASKDSA",
"kVMButtons_Keyboard": "FDSPFSDOFISDIOPFUPÜS",
"KVMButtons_CtrlAltDel": "ASDOASD+SDOSD+DSI",
"kVMButtons_VoteForReset": "DKSI DAS( RESET",
"kVMButtons_Screenshot": "ASD=ASDASPDIASPDOIASDPIASD",
"kQEMUMonitor": "ADSIOADOASDIPAOSDIASPÜDSAD",
"kAdminVMButtons_PassVote": "SADI)AS(DASDJASD KASJDIASDI ISAI",
"kAdminVMButtons_CancelVote": "SDIASOD IOASDOI",
"kAdminVMButtons_Restore": "SADKASDIAPSODAPSÄDIASUIDPIUASDPASUIDPIASD",
"kAdminVMButtons_Reboot": "ASPODASUIOPDIASDUASPUIDASOIDASIDSAIDOASIDPA",
"kAdminVMButtons_ClearTurnQueue": "SADOPASOKIDSAOÜDIOASDÜSAD",
"kAdminVMButtons_BypassTurn": "ASDKOASDPOASD OASIDASOPD",
"kAdminVMButtons_IndefiniteTurn": "SADKASOÜDOÜ OSADOISÜ",
"kAdminVMButtons_GhostTurnOn": "SADASKDOASDOIASDIO DASIDUSAUID (SAODPAISD)",
"kAdminVMButtons_GhostTurnOff": "SADASKDOASDOIASDIO DASIDUSAUID (SAKDSOAD)",
"kAdminVMButtons_Ban": "ISADASPÜD",
"kAdminVMButtons_Kick": "SDIOASDISAD",
"kAdminVMButtons_TempMute": "SDLKASDPIOAIPD ASIDAS",
"kAdminVMButtons_IndefMute": "SADOASDIPÜASD ASIDAS",
"kAdminVMButtons_Unmute": "ASDIASIDAS",
"kAdminVMButtons_GetIP": "SSDPASDASDÜ)A=D",
"kVMPrompts_AdminChangeUsernamePrompt": "DISUADOASUDZ OASDd iSA DSd {0}:",
"kVMPrompts_AdminRestoreVMPrompt": "ASOIAIPDSDUA sad9 asd9asd uiasd aosd",
"kVMPrompts_EnterNewUsernamePrompt": "ODUSPADIUASP asp9dasp9 asd9 uasid usadi",
"kError_UnexpectedDisconnection": "d9sa98d0asd08SDASDASD",
"kError_UsernameTaken": "saoidipasdpas uasdiuasuodiiouIDUIASDP1",
"kError_UsernameInvalid": "ASDPOASUDA )(ASDIUASD PIUSAIDU SAIDU IDISAUDUISAUISAiuaisa",
"kError_UsernameBlacklisted": "DOSADI OASD ISS=AD =SDJ PSAIDP SIDPJ",
"kError_IncorrectPassword": "DUIASDASD =SAD)A=",
"kAccountModal_Verify": "SADKOSADO OSAIDOISAI",
"kAccountModal_AccountSettings": "SAOSDIIPASD ISADSUAPD",
"kAccountModal_ResetPassword": "SDAOIDOASID IOODOASIO",
"kAccountModal_NewPassword": "XSAPDIASPD IASDOASI)SPAD",
"kAccountModal_ConfirmNewPassword": "DPASDKO ASPDPSD DPAS",
"kAccountModal_CurrentPassword": "DLSADOASD DAOIIO",
"kAccountModal_ConfirmPassword": "ASD=AI OWEJSDJLSD LIASIDI",
"kAccountModal_HideFlag": "DASOSAO OFOGP ÜAÜASDAPDSDASDI IDPSPOPD",
"kMissingCaptcha": "ASDOASIDPOASD SDOSAD OSDIO CAPTCHA",
"kPasswordsMustMatch": "ASODIO SAIOD OISADIO IO SADIOIO",
"kAccountModal_VerifyText": "DL SODSI ID SAIOD SOID JSADSIOADJASJID ASDOSA JDSAOJ",
"kAccountModal_VerifyPasswordResetText": "SADIO ASDIP ASD(I PSAIOD P)ASD( AS)D )ASD) A)SD )SAUJD IASD J JSA J ASODOSADOSADISAIPD",
"kAccountModal_PasswordResetSuccess": "SADSA)D )SAD(AS)D )SAD)()(A )(SAD())(S ()AS()D)(ASDASD",
"kNotLoggedIn": "DASKD KSAKJSA DSJ"
}
}

View File

@ -23,7 +23,7 @@
"kGeneric_Update": "Update", "kGeneric_Update": "Update",
"kGeneric_Logout": "Log out", "kGeneric_Logout": "Log out",
"kWelcomeModal_Header": "Welcome to CollabVM", "kWelcomeModal_Header": "Welcome to CollabVM (Tweaked)",
"kWelcomeModal_Body": "<p>Before continuing, please familiarize yourself with our rules:</p> <h3>R1. Don't break the law.</h3> Do not use CollabVM or CollabVM's network to violate United States federal law, New York state law, or international law. If CollabVM becomes aware a crime has been committed through its service, you will be immediately banned, and your activities may be reported to the authorities if necessary.<br><br>CollabVM is required by law to notify law enforcement agencies if it becomes aware of the presence of child pornography on, or being transmitted through its network.<br><br>COPPA is also enforced, please do not use CollabVM if you are under the age of 13 years old. <h3>R2. No running DoS/DDoS tools.</h3> Do not use CollabVM to DoS/DDoS an indivdiual, business, company, or anyone else. <h3>R3. No spam distribution.</h3> Do not spam any emails using this service or push spam in general. <h3>R4. Do not abuse any exploits.</h3> Do not abuse any exploits, additionally if you see someone abusing exploits or you need to report one, please contact me at: computernewbab@gmail.com <h3>R5. Don't impersonate other users.</h3> Do not impersonate other members of CollabVM. If caught, you'll be temporarily disconnected, and banned if necessary. <h3>R6. One vote per person.</h3> Do not use any methods or tools to bypass the vote restriction. Only one vote per person is allowed, no matter what. Anybody who is caught doing this will be banned. <h3>R7. No Remote Administration Tools.</h3> Do not use any remote administration tools (ex: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, etc.) <h3>R8. No bypassing CollabNet.</h3> Do not attempt to bypass the blocking provided by CollabNet, especially if it is being used to break Rule 1, Rule 2, or Rule 7 (or run stupid over-used things). <h3>R9. No performing destructive actions constantly.</h3> Any user may not destroy the VM (rendering it unusable constantly), install/reinstall the operating system (except on VM7 or VM8), or run bots that do such. This includes bots that spam massive amounts of keyboard/mouse input (\"kitting\"). <h3>R10. No Cryptomining</h3> Attempting to mine cryptocurrency on the VMs will result in a kick, and then a permanent ban if you keep attempting. Besides, it's not like you're gonna make any money off it. <h3>NSFW Warning</h3> Please note that NSFW content is allowed on our anarchy VM (VM0b0t), and is viewed regularly. In addition, while we give a good effort to keep NSFW off the main VMs, people will occasionally slip it through.", "kWelcomeModal_Body": "<p>Before continuing, please familiarize yourself with our rules:</p> <h3>R1. Don't break the law.</h3> Do not use CollabVM or CollabVM's network to violate United States federal law, New York state law, or international law. If CollabVM becomes aware a crime has been committed through its service, you will be immediately banned, and your activities may be reported to the authorities if necessary.<br><br>CollabVM is required by law to notify law enforcement agencies if it becomes aware of the presence of child pornography on, or being transmitted through its network.<br><br>COPPA is also enforced, please do not use CollabVM if you are under the age of 13 years old. <h3>R2. No running DoS/DDoS tools.</h3> Do not use CollabVM to DoS/DDoS an indivdiual, business, company, or anyone else. <h3>R3. No spam distribution.</h3> Do not spam any emails using this service or push spam in general. <h3>R4. Do not abuse any exploits.</h3> Do not abuse any exploits, additionally if you see someone abusing exploits or you need to report one, please contact me at: computernewbab@gmail.com <h3>R5. Don't impersonate other users.</h3> Do not impersonate other members of CollabVM. If caught, you'll be temporarily disconnected, and banned if necessary. <h3>R6. One vote per person.</h3> Do not use any methods or tools to bypass the vote restriction. Only one vote per person is allowed, no matter what. Anybody who is caught doing this will be banned. <h3>R7. No Remote Administration Tools.</h3> Do not use any remote administration tools (ex: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, etc.) <h3>R8. No bypassing CollabNet.</h3> Do not attempt to bypass the blocking provided by CollabNet, especially if it is being used to break Rule 1, Rule 2, or Rule 7 (or run stupid over-used things). <h3>R9. No performing destructive actions constantly.</h3> Any user may not destroy the VM (rendering it unusable constantly), install/reinstall the operating system (except on VM7 or VM8), or run bots that do such. This includes bots that spam massive amounts of keyboard/mouse input (\"kitting\"). <h3>R10. No Cryptomining</h3> Attempting to mine cryptocurrency on the VMs will result in a kick, and then a permanent ban if you keep attempting. Besides, it's not like you're gonna make any money off it. <h3>NSFW Warning</h3> Please note that NSFW content is allowed on our anarchy VM (VM0b0t), and is viewed regularly. In addition, while we give a good effort to keep NSFW off the main VMs, people will occasionally slip it through.",
"kSiteButtons_Home": "Home", "kSiteButtons_Home": "Home",
@ -31,7 +31,7 @@
"kSiteButtons_Rules": "Rules", "kSiteButtons_Rules": "Rules",
"kSiteButtons_DarkMode": "Dark Mode", "kSiteButtons_DarkMode": "Dark Mode",
"kSiteButtons_LightMode": "Light Mode", "kSiteButtons_LightMode": "Light Mode",
"kSiteButtons_Languages": "Languages", "kSiteButtons_Languages": "CollabVM Languages",
"kVM_UsersOnlineText": "Users Online:", "kVM_UsersOnlineText": "Users Online:",
@ -44,8 +44,8 @@
"kVMButtons_TakeTurn": "Take Turn", "kVMButtons_TakeTurn": "Take Turn",
"kVMButtons_EndTurn": "End Turn", "kVMButtons_EndTurn": "End Turn",
"kVMButtons_ChangeUsername": "Change Username", "kVMButtons_ChangeUsername": "Username Settings",
"kVMButtons_Keyboard": "Keyboard", "kVMButtons_Keyboard": "Show Keyboard",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del", "KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Vote For Reset", "kVMButtons_VoteForReset": "Vote For Reset",
@ -66,7 +66,7 @@
"kAdminVMButtons_Ban": "Ban", "kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Kick", "kAdminVMButtons_Kick": "Kick",
"kAdminVMButtons_TempMute": "Temporary Mute", "kAdminVMButtons_TempMute": "Temporary Mute",
"kAdminVMButtons_IndefMute": "Indefinite Mute", "kAdminVMButtons_IndefMute": "Permanent Mute",
"kAdminVMButtons_Unmute": "Unmute", "kAdminVMButtons_Unmute": "Unmute",
"kAdminVMButtons_GetIP": "Get IP", "kAdminVMButtons_GetIP": "Get IP",
@ -88,7 +88,7 @@
"kAccountModal_ConfirmNewPassword": "Confirm New Password", "kAccountModal_ConfirmNewPassword": "Confirm New Password",
"kAccountModal_CurrentPassword": "Current Password", "kAccountModal_CurrentPassword": "Current Password",
"kAccountModal_ConfirmPassword": "Confirm Password", "kAccountModal_ConfirmPassword": "Confirm Password",
"kAccountModal_HideFlag": "Hide my Country Flag", "kAccountModal_HideFlag": "Hide my Country Flag from the Users Online section",
"kMissingCaptcha": "Please fill out the captcha.", "kMissingCaptcha": "Please fill out the captcha.",
"kPasswordsMustMatch": "Passwords must match.", "kPasswordsMustMatch": "Passwords must match.",

View File

@ -4,6 +4,10 @@
"languageName": "English (US)", "languageName": "English (US)",
"flag": "🇺🇸" "flag": "🇺🇸"
}, },
"cv-m": {
"languageName": "nonsense",
"flag": "NS"
},
"fr-fr": { "fr-fr": {
"languageName": "Français", "languageName": "Français",
"flag": "🇫🇷" "flag": "🇫🇷"

View File

@ -32,8 +32,7 @@
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
"typeRoots": [ "typeRoots": [
"node_modules/@hcaptcha", "node_modules/@hcaptcha"
"node_modules/@types"
], /* Specify multiple folders that act like './node_modules/@types'. */ ], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */

View File

@ -1446,11 +1446,6 @@
dependencies: dependencies:
"@popperjs/core" "^2.9.2" "@popperjs/core" "^2.9.2"
"@types/cloudflare-turnstile@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@types/cloudflare-turnstile/-/cloudflare-turnstile-0.2.2.tgz#3364d65b00f03376f4e555820db270173807a52c"
integrity sha512-3Yf7b1Glci+V2bFWwWBbZkRgTuegp7RDgNTOG4U0UNPB9RV4AWvwqg2/qqLff8G+SwKFNXoXvTkqaRBZrAFdKA==
"@types/dompurify@^3.0.5": "@types/dompurify@^3.0.5":
version "3.0.5" version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7" resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
@ -1465,11 +1460,6 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/grecaptcha@^3.0.9":
version "3.0.9"
resolved "https://registry.yarnpkg.com/@types/grecaptcha/-/grecaptcha-3.0.9.tgz#9f3b07ec06c8fff221aa6fc124fe5b8a0e2c3349"
integrity sha512-fFxMtjAvXXMYTzDFK5NpcVB7WHnrHVLl00QzEGpuFxSAC789io6M+vjcn+g5FTEamIJtJr/IHkCDsqvJxeWDyw==
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"