diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index ad173da..2f7b664 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -365,6 +365,15 @@ jobs: sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + - name: change download link to custom + if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + - name: allow custom.txt continue-on-error: true shell: bash diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index 71446d5..1963862 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -311,6 +311,15 @@ jobs: sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + - name: change download link to custom + if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + - name: fix connection delay continue-on-error: true if: ${{ fromJson(inputs.extras).delayFix == 'true' }} diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 6cb0b87..9293d1e 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -210,10 +210,22 @@ jobs: sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + - name: change download link to custom + if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + - name: allow custom.txt continue-on-error: true shell: bash run: | + sed -i -e 's|rs-ny.rustdesk.com|${{ env.RENDEZVOUS_SERVER }}|' ./libs/hbb_common/src/config.rs + sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.RS_PUB_KEY }}|' ./libs/hbb_common/src/config.rs + sed -i -e 's|For faster connection, please set up your own server||' ./src/lang/en.rs sed -i -e '/const KEY:/,/};/d' ./src/common.rs sed -i -e '/let Ok(data) = sign::verify(&data, &pk)/,/};/d' ./src/common.rs # ./flutter/pubspec.yaml @@ -241,7 +253,6 @@ jobs: # https://github.com/flutter/flutter/issues/155685 - name: Replace engine with rustdesk custom flutter engine - if: false run: | flutter doctor -v flutter precache --windows diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index 7429217..f25c062 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -28,10 +28,13 @@ class GenerateForm(forms.Form): apiServer = forms.CharField(label="API Server", required=False) key = forms.CharField(label="Key", required=False) urlLink = forms.CharField(label="Custom URL for links", required=False) + downloadLink = forms.CharField(label="Custom URL for downloading new versions", required=False) #Visual iconfile = forms.FileField(label="Custom App Icon (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) logofile = forms.FileField(label="Custom App Logo (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) + iconbase64 = forms.CharField(required=False) + logobase64 = forms.CharField(required=False) theme = forms.ChoiceField(choices=[ ('light', 'Light'), ('dark', 'Dark'), diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html index de48496..3495ee8 100644 --- a/rdgenerator/templates/generator.html +++ b/rdgenerator/templates/generator.html @@ -106,11 +106,37 @@ max-height: 100px; margin-top: 10px; } + .save-load-section-container { /* New container for fixed positioning */ + position: fixed; + top: 20px; /* Adjust as needed */ + left: 20px; /* Adjust as needed */ + background-color: #111; /* Match your section background */ + padding: 0px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + z-index: 100; /* Ensure it's above other content */ + } + .save-load-section { + display: none; /* Initially hidden */ + } + .error { + color: red; + }

RustDesk Custom Client Builder

-
+ +
+
+

Save/Load Configuration

+
+ + + +
+
+

Select Platform

@@ -134,7 +160,7 @@

General

- {{ form.exename }}

+ {{ form.exename }}

{{ form.appname }}

@@ -155,6 +181,8 @@ {{ form.apiServer }}

{{ form.urlLink }}

+ + {{ form.downloadLink }}

@@ -177,6 +205,8 @@

Visual

+ {{ form.iconbase64.as_hidden }} + {{ form.logobase64.as_hidden }} {{ form.iconfile }}

