how to decrypt data encrypted with the weakEncrypt function from javascript nodejs?

bool weakEncrypt(std::string& data, size_t tagSize)
{
    if (data.empty()) 
        return false;

    unsigned char randomByte;
    RAND_bytes(&randomByte, 1);

    // Generate the salt from the password + the data length + a random byte
    unsigned char pwhash[SHA256_DIGEST_LENGTH];
    std::vector<unsigned char> saltInput(reinterpret_cast<const unsigned char*>(password.data()), 
                                         reinterpret_cast<const unsigned char*>(password.data()) + password.size());
    saltInput.push_back(static_cast<unsigned char>(data.length()));
    saltInput.push_back(randomByte);
    SHA256(saltInput.data(), saltInput.size(), pwhash);
    std::vector<unsigned char> salt(pwhash, pwhash + 12); // ChaCha20-Poly1305 uses 12-byte nonce

    // Derive key from password
    unsigned char key[32];
    SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), key);
    
    // Encrypt with ChaCha20-Poly1305
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) 
        return false;
    if (EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), nullptr, key, salt.data()) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    
    // Use data string directly for in-place encryption
    size_t originalSize  = data.size();
    std::string dataCopy = data;
    RtlSecureZeroMemory(&data[0], data.size());
    data.resize(originalSize + tagSize);
    int ciphertextLen;
    if (EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char*>(&data[0]), &ciphertextLen, 
                               reinterpret_cast<const unsigned char*>(dataCopy.data()), originalSize) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    RtlSecureZeroMemory(&dataCopy[0], dataCopy.size());

    int finalLen;
    if (EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char*>(&data[ciphertextLen]), &finalLen) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    ciphertextLen += finalLen;
    
    // Get the authentication tag
    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagSize, reinterpret_cast<unsigned char*>(&data[ciphertextLen])) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }

	qq << "\nkey\n"   << QByteArray(reinterpret_cast<char*>(key), sizeof(key)).toBase64();
	qq << "\nsalt\n"  << QByteArray(reinterpret_cast<char*>(salt.data()), salt.size()).toBase64();
	qq << "\ntag\n"   << QByteArray(reinterpret_cast<char*>(&data[ciphertextLen]), tagSize).toBase64();
    
    EVP_CIPHER_CTX_free(ctx);
    OPENSSL_cleanse(key, sizeof(key));
	data.append(1, static_cast<char>(randomByte));
    return true;
}



bool weakDecrypt(std::string& data, size_t tagSize)
{
    if (data.empty())
        return false;

    int realSize = data.length() - tagSize - 1;

    // Generate the salt
    unsigned char pwhash[SHA256_DIGEST_LENGTH];
    std::vector<unsigned char> saltInput(reinterpret_cast<const unsigned char*>(password.data()), 
                                         reinterpret_cast<const unsigned char*>(password.data()) + password.size());
    saltInput.push_back(static_cast<unsigned char>(realSize));
    saltInput.push_back(static_cast<unsigned char>(data[data.length() - 1]));
    SHA256(saltInput.data(), saltInput.size(), pwhash);
    std::vector<unsigned char> salt(pwhash, pwhash + 12); // ChaCha20-Poly1305 uses 12-byte nonce

    // Derive key from password
    unsigned char key[32];
    SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), key);
    
    // Decrypt with ChaCha20-Poly1305
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) return false;
    if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), nullptr, key, salt.data()) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    
    // Set expected tag
    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagSize, 
                            const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&data[realSize]))) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    
    // Decrypt
    int decryptedLen;
    if (EVP_DecryptUpdate(ctx, reinterpret_cast<unsigned char*>(&data[0]), &decryptedLen,
                               reinterpret_cast<const unsigned char*>(&data[0]), realSize) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    
    int finalLen;
    if (EVP_DecryptFinal_ex(ctx, reinterpret_cast<unsigned char*>(&data[decryptedLen]), &finalLen) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    decryptedLen += finalLen;
    
    EVP_CIPHER_CTX_free(ctx);
    OPENSSL_cleanse(key, sizeof(key));
    
    data.resize(decryptedLen);
    return true;
}