Merge branch 'dev' into feature/FixTv2

This commit is contained in:
2026-05-05 02:15:34 +02:00
4 changed files with 114 additions and 4 deletions

View File

@@ -2704,9 +2704,11 @@ if (protocolHandledPorts.size > 0) console.log(` * Started ${protocolHandledPort
const listening_ip_string = (minisrv_config.config.bind_ip !== "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces";
console.log(" * Listening on", listening_ip_string, "~", "Service IP:", service_ip);
// Security warning for default user data encryption key
if (minisrv_config.config.keys.user_data_key === "PNa$WN7gz}!T=t6X7^=|Ii##CEB~p\\EP") {
console.log(" * WARNING: You are using the default user data encryption key. This is not secure, and you should change it in the configuration file before registering any users.");
console.log(" * To generate a random key in bash or PowerShell, you can run: node ./tools/configurator.js config.keys.user_data_key $(openssl rand -base64 32)");
console.log(" * After changing the key in the user_config, you can run tools/update_user_data_key.js to update existing accounts with the new key.");
console.log(" * After changing the key in the user_config, please restart the server.");
console.log(" * If you had existing users prior to changing the key, you can run tools/update_user_data_key.js to update existing accounts with the new key.");
console.log(" * Making a backup of your user accounts before doing this is highly recommended, in case something goes wrong during the update process.");
}

View File

@@ -38,6 +38,7 @@ async function processLC2DownloadPage(flashrom_info, headers, numparts = null) {
if (!flashrom_info.part_count) flashrom_info.part_count = parseInt(flashrom_info.message.slice(flashrom_info.message.length - 4).replace(/\D/g, ''));
if (parseInt(flashrom_info.part_number) >= 0 && flashrom_info.rompath && flashrom_info.next_rompath) {
if (!flashrom_info.message && flashrom_info.is_bootrom) {
flashrom_info.part_count = 16;
flashrom_info.message = "BootRom Part " + (flashrom_info.part_number + 1) + " of " + flashrom_info.part_count;
}

View File

@@ -56,18 +56,32 @@ class WTVClientSessionData {
this.loginWhitelist.push("wtv-head-waiter:/confirm-transfer");
}
/**
* Assigns a new WTVMail instance to the session's mailstore property, using the current minisrv_config and session data.
*/
assignMailStore() {
this.mailstore = new WTVMail(this.minisrv_config, this)
}
/**
* Assigns a new WTVFavorites instance to the session's favstore property, using the current minisrv_config and session data.
*/
assignFavoriteStore() {
this.mailstore = this.favstore = new WTVFavorites(this.minisrv_config, this)
this.favstore = new WTVFavorites(this.minisrv_config, this)
}
/**
* Creates a new WTVSec session. Used for RC4 SECURE ON requests.
* @returns {WTVSec} A new WTVSec session instance
*/
createWTVSecSession() {
return new WTVSec(this.minisrv_config)
}
/**
* Retrieves the total number of unread messages for the primary account.
* @returns {number} Number of unread messages
*/
getAccountTotalUnreadMessages() {
if (!this.isRegistered()) return false; // unregistered
if (this.user_id > 0) return false; // not primary user or pre-login
@@ -88,6 +102,9 @@ class WTVClientSessionData {
return total_unread_messages;
}
/**
* Clears all user session data from memory, including session store and data store, and resets mail and favorite stores.
*/
clearUserSessionMemory() {
this.setUserLoggedIn(false);
this.data_store = [];
@@ -96,6 +113,13 @@ class WTVClientSessionData {
this.assignMailStore()
}
/**
* Switches the current user ID and optionally updates related data stores.
* @param {number} user_id The user ID to switch to
* @param {boolean} update_mail Whether to update the mail store
* @param {boolean} update_ticket Whether to update the ticket data
* @param {boolean} update_favorite Whether to update the favorite store
*/
switchUserID(user_id, update_mail = true, update_ticket = true, update_favorite = true) {
this.user_id = parseInt(user_id);
if (user_id !== null) {
@@ -143,6 +167,11 @@ class WTVClientSessionData {
return true;
}
/**
* Checks if a given mail address is in the address book.
* @param {string} addr The mail address to check against the address book
* @returns {boolean} True if the address is in the address book, false otherwise
*/
isAddressInAddressBook(addr) {
const addresses = this.getSessionData("address_book");
if (addresses) {
@@ -156,6 +185,11 @@ class WTVClientSessionData {
return false;
}
/**
* Finds the first available user slot for a new user.
* Can only be used by the primary account (user_id 0).
* @returns {number|boolean} The first available user slot index, or false if no slots are available
*/
findFreeUserSlot() {
if (this.user_id !== 0) return false; // subscriber only command
const master_directory = this.getUserStoreDirectory(true);
@@ -170,16 +204,30 @@ class WTVClientSessionData {
return false;
}
/**
* Returns the display name of the current user.
* @returns {string} The subscriber's display name if user_id is 0, otherwise the current user's display name.
*/
getDisplayName() {
return (this.user_id === 0) ? this.getSessionData("subscriber_name") : this.getSessionData("display_name");
}
/**
* Gets the number of users for this SSID.
* Can only be used by the primary account (user_id 0).
* @returns {number} The number of users this SSID has
*/
getNumberOfUserAccounts() {
if (!this.isRegistered()) return false;
if (this.user_id !== 0) return false; // subscriber only command
return Object.keys(this.listPrimaryAccountUsers()).length;
}
/**
* Lists all primary account users for this SSID.
*
* @returns {Array} An array containing the account data of all users for this SSID
*/
listPrimaryAccountUsers() {
if (this.user_id !== 0) return false; // subscriber only command
@@ -206,6 +254,10 @@ class WTVClientSessionData {
return account_data;
}
/**
* Recursively creates directories for the given path.
* @param {string} thedir The directory path to create
*/
mkdirRecursive(thedir) {
thedir.split(this.path.sep).reduce(
(directories, directory) => {
@@ -307,6 +359,13 @@ class WTVClientSessionData {
return false;
}
/**
* A part of the account transfer process, creates a pending transfer file in both the source and target account
* store directories with the relevant SSID and transfer type (source or target) for each account.
* This allows the transfer process to be completed or cancelled later, and ensures that only accounts
* with a pending transfer can complete the transfer process.
* @param {string} ssid
*/
setPendingTransfer(ssid) {
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
let ssidobj = { "ssid": ssid, "type": "source" };
@@ -319,6 +378,10 @@ class WTVClientSessionData {
this.fs.writeFileSync(dest_pending_file, JSON.stringify(ssidobj));
}
/**
* Cancels a pending account transfer, if it exists.
* @returns {string|null} The SSID of the cancelled transfer if a pending transfer was found and cancelled, or null if no pending transfer was found
*/
cancelPendingTransfer() {
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
if (this.fs.existsSync(pending_file)) {
@@ -334,6 +397,10 @@ class WTVClientSessionData {
return null;
}
/**
* Finalize the transfer, completely moving all user data from the source account to the target account, and removing the pending transfer files.
* @returns {boolean} Success of the transfer
*/
finalizePendingTransfer() {
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
const file = this.fs.readFileSync(pending_file)
@@ -353,13 +420,18 @@ class WTVClientSessionData {
return true;
}
/**
* Check if there is a pending transfer for this account, and optionally check if the pending transfer type matches the specified dtype.
* @param {string} dtype {source|target} If specified, only returns the SSID if the pending transfer type matches the specified dtype. If null, returns the pending transfer object with ssid and type.
* @returns {string|object|boolean} The SSID of the pending transfer if dtype matches, the pending transfer object if dtype is null, or false if no pending transfer is found
*/
hasPendingTransfer(dtype = null) {
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
if (this.fs.existsSync(pending_file)) {
const ssidobj = JSON.parse(this.fs.readFileSync(pending_file));
console.log(ssidobj)
if (dtype) {
(ssidobj.type === dtype) ? ssidobj.ssid : false;
return (ssidobj.type === dtype) ? ssidobj.ssid : false;
}
else {
return ssidobj;
@@ -402,6 +474,10 @@ class WTVClientSessionData {
return result !== false;
}
/**
* Checks if the user has a scrapbook directory.
* @returns {boolean} True if the scrapbook directory exists, false otherwise.
*/
scrapbookExists() {
if (this.scrapbook_dir === null) {
const userstore_dir = this.getUserStoreDirectory();
@@ -411,6 +487,10 @@ class WTVClientSessionData {
return this.fs.existsSync(this.scrapbook_dir);
}
/**
* Creates a scrapbook directory for the user if it does not already exist.
* @returns {boolean} Success of the creation
*/
createScrapbook() {
if (!this.scrapbookExists()) {
try {
@@ -421,6 +501,10 @@ class WTVClientSessionData {
return false
}
/**
* A wrapper that returns the scrapbook directory, and creates it if it doesn't exist.
* @returns {string} The path to the scrapbook directory
*/
scrapbookDir() {
if (!this.scrapbookExists()) {
this.createScrapbook();
@@ -428,6 +512,10 @@ class WTVClientSessionData {
return this.scrapbook_dir;
}
/**
* List the files in the user's scrapbook directory, sorted in ascending order, and excluding any .meta files.
* @returns {Array} A filelist of the user's scrapbook files
*/
listScrapbook() {
if (!this.scrapbookExists()) {
this.createScrapbook();
@@ -442,6 +530,10 @@ class WTVClientSessionData {
return filteredFiles;
}
/**
* Finds the next available ID slot for a new scrapbook entry.
* @returns {number} An available ID slot
*/
getFreeScrapbookID() {
if (!this.scrapbookExists()) {
this.createScrapbook();
@@ -458,6 +550,10 @@ class WTVClientSessionData {
return id;
}
/**
* Calculates the total size of the user's scrapbook directory.
* @returns {number} The total size in bytes
*/
getScrapbookUsage() {
if (!this.scrapbookExists()) {
this.createScrapbook();
@@ -475,6 +571,10 @@ class WTVClientSessionData {
return total_size;
}
/**
* Calculates the percentage of the scrapbook storage space that is in use.
* @returns {number} Percentage of the scrapbook storage space that is in use, out of the total allotted.
*/
getScrapbookUsagePercent() {
if (!this.scrapbookExists()) {
this.createScrapbook();
@@ -486,6 +586,11 @@ class WTVClientSessionData {
return Math.round(usage_percent, 2);
}
/**
* Get a scrapbook image by its ID.
* @param {number} id
* @returns {Buffer|null} The image data as a Buffer, or null if the image does not exist
*/
getScrapbookImage(id) {
if (!this.scrapbookExists()) {
this.createScrapbook();

View File

@@ -1130,6 +1130,7 @@ class WTVShared {
// DON'T USE THIS
// Saved for reference until I come up with a better way
// If used, this will exceed the stack limit over time
/*
unloadModule(moduleName) {
// Prevent usage
return;
@@ -1141,7 +1142,8 @@ class WTVShared {
delete require.cache[resolvedPath];
}
}
*/
/**
* Returns an absolute path without an trailing path seperator
* @param {string} path