@@ -260,6 +290,142 @@ reader.readAsDataURL(input.files[0]); } } + const saveLoadTitle = document.getElementById("saveLoadTitle"); + const saveLoadSection = document.querySelector(".save-load-section"); + + saveLoadTitle.addEventListener("click", () => { + if (!saveLoadSection.style.display || saveLoadSection.style.display === "none") { + saveLoadSection.style.display = "block"; + } else { + saveLoadSection.style.display = "none"; + } + + const icon = saveLoadTitle.querySelector("i"); + icon.classList.toggle("fa-chevron-down"); + icon.classList.toggle("fa-chevron-up"); + }); + + async function saveFormData() { + const filename = document.getElementById("{{ form.exename.id_for_label }}").value; + if (!filename) { + document.getElementById("filenameError").textContent = "Filename is required."; + return; + } else { + document.getElementById("filenameError").textContent = ""; + } + + const formData = {}; + const form = document.getElementById("myForm"); // Get the form element + + // 1. Use FormData for robust data collection (handles most input types): + const formDataObj = new FormData(form); // Create a FormData object + + formDataObj.forEach((value, key) => { + formData[key] = value; // Add each key/value pair to the formData object. + }); + + // 2. Handle select elements separately (more reliable): + const selectElements = form.querySelectorAll('select'); + selectElements.forEach(select => { + formData[select.name] = select.value; + }); + + // 3. Handle checkboxes and radio buttons (important!): + const checkboxRadioElements = form.querySelectorAll('input[type="checkbox"], input[type="radio"]'); + checkboxRadioElements.forEach(input => { + if (input.name) { //only add to form data if it has a name + if (input.checked) { + formData[input.name] = input.value; + } else if (!formData.hasOwnProperty(input.name) && input.type === 'checkbox') { //if it's a checkbox and it's not checked, add it as false + formData[input.name] = false; + } + } + }); + + // 4. Handle icon and logo + const iconPreview = document.getElementById('icon-preview'); + if (iconPreview.firstChild && iconPreview.firstChild.src.startsWith('data:image/png;base64')) { + formData.iconfile = iconPreview.firstChild.src; // Use existing base64 + } else { //if it's a file upload + const iconFile = document.getElementById("{{ form.iconfile.id_for_label }}").files[0]; + if (iconFile) { + formData.iconfile = await readFileAsBase64(iconFile); + } + } + + const logoPreview = document.getElementById('logo-preview'); + if (logoPreview.firstChild && logoPreview.firstChild.src.startsWith('data:image/png;base64')) { + formData.logofile = logoPreview.firstChild.src; // Use existing base64 + } else { //if it's a file upload + const logoFile = document.getElementById("{{ form.logofile.id_for_label }}").files[0]; + if (logoFile) { + formData.logofile = await readFileAsBase64(logoFile); + } + } + + const jsonData = JSON.stringify(formData, null, 2); + const blob = new Blob([jsonData], { type: "application/json" }); + const url = URL.createObjectURL(blob); + + const a = document.createElement("a"); + a.href = url; + a.download = filename + ".json"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + + async function readFileAsBase64(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (event) => resolve(event.target.result); + reader.onerror = (error) => reject(error); + reader.readAsDataURL(file); + }); + } + + + function loadFormData() { + const fileInput = document.getElementById("fileInput"); + fileInput.click(); + + fileInput.addEventListener("change", (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const formData = JSON.parse(e.target.result); + for (const key in formData) { + const element = document.querySelector(`[name="${key}"]`); + + if (element) { // Check if the element exists + if (element.type === 'checkbox' || element.type === 'radio') { + element.checked = formData[key]; // Set checked property + } else if (element.type !== 'file') { //for other elements + element.value = formData[key]; + } + + // Handle image previews (as before) + if (key === 'iconfile' && formData[key]) { + document.getElementById('id_iconbase64').value = formData[key] + document.getElementById('icon-preview').innerHTML = ``; + } + if (key === 'logofile' && formData[key]) { + document.getElementById('id_logobase64').value = formData[key] + document.getElementById('logo-preview').innerHTML = ``; + } + } + } + } catch (error) { + alert("Error loading data. Invalid JSON file."); + } + }; + reader.readAsText(file); + } + }); + } \ No newline at end of file diff --git a/rdgenerator/views.py b/rdgenerator/views.py index ea2edad..80f41b0 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -32,6 +32,7 @@ def generator_view(request): key = form.cleaned_data['key'] apiServer = form.cleaned_data['apiServer'] urlLink = form.cleaned_data['urlLink'] + downloadLink = form.cleaned_data['downloadLink'] if not server: server = 'rs-ny.rustdesk.com' #default rustdesk server if not key: @@ -40,6 +41,8 @@ def generator_view(request): apiServer = server+":21114" if not urlLink: urlLink = "https://rustdesk.com" + if not downloadLink: + downloadLink = "https://rustdesk.com/download" direction = form.cleaned_data['direction'] installation = form.cleaned_data['installation'] settings = form.cleaned_data['settings'] @@ -76,20 +79,20 @@ def generator_view(request): host = request.get_host() full_url = f"{protocol}://{host}" try: - iconfile = form.cleaned_data['iconfile'] - #iconbase64 = resize_and_encode_icon(iconfile) + iconfile = form.cleaned_data.get('iconfile') + if not iconfile: + iconfile = form.cleaned_data.get('iconbase64') iconlink = save_png(iconfile,myuuid,full_url,"icon.png") except: print("failed to get icon, using default") - #iconbase64 = b"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" iconlink = "false" try: - logofile = form.cleaned_data['logofile'] - #logobase64 = resize_and_encode_icon(logofile) + logofile = form.cleaned_data.get('logofile') + if not logofile: + logofile = form.cleaned_data.get('logobase64') logolink = save_png(logofile,myuuid,full_url,"logo.png") except: print("failed to get logo") - #logobase64 = b"" logolink = "false" ###create the custom.txt json here and send in as inputs below @@ -158,6 +161,7 @@ def generator_view(request): extras['genurl'] = _settings.GENURL extras['runasadmin'] = runasadmin extras['urlLink'] = urlLink + extras['downloadLink'] = downloadLink extras['delayFix'] = 'true' if delayFix else 'false' extras['version'] = version extras['rdgen'] = 'true' @@ -365,6 +369,19 @@ def startgh(request): def save_png(file, uuid, domain, name): file_save_path = "png/%s/%s" % (uuid, name) Path("png/%s" % uuid).mkdir(parents=True, exist_ok=True) + + if isinstance(file, str): # Check if it's a base64 string + try: + header, encoded = file.split(';base64,') + decoded_img = base64.b64decode(encoded) + file = ContentFile(decoded_img, name=name) # Create a file-like object + except ValueError: + print("Invalid base64 data") + return None # Or handle the error as you see fit + except Exception as e: # Catch general exceptions during decoding + print(f"Error decoding base64: {e}") + return None + with open(file_save_path, "wb+") as f: for chunk in file.chunks(): f.write(chunk)