commit b401aa531b65af74e202dd3492e37527be3beda8 Author: Bryan Gerlach Date: Tue Sep 24 16:04:47 2024 -0500 initial commit diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..4232cba --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rdgen.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/rdgen/__init__.py b/rdgen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdgen/__pycache__/__init__.cpython-312.pyc b/rdgen/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..dc95201 Binary files /dev/null and b/rdgen/__pycache__/__init__.cpython-312.pyc differ diff --git a/rdgen/__pycache__/settings.cpython-312.pyc b/rdgen/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..f742648 Binary files /dev/null and b/rdgen/__pycache__/settings.cpython-312.pyc differ diff --git a/rdgen/__pycache__/urls.cpython-312.pyc b/rdgen/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..b58eab8 Binary files /dev/null and b/rdgen/__pycache__/urls.cpython-312.pyc differ diff --git a/rdgen/__pycache__/wsgi.cpython-312.pyc b/rdgen/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000..f711d6b Binary files /dev/null and b/rdgen/__pycache__/wsgi.cpython-312.pyc differ diff --git a/rdgen/asgi.py b/rdgen/asgi.py new file mode 100644 index 0000000..b69d3d1 --- /dev/null +++ b/rdgen/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for rdgen project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rdgen.settings') + +application = get_asgi_application() diff --git a/rdgen/settings.py b/rdgen/settings.py new file mode 100644 index 0000000..322e9a5 --- /dev/null +++ b/rdgen/settings.py @@ -0,0 +1,130 @@ +""" +Django settings for rdgen project. + +Generated by 'django-admin startproject' using Django 5.0.3. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-!(t-!f#6g#sr%yfded9(xha)g+=!6craeez^cp+*&bz_7vdk61' +GHUSER = os.environ.get("GHUSER", '') +GHBEARER = os.environ.get("GHBEARER", '') + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rdgenerator', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + #'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'rdgen.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'rdgen.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/rdgen/urls.py b/rdgen/urls.py new file mode 100644 index 0000000..cde3821 --- /dev/null +++ b/rdgen/urls.py @@ -0,0 +1,33 @@ +""" +URL configuration for rdgen project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +import django + +from rdgenerator import views as views +if django.__version__.split('.')[0]>='4': + from django.urls import re_path as url +else: + from django.conf.urls import url, include + +urlpatterns = [ + url(r'^$',views.generator_view), + url(r'^generator',views.generator_view), + url(r'^check_for_file',views.check_for_file), + url(r'^download',views.download), + url(r'^creategh',views.create_github_run), + url(r'^updategh',views.update_github_run), + url(r'^startgh',views.startgh), +] diff --git a/rdgen/wsgi.py b/rdgen/wsgi.py new file mode 100644 index 0000000..a974cfa --- /dev/null +++ b/rdgen/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for rdgen project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rdgen.settings') + +application = get_wsgi_application() diff --git a/rdgenerator/__init__.py b/rdgenerator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdgenerator/__pycache__/__init__.cpython-312.pyc b/rdgenerator/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e851237 Binary files /dev/null and b/rdgenerator/__pycache__/__init__.cpython-312.pyc differ diff --git a/rdgenerator/__pycache__/admin.cpython-312.pyc b/rdgenerator/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..330d91a Binary files /dev/null and b/rdgenerator/__pycache__/admin.cpython-312.pyc differ diff --git a/rdgenerator/__pycache__/apps.cpython-312.pyc b/rdgenerator/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..3f1108e Binary files /dev/null and b/rdgenerator/__pycache__/apps.cpython-312.pyc differ diff --git a/rdgenerator/__pycache__/models.cpython-312.pyc b/rdgenerator/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..a47b9ab Binary files /dev/null and b/rdgenerator/__pycache__/models.cpython-312.pyc differ diff --git a/rdgenerator/__pycache__/views.cpython-312.pyc b/rdgenerator/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000..6637b7a Binary files /dev/null and b/rdgenerator/__pycache__/views.cpython-312.pyc differ diff --git a/rdgenerator/admin.py b/rdgenerator/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/rdgenerator/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/rdgenerator/apps.py b/rdgenerator/apps.py new file mode 100644 index 0000000..24e7b91 --- /dev/null +++ b/rdgenerator/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class RdgeneratorConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'rdgenerator' diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py new file mode 100644 index 0000000..bcae2af --- /dev/null +++ b/rdgenerator/forms.py @@ -0,0 +1,66 @@ +from django import forms + +class GenerateForm(forms.Form): + #Platform + platform = forms.ChoiceField(choices=[('windows','Windows'),('linux','Linux (currently unavailable)'),('android','Android (currently unavailable)')], initial='windows') + + #General + exename = forms.CharField(label="Name for EXE file", required=True) + appname = forms.CharField(label="Custom App Name", required=False) + direction = forms.ChoiceField(widget=forms.RadioSelect, choices=[ + ('incoming', 'Incoming Only'), + ('outgoing', 'Outgoing Only'), + ('both', 'Bidirectional') + ], initial='both') + installation = forms.ChoiceField(label="Disable Installation", choices=[ + ('installationY', 'No, enable installation'), + ('installationN', 'Yes, DISABLE installation') + ], initial='installationY') + settings = forms.ChoiceField(label="Disable Settings", choices=[ + ('settingsY', 'No, enable settings'), + ('settingsN', 'Yes, DISABLE settings') + ], initial='settingsY') + + #Custom Server + serverIP = forms.CharField(label="Host", required=False) + 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) + + #Visual + iconfile = forms.FileField(label="Custom App Icon (in .png format)", required=False) + logofile = forms.FileField(label="Custom App Logo (in .png format)", required=False) + theme = forms.ChoiceField(choices=[ + ('light', 'Light'), + ('dark', 'Dark'), + ('system', 'Follow System') + ], initial='system') + themeDorO = forms.ChoiceField(choices=[('default', 'Default'),('override', 'Override')], initial='default') + + #Security + passApproveMode = forms.ChoiceField(choices=[('password','Accept sessions via password'),('click','Accept sessions via click'),('password-click','Accepts sessions via both')],initial='password-click') + permanentPassword = forms.CharField(widget=forms.PasswordInput(), required=False) + runasadmin = forms.ChoiceField(choices=[('false','No'),('true','Yes')], initial='false') + denyLan = forms.BooleanField(initial=False, required=False) + enableDirectIP = forms.BooleanField(initial=False, required=False) + #ipWhitelist = forms.BooleanField(initial=False, required=False) + autoClose = forms.BooleanField(initial=False, required=False) + + #Permissions + permissionsDorO = forms.ChoiceField(choices=[('default', 'Default'),('override', 'Override')], initial='default') + permissionsType = forms.ChoiceField(choices=[('custom', 'Custom'),('full', 'Full Access'),('view','Screen share')], initial='custom') + enableKeyboard = forms.BooleanField(initial=True, required=False) + enableClipboard = forms.BooleanField(initial=True, required=False) + enableFileTransfer = forms.BooleanField(initial=True, required=False) + enableAudio = forms.BooleanField(initial=True, required=False) + enableTCP = forms.BooleanField(initial=True, required=False) + enableRemoteRestart = forms.BooleanField(initial=True, required=False) + enableRecording = forms.BooleanField(initial=True, required=False) + enableBlockingInput = forms.BooleanField(initial=True, required=False) + enableRemoteModi = forms.BooleanField(initial=False, required=False) + + #Other + removeWallpaper = forms.BooleanField(initial=True, required=False) + + defaultManual = forms.CharField(widget=forms.Textarea, required=False) + overrideManual = forms.CharField(widget=forms.Textarea, required=False) \ No newline at end of file diff --git a/rdgenerator/migrations/__init__.py b/rdgenerator/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdgenerator/migrations/__pycache__/__init__.cpython-312.pyc b/rdgenerator/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..eb9cc2a Binary files /dev/null and b/rdgenerator/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/rdgenerator/models.py b/rdgenerator/models.py new file mode 100644 index 0000000..a4f95c1 --- /dev/null +++ b/rdgenerator/models.py @@ -0,0 +1,6 @@ +from django.db import models + +class GithubRun(models.Model): + id = models.IntegerField(verbose_name="ID",primary_key=True) + uuid = models.CharField(verbose_name="uuid", max_length=100) + status = models.CharField(verbose_name="status", max_length=100) diff --git a/rdgenerator/templates/generated.html b/rdgenerator/templates/generated.html new file mode 100644 index 0000000..8af93ea --- /dev/null +++ b/rdgenerator/templates/generated.html @@ -0,0 +1 @@ +{{filename}} \ No newline at end of file diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html new file mode 100644 index 0000000..189a7cd --- /dev/null +++ b/rdgenerator/templates/generator.html @@ -0,0 +1,116 @@ + + + + Server Configuration Form + + + +
+

Select Platform

+ + {{ form.platform }}

+
+

General

+ + {{ form.exename }}

+ + {{ form.appname }}

+ + {{ form.direction }}

+ + {{ form.installation }}

+ + {{ form.settings }}

+
+ +
+

Custom Server

+ + {{ form.serverIP }}

+ + {{ form.key }}

+ + {{ form.apiServer }}

+ + {{ form.urlLink }}

+
+ +
+

Security

+ + {{ form.runasadmin }}

+ + {{ form.passApproveMode }}

+ + {{ form.permanentPassword }} *The password is used as default, but can be changed by the client

+ + {{ form.denyLan }} +

+ {{ form.enableDirectIP }} +

+ {{ form.autoClose }} +

+
+ +
+

Visual

+ + {{ form.iconfile }}

+ + {{ form.logofile }}

+ + {{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.

+
+ +
+

Permissions

+ The following Permissions can be set as default (the user can change the settins) or override (the settings cannot be changed).
+ {{ form.permissionsDorO }} + + {{ form.permissionsType }}

+ {{ form.enableKeyboard }} + {{ form.enableClipboard }}
+ {{ form.enableFileTransfer }} + {{ form.enableAudio }}
+ {{ form.enableTCP }} + {{ form.enableRemoteRestart }}
+ {{ form.enableRecording }} + {{ form.enableBlockingInput }}
+ {{ form.enableRemoteModi }}

+
+ +
+

Other

+ {{ form.removeWallpaper }}

+
+ {{ form.defaultManual }}

+
+ {{ form.overrideManual }}

+
+
+ Generate a custom client exe file +
+
+ + \ No newline at end of file diff --git a/rdgenerator/templates/waiting.html b/rdgenerator/templates/waiting.html new file mode 100644 index 0000000..a54d4a5 --- /dev/null +++ b/rdgenerator/templates/waiting.html @@ -0,0 +1,15 @@ + + + + Generating Exe File + + + Please wait...This can take 10-15 minutes (or longer if there are other users).

+ Status: {{status}} + + + \ No newline at end of file diff --git a/rdgenerator/tests.py b/rdgenerator/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/rdgenerator/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/rdgenerator/views.py b/rdgenerator/views.py new file mode 100644 index 0000000..b401471 --- /dev/null +++ b/rdgenerator/views.py @@ -0,0 +1,253 @@ +from django.http import HttpResponse +from django.shortcuts import render +import os +import re +import requests +import base64 +import json +import uuid +from django.conf import settings as _settings +from django.db.models import Q +from .forms import GenerateForm +from .models import GithubRun + +def generator_view(request): + if request.method == 'POST': + form = GenerateForm(request.POST, request.FILES) + if form.is_valid(): + platform = form.cleaned_data['platform'] + server = form.cleaned_data['serverIP'] + key = form.cleaned_data['key'] + apiServer = form.cleaned_data['apiServer'] + urlLink = form.cleaned_data['urlLink'] + if not server: + server = 'rs-ny.rustdesk.com' #default rustdesk server + if not key: + key = 'OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=' #default rustdesk key + if not apiServer: + apiServer = server+":21114" + if not urlLink: + urlLink = "https://rustdesk.com" + direction = form.cleaned_data['direction'] + installation = form.cleaned_data['installation'] + settings = form.cleaned_data['settings'] + appname = form.cleaned_data['appname'] + filename = form.cleaned_data['exename'] + permPass = form.cleaned_data['permanentPassword'] + theme = form.cleaned_data['theme'] + themeDorO = form.cleaned_data['themeDorO'] + runasadmin = form.cleaned_data['runasadmin'] + passApproveMode = form.cleaned_data['passApproveMode'] + denyLan = form.cleaned_data['denyLan'] + enableDirectIP = form.cleaned_data['enableDirectIP'] + #ipWhitelist = form.cleaned_data['ipWhitelist'] + autoClose = form.cleaned_data['autoClose'] + permissionsDorO = form.cleaned_data['permissionsDorO'] + permissionsType = form.cleaned_data['permissionsType'] + enableKeyboard = form.cleaned_data['enableKeyboard'] + enableClipboard = form.cleaned_data['enableClipboard'] + enableFileTransfer = form.cleaned_data['enableFileTransfer'] + enableAudio = form.cleaned_data['enableAudio'] + enableTCP = form.cleaned_data['enableTCP'] + enableRemoteRestart = form.cleaned_data['enableRemoteRestart'] + enableRecording = form.cleaned_data['enableRecording'] + enableBlockingInput = form.cleaned_data['enableBlockingInput'] + enableRemoteModi = form.cleaned_data['enableRemoteModi'] + removeWallpaper = form.cleaned_data['removeWallpaper'] + defaultManual = form.cleaned_data['defaultManual'] + overrideManual = form.cleaned_data['overrideManual'] + + + filename = re.sub(r'[^\w\s-]', '_', filename).strip() + myuuid = str(uuid.uuid4()) + try: + iconfile = form.cleaned_data['iconfile'] + iconbase64 = base64.b64encode(iconfile.file.read()) + except: + print("failed to get icon, using default") + iconbase64 = b"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" + try: + logofile = form.cleaned_data['logofile'] + logobase64 = base64.b64encode(logofile.file.read()) + except: + logobase64 = b"" + + ###create the custom.txt json here and send in as inputs below + decodedCustom = {} + if direction != "Both": + decodedCustom['conn-type'] = direction + if installation == "installationN": + decodedCustom['disable-installation'] = 'Y' + if settings == "settingsN": + decodedCustom['disable-settings'] = 'Y' + if appname.upper != "rustdesk".upper and appname != "": + decodedCustom['app-name'] = appname + decodedCustom['override-settings'] = {} + decodedCustom['default-settings'] = {} + if permPass != "": + decodedCustom['password'] = permPass + if theme != "system": + if themeDorO == "default": + decodedCustom['default-settings']['theme'] = theme + elif themeDorO == "override": + decodedCustom['override-settings']['theme'] = theme + decodedCustom['approve-mode'] = passApproveMode + decodedCustom['enable-lan-discovery'] = 'N' if denyLan else 'Y' + decodedCustom['direct-server'] = 'Y' if enableDirectIP else 'N' + decodedCustom['allow-auto-disconnect'] = 'Y' if autoClose else 'N' + decodedCustom['allow-remove-wallpaper'] = 'Y' if removeWallpaper else 'N' + if permissionsDorO == "default": + decodedCustom['default-settings']['access-mode'] = permissionsType + decodedCustom['default-settings']['enable-keyboard'] = 'Y' if enableKeyboard else 'N' + decodedCustom['default-settings']['enable-clipboard'] = 'Y' if enableClipboard else 'N' + decodedCustom['default-settings']['enable-file-transfer'] = 'Y' if enableFileTransfer else 'N' + decodedCustom['default-settings']['enable-audio'] = 'Y' if enableAudio else 'N' + decodedCustom['default-settings']['enable-tunnel'] = 'Y' if enableTCP else 'N' + decodedCustom['default-settings']['enable-remote-restart'] = 'Y' if enableRemoteRestart else 'N' + decodedCustom['default-settings']['enable-record-session'] = 'Y' if enableRecording else 'N' + decodedCustom['default-settings']['enable-block-input'] = 'Y' if enableBlockingInput else 'N' + decodedCustom['default-settings']['allow-remote-config-modification'] = 'Y' if enableRemoteModi else 'N' + else: + decodedCustom['override-settings']['access-mode'] = permissionsType + decodedCustom['override-settings']['enable-keyboard'] = 'Y' if enableKeyboard else 'N' + decodedCustom['override-settings']['enable-clipboard'] = 'Y' if enableClipboard else 'N' + decodedCustom['override-settings']['enable-file-transfer'] = 'Y' if enableFileTransfer else 'N' + decodedCustom['override-settings']['enable-audio'] = 'Y' if enableAudio else 'N' + decodedCustom['override-settings']['enable-tunnel'] = 'Y' if enableTCP else 'N' + decodedCustom['override-settings']['enable-remote-restart'] = 'Y' if enableRemoteRestart else 'N' + decodedCustom['override-settings']['enable-record-session'] = 'Y' if enableRecording else 'N' + decodedCustom['override-settings']['enable-block-input'] = 'Y' if enableBlockingInput else 'N' + decodedCustom['override-settings']['allow-remote-config-modification'] = 'Y' if enableRemoteModi else 'N' + + for line in defaultManual.splitlines(): + key, value = line.split('=') + decodedCustom['default-settings'][key.strip()] = value.strip() + + for line in overrideManual.splitlines(): + key, value = line.split('=') + decodedCustom['override-settings'][key.strip()] = value.strip() + + decodedCustomJson = json.dumps(decodedCustom) + + string_bytes = decodedCustomJson.encode("ascii") + base64_bytes = base64.b64encode(string_bytes) + encodedCustom = base64_bytes.decode("ascii") + + #github limits inputs to 10, so lump extras into one with json + extras = {} + extras['runasadmin'] = runasadmin + extras['urlLink'] = urlLink + extra_input = json.dumps(extras) + + ####from here run the github action, we need user, repo, access token. + if platform == 'windows': + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' + elif platform == 'linux': + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/generator-linux.yml/dispatches' + elif platform == 'android': + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/generator-android.yml/dispatches' + else: + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' + + #url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' + data = { + "ref":"master", + "inputs":{ + "server":server, + "key":key, + "apiServer":apiServer, + "custom":encodedCustom, + "uuid":myuuid, + "iconbase64":iconbase64.decode("utf-8"), + "logobase64":logobase64.decode("utf-8") if logobase64 else "", + "appname":appname, + "extras":extra_input, + "filename":filename + } + } + print(data) + headers = { + 'Accept': 'application/vnd.github+json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+_settings.GHBEARER, + 'X-GitHub-Api-Version': '2022-11-28' + } + create_github_run(myuuid) + response = requests.post(url, json=data, headers=headers) + print(response) + + return render(request, 'waiting.html', {'filename':filename, 'uuid':myuuid, 'status':"Starting generator...please wait"}) + else: + form = GenerateForm() + return render(request, 'generator.html', {'form': form}) + + +def check_for_file(request): + filename = request.GET['filename'] + uuid = request.GET['uuid'] + gh_run = GithubRun.objects.filter(Q(uuid=uuid)).first() + status = gh_run.status + + #if file_exists: + if status == "Success": + return render(request, 'generated.html', {'filename': filename, 'uuid':uuid}) + else: + return render(request, 'waiting.html', {'filename':filename, 'uuid':uuid, 'status':status}) + +def download(request): + filename = request.GET['filename'] + uuid = request.GET['uuid'] + filename = filename+".exe" + file_path = os.path.join('exe',uuid,filename) + with open(file_path, 'rb') as file: + response = HttpResponse(file, headers={ + 'Content-Type': 'application/vnd.microsoft.portable-executable', + 'Content-Disposition': f'attachment; filename="{filename}"' + }) + + return response + +def create_github_run(myuuid): + new_github_run = GithubRun( + uuid=myuuid, + status="Starting generator...please wait" + ) + new_github_run.save() + +def update_github_run(request): + data = json.loads(request.body) + myuuid = data.get('uuid') + mystatus = data.get('status') + GithubRun.objects.filter(Q(uuid=myuuid)).update(status=mystatus) + return HttpResponse('') + +#the following is used when accessed from an external source, like the rustdesk api server +def startgh(request): + print(request) + data_ = json.loads(request.body) + ####from here run the github action, we need user, repo, access token. + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/generator.yml/dispatches' + data = { + "ref":"master", + "inputs":{ + "server":data_.get('server'), + "key":data_.get('key'), + "apiServer":data_.get('apiServer'), + "custom":data_.get('custom'), + "uuid":data_.get('uuid'), + "iconbase64":data_.get('iconbase64'), + "logobase64":data_.get('logobase64'), + "appname":data_.get('appname'), + "extras":data_.get('extras'), + "filename":data_.get('filename') + } + } + headers = { + 'Accept': 'application/vnd.github+json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer '+_settings.GHBEARER, + 'X-GitHub-Api-Version': '2022-11-28' + } + response = requests.post(url, json=data, headers=headers) + print(response) + return HttpResponse('') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5db63d6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +django +requests \ No newline at end of file diff --git a/setup.txt b/setup.txt new file mode 100644 index 0000000..6aaf976 --- /dev/null +++ b/setup.txt @@ -0,0 +1,19 @@ +To fully host the client generator yourself, you will need to following: + +1) A Github account that has a fork of rustdesk +2) A Github fine-grained access token with permissions for your rustdesk repository +3) An FTP server to upload the generated clients +4) On the server running the client generator: + a) environment variables: + GHUSER="your github username" + GHBEARER="your fine-graned access token" + b) github secrets: + GENURL="example.com:8083" + GEN_FTP_SERVER="ftp.example.com" + GEN_FTP_USER="username" + GEN_FTP_PASSWORD="password" + c) optional github secrets (for signing the code): + WINDOWS_PFX_BASE64 + WINDOWS_PFX_PASSWORD + WINDOWS_PFX_SHA1_THUMBPRINT +5) A windows computer or VM that is set up to build rustdesk, and is setup as a self-hosted github runner