First Upload
This commit is contained in:
		
							
								
								
									
										0
									
								
								api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								api/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								api/admin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
from .admin_user import *
 | 
			
		||||
							
								
								
									
										101
									
								
								api/admin_user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								api/admin_user.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
# cython:language_level=3
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from api import models
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.auth.models import Group
 | 
			
		||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
 | 
			
		||||
from django.contrib.auth.forms import ReadOnlyPasswordHashField
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserCreationForm(forms.ModelForm):
 | 
			
		||||
    """A form for creating new users. Includes all the required
 | 
			
		||||
    fields, plus a repeated password."""
 | 
			
		||||
    password1 = forms.CharField(label=_('密码'), widget=forms.PasswordInput)
 | 
			
		||||
    password2 = forms.CharField(label=_('再次输入密码'), widget=forms.PasswordInput)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = models.UserProfile
 | 
			
		||||
        fields = ('username','is_active','is_admin')
 | 
			
		||||
 | 
			
		||||
    def clean_password2(self):
 | 
			
		||||
        # Check that the two password entries match
 | 
			
		||||
        password1 = self.cleaned_data.get("password1")
 | 
			
		||||
        password2 = self.cleaned_data.get("password2")
 | 
			
		||||
        if password1 and password2 and password1 != password2:
 | 
			
		||||
            raise forms.ValidationError(_("密码校验失败,两次密码不一致。"))
 | 
			
		||||
        return password2
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    def save(self, commit=True):
 | 
			
		||||
        # Save the provided password in hashed format
 | 
			
		||||
        user = super(UserCreationForm, self).save(commit=False)
 | 
			
		||||
        user.set_password(self.cleaned_data["password1"])
 | 
			
		||||
        if commit:
 | 
			
		||||
            user.save()
 | 
			
		||||
        return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserChangeForm(forms.ModelForm):
 | 
			
		||||
    """A form for updating users. Includes all the fields on
 | 
			
		||||
    the user, but replaces the password field with admin's
 | 
			
		||||
    password hash display field.
 | 
			
		||||
    """
 | 
			
		||||
    password = ReadOnlyPasswordHashField(label=(_("密码Hash值")), help_text=("<a href=\"../password/\">点击修改密码</a>."))
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = models.UserProfile
 | 
			
		||||
        fields = ('username', 'is_active', 'is_admin')
 | 
			
		||||
 | 
			
		||||
    def clean_password(self):
 | 
			
		||||
        # Regardless of what the user provides, return the initial value.
 | 
			
		||||
        # This is done here, rather than on the field, because the
 | 
			
		||||
        # field does not have access to the initial value
 | 
			
		||||
        return self.initial["password"]
 | 
			
		||||
        #return self.initial["password"]
 | 
			
		||||
    
 | 
			
		||||
    def save(self, commit=True):
 | 
			
		||||
        # Save the provided password in hashed format
 | 
			
		||||
        user = super(UserChangeForm, self).save(commit=False)
 | 
			
		||||
        
 | 
			
		||||
        if commit:
 | 
			
		||||
            user.save()
 | 
			
		||||
        return user
 | 
			
		||||
 | 
			
		||||
class UserAdmin(BaseUserAdmin):
 | 
			
		||||
    # The forms to add and change user instances
 | 
			
		||||
    form = UserChangeForm
 | 
			
		||||
    add_form = UserCreationForm
 | 
			
		||||
    password = ReadOnlyPasswordHashField(label=("Password HASH value"), help_text=("<a href=\"../password/\">Click to modify the password</a>."))
 | 
			
		||||
    # The fields to be used in displaying the User model.
 | 
			
		||||
    # These override the definitions on the base UserAdmin
 | 
			
		||||
    # that reference specific fields on auth.User.
 | 
			
		||||
    list_display = ('username', 'rid')
 | 
			
		||||
    list_filter = ('is_admin', 'is_active')
 | 
			
		||||
    fieldsets = (
 | 
			
		||||
        (_('基本信息'), {'fields': ('username', 'password', 'is_active', 'is_admin', 'rid', 'uuid', 'deviceInfo',)}),
 | 
			
		||||
      
 | 
			
		||||
    )
 | 
			
		||||
    readonly_fields = ( 'rid', 'uuid')
 | 
			
		||||
    add_fieldsets = (
 | 
			
		||||
        (None, {
 | 
			
		||||
            'classes': ('wide',),
 | 
			
		||||
            'fields': ('username',  'is_active', 'is_admin', 'password1', 'password2',  )}
 | 
			
		||||
         ),
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
    search_fields = ('username', )
 | 
			
		||||
    ordering = ('username',)
 | 
			
		||||
    filter_horizontal = ()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
admin.site.register(models.UserProfile, UserAdmin)
 | 
			
		||||
admin.site.register(models.RustDeskToken, models.RustDeskTokenAdmin)
 | 
			
		||||
admin.site.register(models.RustDeskTag, models.RustDeskTagAdmin)
 | 
			
		||||
admin.site.register(models.RustDeskPeer, models.RustDeskPeerAdmin)
 | 
			
		||||
admin.site.register(models.RustDesDevice, models.RustDesDeviceAdmin)
 | 
			
		||||
admin.site.register(models.ShareLink, models.ShareLinkAdmin)
 | 
			
		||||
admin.site.register(models.ConnLog, models.ConnLogAdmin)
 | 
			
		||||
admin.site.register(models.FileLog, models.FileLogAdmin)
 | 
			
		||||
admin.site.unregister(Group)
 | 
			
		||||
admin.site.site_header = _('RustDesk自建Web')
 | 
			
		||||
admin.site.site_title = _('未定义')
 | 
			
		||||
							
								
								
									
										5
									
								
								api/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								api/apps.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ApiConfig(AppConfig):
 | 
			
		||||
    name = 'api'
 | 
			
		||||
							
								
								
									
										102
									
								
								api/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								api/forms.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
from api.models import UserProfile
 | 
			
		||||
 | 
			
		||||
class GenerateForm(forms.Form):
 | 
			
		||||
    #Platform
 | 
			
		||||
    platform = forms.ChoiceField(choices=[('windows','Windows'),('linux','Linux (currently unavailable)'),('android','Android (testing now available)')], initial='windows')
 | 
			
		||||
    version = forms.ChoiceField(choices=[('master','beta'),('1.3.2','1.3.2'),('1.3.1','1.3.1'),('1.3.0','1.3.0')], initial='1.3.2')
 | 
			
		||||
    delayFix = forms.BooleanField(initial=True, required=False)
 | 
			
		||||
 | 
			
		||||
    #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, 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'}))
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
class AddPeerForm(forms.Form):
 | 
			
		||||
    clientID = forms.CharField(label="Client Rustdesk ID", required=True)
 | 
			
		||||
    alias = forms.CharField(label="Client alias", required=True)
 | 
			
		||||
    tags = forms.CharField(label="Tags", required=False)
 | 
			
		||||
    username = forms.CharField(label="Username", required=False)
 | 
			
		||||
    hostname = forms.CharField(label="OS", required=False)
 | 
			
		||||
    platform = forms.CharField(label="Platform", required=False)
 | 
			
		||||
    ip = forms.CharField(label="IP", required=False)
 | 
			
		||||
 | 
			
		||||
class AssignPeerForm(forms.Form):
 | 
			
		||||
    uid = forms.ModelChoiceField(
 | 
			
		||||
        queryset=UserProfile.objects.all(),
 | 
			
		||||
        to_field_name='id',
 | 
			
		||||
        empty_label='Select a User',
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
    clientID = forms.CharField(label="Client Rustdesk ID", required=True)
 | 
			
		||||
    alias = forms.CharField(label="Client alias", required=True)
 | 
			
		||||
    tags = forms.CharField(label="Tags", required=False)
 | 
			
		||||
    username = forms.CharField(label="Username", required=False)
 | 
			
		||||
    hostname = forms.CharField(label="Hostname", required=False)
 | 
			
		||||
    platform = forms.CharField(label="Platform", required=False)
 | 
			
		||||
    ip = forms.CharField(label="IP", required=False)
 | 
			
		||||
 | 
			
		||||
class EditPeerForm(forms.Form):
 | 
			
		||||
    clientID = forms.CharField(label="Client Rustdesk ID", required=True)
 | 
			
		||||
    alias = forms.CharField(label="Client alias", required=True)
 | 
			
		||||
    tags = forms.CharField(label="Tags", required=False)
 | 
			
		||||
    username = forms.CharField(label="Username", required=False)
 | 
			
		||||
    hostname = forms.CharField(label="OS", required=False)
 | 
			
		||||
    platform = forms.CharField(label="Platform", required=False)
 | 
			
		||||
    ip = forms.CharField(label="IP", required=False)
 | 
			
		||||
							
								
								
									
										78
									
								
								api/front_locale.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								api/front_locale.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_('管理后台')
 | 
			
		||||
_('ID列表')
 | 
			
		||||
_('分享机器')
 | 
			
		||||
_('这么简易的东西,忘记密码这功能就没必要了吧。')
 | 
			
		||||
_('立即注册')
 | 
			
		||||
_('创建时间')
 | 
			
		||||
_('注册成功,请前往登录页登录。')
 | 
			
		||||
_('注册日期')
 | 
			
		||||
_('2、所分享的机器,被分享人享有相同的权限,如果机器设置了保存密码,被分享人也可以直接连接。')
 | 
			
		||||
_('导出xlsx')
 | 
			
		||||
_('生成分享链接')
 | 
			
		||||
_('请输入8~20位密码。可以包含字母、数字和特殊字符。')
 | 
			
		||||
_('尾页')
 | 
			
		||||
_('请确认密码')
 | 
			
		||||
_('注册')
 | 
			
		||||
_('内存')
 | 
			
		||||
_('首页')
 | 
			
		||||
_('网页控制')
 | 
			
		||||
_('注册时间')
 | 
			
		||||
_('链接地址')
 | 
			
		||||
_('请输入密码')
 | 
			
		||||
_('系统用户名')
 | 
			
		||||
_('状态')
 | 
			
		||||
_('已有账号?立即登录')
 | 
			
		||||
_('密码')
 | 
			
		||||
_('别名')
 | 
			
		||||
_('上一页')
 | 
			
		||||
_('更新时间')
 | 
			
		||||
_('综合屏')
 | 
			
		||||
_('平台')
 | 
			
		||||
_('全部用户')
 | 
			
		||||
_('注册页')
 | 
			
		||||
_('分享机器给其他用户')
 | 
			
		||||
_('所有设备')
 | 
			
		||||
_('连接密码')
 | 
			
		||||
_('设备统计')
 | 
			
		||||
_('所属用户')
 | 
			
		||||
_('分享')
 | 
			
		||||
_('请输入用户名')
 | 
			
		||||
_('1、链接有效期为15分钟,切勿随意分享给他人。')
 | 
			
		||||
_('CPU')
 | 
			
		||||
_('客户端ID')
 | 
			
		||||
_('下一页')
 | 
			
		||||
_('登录')
 | 
			
		||||
_('退出')
 | 
			
		||||
_('请将要分享的机器调整到右侧')
 | 
			
		||||
_('成功!如需分享,请复制以下链接给其他人:<br>')
 | 
			
		||||
_('忘记密码?')
 | 
			
		||||
_('计算机名')
 | 
			
		||||
_('两次输入密码不一致!')
 | 
			
		||||
_('页码')
 | 
			
		||||
_('版本')
 | 
			
		||||
_('用户名')
 | 
			
		||||
_('3、为保障安全,链接有效期为15分钟、链接仅有效1次。链接一旦被(非分享人的登录用户)访问,分享生效,后续访问链接失效。')
 | 
			
		||||
_('系统')
 | 
			
		||||
_('我的机器')
 | 
			
		||||
_('信息')
 | 
			
		||||
_('远程ID')
 | 
			
		||||
_('远程别名')
 | 
			
		||||
_('用户ID')
 | 
			
		||||
_('用户别名')
 | 
			
		||||
_('用户IP')
 | 
			
		||||
_('文件大小')
 | 
			
		||||
_('发送/接受')
 | 
			
		||||
_('记录于')
 | 
			
		||||
_('连接开始时间')
 | 
			
		||||
_('连接结束时间')
 | 
			
		||||
_('时长')
 | 
			
		||||
_('连接日志')
 | 
			
		||||
_('文件传输日志')
 | 
			
		||||
_('页码 #')
 | 
			
		||||
_('下一页 #')
 | 
			
		||||
_('上一页 #')
 | 
			
		||||
_('第一页')
 | 
			
		||||
_('上页')
 | 
			
		||||
							
								
								
									
										243
									
								
								api/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								api/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
			
		||||
# Generated by Django 4.2.7 on 2023-12-14 12:08
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("auth", "0012_alter_user_first_name_max_length"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="RustDesDevice",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "rid",
 | 
			
		||||
                    models.CharField(blank=True, max_length=60, verbose_name="Client ID"),
 | 
			
		||||
                ),
 | 
			
		||||
                ("cpu", models.CharField(max_length=20, verbose_name="CPU")),
 | 
			
		||||
                ("hostname", models.CharField(max_length=20, verbose_name="CPU name")),
 | 
			
		||||
                ("memory", models.CharField(max_length=20, verbose_name="Memory")),
 | 
			
		||||
                ("os", models.CharField(max_length=20, verbose_name="operating system")),
 | 
			
		||||
                ("uuid", models.CharField(max_length=60, verbose_name="uuid")),
 | 
			
		||||
                (
 | 
			
		||||
                    "username",
 | 
			
		||||
                    models.CharField(blank=True, max_length=60, verbose_name="System username"),
 | 
			
		||||
                ),
 | 
			
		||||
                ("version", models.CharField(max_length=20, verbose_name="Client version")),
 | 
			
		||||
                (
 | 
			
		||||
                    "create_time",
 | 
			
		||||
                    models.DateTimeField(auto_now_add=True, verbose_name="Equipment registration time"),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "update_time",
 | 
			
		||||
                    models.DateTimeField(auto_now=True, verbose_name="Equipment update time"),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "equipment",
 | 
			
		||||
                "verbose_name_plural": "Device List",
 | 
			
		||||
                "ordering": ("-rid",),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="RustDeskPeer",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("uid", models.CharField(max_length=16, verbose_name="User ID")),
 | 
			
		||||
                ("rid", models.CharField(max_length=60, verbose_name="Client ID")),
 | 
			
		||||
                ("username", models.CharField(max_length=20, verbose_name="System username")),
 | 
			
		||||
                ("hostname", models.CharField(max_length=30, verbose_name="Operating system name")),
 | 
			
		||||
                ("alias", models.CharField(max_length=30, verbose_name="Alias")),
 | 
			
		||||
                ("platform", models.CharField(max_length=30, verbose_name="platform")),
 | 
			
		||||
                ("tags", models.CharField(max_length=30, verbose_name="Label")),
 | 
			
		||||
                ("rhash", models.CharField(max_length=60, verbose_name="Device link password")),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Peers",
 | 
			
		||||
                "verbose_name_plural": "PEERS list",
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="RustDeskTag",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("uid", models.CharField(max_length=16, verbose_name="User ID")),
 | 
			
		||||
                ("tag_name", models.CharField(max_length=60, verbose_name="Tag name")),
 | 
			
		||||
                (
 | 
			
		||||
                    "tag_color",
 | 
			
		||||
                    models.CharField(blank=True, max_length=60, verbose_name="Tag color"),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Tags",
 | 
			
		||||
                "verbose_name_plural": "Tags list",
 | 
			
		||||
                "ordering": ("-uid",),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="RustDeskToken",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("username", models.CharField(max_length=20, verbose_name="username")),
 | 
			
		||||
                ("rid", models.CharField(max_length=16, verbose_name="RustDesk ID")),
 | 
			
		||||
                ("uid", models.CharField(max_length=16, verbose_name="User ID")),
 | 
			
		||||
                ("uuid", models.CharField(max_length=60, verbose_name="uuid")),
 | 
			
		||||
                (
 | 
			
		||||
                    "access_token",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        blank=True, max_length=60, verbose_name="access_token"
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "create_time",
 | 
			
		||||
                    models.DateTimeField(auto_now_add=True, verbose_name="Log in time"),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Token",
 | 
			
		||||
                "verbose_name_plural": "Token list",
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="ShareLink",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("uid", models.CharField(max_length=16, verbose_name="User ID")),
 | 
			
		||||
                ("shash", models.CharField(max_length=60, verbose_name="Link Key")),
 | 
			
		||||
                ("peers", models.CharField(max_length=20, verbose_name="Machine ID list")),
 | 
			
		||||
                ("is_used", models.BooleanField(default=False, verbose_name="use or not")),
 | 
			
		||||
                ("is_expired", models.BooleanField(default=False, verbose_name="Whether to expire")),
 | 
			
		||||
                (
 | 
			
		||||
                    "create_time",
 | 
			
		||||
                    models.DateTimeField(auto_now_add=True, verbose_name="Generation time"),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Share link",
 | 
			
		||||
                "verbose_name_plural": "Link list",
 | 
			
		||||
                "ordering": ("-create_time",),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="UserProfile",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.AutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("password", models.CharField(max_length=128, verbose_name="password")),
 | 
			
		||||
                (
 | 
			
		||||
                    "last_login",
 | 
			
		||||
                    models.DateTimeField(
 | 
			
		||||
                        blank=True, null=True, verbose_name="last login"
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "is_superuser",
 | 
			
		||||
                    models.BooleanField(
 | 
			
		||||
                        default=False,
 | 
			
		||||
                        help_text="Designates that this user has all permissions without explicitly assigning them.",
 | 
			
		||||
                        verbose_name="superuser status",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "username",
 | 
			
		||||
                    models.CharField(max_length=50, unique=True, verbose_name="username"),
 | 
			
		||||
                ),
 | 
			
		||||
                ("rid", models.CharField(max_length=16, verbose_name="RustDesk ID")),
 | 
			
		||||
                ("uuid", models.CharField(max_length=60, verbose_name="uuid")),
 | 
			
		||||
                (
 | 
			
		||||
                    "autoLogin",
 | 
			
		||||
                    models.BooleanField(default=True, verbose_name="autoLogin"),
 | 
			
		||||
                ),
 | 
			
		||||
                ("rtype", models.CharField(max_length=20, verbose_name="rtype")),
 | 
			
		||||
                ("deviceInfo", models.TextField(blank=True, verbose_name="login information:")),
 | 
			
		||||
                ("is_active", models.BooleanField(default=True, verbose_name="Activate now")),
 | 
			
		||||
                ("is_admin", models.BooleanField(default=False, verbose_name="Whether a administrator")),
 | 
			
		||||
                (
 | 
			
		||||
                    "groups",
 | 
			
		||||
                    models.ManyToManyField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
 | 
			
		||||
                        related_name="user_set",
 | 
			
		||||
                        related_query_name="user",
 | 
			
		||||
                        to="auth.group",
 | 
			
		||||
                        verbose_name="groups",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "user_permissions",
 | 
			
		||||
                    models.ManyToManyField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        help_text="Specific permissions for this user.",
 | 
			
		||||
                        related_name="user_set",
 | 
			
		||||
                        related_query_name="user",
 | 
			
		||||
                        to="auth.permission",
 | 
			
		||||
                        verbose_name="user permissions",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "user",
 | 
			
		||||
                "verbose_name_plural": "user list",
 | 
			
		||||
                "permissions": (
 | 
			
		||||
                    ("view_task", "Can see available tasks"),
 | 
			
		||||
                    ("change_task_status", "Can change the status of tasks"),
 | 
			
		||||
                    ("close_task", "Can remove a task by setting its status as closed"),
 | 
			
		||||
                ),
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
# Generated by Django 4.2.7 on 2024-02-21 10:00
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("api", "0001_initial"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="cpu",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="CPU"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="hostname",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="主机名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="memory",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="内存"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="os",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="操作系统"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=100, verbose_name="系统用户名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="uuid",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="uuid"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="version",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="客户端版本"),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										238
									
								
								api/migrations/0003_alter_rustdesdevice_options_and_more.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								api/migrations/0003_alter_rustdesdevice_options_and_more.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,238 @@
 | 
			
		||||
# Generated by Django 4.2.7 on 2024-03-15 20:52
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("api", "0002_alter_rustdesdevice_cpu_alter_rustdesdevice_hostname_and_more"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesdevice",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-rid",),
 | 
			
		||||
                "verbose_name": "Device",
 | 
			
		||||
                "verbose_name_plural": "Device List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdeskpeer",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
                "verbose_name": "Peers",
 | 
			
		||||
                "verbose_name_plural": "Peers List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesktag",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-uid",),
 | 
			
		||||
                "verbose_name": "Tags",
 | 
			
		||||
                "verbose_name_plural": "Tags List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesktoken",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
                "verbose_name": "Token",
 | 
			
		||||
                "verbose_name_plural": "Token List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="sharelink",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-create_time",),
 | 
			
		||||
                "verbose_name": "Share Link",
 | 
			
		||||
                "verbose_name_plural": "Link List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="userprofile",
 | 
			
		||||
            options={
 | 
			
		||||
                "permissions": (
 | 
			
		||||
                    ("view_task", "Can see available tasks"),
 | 
			
		||||
                    ("change_task_status", "Can change the status of tasks"),
 | 
			
		||||
                    ("close_task", "Can remove a task by setting its status as closed"),
 | 
			
		||||
                ),
 | 
			
		||||
                "verbose_name": "User",
 | 
			
		||||
                "verbose_name_plural": "User List",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(
 | 
			
		||||
                auto_now_add=True, verbose_name="Device Registration Time"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="hostname",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="Hostname"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="memory",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="Memory"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="os",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="Operating System"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="rid",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name="Client ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                blank=True, max_length=100, verbose_name="System Username"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="version",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="Client Version"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="alias",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="Alias"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="hostname",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="Operating System Name"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="platform",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="Platform"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="rhash",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                max_length=60, verbose_name="Device Connection Password"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="rid",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="Client ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="tags",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="Tag"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="User ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="System Username"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="tag_color",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name="Tag Color"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="tag_name",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="Tag Name"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="Belongs to User ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="access_token",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                blank=True, max_length=60, verbose_name="Access Token"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name="Login Time"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="User ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="Username"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="uuid",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="UUID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name="Creation Time"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="is_expired",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="Is Expired"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="is_used",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="Is Used"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="peers",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="Machine ID List"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="shash",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="Link Key"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="User ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="deviceInfo",
 | 
			
		||||
            field=models.TextField(blank=True, verbose_name="Login Information:"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="is_active",
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name="Is Activated"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="is_admin",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="Is Admin"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=50, unique=True, verbose_name="Username"),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										232
									
								
								api/migrations/0004_alter_rustdesdevice_options_and_more.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								api/migrations/0004_alter_rustdesdevice_options_and_more.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,232 @@
 | 
			
		||||
# Generated by Django 4.2.7 on 2024-03-15 23:52
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("api", "0003_alter_rustdesdevice_options_and_more"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesdevice",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-rid",),
 | 
			
		||||
                "verbose_name": "设备",
 | 
			
		||||
                "verbose_name_plural": "设备列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdeskpeer",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
                "verbose_name": "Peers",
 | 
			
		||||
                "verbose_name_plural": "Peers列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesktag",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-uid",),
 | 
			
		||||
                "verbose_name": "Tags",
 | 
			
		||||
                "verbose_name_plural": "Tags列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="rustdesktoken",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-username",),
 | 
			
		||||
                "verbose_name": "Token",
 | 
			
		||||
                "verbose_name_plural": "Token列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="sharelink",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("-create_time",),
 | 
			
		||||
                "verbose_name": "分享链接",
 | 
			
		||||
                "verbose_name_plural": "链接列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="userprofile",
 | 
			
		||||
            options={
 | 
			
		||||
                "permissions": (
 | 
			
		||||
                    ("view_task", "Can see available tasks"),
 | 
			
		||||
                    ("change_task_status", "Can change the status of tasks"),
 | 
			
		||||
                    ("close_task", "Can remove a task by setting its status as closed"),
 | 
			
		||||
                ),
 | 
			
		||||
                "verbose_name": "用户",
 | 
			
		||||
                "verbose_name_plural": "用户列表",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name="设备注册时间"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="hostname",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="主机名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="memory",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="内存"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="os",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="操作系统"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="rid",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name="客户端ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=100, verbose_name="系统用户名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesdevice",
 | 
			
		||||
            name="version",
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name="客户端版本"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="alias",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="别名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="hostname",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="操作系统名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="platform",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="平台"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="rhash",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="设备链接密码"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="rid",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="客户端ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="tags",
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name="标签"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="用户ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdeskpeer",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="系统用户名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="tag_color",
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name="标签颜色"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="tag_name",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="标签名称"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktag",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="所属用户ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="access_token",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                blank=True, max_length=60, verbose_name="access_token"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name="登录时间"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="用户ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="用户名"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="rustdesktoken",
 | 
			
		||||
            name="uuid",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="uuid"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="create_time",
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name="生成时间"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="is_expired",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="是否过期"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="is_used",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="是否使用"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="peers",
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name="机器ID列表"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="shash",
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name="链接Key"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sharelink",
 | 
			
		||||
            name="uid",
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name="用户ID"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="deviceInfo",
 | 
			
		||||
            field=models.TextField(blank=True, verbose_name="登录信息:"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="is_active",
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name="是否激活"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="is_admin",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="是否管理员"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="username",
 | 
			
		||||
            field=models.CharField(max_length=50, unique=True, verbose_name="用户名"),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										253
									
								
								api/migrations/0005_connlog_filelog_githubrun_and_more.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								api/migrations/0005_connlog_filelog_githubrun_and_more.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,253 @@
 | 
			
		||||
# Generated by Django 5.0.3 on 2024-10-28 10:13
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('api', '0004_alter_rustdesdevice_options_and_more'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='ConnLog',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.IntegerField(primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('action', models.CharField(max_length=20, null=True, verbose_name='Action')),
 | 
			
		||||
                ('conn_id', models.CharField(max_length=10, null=True, verbose_name='Connection ID')),
 | 
			
		||||
                ('from_ip', models.CharField(max_length=30, null=True, verbose_name='From IP')),
 | 
			
		||||
                ('from_id', models.CharField(max_length=20, null=True, verbose_name='From ID')),
 | 
			
		||||
                ('rid', models.CharField(max_length=20, null=True, verbose_name='To ID')),
 | 
			
		||||
                ('conn_start', models.DateTimeField(null=True, verbose_name='Connected')),
 | 
			
		||||
                ('conn_end', models.DateTimeField(null=True, verbose_name='Disconnected')),
 | 
			
		||||
                ('session_id', models.CharField(max_length=60, null=True, verbose_name='Session ID')),
 | 
			
		||||
                ('uuid', models.CharField(max_length=60, null=True, verbose_name='UUID')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='FileLog',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.IntegerField(primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('file', models.CharField(max_length=500, verbose_name='Path')),
 | 
			
		||||
                ('remote_id', models.CharField(default='0', max_length=20, verbose_name='Remote ID')),
 | 
			
		||||
                ('user_id', models.CharField(default='0', max_length=20, verbose_name='User ID')),
 | 
			
		||||
                ('user_ip', models.CharField(default='0', max_length=20, verbose_name='User IP')),
 | 
			
		||||
                ('filesize', models.CharField(default='', max_length=500, verbose_name='Filesize')),
 | 
			
		||||
                ('direction', models.IntegerField(default=0, verbose_name='Direction')),
 | 
			
		||||
                ('logged_at', models.DateTimeField(null=True, verbose_name='Logged At')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='GithubRun',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.IntegerField(primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('uuid', models.CharField(max_length=100, verbose_name='uuid')),
 | 
			
		||||
                ('status', models.CharField(max_length=100, verbose_name='status')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='rustdesdevice',
 | 
			
		||||
            options={'ordering': ('-rid',), 'verbose_name': 'Device', 'verbose_name_plural': 'Device List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='rustdeskpeer',
 | 
			
		||||
            options={'ordering': ('-username',), 'verbose_name': 'Peers', 'verbose_name_plural': 'Peers List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='rustdesktag',
 | 
			
		||||
            options={'ordering': ('-uid',), 'verbose_name': 'Tags', 'verbose_name_plural': 'Tags List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='rustdesktoken',
 | 
			
		||||
            options={'ordering': ('-username',), 'verbose_name': 'Token', 'verbose_name_plural': 'Token List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='sharelink',
 | 
			
		||||
            options={'ordering': ('-create_time',), 'verbose_name': 'Share Link', 'verbose_name_plural': 'Link List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name='userprofile',
 | 
			
		||||
            options={'permissions': (('view_task', 'Can see available tasks'), ('change_task_status', 'Can change the status of tasks'), ('close_task', 'Can remove a task by setting its status as closed')), 'verbose_name': 'User', 'verbose_name_plural': 'User List'},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='ip',
 | 
			
		||||
            field=models.CharField(default='', max_length=16, verbose_name='IP Address'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='ip',
 | 
			
		||||
            field=models.CharField(blank=True, default='', max_length=16, verbose_name='IP Address'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='create_time',
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name='Device Registration Time'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='hostname',
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name='Hostname'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='memory',
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name='Memory'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='os',
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name='Operating System'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='rid',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name='Client ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='update_time',
 | 
			
		||||
            field=models.DateTimeField(auto_now=True, verbose_name='设备更新时间'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='username',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=100, verbose_name='System Username'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesdevice',
 | 
			
		||||
            name='version',
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name='Client Version'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='alias',
 | 
			
		||||
            field=models.CharField(max_length=30, verbose_name='Alias'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='hostname',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=30, verbose_name='Operating System Name'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='platform',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=30, verbose_name='Platform'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='rhash',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name='Device Connection Password'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='rid',
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name='Client ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='tags',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=30, verbose_name='Tag'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='uid',
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name='User ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdeskpeer',
 | 
			
		||||
            name='username',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=20, verbose_name='System Username'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktag',
 | 
			
		||||
            name='tag_color',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name='Tag Color'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktag',
 | 
			
		||||
            name='tag_name',
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name='Tag Name'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktag',
 | 
			
		||||
            name='uid',
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name='Belongs to User ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktoken',
 | 
			
		||||
            name='access_token',
 | 
			
		||||
            field=models.CharField(blank=True, max_length=60, verbose_name='Access Token'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktoken',
 | 
			
		||||
            name='create_time',
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name='Login Time'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktoken',
 | 
			
		||||
            name='uid',
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name='User ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktoken',
 | 
			
		||||
            name='username',
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name='Username'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='rustdesktoken',
 | 
			
		||||
            name='uuid',
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name='UUID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='create_time',
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name='Generation Time'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='is_expired',
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name='Is Expired'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='is_used',
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name='Is Used'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='peers',
 | 
			
		||||
            field=models.CharField(max_length=20, verbose_name='Machine ID List'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='shash',
 | 
			
		||||
            field=models.CharField(max_length=60, verbose_name='Link Key'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='sharelink',
 | 
			
		||||
            name='uid',
 | 
			
		||||
            field=models.CharField(max_length=16, verbose_name='User ID'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='userprofile',
 | 
			
		||||
            name='deviceInfo',
 | 
			
		||||
            field=models.TextField(blank=True, verbose_name='Login Information:'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='userprofile',
 | 
			
		||||
            name='is_active',
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name='Is Active'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='userprofile',
 | 
			
		||||
            name='is_admin',
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name='Is Administrator'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='userprofile',
 | 
			
		||||
            name='username',
 | 
			
		||||
            field=models.CharField(max_length=50, unique=True, verbose_name='Username'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										0
									
								
								api/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2
									
								
								api/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								api/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
from .models_work import *
 | 
			
		||||
from .models_user import *
 | 
			
		||||
							
								
								
									
										89
									
								
								api/models_user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								api/models_user.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
# cython:language_level=3
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.contrib.auth.models import (
 | 
			
		||||
    BaseUserManager,AbstractBaseUser,PermissionsMixin
 | 
			
		||||
)
 | 
			
		||||
from .models_work import *
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
class MyUserManager(BaseUserManager):
 | 
			
		||||
    def create_user(self, username, password=None):
 | 
			
		||||
        if not username:
 | 
			
		||||
            raise ValueError('Users must have an username')
 | 
			
		||||
 | 
			
		||||
        user = self.model(username=username,
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
        user.set_password(password)
 | 
			
		||||
        user.save(using=self._db)
 | 
			
		||||
        return user
 | 
			
		||||
 
 | 
			
		||||
    def create_superuser(self, username, password):
 | 
			
		||||
        user = self.create_user(username,
 | 
			
		||||
            password=password,
 | 
			
		||||
            
 | 
			
		||||
        )
 | 
			
		||||
        user.is_admin = True
 | 
			
		||||
        user.save(using=self._db)
 | 
			
		||||
        return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserProfile(AbstractBaseUser, PermissionsMixin):
 | 
			
		||||
    username = models.CharField(_('用户名'), 
 | 
			
		||||
                                unique=True,
 | 
			
		||||
                                max_length=50)
 | 
			
		||||
    
 | 
			
		||||
    rid = models.CharField(verbose_name='RustDesk ID', max_length=16)
 | 
			
		||||
    uuid = models.CharField(verbose_name='uuid', max_length=60)
 | 
			
		||||
    autoLogin = models.BooleanField(verbose_name='autoLogin', default=True)
 | 
			
		||||
    rtype = models.CharField(verbose_name='rtype', max_length=20)
 | 
			
		||||
    deviceInfo = models.TextField(verbose_name=_('登录信息:'), blank=True)
 | 
			
		||||
    
 | 
			
		||||
    is_active = models.BooleanField(verbose_name=_('是否激活'), default=True)
 | 
			
		||||
    is_admin = models.BooleanField(verbose_name=_('是否管理员'), default=False)
 | 
			
		||||
 | 
			
		||||
    objects = MyUserManager()
 | 
			
		||||
 
 | 
			
		||||
    USERNAME_FIELD = 'username'  # Fields used for household names
 | 
			
		||||
    REQUIRED_FIELDS = ['password']     #The field that must be filled in
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    def get_full_name(self):
 | 
			
		||||
        # The user is identified by their email address
 | 
			
		||||
        return self.username
 | 
			
		||||
 
 | 
			
		||||
    def get_short_name(self):
 | 
			
		||||
        # The user is identified by their email address
 | 
			
		||||
        return self.username
 | 
			
		||||
 
 | 
			
		||||
    def __str__(self):              # __unicode__ on Python 2
 | 
			
		||||
        return self.username
 | 
			
		||||
 
 | 
			
		||||
    def has_perm(self, perm, obj=None):    #Is there any specified permission
 | 
			
		||||
        "Does the user have a specific permission?"
 | 
			
		||||
        # Simplest possible answer: Yes, always
 | 
			
		||||
        return True
 | 
			
		||||
 
 | 
			
		||||
    def has_module_perms(self, app_label):
 | 
			
		||||
        "Does the user have permissions to view the app `app_label`?"
 | 
			
		||||
        # Simplest possible answer: Yes, always
 | 
			
		||||
        return True
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_staff(self):
 | 
			
		||||
        "Is the user a member of staff?"
 | 
			
		||||
        # Simplest possible answer: All admins are staff 
 | 
			
		||||
        return self.is_admin
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
    
 | 
			
		||||
        verbose_name = _("用户")
 | 
			
		||||
        verbose_name_plural = _("用户列表")
 | 
			
		||||
        permissions = (
 | 
			
		||||
            ("view_task", "Can see available tasks"),
 | 
			
		||||
            ("change_task_status", "Can change the status of tasks"),
 | 
			
		||||
            ("close_task", "Can remove a task by setting its status as closed"),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										151
									
								
								api/models_work.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								api/models_work.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
# cython:language_level=3
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
class RustDeskToken(models.Model):
 | 
			
		||||
    ''' Token
 | 
			
		||||
    '''
 | 
			
		||||
    username = models.CharField(verbose_name=_('用户名'), max_length=20)
 | 
			
		||||
    rid = models.CharField(verbose_name=_('RustDesk ID'), max_length=16)
 | 
			
		||||
    uid = models.CharField(verbose_name=_('用户ID'), max_length=16)
 | 
			
		||||
    uuid = models.CharField(verbose_name=_('uuid'), max_length=60)
 | 
			
		||||
    access_token = models.CharField(verbose_name=_('access_token'), max_length=60, blank=True)
 | 
			
		||||
    create_time = models.DateTimeField(verbose_name=_('登录时间'), auto_now_add=True)
 | 
			
		||||
    #expire_time = models.DateTimeField(verbose_name='过期时间')
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('-username',)
 | 
			
		||||
        verbose_name = "Token"
 | 
			
		||||
        verbose_name_plural = _("Token列表") 
 | 
			
		||||
 | 
			
		||||
class ConnLog(models.Model):
 | 
			
		||||
    id = models.IntegerField(verbose_name=_('ID'),primary_key=True)
 | 
			
		||||
    action = models.CharField(verbose_name=_('Action'), max_length=20, null=True)
 | 
			
		||||
    conn_id = models.CharField(verbose_name=_('Connection ID'), max_length=10, null=True)
 | 
			
		||||
    from_ip = models.CharField(verbose_name=_('From IP'), max_length=30, null=True)
 | 
			
		||||
    from_id = models.CharField(verbose_name=_('From ID'), max_length=20, null=True)
 | 
			
		||||
    rid = models.CharField(verbose_name=_('To ID'), max_length=20, null=True)
 | 
			
		||||
    conn_start = models.DateTimeField(verbose_name=_('Connected'), null=True)
 | 
			
		||||
    conn_end = models.DateTimeField(verbose_name=_('Disconnected'), null=True)
 | 
			
		||||
    session_id = models.CharField(verbose_name=_('Session ID'), max_length=60, null=True)
 | 
			
		||||
    uuid = models.CharField(verbose_name=_('uuid'), max_length=60, null=True)
 | 
			
		||||
 | 
			
		||||
class ConnLogAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('id', 'action', 'conn_id', 'from_ip', 'from_id', 'rid', 'conn_start', 'conn_end', 'session_id', 'uuid')
 | 
			
		||||
    search_fields = ('from_ip', 'rid')
 | 
			
		||||
    list_filter = ('id', 'from_ip', 'from_id', 'rid', 'conn_start', 'conn_end')
 | 
			
		||||
 | 
			
		||||
class FileLog(models.Model):
 | 
			
		||||
    id = models.IntegerField(verbose_name=_('ID'),primary_key=True)
 | 
			
		||||
    file = models.CharField(verbose_name=_('Path'), max_length=500)
 | 
			
		||||
    remote_id = models.CharField(verbose_name=_('Remote ID'), max_length=20, default='0')
 | 
			
		||||
    user_id = models.CharField(verbose_name=_('User ID'), max_length=20, default='0')
 | 
			
		||||
    user_ip = models.CharField(verbose_name=_('User IP'), max_length=20, default='0')
 | 
			
		||||
    filesize = models.CharField(verbose_name=_('Filesize'), max_length=500, default='')
 | 
			
		||||
    direction = models.IntegerField(verbose_name=_('Direction'), default=0)
 | 
			
		||||
    logged_at = models.DateTimeField(verbose_name=_('Logged At'), null=True)
 | 
			
		||||
 | 
			
		||||
class FileLogAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('id', 'file', 'remote_id', 'user_id', 'user_ip', 'filesize', 'direction', 'logged_at')
 | 
			
		||||
    search_fields = ('file', 'remote_id', 'user_id', 'user_ip')
 | 
			
		||||
    list_filter = ('id', 'file', 'remote_id', 'user_id', 'user_ip', 'filesize', 'direction', 'logged_at')
 | 
			
		||||
 | 
			
		||||
class RustDeskTokenAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('username', 'uid')
 | 
			
		||||
    search_fields = ('username', 'uid')
 | 
			
		||||
    list_filter = ('create_time', ) #filter
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
class RustDeskTag(models.Model):
 | 
			
		||||
    ''' Tags
 | 
			
		||||
    '''
 | 
			
		||||
    uid = models.CharField(verbose_name=_('所属用户ID'), max_length=16)
 | 
			
		||||
    tag_name = models.CharField(verbose_name=_('标签名称'), max_length=60)
 | 
			
		||||
    tag_color = models.CharField(verbose_name=_('标签颜色'), max_length=60, blank=True)
 | 
			
		||||
    
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('-uid',)
 | 
			
		||||
        verbose_name = "Tags"
 | 
			
		||||
        verbose_name_plural = _("Tags列表")
 | 
			
		||||
 | 
			
		||||
class RustDeskTagAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('tag_name', 'uid', 'tag_color')
 | 
			
		||||
    search_fields = ('tag_name', 'uid')
 | 
			
		||||
    list_filter = ('uid', )
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
class RustDeskPeer(models.Model):
 | 
			
		||||
    ''' Pees
 | 
			
		||||
    '''
 | 
			
		||||
    uid = models.CharField(verbose_name=_('用户ID'), max_length=16)
 | 
			
		||||
    rid = models.CharField(verbose_name=_('客户端ID'), max_length=60)
 | 
			
		||||
    username = models.CharField(verbose_name=_('系统用户名'), max_length=20, blank=True)
 | 
			
		||||
    hostname = models.CharField(verbose_name=_('操作系统名'), max_length=30, blank=True)
 | 
			
		||||
    alias = models.CharField(verbose_name=_('别名'), max_length=30)
 | 
			
		||||
    platform = models.CharField(verbose_name=_('平台'), max_length=30, blank=True)
 | 
			
		||||
    tags = models.CharField(verbose_name=_('标签'), max_length=30, blank=True)
 | 
			
		||||
    rhash = models.CharField(verbose_name=_('设备链接密码'), max_length=60, blank=True)
 | 
			
		||||
    ip = models.CharField(verbose_name=_('IP Address'), max_length=16, default="", blank=True)
 | 
			
		||||
    
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('-username',)
 | 
			
		||||
        verbose_name = "Peers"
 | 
			
		||||
        verbose_name_plural = _("Peers列表" )
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
class RustDeskPeerAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('rid', 'uid', 'username', 'hostname', 'platform', 'alias', 'tags', 'ip')
 | 
			
		||||
    search_fields = ('deviceid', 'alias')
 | 
			
		||||
    list_filter = ('rid', 'uid', )
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
class RustDesDevice(models.Model):
 | 
			
		||||
    rid = models.CharField(verbose_name=_('客户端ID'), max_length=60, blank=True)
 | 
			
		||||
    cpu = models.CharField(verbose_name='CPU', max_length=100)
 | 
			
		||||
    hostname = models.CharField(verbose_name=_('主机名'), max_length=100)
 | 
			
		||||
    memory = models.CharField(verbose_name=_('内存'), max_length=100)
 | 
			
		||||
    os = models.CharField(verbose_name=_('操作系统'), max_length=100)
 | 
			
		||||
    uuid = models.CharField(verbose_name='uuid', max_length=100)
 | 
			
		||||
    username = models.CharField(verbose_name=_('系统用户名'), max_length=100, blank=True)
 | 
			
		||||
    version = models.CharField(verbose_name=_('客户端版本'), max_length=100)
 | 
			
		||||
    create_time = models.DateTimeField(verbose_name=_('设备注册时间'), auto_now_add=True)
 | 
			
		||||
    update_time = models.DateTimeField(verbose_name=('设备更新时间'), auto_now=True, blank=True)
 | 
			
		||||
    ip = models.CharField(verbose_name=_('IP Address'), max_length=16, default="")
 | 
			
		||||
    
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('-rid',)
 | 
			
		||||
        verbose_name = _("设备")
 | 
			
		||||
        verbose_name_plural = _("设备列表" )
 | 
			
		||||
    
 | 
			
		||||
class RustDesDeviceAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('rid', 'hostname', 'memory', 'uuid', 'version', 'create_time', 'update_time', 'ip')
 | 
			
		||||
    search_fields = ('hostname', 'memory')
 | 
			
		||||
    list_filter = ('rid', )
 | 
			
		||||
 | 
			
		||||
class ShareLink(models.Model):
 | 
			
		||||
    ''' Share link
 | 
			
		||||
    '''
 | 
			
		||||
    uid = models.CharField(verbose_name=_('用户ID'), max_length=16)
 | 
			
		||||
    shash = models.CharField(verbose_name=_('链接Key'), max_length=60)
 | 
			
		||||
    peers = models.CharField(verbose_name=_('机器ID列表'), max_length=20)
 | 
			
		||||
    is_used = models.BooleanField(verbose_name=_('是否使用'), default=False)
 | 
			
		||||
    is_expired = models.BooleanField(verbose_name=_('是否过期'), default=False)
 | 
			
		||||
    create_time = models.DateTimeField(verbose_name=_('生成时间'), auto_now_add=True)
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    class Meta:
 | 
			
		||||
        ordering = ('-create_time',)
 | 
			
		||||
        verbose_name = _("分享链接")
 | 
			
		||||
        verbose_name_plural = _("链接列表" )
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
class ShareLinkAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ('shash', 'uid', 'peers', 'is_used', 'is_expired', 'create_time')
 | 
			
		||||
    search_fields = ('peers', )
 | 
			
		||||
    list_filter = ('is_used', 'uid', 'is_expired' )
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
							
								
								
									
										28
									
								
								api/templates/add_peer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								api/templates/add_peer.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Add Peer" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
    <div class="layui-row layui-col-space15">
 | 
			
		||||
      <div class="layui-col-md15">
 | 
			
		||||
        <div class="layui-card">
 | 
			
		||||
          <div class="layui-card-header">{{ "Add Client" }}</div>
 | 
			
		||||
          <div class="layui-card-body">
 | 
			
		||||
            <form action="/api/add_peer" method="post" enctype="multipart/form-data">
 | 
			
		||||
                <label for="{{ form.clientID.id_for_label }}">Rustdesk client ID:</label>
 | 
			
		||||
                {{ form.clientID }}<br><br>
 | 
			
		||||
                <label for="{{ form.alias.id_for_label }}">Alias:</label>
 | 
			
		||||
                {{ form.alias }}<br><br>
 | 
			
		||||
                <label for="{{ form.tags.id_for_label }}">Tags:</label>
 | 
			
		||||
                {{ form.tags }}<br><br>
 | 
			
		||||
                <button type="submit">Add Client</button>
 | 
			
		||||
            </form>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<script>
 | 
			
		||||
    document.getElementById("{{ form.clientID.id_for_label }}").value = "{{rid}}"
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										29
									
								
								api/templates/assign_peer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								api/templates/assign_peer.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Add Peer" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
    <div class="layui-row layui-col-space15">
 | 
			
		||||
      <div class="layui-col-md15">
 | 
			
		||||
        <div class="layui-card">
 | 
			
		||||
          <div class="layui-card-header">{{ "Assign Client to User" }}</div>
 | 
			
		||||
          <div class="layui-card-body">
 | 
			
		||||
            <form action="/api/assign_peer" method="post" enctype="multipart/form-data">
 | 
			
		||||
                {{ form.uid }}<br><br>
 | 
			
		||||
                <label for="{{ form.clientID.id_for_label }}">Rustdesk client ID:</label>
 | 
			
		||||
                {{ form.clientID }}<br><br>
 | 
			
		||||
                <label for="{{ form.alias.id_for_label }}">Alias:</label>
 | 
			
		||||
                {{ form.alias }}<br><br>
 | 
			
		||||
                <label for="{{ form.tags.id_for_label }}">Tags:</label>
 | 
			
		||||
                {{ form.tags }}<br><br>
 | 
			
		||||
                <button type="submit">Assign Client</button>
 | 
			
		||||
            </form>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<script>
 | 
			
		||||
    document.getElementById("{{ form.clientID.id_for_label }}").value = "{{rid}}"
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										75
									
								
								api/templates/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								api/templates/base.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <title>{% block title %}{% endblock %}</title>
 | 
			
		||||
    <meta name="renderer" content="webkit">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 | 
			
		||||
    <link rel="stylesheet" href="{% static 'layui/css/layui.css' %}">
 | 
			
		||||
    {% block link %}{% endblock %}
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <script src={% static "layui/layui.js" %}></script>
 | 
			
		||||
    <script>
 | 
			
		||||
        layui.use('element', function () {
 | 
			
		||||
            var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
 | 
			
		||||
 | 
			
		||||
            //监听导航点击
 | 
			
		||||
            element.on('nav(demo)', function (elem) {
 | 
			
		||||
                //console.log(elem)
 | 
			
		||||
                layer.msg(elem.text());
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        document.addEventListener('DOMContentLoaded', (event) => {
 | 
			
		||||
        // Place your globalSearch function or any DOM-related JS here
 | 
			
		||||
        window.globalSearch = function() {
 | 
			
		||||
            var input, filter, tables, tr, td, txtValue;
 | 
			
		||||
            input = document.getElementById("globalSearchInput");
 | 
			
		||||
            filter = input.value.toUpperCase();
 | 
			
		||||
            tables = document.getElementsByTagName("table");
 | 
			
		||||
 | 
			
		||||
            for (let table of tables) {
 | 
			
		||||
                tr = table.getElementsByTagName("tr");
 | 
			
		||||
                for (let i = 1; i < tr.length; i++) {
 | 
			
		||||
                    let row = tr[i];
 | 
			
		||||
                    let cells = row.getElementsByTagName("td");
 | 
			
		||||
                    let textContent = Array.from(cells).map(cell => cell.textContent.toUpperCase());
 | 
			
		||||
                    row.style.display = textContent.some(text => text.includes(filter)) ? "" : "none";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    </script>
 | 
			
		||||
 | 
			
		||||
    <ul class="layui-nav">
 | 
			
		||||
        <li class="layui-nav-item"><a href="/">User Devices</a></li>
 | 
			
		||||
        {% if u.is_admin %}
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/work?show_type=admin">{% trans "所有设备" %}</a>
 | 
			
		||||
        </li>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <!-- <li class="layui-nav-item"><a href="/api/share">{% trans "分享" %}</a></li> -->
 | 
			
		||||
        <li class="layui-nav-item"><a href="/webui" target="_blank">{% trans "网页控制" %}</a></li>
 | 
			
		||||
 | 
			
		||||
        {% if u.is_admin %}
 | 
			
		||||
        <li class="layui-nav-item"><a href="/admin" target="_blank">{% trans "管理后台" %}</a>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/conn_log">Connection Log</a></li>
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/file_log">File Transfer Log</a></li>
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/sys_info">Server Information</a></li>
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/clients">Client Downloads</a></li>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <li class="layui-nav-item"><a href="/api/user_action?action=logout" target="_blank">{% trans "退出" %}</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
 | 
			
		||||
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
 | 
			
		||||
        <legend>{% block legend_name %}{% endblock %}</legend>
 | 
			
		||||
    </fieldset>
 | 
			
		||||
    {% block content %}{% endblock %}
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										84
									
								
								api/templates/base_phone.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								api/templates/base_phone.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <title>{% block title %}{% endblock %}</title>
 | 
			
		||||
    <meta name="renderer" content="webkit">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 | 
			
		||||
    <link rel="stylesheet" href="{% static 'layui/css/layui.css' %}">
 | 
			
		||||
    {% block link %}{% endblock %}
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <script src={% static "layui/layui.js" %}></script>
 | 
			
		||||
    <script>
 | 
			
		||||
        layui.use('element', function () {
 | 
			
		||||
            var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
 | 
			
		||||
 | 
			
		||||
            //监听导航点击
 | 
			
		||||
            element.on('nav(demo)', function (elem) {
 | 
			
		||||
                //console.log(elem)
 | 
			
		||||
                layer.msg(elem.text());
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        document.addEventListener('DOMContentLoaded', (event) => {
 | 
			
		||||
        // Place your globalSearch function or any DOM-related JS here
 | 
			
		||||
        window.globalSearch = function() {
 | 
			
		||||
            var input, filter, tables, tr, td, txtValue;
 | 
			
		||||
            input = document.getElementById("globalSearchInput");
 | 
			
		||||
            filter = input.value.toUpperCase();
 | 
			
		||||
            tables = document.getElementsByTagName("table");
 | 
			
		||||
 | 
			
		||||
            for (let table of tables) {
 | 
			
		||||
                tr = table.getElementsByTagName("tr");
 | 
			
		||||
                for (let i = 1; i < tr.length; i++) {
 | 
			
		||||
                    let row = tr[i];
 | 
			
		||||
                    let cells = row.getElementsByTagName("td");
 | 
			
		||||
                    let textContent = Array.from(cells).map(cell => cell.textContent.toUpperCase());
 | 
			
		||||
                    row.style.display = textContent.some(text => text.includes(filter)) ? "" : "none";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    </script>
 | 
			
		||||
        <div id="container">
 | 
			
		||||
            <nav role="navigation">
 | 
			
		||||
              <div id="menuToggle">
 | 
			
		||||
                <input type="checkbox" />
 | 
			
		||||
                  <span></span>
 | 
			
		||||
                  <span></span>
 | 
			
		||||
                  <span></span>
 | 
			
		||||
              <ul id="menu">
 | 
			
		||||
                <li class="layui-nav-item"><a href="/">User Devices</a></li>
 | 
			
		||||
                {% if u.is_admin %}
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/work?show_type=admin">{% trans "所有设备" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <!-- <li class="layui-nav-item"><a href="/api/share">{% trans "分享" %}</a></li> -->
 | 
			
		||||
                <li class="layui-nav-item"><a href="/webui" target="_blank">{% trans "网页控制" %}</a></li>
 | 
			
		||||
 | 
			
		||||
                {% if u.is_admin %}
 | 
			
		||||
                <li class="layui-nav-item"><a href="/admin" target="_blank">{% trans "管理后台" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/conn_log">Connection Log</a></li>
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/file_log">File Transfer Log</a></li>
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/sys_info">Server Information</a></li>
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/clients">Client Downloads</a></li>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <li class="layui-nav-item"><a href="/api/user_action?action=logout" target="_blank">{% trans "退出" %}</a></li>
 | 
			
		||||
              </ul>
 | 
			
		||||
             </div>
 | 
			
		||||
            </nav>
 | 
			
		||||
        </div>
 | 
			
		||||
        <br>
 | 
			
		||||
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
 | 
			
		||||
        <legend>{% block legend_name %}{% endblock %}</legend>
 | 
			
		||||
    </fieldset>
 | 
			
		||||
    {% block content %}{% endblock %}
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										89
									
								
								api/templates/clients.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								api/templates/clients.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Client Downloads" | translate }}{% endblock %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<script src="{% static 'js/sorttable.js' %}"></script>
 | 
			
		||||
<style>
 | 
			
		||||
    .container {
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-template-columns: 50% 50%;
 | 
			
		||||
    grid-template-rows: repeat(2, auto); /* Adjust the number of rows as needed */
 | 
			
		||||
    padding: 20px; /* Adjust the padding value as needed */
 | 
			
		||||
    max-width: 1000px; /* Set a maximum width for the container */
 | 
			
		||||
    margin: 0 auto; /* Center the container horizontally */
 | 
			
		||||
    }
 | 
			
		||||
    .section {
 | 
			
		||||
        border: 1px solid #ccc;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        margin-bottom: 20px;
 | 
			
		||||
        margin-left: 10px;
 | 
			
		||||
        margin-right: 10px;
 | 
			
		||||
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Add a subtle box shadow */
 | 
			
		||||
        border-radius: 5px; /* Add rounded corners for a more 3D effect */
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
    document.addEventListener('DOMContentLoaded', (event) => {
 | 
			
		||||
    // Place your globalSearch function or any DOM-related JS here
 | 
			
		||||
    window.globalSearch = function() {
 | 
			
		||||
        var input, filter, tables, tr, td, txtValue;
 | 
			
		||||
        input = document.getElementById("globalSearchInput");
 | 
			
		||||
        filter = input.value.toUpperCase();
 | 
			
		||||
        tables = document.getElementsByTagName("table");
 | 
			
		||||
 | 
			
		||||
        for (let table of tables) {
 | 
			
		||||
            tr = table.getElementsByTagName("tr");
 | 
			
		||||
            for (let i = 1; i < tr.length; i++) {
 | 
			
		||||
                let row = tr[i];
 | 
			
		||||
                let cells = row.getElementsByTagName("td");
 | 
			
		||||
                let textContent = Array.from(cells).map(cell => cell.textContent.toUpperCase());
 | 
			
		||||
                row.style.display = textContent.some(text => text.includes(filter)) ? "" : "none";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<input type="text" id="globalSearchInput" onkeyup="globalSearch()" placeholder="Search all fields..." style="width: 200px; height: 20px; margin-bottom: 10px;">
 | 
			
		||||
<a href="/api/generator">Client Generator</a>
 | 
			
		||||
<div class="container">
 | 
			
		||||
    <div class="section">
 | 
			
		||||
        <h1>Github Clients</h1>
 | 
			
		||||
        <table class="sortable">
 | 
			
		||||
            <thead></thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th>File</th>
 | 
			
		||||
                    <th>Date</th>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% for filename, fileinfo in client_files.items %}
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td><a href='/api/download?filename={{filename}}&path={{fileinfo.path}}'>{{filename}}</a></td>
 | 
			
		||||
                    <td>{{fileinfo.modified}}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="section">
 | 
			
		||||
        <h1>Custom Clients</h1>
 | 
			
		||||
        <table class="sortable">
 | 
			
		||||
            <thead></thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th>File</th>
 | 
			
		||||
                    <th>Date</th>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% for filename, fileinfo in client_custom_files.items %}
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td><a href='/api/download?filename={{filename}}&path={{fileinfo.path}}'>{{filename}}</a></td>
 | 
			
		||||
                    <td>{{fileinfo.modified}}</td>
 | 
			
		||||
                    <td><a href='/api/delete_file?filename={{filename}}&path={{fileinfo.path}}'>Delete</a></td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										32
									
								
								api/templates/edit_peer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								api/templates/edit_peer.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Edit Peer" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
{{peer.rid}}
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
    <div class="layui-row layui-col-space15">
 | 
			
		||||
      <div class="layui-col-md15">
 | 
			
		||||
        <div class="layui-card">
 | 
			
		||||
          <div class="layui-card-header">{{ "Edit Peer" }} {{ peer.rid }}</div>
 | 
			
		||||
          <div class="layui-card-body">
 | 
			
		||||
            <form action="/api/edit_peer" method="post" enctype="multipart/form-data">
 | 
			
		||||
                <label for="{{ form.clientID.id_for_label }}">Client Rustdesk ID:</label>
 | 
			
		||||
                {{ form.clientID }}<br><br>
 | 
			
		||||
                <label for="{{ form.alias.id_for_label }}">Alias:</label>
 | 
			
		||||
                {{ form.alias }}<br><br>
 | 
			
		||||
                <label for="{{ form.tags.id_for_label }}">Tags:</label>
 | 
			
		||||
                {{ form.tags }}<br><br>
 | 
			
		||||
                <label for="{{ form.username.id_for_label }}">System Username:</label>
 | 
			
		||||
                {{ form.username }}<br><br>
 | 
			
		||||
                <label for="{{ form.hostname.id_for_label }}">Computer Name:</label>
 | 
			
		||||
                {{ form.hostname }}<br><br>
 | 
			
		||||
                <label for="{{ form.platform.id_for_label }}">Platform:</label>
 | 
			
		||||
                {{ form.platform }}<br><br>
 | 
			
		||||
                <button type="submit">Save Client</button>
 | 
			
		||||
            </form>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										7
									
								
								api/templates/generated.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								api/templates/generated.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Client Generator" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<a href='/api/download_client?filename={{filename}}&uuid={{uuid}}'>{{filename}}</a>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										253
									
								
								api/templates/generator.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								api/templates/generator.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,253 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Client Generator" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 | 
			
		||||
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: 'Roboto', sans-serif;
 | 
			
		||||
            background-color: #000;
 | 
			
		||||
            color: #e0e0e0;
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
        }
 | 
			
		||||
        .platform {
 | 
			
		||||
            display: grid;
 | 
			
		||||
            grid-template-columns: 1fr;
 | 
			
		||||
            grid-gap: 20px;
 | 
			
		||||
            margin: 0 auto;
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
            max-width: 1200px;
 | 
			
		||||
        }
 | 
			
		||||
        .container {
 | 
			
		||||
            display: grid;
 | 
			
		||||
            grid-template-columns: 1fr 1fr;  /* Adjust as needed */
 | 
			
		||||
            grid-gap: 20px;
 | 
			
		||||
            margin: 0 auto;  /* Center the container horizontally */
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
            max-width: 1200px;
 | 
			
		||||
        }
 | 
			
		||||
        .column {
 | 
			
		||||
            flex: 50%;
 | 
			
		||||
        }
 | 
			
		||||
        h1 {
 | 
			
		||||
            color: #fff;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            grid-column: 1 / -1;
 | 
			
		||||
        }
 | 
			
		||||
        h2 {
 | 
			
		||||
            color: #fff;
 | 
			
		||||
            margin-top: 0;
 | 
			
		||||
        }
 | 
			
		||||
        .section {
 | 
			
		||||
            background-color: #111;
 | 
			
		||||
            border-radius: 8px;
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 | 
			
		||||
            flex: 50%;
 | 
			
		||||
        }
 | 
			
		||||
        label {
 | 
			
		||||
            display: block;
 | 
			
		||||
            margin-bottom: 5px;
 | 
			
		||||
            color: #bbb;
 | 
			
		||||
        }
 | 
			
		||||
        input[type="text"], input[type="password"], select, textarea {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            padding: 8px;
 | 
			
		||||
            margin-bottom: 10px;
 | 
			
		||||
            background-color: #222;
 | 
			
		||||
            border: 1px solid #444;
 | 
			
		||||
            border-radius: 4px;
 | 
			
		||||
            color: #fff;
 | 
			
		||||
        }
 | 
			
		||||
        input[type="radio"], input[type="checkbox"] {
 | 
			
		||||
            margin-right: 5px;
 | 
			
		||||
        }
 | 
			
		||||
        button {
 | 
			
		||||
            background-color: #0077ff;
 | 
			
		||||
            color: #fff;
 | 
			
		||||
            border: none;
 | 
			
		||||
            padding: 10px 20px;
 | 
			
		||||
            border-radius: 4px;
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            transition: background-color 0.3s ease;
 | 
			
		||||
        }
 | 
			
		||||
        button:hover {
 | 
			
		||||
            background-color: #0066cc;
 | 
			
		||||
        }
 | 
			
		||||
        .platform-icons {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: space-around;
 | 
			
		||||
            margin-bottom: 20px;
 | 
			
		||||
        }
 | 
			
		||||
        .platform-icon {
 | 
			
		||||
            font-size: 32px;
 | 
			
		||||
            color: #bbb;
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            transition: color 0.3s ease;
 | 
			
		||||
        }
 | 
			
		||||
        .platform-icon:hover, .platform-icon.active {
 | 
			
		||||
            color: #fff;
 | 
			
		||||
        }
 | 
			
		||||
        .checkbox-group {
 | 
			
		||||
            display: grid;
 | 
			
		||||
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
 | 
			
		||||
            gap: 10px;
 | 
			
		||||
        }
 | 
			
		||||
        .checkbox-group label {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            align-items: center;
 | 
			
		||||
        }
 | 
			
		||||
        .preview-image {
 | 
			
		||||
            max-width: 100%;
 | 
			
		||||
            max-height: 100px;
 | 
			
		||||
            margin-top: 10px;
 | 
			
		||||
        }
 | 
			
		||||
      </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1><i class="fas fa-cogs"></i> RustDesk Custom Client Builder</h1>
 | 
			
		||||
    <form action="/api/generator" method="post" enctype="multipart/form-data">
 | 
			
		||||
        <div class="platform">
 | 
			
		||||
            <h2><i class="fas fa-desktop"></i> Select Platform</h2>
 | 
			
		||||
                <div class="platform-icons">
 | 
			
		||||
                    <i class="fab fa-windows platform-icon active" data-platform="windows"></i>
 | 
			
		||||
                    <i class="fab fa-linux platform-icon" data-platform="linux"></i>
 | 
			
		||||
                    <i class="fab fa-android platform-icon" data-platform="android"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
                <select name="platform" id="id_platform">
 | 
			
		||||
                    <option value="windows" selected>Windows</option>
 | 
			
		||||
                    <option value="linux">Linux</option>
 | 
			
		||||
                    <option value="android">Android</option>
 | 
			
		||||
                </select>
 | 
			
		||||
                <label for="{{ form.version.id_for_label }}">Rustdesk Version:</label>
 | 
			
		||||
                {{ form.version }}
 | 
			
		||||
                <label for="{{ form.delayFix.id_for_label }}">{{ form.delayFix }} Fix connection delay when using third-party API</label>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="container">   
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-sliders-h"></i> General</h2>
 | 
			
		||||
                    <label for="{{ form.exename.id_for_label }}">Name of the configuration:</label>
 | 
			
		||||
                    {{ form.exename }}<br><br>
 | 
			
		||||
                    <label for="{{ form.appname.id_for_label }}">Custom Application Name:</label>
 | 
			
		||||
                    {{ form.appname }}<br><br>
 | 
			
		||||
                    <label for="{{ form.direction.id_for_label }}">Connection Type:</label>
 | 
			
		||||
                    {{ form.direction }}<br><br>
 | 
			
		||||
                    <label for="{{ form.installation.id_for_label }}">Disable Installation:</label>
 | 
			
		||||
                    {{ form.installation }}<br><br>
 | 
			
		||||
                    <label for="{{ form.settings.id_for_label }}">Disable Settings:</label>
 | 
			
		||||
                    {{ form.settings }}<br><br>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-server"></i> Custom Server</h2>
 | 
			
		||||
                    <label for="{{ form.serverIP.id_for_label }}">Host:</label>
 | 
			
		||||
                    {{ form.serverIP }}<br><br>
 | 
			
		||||
                    <label for="{{ form.key.id_for_label }}">Key:</label>
 | 
			
		||||
                    {{ form.key }}<br><br>
 | 
			
		||||
                    <label for="{{ form.apiServer.id_for_label }}">API:</label>
 | 
			
		||||
                    {{ form.apiServer }}<br><br>
 | 
			
		||||
                    <label for="{{ form.urlLink.id_for_label }}">Custom URL for links (replaces https://rustdesk.com):</label>
 | 
			
		||||
                    {{ form.urlLink }}<br><br>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-shield-alt"></i> Security</h2>
 | 
			
		||||
                    <label for="{{ form.runasadmin.id_for_label }}">Always run as Administrator?</label>
 | 
			
		||||
                    {{ form.runasadmin }}<br><br>
 | 
			
		||||
                    <label for="{{ form.passApproveMode.id_for_label }}">Password Approve mode:</label>
 | 
			
		||||
                    {{ form.passApproveMode }}<br><br>
 | 
			
		||||
                    <label for="{{ form.permanentPassword.id_for_label }}">Set Permanent Password:</label>
 | 
			
		||||
                    {{ form.permanentPassword }} *The password is used as default, but can be changed by the client<br><br>
 | 
			
		||||
                    
 | 
			
		||||
                    
 | 
			
		||||
                    <label for="{{ form.denyLan.id_for_label }}">{{ form.denyLan }} Deny LAN discovery</label><br>
 | 
			
		||||
                    
 | 
			
		||||
                    <label for="{{ form.enableDirectIP.id_for_label }}">{{ form.enableDirectIP }} Enable direct IP access</label><br>
 | 
			
		||||
                    
 | 
			
		||||
                    <label for="{{ form.autoClose.id_for_label }}">{{ form.autoClose }} Automatically close incoming sessions on user inactivity</label><br>  
 | 
			
		||||
            </div>
 | 
			
		||||
        
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-paint-brush"></i> Visual</h2>
 | 
			
		||||
                    <label for="{{ form.iconfile.id_for_label }}">Custom App Icon (in .png format)</label>
 | 
			
		||||
                    {{ form.iconfile }}<br><br>
 | 
			
		||||
                    <!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> -->
 | 
			
		||||
                    <div id="icon-preview"></div><br><br>
 | 
			
		||||
                    <label for="{{ form.logofile.id_for_label }}">Custom App Logo (in .png format)</label>
 | 
			
		||||
                    {{ form.logofile }}<br><br>
 | 
			
		||||
                    <!-- <input type="file" name="logofile" id="logofile" accept="image/png"> -->
 | 
			
		||||
                    <div id="logo-preview"></div><br><br>
 | 
			
		||||
                    <label for="{{ form.theme.id_for_label }}">Theme:</label>
 | 
			
		||||
                    {{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.<br><br>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-lock"></i> Permissions</h2>
 | 
			
		||||
                    The following Permissions can be set as default (the user can change the settins) or override (the settings cannot be changed).<br>
 | 
			
		||||
                    {{ form.permissionsDorO }}
 | 
			
		||||
                    <label for="{{ form.permissionsType.id_for_label }}">Permission type:</label>
 | 
			
		||||
                    {{ form.permissionsType }}<br><br>
 | 
			
		||||
                    <div class="checkbox-group">
 | 
			
		||||
                        <label for="{{ form.enableKeyboard.id_for_label }}">{{ form.enableKeyboard }} Enable keyboard/mouse</label>
 | 
			
		||||
                        <label for="{{ form.enableClipboard.id_for_label }}">{{ form.enableClipboard }} Enable clipboard</label>
 | 
			
		||||
                        <label for="{{ form.enableFileTransfer.id_for_label }}">{{ form.enableFileTransfer }} Enable file transfer</label>
 | 
			
		||||
                        <label for="{{ form.enableAudio.id_for_label }}">{{ form.enableAudio }} Enable audio</label>
 | 
			
		||||
                        <label for="{{ form.enableTCP.id_for_label }}">{{ form.enableTCP }} Enable TCP tunneling</label>
 | 
			
		||||
                        <label for="{{ form.enableRemoteRestart.id_for_label }}">{{ form.enableRemoteRestart }} Enable remote restart</label>
 | 
			
		||||
                        <label for="{{ form.enableRecording.id_for_label }}">{{ form.enableRecording }} Enable recording session</label>
 | 
			
		||||
                        <label for="{{ form.enableBlockingInput.id_for_label }}">{{ form.enableBlockingInput }} Enable blocking user input</label>
 | 
			
		||||
                        <label for="{{ form.enableRemoteModi.id_for_label }}">{{ form.enableRemoteModi }} Enable remote configuration modification</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <h2><i class="fas fa-cog"></i> Other</h2>
 | 
			
		||||
                    <label for="{{ form.removeWallpaper.id_for_label }}">{{ form.removeWallpaper }} Remove wallpaper during incoming sessions</label><br>
 | 
			
		||||
                    <label for="{{ form.defaultManual.id_for_label }}">Default settings</label><br>
 | 
			
		||||
                    {{ form.defaultManual }}<br><br>
 | 
			
		||||
                    <label for="{{ form.overrideManual.id_for_label }}">Override settings</label><br>
 | 
			
		||||
                    {{ form.overrideManual }}<br><br>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="platform">
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                <button type="submit"><i class="fas fa-rocket"></i> Generate Custom Client</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
    <script>
 | 
			
		||||
        document.querySelectorAll('.platform-icon').forEach(icon => {
 | 
			
		||||
            icon.addEventListener('click', function() {
 | 
			
		||||
                document.querySelectorAll('.platform-icon').forEach(i => i.classList.remove('active'));
 | 
			
		||||
                this.classList.add('active');
 | 
			
		||||
                document.getElementById('id_platform').value = this.dataset.platform;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        document.getElementById("{{ form.iconfile.id_for_label }}").addEventListener('change', function(event) {
 | 
			
		||||
            previewImage(event.target, 'icon-preview');
 | 
			
		||||
        });
 | 
			
		||||
        document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) {
 | 
			
		||||
            previewImage(event.target, 'logo-preview');
 | 
			
		||||
        });
 | 
			
		||||
        function previewImage(input, previewContainerId) {
 | 
			
		||||
            if (input.files && input.files[0]) {
 | 
			
		||||
                var reader = new FileReader();
 | 
			
		||||
                reader.onload = function(e) {
 | 
			
		||||
                    var img = document.createElement('img');
 | 
			
		||||
                    img.src = e.target.result;   
 | 
			
		||||
                    img.style.maxWidth = '300px';
 | 
			
		||||
                    img.style.maxHeight = '60px';
 | 
			
		||||
                    document.getElementById(previewContainerId).innerHTML = '';
 | 
			
		||||
                    document.getElementById(previewContainerId).appendChild(img);
 | 
			
		||||
                };
 | 
			
		||||
                reader.readAsDataURL(input.files[0]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										70
									
								
								api/templates/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								api/templates/login.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
 
 | 
			
		||||
    <title>{{ "登录" | translate }}_【RustDeskWeb】</title>
 | 
			
		||||
        <link rel="stylesheet" href="{% static 'layui/css/layui.css' %}">
 | 
			
		||||
        <link rel="stylesheet" href="{% static 'layui/css/style.css' %}">
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 
 | 
			
		||||
<div class="login-main">
 | 
			
		||||
    <header class="layui-elip">{{ "登录" | translate }}</header>
 | 
			
		||||
    <form class="layui-form">
 | 
			
		||||
        <div class="layui-input-inline">
 | 
			
		||||
            <input type="text" name="account" required lay-verify="required" placeholder="{{ "用户名" | translate }}" autocomplete="off"
 | 
			
		||||
                   class="layui-input">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="layui-input-inline">
 | 
			
		||||
            <input type="password" name="password" required lay-verify="required" placeholder="{{ "密码" | translate }}" autocomplete="off"
 | 
			
		||||
                   class="layui-input">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="layui-input-inline login-btn">
 | 
			
		||||
            <button lay-submit lay-filter="login" class="layui-btn">{{ "登录" | translate }}</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <hr/>
 | 
			
		||||
        <p><a href="/api/user_action?action=register" class="fl">{{ "立即注册" | translate }}</a><a href="javascript:;" class="fr">{{ "忘记密码?" | translate }}</a></p>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
<script src={% static "layui/layui.js" %}></script>
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
    layui.use(['form','layer','jquery'], function () {
 | 
			
		||||
 
 | 
			
		||||
        // 操作对象
 | 
			
		||||
        var form = layui.form;
 | 
			
		||||
        var $ = layui.jquery;
 | 
			
		||||
        form.on('submit(login)',function (data) {
 | 
			
		||||
            console.log(data.field);
 | 
			
		||||
            $.ajax({
 | 
			
		||||
                url:'/api/user_action?action=login',
 | 
			
		||||
                data:data.field,
 | 
			
		||||
                dataType:'json',
 | 
			
		||||
                type:'post',
 | 
			
		||||
 | 
			
		||||
                success: function(resp) {
 | 
			
		||||
		    			if(resp.code==1) {
 | 
			
		||||
		    				//layer.alert(resp.msg,{icon:1});
 | 
			
		||||
                            location.href = resp.url;
 | 
			
		||||
		    			} else {
 | 
			
		||||
		    				layer.alert(resp.msg,{icon:5});
 | 
			
		||||
		    			}
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            return false;
 | 
			
		||||
        })
 | 
			
		||||
        $('.fr').on('click', function(){
 | 
			
		||||
            layer.alert("{{ "这么简易的东西,忘记密码这功能就没必要了吧。" | translate }}",{icon:5});
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										13
									
								
								api/templates/msg.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/templates/msg.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}{{title}}{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "信息" | translate }}{% endblock %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
  <div class="layui-row layui-col-space15">
 | 
			
		||||
{% autoescape off %}
 | 
			
		||||
    {{msg}}
 | 
			
		||||
 {% endautoescape %}
 | 
			
		||||
</div></div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										144
									
								
								api/templates/reg.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								api/templates/reg.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="renderer" content="webkit">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 | 
			
		||||
    <title>{{ "注册" | translate }}_【RustDeskWeb】</title>
 | 
			
		||||
        <link rel="stylesheet" href="{% static 'layui/css/layui.css' %}">
 | 
			
		||||
        <link rel="stylesheet" href="{% static 'layui/css/style.css' %}">
 | 
			
		||||
    <link rel="icon" href="../frame/static/image/code.png">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 
 | 
			
		||||
<div class="login-main">
 | 
			
		||||
    <header class="layui-elip" style="width: 82%">{{ "注册页" | translate }}</header>
 | 
			
		||||
 
 | 
			
		||||
    <!-- 表单选项 -->
 | 
			
		||||
    <form class="layui-form">
 | 
			
		||||
        <div class="layui-input-inline">
 | 
			
		||||
            <!-- 用户名 -->
 | 
			
		||||
            <div class="layui-inline" style="width: 85%">
 | 
			
		||||
                <input type="text" id="user" name="account" required  lay-verify="required" placeholder="{{ "请输入用户名" | translate }}" autocomplete="off" class="layui-input">
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 对号 -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="ri" style="color: green;font-weight: bolder;" hidden></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 错号 -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="wr" style="color: red; font-weight: bolder;" hidden>ဆ</i>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
            <!-- 密码 -->
 | 
			
		||||
        <div class="layui-input-inline">
 | 
			
		||||
            <div class="layui-inline" style="width: 85%">
 | 
			
		||||
                <input type="password" id="pwd" name="password" required  lay-verify="required" placeholder="{{ "请输入密码" | translate }}" autocomplete="off" class="layui-input">
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- Signs -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="pri" style="color: green;font-weight: bolder;" hidden></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- Wrong number -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="pwr" style="color: red; font-weight: bolder;" hidden>Gauge</i>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
            <!-- 确认密码 -->
 | 
			
		||||
        <div class="layui-input-inline">
 | 
			
		||||
            <div class="layui-inline" style="width: 85%">
 | 
			
		||||
                <input type="password" id="rpwd" name="repassword" required  lay-verify="required" placeholder="{{ "请确认密码" | translate }}" autocomplete="off" class="layui-input">
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 对号 -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="rpri" style="color: green;font-weight: bolder;" hidden></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 错号 -->
 | 
			
		||||
            <div class="layui-inline">
 | 
			
		||||
                <i class="layui-icon" id="rpwr" style="color: red; font-weight: bolder;" hidden>ဆ</i>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
        <div class="layui-input-inline login-btn" style="width: 85%">
 | 
			
		||||
            <button type="submit" lay-submit lay-filter="sub" class="layui-btn">{{ "注册" | translate }}</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <hr style="width: 85%" />
 | 
			
		||||
        <p style="width: 85%"><a href="/api/user_action?action=login" class="fl">{{ "已有账号?立即登录" | translate }}</a></p>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
<script src={% static "layui/layui.js" %}></script>
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
    layui.use(['form','jquery','layer'], function () {
 | 
			
		||||
        var form   = layui.form;
 | 
			
		||||
        var $      = layui.jquery;
 | 
			
		||||
        var layer  = layui.layer;
 | 
			
		||||
        //添加表单失焦事件
 | 
			
		||||
        //验证表单
 | 
			
		||||
        $('#user').blur(function() {
 | 
			
		||||
            var user = $(this).val();
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
        // you code ...
 | 
			
		||||
        // 为密码添加正则验证
 | 
			
		||||
        $('#pwd').blur(function() {
 | 
			
		||||
                var reg = /^[\w\S]{8,20}$/;
 | 
			
		||||
                if(!($('#pwd').val().match(reg))){
 | 
			
		||||
                    //layer.msg('请输入合法密码');
 | 
			
		||||
                    $('#pwr').removeAttr('hidden');
 | 
			
		||||
                    $('#pri').attr('hidden','hidden');
 | 
			
		||||
                    layer.msg('{{ "请输入8~20位密码。可以包含字母、数字和特殊字符。" | translate }}');
 | 
			
		||||
                }else {
 | 
			
		||||
                    $('#pri').removeAttr('hidden');
 | 
			
		||||
                    $('#pwr').attr('hidden','hidden');
 | 
			
		||||
                }
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
        //验证两次密码是否一致
 | 
			
		||||
        $('#rpwd').blur(function() {
 | 
			
		||||
                if($('#pwd').val() != $('#rpwd').val()){
 | 
			
		||||
                    $('#rpwr').removeAttr('hidden');
 | 
			
		||||
                    $('#rpri').attr('hidden','hidden');
 | 
			
		||||
                    layer.msg('{{ "两次输入密码不一致!" | translate }}');
 | 
			
		||||
                }else {
 | 
			
		||||
                    $('#rpri').removeAttr('hidden');
 | 
			
		||||
                    $('#rpwr').attr('hidden','hidden');
 | 
			
		||||
                };
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
        //
 | 
			
		||||
        //添加表单监听事件,提交注册信息
 | 
			
		||||
        form.on('submit(sub)', function() {
 | 
			
		||||
            $.ajax({
 | 
			
		||||
                url:'/api/user_action?action=register',
 | 
			
		||||
                type:'post',
 | 
			
		||||
                dataType:'json',
 | 
			
		||||
                data:{
 | 
			
		||||
                    user:$('#user').val(),
 | 
			
		||||
                    pwd:$('#pwd').val(),
 | 
			
		||||
                },
 | 
			
		||||
                success:function(data){
 | 
			
		||||
                    if (data.code == 1) {
 | 
			
		||||
                        layer.msg('{{ "注册成功,请前往登录页登录。" | translate }}');
 | 
			
		||||
                        ///location.href = "login.html";
 | 
			
		||||
                    }else {
 | 
			
		||||
                        layer.msg(data.msg);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            //防止页面跳转
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										106
									
								
								api/templates/share.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								api/templates/share.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
 | 
			
		||||
{% extends "base.html" %}{% load static %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}{{ "分享机器" | translate }}{% endblock %}
 | 
			
		||||
{% block link %}<link rel="stylesheet" href="{% static 'layui/css/style.css' %}">{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "分享机器给其他用户" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
<div class="layui-container">
 | 
			
		||||
    <div class="layui-card layui-col-md3-offset2">
 | 
			
		||||
        <div class="layui-card-header">{{ "请将要分享的机器调整到右侧" | translate }}</div>
 | 
			
		||||
        <div id="showdevice"></div>
 | 
			
		||||
        <button id="create" type="button" class="layui-btn padding-5" lay-on="getData">{{ "生成分享链接" | translate }}</button>
 | 
			
		||||
    </div> 
 | 
			
		||||
    <div class="layui-card">{{ "1、链接有效期为15分钟,切勿随意分享给他人。" | translate }}</div>
 | 
			
		||||
    <div class="layui-card">{{ "2、所分享的机器,被分享人享有相同的权限,如果机器设置了保存密码,被分享人也可以直接连接。" | translate }}</div>
 | 
			
		||||
    <div class="layui-card">{{ "3、为保障安全,链接有效期为15分钟、链接仅有效1次。链接一旦被(非分享人的登录用户)访问,分享生效,后续访问链接失效。" | translate }}</div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="layui-card layui-col-md6-offset1">
 | 
			
		||||
          <table class="layui-table">
 | 
			
		||||
            <colgroup>
 | 
			
		||||
              <col width="30">
 | 
			
		||||
              <col width="150">
 | 
			
		||||
              <col width="200">
 | 
			
		||||
              <col>
 | 
			
		||||
            </colgroup>
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>{{ "链接地址" | translate }}</th>
 | 
			
		||||
                <th>{{ "创建时间" | translate }}</th>
 | 
			
		||||
                <th>{{ "ID列表" | translate }}</th>
 | 
			
		||||
              </tr> 
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            
 | 
			
		||||
            {% for one in sharelinks %}
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td><script> document.write(window.location);</script>/{{one.shash}} </td>
 | 
			
		||||
                <td>{{one.create_time}}		</td>
 | 
			
		||||
                <td>{{one.peers}}		</td>
 | 
			
		||||
 | 
			
		||||
              </tr>
 | 
			
		||||
              {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
    </div> 
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 <script>
 | 
			
		||||
  layui.use(['transfer', 'jquery', 'layer'], function(){
 | 
			
		||||
    var transfer = layui.transfer;
 | 
			
		||||
    var $      = layui.jquery;
 | 
			
		||||
    var layer  = layui.layer;
 | 
			
		||||
 | 
			
		||||
    //渲染
 | 
			
		||||
    transfer.render({
 | 
			
		||||
      elem: '#showdevice'  //绑定元素
 | 
			
		||||
      ,title: ['{{ "我的机器" | translate }}', '{{ "分享机器" | translate }}']  //自定义标题
 | 
			
		||||
      //,width: 500 //定义宽度
 | 
			
		||||
      //,height: 300 //定义高度
 | 
			
		||||
      ,data: [//定义数据源
 | 
			
		||||
      {%for peer in peers %}
 | 
			
		||||
      {"value": "{{peer.id}}", "title": "{{peer.name}}"},
 | 
			
		||||
      {%endfor%}
 | 
			
		||||
 | 
			
		||||
      ]  //disabled Whether to disable  checked Whether to choose
 | 
			
		||||
      ,id: 'device' //Define indexes You can use it when reloading RELOAD or getting the right data
 | 
			
		||||
    });
 | 
			
		||||
    $("#create_bak").click(function(){
 | 
			
		||||
        
 | 
			
		||||
        var getData = transfer.getData('device');
 | 
			
		||||
        alert(JSON.stringify(getData));
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
    $("#create").click(function(){
 | 
			
		||||
        var getData = transfer.getData('device');
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            url:'/api/share',
 | 
			
		||||
            type:'post',
 | 
			
		||||
            dataType:'json',
 | 
			
		||||
            data:{
 | 
			
		||||
                data:JSON.stringify(getData),
 | 
			
		||||
            },
 | 
			
		||||
            success:function(data){
 | 
			
		||||
                if (data.code == 1) {
 | 
			
		||||
                   // var myMsg = layer.msg('处理中', {
 | 
			
		||||
                    //      shade: 0.4,
 | 
			
		||||
                     //     time:false //取消自动关闭
 | 
			
		||||
                    // });  
 | 
			
		||||
                    //layer.msg('注册成功,请前往登录页登录。');
 | 
			
		||||
                    layer.alert('{{ "成功!如需分享,请复制以下链接给其他人:<br>" | translate }}'+ window.location + '/' +data.shash, function (index) {
 | 
			
		||||
                        location.reload();});                    
 | 
			
		||||
                }else {
 | 
			
		||||
                    layer.msg(data.msg);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  });
 | 
			
		||||
  </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										66
									
								
								api/templates/show_conn_log.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								api/templates/show_conn_log.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Connection Log" | translate }}{% endblock %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<script src="{% static 'js/sorttable.js' %}"></script>
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
  <div class="layui-row layui-col-space15">
 | 
			
		||||
    <div class="layui-col-md15">
 | 
			
		||||
        <input type="text" id="globalSearchInput" onkeyup="globalSearch()" placeholder="Search all fields..." style="width: 200px; height: 20px; margin-bottom: 10px;">
 | 
			
		||||
      <div class="layui-card">
 | 
			
		||||
        <div class="layui-card-header">{{ "Connection Log" }}:【{{u.username}}】</div>
 | 
			
		||||
        <div class="layui-card-body">
 | 
			
		||||
          <table class="layui-table sortable">
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>User IP</th>
 | 
			
		||||
                <th>User ID</th>
 | 
			
		||||
                <th>User Alias</th>
 | 
			
		||||
                <th>Remote ID</th>
 | 
			
		||||
                <th>Remote Alias</th>
 | 
			
		||||
                <th>Connection Start Time</th>
 | 
			
		||||
                <th>Connection End Time</th>
 | 
			
		||||
                <th>Duration (HH:MM:SS)</th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
              {% for one in page_obj %}
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>{{one.from_ip}}</td>
 | 
			
		||||
                <td>{{one.from_id}}</td>
 | 
			
		||||
                <td>{{one.from_alias}}</td>
 | 
			
		||||
                <td>{{one.rid}}</td>  
 | 
			
		||||
                <td>{{one.alias}}</td> 
 | 
			
		||||
                <td>{{one.conn_start}}</td>
 | 
			
		||||
                <td>{{one.conn_end}}</td>
 | 
			
		||||
                <td>{{one.duration}}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="layui-col-md4 layui-col-md-offset4">
 | 
			
		||||
      <span class="step-links">
 | 
			
		||||
          {% if page_obj.has_previous %}
 | 
			
		||||
              <button class="layui-btn" ><a href="?page=1">« {{ "首页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.previous_page_number }}">{{ "上一页" | translate }}</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.paginator.num_pages > 1 %}
 | 
			
		||||
          <span class="current">
 | 
			
		||||
              {{ "页码" | translate }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.has_next %}
 | 
			
		||||
              <button class="layui-btn" > <a href="?page={{ page_obj.next_page_number }}">{{ "下一页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.paginator.num_pages }}">{{ "尾页" | translate }} »</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </span>
 | 
			
		||||
  </div>  
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										72
									
								
								api/templates/show_file_log.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								api/templates/show_file_log.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "File Transfer Log" | translate }}{% endblock %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<script src="{% static 'js/sorttable.js' %}"></script>
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
  <div class="layui-row layui-col-space15">
 | 
			
		||||
    <div class="layui-col-md15">
 | 
			
		||||
        <input type="text" id="globalSearchInput" onkeyup="globalSearch()" placeholder="Search all fields..." style="width: 200px; height: 20px; margin-bottom: 10px;">
 | 
			
		||||
      <div class="layui-card">
 | 
			
		||||
        <div class="layui-card-header">{{ "File Transfer Log" }}:【{{u.username}}】</div>
 | 
			
		||||
        <div class="layui-card-body">
 | 
			
		||||
          <table class="layui-table sortable">
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>File</th>
 | 
			
		||||
                <th>Remote ID</th>
 | 
			
		||||
                <th>Remote Alias</th>
 | 
			
		||||
                <th>User ID</th>
 | 
			
		||||
                <th>User Alias</th>
 | 
			
		||||
                <th>User IP</th>
 | 
			
		||||
                <th>Filesize</th>
 | 
			
		||||
                <th>Sent/Received</th>
 | 
			
		||||
                <th>Logged At</th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
              {% for one in page_obj %}
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>{{one.file}}</td>
 | 
			
		||||
                <td>{{one.remote_id}} </td>
 | 
			
		||||
                <td>{{one.remote_alias}}</td>
 | 
			
		||||
                <td>{{one.user_id}}</td>
 | 
			
		||||
                <td>{{one.user_alias}}</td>
 | 
			
		||||
                <td>{{one.user_ip}}</td>
 | 
			
		||||
                <td>{{one.filesize}}</td>
 | 
			
		||||
                {% if one.direction == 0 %}
 | 
			
		||||
                <td>User Received File</td>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                <td>User Sent File</td>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <td>{{one.logged_at}}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="layui-col-md4 layui-col-md-offset4">
 | 
			
		||||
      <span class="step-links">
 | 
			
		||||
          {% if page_obj.has_previous %}
 | 
			
		||||
              <button class="layui-btn" ><a href="?page=1">« {{ "首页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.previous_page_number }}">{{ "上一页" | translate }}</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.paginator.num_pages > 1 %}
 | 
			
		||||
          <span class="current">
 | 
			
		||||
              {{ "页码" | translate }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.has_next %}
 | 
			
		||||
              <button class="layui-btn" > <a href="?page={{ page_obj.next_page_number }}">{{ "下一页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.paginator.num_pages }}">{{ "尾页" | translate }} »</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </span>
 | 
			
		||||
  </div>  
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										14
									
								
								api/templates/show_sys_info.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								api/templates/show_sys_info.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Server Information" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<br>Hostname: 
 | 
			
		||||
{{ hostname }}
 | 
			
		||||
<br>CPU Usage: 
 | 
			
		||||
{{ cpu_usage }}
 | 
			
		||||
<br>Memory Usage: 
 | 
			
		||||
{{ memory_usage }}
 | 
			
		||||
<br>Disk Usage: 
 | 
			
		||||
{{ disk_usage }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										161
									
								
								api/templates/show_work.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								api/templates/show_work.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Devices" | translate }}{% endblock %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<script src="{% static 'js/sorttable.js' %}"></script>
 | 
			
		||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
 | 
			
		||||
<div style="padding: 20px; background-color: #F2F2F2;">
 | 
			
		||||
  <div class="layui-row layui-col-space15">
 | 
			
		||||
    {% if not show_all %}
 | 
			
		||||
    <div class="layui-col-md15">
 | 
			
		||||
      <input type="text" id="globalSearchInput" onkeyup="globalSearch()" placeholder="Search all fields..." style="width: 200px; height: 20px; margin-bottom: 10px;">
 | 
			
		||||
      <div class="layui-card">
 | 
			
		||||
        <div class="layui-card-header">{{ online_count_single }}/{{page_obj.paginator.count }}: {{ "User Devices Online" | translate }} - 【{{ "用户名" | translate }}:{{u.username}}】
 | 
			
		||||
          <a href="/api/add_peer"><i class='fa fa-plus'></i> Add Client</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="layui-card-body">
 | 
			
		||||
          <table class="layui-table sortable">
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>Edit / Delete</th>
 | 
			
		||||
                <th>{{ "客户端ID" | translate }}</th>
 | 
			
		||||
                <th>{{ "状态" | translate }}</th>
 | 
			
		||||
                <th>{{ "别名" | translate }}</th>
 | 
			
		||||
                <th>{{ "版本" | translate }}</th>
 | 
			
		||||
                <th>{{ "连接密码" | translate }}</th>
 | 
			
		||||
                <th>{{ "系统用户名" | translate }}</th>
 | 
			
		||||
                <th>{{ "计算机名" | translate }}</th>
 | 
			
		||||
                <th>{{ "平台" | translate }}</th>
 | 
			
		||||
                <th>{{ "系统" | translate }}</th>
 | 
			
		||||
                <th>{{ "CPU" | translate }}</th>
 | 
			
		||||
                <th>{{ "内存" | translate }}</th>
 | 
			
		||||
                <th>{{ "注册时间" | translate }}</th>
 | 
			
		||||
                <th>{{ "更新时间" | translate }}</th>
 | 
			
		||||
                <th>{{ "IP Address" | translate }}</th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
              {% for one in page_obj %}
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td><a href="/api/edit_peer?rid={{one.rid}}"><i class='fa fa-edit'></i></a> / <a href="/api/delete_peer?rid={{one.rid}}"><i class='fa fa-trash'></i></a></td>
 | 
			
		||||
                <td><a href=rustdesk://{{one.rid}}>{{one.rid}}</a> </td>
 | 
			
		||||
                <td>{{one.status}} </td>
 | 
			
		||||
                <td>{{one.alias}}</td>
 | 
			
		||||
                <td>{{one.version}}</td>
 | 
			
		||||
                <td>{{one.has_rhash}}</td>
 | 
			
		||||
                <td>{{one.username}}</td>
 | 
			
		||||
                <td>{{one.hostname}}</td>
 | 
			
		||||
                <td>{{one.platform}}</td>
 | 
			
		||||
                <td>{{one.os}}</td>
 | 
			
		||||
                <td>{{one.cpu}}</td>
 | 
			
		||||
                <td>{{one.memory}}</td>
 | 
			
		||||
                <td>{{one.create_time}}</td>
 | 
			
		||||
                <td>{{one.update_time}}</td>
 | 
			
		||||
                <td>{{one.ip}}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="layui-col-md4 layui-col-md-offset4">
 | 
			
		||||
      <span class="step-links">
 | 
			
		||||
          {% if page_obj.has_previous %}
 | 
			
		||||
              <button class="layui-btn" ><a href="?page=1">« {{ "首页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.previous_page_number }}">{{ "上一页" | translate }}</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.paginator.num_pages > 1 %}
 | 
			
		||||
          <span class="current">
 | 
			
		||||
              {{ "页码" | translate }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.has_next %}
 | 
			
		||||
              <button class="layui-btn" > <a href="?page={{ page_obj.next_page_number }}">{{ "下一页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?page={{ page_obj.paginator.num_pages }}">{{ "尾页" | translate }} »</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </span>
 | 
			
		||||
  </div>  
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    {% if u.is_admin and show_all %}
 | 
			
		||||
    <div class="layui-col-md15">
 | 
			
		||||
      <input type="text" id="globalSearchInput" onkeyup="globalSearch()" placeholder="Search all fields..." style="width: 200px; height: 20px; margin-bottom: 10px;">
 | 
			
		||||
      <div class="layui-card">
 | 
			
		||||
        <div class="layui-card-header">{{ online_count_all }}/{{page_obj.paginator.count }}: {{ "All Devices" | translate }} »
 | 
			
		||||
          <div class="layui-btn" ><a href="/api/down_peers">{{ "导出xlsx" | translate }}</a></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="layui-card-body">
 | 
			
		||||
          <table class="layui-table sortable">
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>{{ "客户端ID" | translate }}</th>
 | 
			
		||||
                <th>{{ "所属用户" | translate }}</th>
 | 
			
		||||
                <th>{{ "版本" | translate }}</th>
 | 
			
		||||
                <th>{{ "系统用户名" | translate }}</th>
 | 
			
		||||
                <th>{{ "计算机名" | translate }}</th>
 | 
			
		||||
                <th>{{ "系统" | translate }}</th>
 | 
			
		||||
                <th>{{ "CPU" | translate }}</th>
 | 
			
		||||
                <th>{{ "内存" | translate }}</th>
 | 
			
		||||
                <th>{{ "注册日期" | translate }}</th>
 | 
			
		||||
                <th>{{ "更新时间" | translate }}</th>
 | 
			
		||||
                <th>{{ "状态" | translate }}</th>
 | 
			
		||||
                <th>{{ "IP Address" | translate }}</th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
 | 
			
		||||
              {% for one in page_obj %}
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td><a href=rustdesk://{{one.rid}}>{{one.rid}}</a> </td>
 | 
			
		||||
                {% if one.rust_user|length > 0 %}
 | 
			
		||||
                <td>{{one.rust_user}} </td>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                <td><a href="/api/assign_peer?rid={{one.rid}}"><i class='fa fa-plus'></i> Assign Peer</a></td>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <td>{{one.version}} </td>
 | 
			
		||||
                <td>{{one.username}} </td>
 | 
			
		||||
                <td>{{one.hostname}} </td>
 | 
			
		||||
                <td>{{one.os}} </td>
 | 
			
		||||
                <td>{{one.cpu}} </td>
 | 
			
		||||
                <td>{{one.memory}} </td>
 | 
			
		||||
                <td>{{one.create_time}} </td>
 | 
			
		||||
                <td>{{one.update_time}} </td>
 | 
			
		||||
                <td>{{one.status}} </td>
 | 
			
		||||
                <td>{{one.ip}}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              {% endfor %}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="layui-col-md4 layui-col-md-offset4">
 | 
			
		||||
      <span class="step-links">
 | 
			
		||||
          {% if page_obj.has_previous %}
 | 
			
		||||
              <button class="layui-btn" ><a href="?show_type=admin&page=1">« {{ "首页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?show_type=admin&page={{ page_obj.previous_page_number }}">{{ "上一页" | translate }}</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.paginator.num_pages > 1 %}
 | 
			
		||||
          <span class="current">
 | 
			
		||||
              {{ "页码" | translate }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
 | 
			
		||||
          </span>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
          {% if page_obj.has_next %}
 | 
			
		||||
              <button class="layui-btn" > <a href="?show_type=admin&page={{ page_obj.next_page_number }}">{{ "下一页" | translate }}</a></button>
 | 
			
		||||
              <button class="layui-btn" ><a href="?show_type=admin&page={{ page_obj.paginator.num_pages }}">{{ "尾页" | translate }} »</a></button>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </span>
 | 
			
		||||
  </div>  
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										13
									
								
								api/templates/waiting.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/templates/waiting.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
{% extends phone_or_desktop %}
 | 
			
		||||
{% load my_filters %}
 | 
			
		||||
{% block title %}RustDesk WebUI{% endblock %}
 | 
			
		||||
{% block legend_name %}{{ "Client Generator" | translate }}{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
Please wait...This can take 20-30 minutes (or longer if there are other users).<br><br>
 | 
			
		||||
Status: {{status}}
 | 
			
		||||
<script>
 | 
			
		||||
    setTimeout(function() {
 | 
			
		||||
        window.location.replace('/api/check_for_file?filename={{filename}}&uuid={{uuid}}&platform={{platform}}');
 | 
			
		||||
    }, 5000); // 5000 milliseconds = 5 seconds
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										0
									
								
								api/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								api/templatetags/my_filters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								api/templatetags/my_filters.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
from django import template
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
register = template.Library()
 | 
			
		||||
 | 
			
		||||
@register.filter
 | 
			
		||||
def translate(text):
 | 
			
		||||
    return _(text)
 | 
			
		||||
							
								
								
									
										3
									
								
								api/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								api/tests.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
							
								
								
									
										42
									
								
								api/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								api/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import django
 | 
			
		||||
if django.__version__.split('.')[0]>='4':
 | 
			
		||||
    from django.urls import re_path as url
 | 
			
		||||
else:
 | 
			
		||||
    from django.conf.urls import  url, include
 | 
			
		||||
 | 
			
		||||
from api import views
 | 
			
		||||
 
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    url(r'^login',views.login),
 | 
			
		||||
    url(r'^logout',views.logout),
 | 
			
		||||
    url(r'^ab$',views.ab),
 | 
			
		||||
    url(r'^ab\/get',views.ab_get), # 兼容 x86-sciter 版客户端
 | 
			
		||||
    url(r'^users',views.users),
 | 
			
		||||
    url(r'^peers',views.peers),
 | 
			
		||||
    url(r'^currentUser',views.currentUser),
 | 
			
		||||
    url(r'^sysinfo',views.sysinfo),
 | 
			
		||||
    url(r'^heartbeat',views.heartbeat),
 | 
			
		||||
    #url(r'^register',views.register), 
 | 
			
		||||
    url(r'^user_action',views.user_action),  # 前端
 | 
			
		||||
    url(r'^work',views.work),                # 前端
 | 
			
		||||
    url(r'^down_peers$',views.down_peers),   # 前端
 | 
			
		||||
    url(r'^share',views.share),              # 前端
 | 
			
		||||
    url(r'^conn_log',views.conn_log),
 | 
			
		||||
    url(r'^file_log',views.file_log),
 | 
			
		||||
    url(r'^audit',views.audit),
 | 
			
		||||
    url(r'^sys_info',views.sys_info),
 | 
			
		||||
    url(r'^clients',views.clients),
 | 
			
		||||
    url(r'^download',views.download),
 | 
			
		||||
    url(r'^generator',views.generator_view),
 | 
			
		||||
    url(r'^check_for_file',views.check_for_file),
 | 
			
		||||
    url(r'^download_client',views.download_client),
 | 
			
		||||
    url(r'^creategh',views.create_github_run),
 | 
			
		||||
    url(r'^updategh',views.update_github_run),
 | 
			
		||||
    url(r'^save_custom_client',views.save_custom_client),
 | 
			
		||||
    url(r'^delete_file',views.delete_file),
 | 
			
		||||
    url(r'^get_png',views.get_png),
 | 
			
		||||
    url(r'^add_peer',views.add_peer),
 | 
			
		||||
    url(r'^delete_peer',views.delete_peer),
 | 
			
		||||
    url(r'^edit_peer',views.edit_peer),
 | 
			
		||||
    url(r'^assign_peer',views.assign_peer),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										36
									
								
								api/util.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								api/util.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
"""
 | 
			
		||||
Created on Thu Nov 19 15:51:21 2020
 | 
			
		||||
 | 
			
		||||
@author: lenovo
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import platform
 | 
			
		||||
import logging
 | 
			
		||||
from .models_user import UserProfile
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
from django.conf import settings as _settings
 | 
			
		||||
 | 
			
		||||
def settings(request):
 | 
			
		||||
    """
 | 
			
		||||
    TEMPLATE_CONTEXT_PROCESSORS
 | 
			
		||||
    """
 | 
			
		||||
    context = { 'settings': _settings }
 | 
			
		||||
    try:
 | 
			
		||||
        username = request.user
 | 
			
		||||
        u = UserProfile.objects.get(username=username)  
 | 
			
		||||
        context['test'] = 'This is a test variable'
 | 
			
		||||
        context['u'] = u
 | 
			
		||||
        #context['user'] = u
 | 
			
		||||
        context['username'] = username
 | 
			
		||||
        context['is_admin'] = u.is_admin
 | 
			
		||||
        context['is_active'] = u.is_active
 | 
			
		||||
        context['domain'] = _settings.ID_SERVER
 | 
			
		||||
        context['is_windows'] = True if platform.system() == 'Windows' else False
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        logger.info("set system status variable")
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error("settings:{}".format( e))
 | 
			
		||||
    return context
 | 
			
		||||
							
								
								
									
										26
									
								
								api/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								api/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.http import HttpResponseRedirect
 | 
			
		||||
from django.contrib.auth.hashers import make_password
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.contrib import auth
 | 
			
		||||
from django.forms.models import model_to_dict
 | 
			
		||||
 | 
			
		||||
from itertools import chain
 | 
			
		||||
from django.db.models.fields import DateTimeField, DateField, CharField, TextField
 | 
			
		||||
 | 
			
		||||
from django.db.models import Model
 | 
			
		||||
 | 
			
		||||
from django.http import JsonResponse
 | 
			
		||||
import json
 | 
			
		||||
import time
 | 
			
		||||
import datetime
 | 
			
		||||
import hashlib
 | 
			
		||||
from api.models import RustDeskToken, UserProfile, RustDeskTag, RustDeskPeer, RustDesDevice
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from .views_front import *
 | 
			
		||||
from .views_api import *
 | 
			
		||||
from .views_generator import *
 | 
			
		||||
from .front_locale import *
 | 
			
		||||
							
								
								
									
										329
									
								
								api/views_api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								api/views_api.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,329 @@
 | 
			
		||||
# cython:language_level=3
 | 
			
		||||
from django.http import JsonResponse
 | 
			
		||||
import json
 | 
			
		||||
import time
 | 
			
		||||
import datetime
 | 
			
		||||
import hashlib
 | 
			
		||||
import math
 | 
			
		||||
from django.contrib import auth
 | 
			
		||||
from django.forms.models import model_to_dict
 | 
			
		||||
from api.models import RustDeskToken, UserProfile, RustDeskTag, RustDeskPeer, RustDesDevice, ConnLog, FileLog
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
import copy
 | 
			
		||||
from .views_front import *
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def login(request):
 | 
			
		||||
    result = {}
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        result['error'] = _('请求方式错误!请使用POST方式。')
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
    data = json.loads(request.body.decode())
 | 
			
		||||
    
 | 
			
		||||
    username = data.get('username', '')
 | 
			
		||||
    password = data.get('password', '')
 | 
			
		||||
    rid = data.get('id', '')
 | 
			
		||||
    uuid = data.get('uuid', '')
 | 
			
		||||
    autoLogin = data.get('autoLogin', True)
 | 
			
		||||
    rtype = data.get('type', '')
 | 
			
		||||
    deviceInfo = data.get('deviceInfo', '')
 | 
			
		||||
    user = auth.authenticate(username=username,password=password)
 | 
			
		||||
    if not user:
 | 
			
		||||
        result['error'] = _('帐号或密码错误!请重试,多次重试后将被锁定IP!')
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    user.rid = rid
 | 
			
		||||
    user.uuid = uuid
 | 
			
		||||
    user.autoLogin = autoLogin
 | 
			
		||||
    user.rtype = rtype 
 | 
			
		||||
    user.deviceInfo = json.dumps(deviceInfo)
 | 
			
		||||
    user.save()
 | 
			
		||||
    
 | 
			
		||||
    token = RustDeskToken.objects.filter(Q(uid=user.id) & Q(username=user.username) & Q(rid=user.rid)).first()
 | 
			
		||||
    
 | 
			
		||||
    # Check whether
 | 
			
		||||
    if token:
 | 
			
		||||
        now_t = datetime.datetime.now()
 | 
			
		||||
        nums = (now_t - token.create_time).seconds if now_t > token.create_time else 0
 | 
			
		||||
        if nums >= EFFECTIVE_SECONDS:
 | 
			
		||||
            token.delete()
 | 
			
		||||
            token = None
 | 
			
		||||
    
 | 
			
		||||
    if not token:
 | 
			
		||||
        # Get and save token
 | 
			
		||||
        token = RustDeskToken(
 | 
			
		||||
            username=user.username,
 | 
			
		||||
            uid=user.id,
 | 
			
		||||
            uuid=user.uuid,
 | 
			
		||||
            rid=user.rid,
 | 
			
		||||
            access_token=getStrMd5(str(time.time())+salt)
 | 
			
		||||
        )
 | 
			
		||||
        token.save()
 | 
			
		||||
 | 
			
		||||
    result['access_token'] = token.access_token
 | 
			
		||||
    result['type'] = 'access_token'
 | 
			
		||||
    result['user'] = {'name':user.username}
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def logout(request):
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        result = {'error':_('请求方式错误!')}
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    
 | 
			
		||||
    data = json.loads(request.body.decode())
 | 
			
		||||
    rid = data.get('id', '')
 | 
			
		||||
    uuid = data.get('uuid', '')
 | 
			
		||||
    user = UserProfile.objects.filter(Q(rid=rid) & Q(uuid=uuid)).first()
 | 
			
		||||
    if not user:
 | 
			
		||||
        result = {'error':_('异常请求!')}
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    token = RustDeskToken.objects.filter(Q(uid=user.id) & Q(rid=user.rid)).first()
 | 
			
		||||
    if token:
 | 
			
		||||
        token.delete()
 | 
			
		||||
 | 
			
		||||
    result = {'code':1}
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def currentUser(request):
 | 
			
		||||
    result = {}
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        result['error'] = _('错误的提交方式!')
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    postdata = json.loads(request.body)
 | 
			
		||||
    rid = postdata.get('id', '')
 | 
			
		||||
    uuid = postdata.get('uuid', '')
 | 
			
		||||
    
 | 
			
		||||
    access_token = request.META.get('HTTP_AUTHORIZATION', '')
 | 
			
		||||
    access_token = access_token.split('Bearer ')[-1]
 | 
			
		||||
    token = RustDeskToken.objects.filter(Q(access_token=access_token) ).first()
 | 
			
		||||
    user = None
 | 
			
		||||
    if token:
 | 
			
		||||
        user = UserProfile.objects.filter(Q(id=token.uid)).first()
 | 
			
		||||
    
 | 
			
		||||
    if user:
 | 
			
		||||
        if token:
 | 
			
		||||
            result['access_token'] = token.access_token
 | 
			
		||||
        result['type'] = 'access_token'
 | 
			
		||||
        result['name'] = user.username
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ab(request):
 | 
			
		||||
    '''
 | 
			
		||||
    '''
 | 
			
		||||
    access_token = request.META.get('HTTP_AUTHORIZATION', '')
 | 
			
		||||
    access_token = access_token.split('Bearer ')[-1]
 | 
			
		||||
    token = RustDeskToken.objects.filter(Q(access_token=access_token) ).first()
 | 
			
		||||
    if not token:
 | 
			
		||||
        result = {'error':_('拉取列表错误!')}
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        result = {}
 | 
			
		||||
        uid = token.uid
 | 
			
		||||
        tags = RustDeskTag.objects.filter(Q(uid=uid) )
 | 
			
		||||
        tag_names = []
 | 
			
		||||
        tag_colors = {}
 | 
			
		||||
        if tags:
 | 
			
		||||
            tag_names = [str(x.tag_name) for x in tags]
 | 
			
		||||
            tag_colors = {str(x.tag_name):int(x.tag_color) for x in tags if x.tag_color!=''}
 | 
			
		||||
        
 | 
			
		||||
        peers_result = []
 | 
			
		||||
        peers = RustDeskPeer.objects.filter(Q(uid=uid) )
 | 
			
		||||
        if peers:
 | 
			
		||||
            for peer in peers:
 | 
			
		||||
                tmp = {
 | 
			
		||||
                    'id':peer.rid,
 | 
			
		||||
                    'username':peer.username,
 | 
			
		||||
                    'hostname':peer.hostname,
 | 
			
		||||
                    'alias':peer.alias,
 | 
			
		||||
                    'platform':peer.platform,
 | 
			
		||||
                    'tags':peer.tags.split(','),
 | 
			
		||||
                    'hash':peer.rhash,
 | 
			
		||||
                }
 | 
			
		||||
                peers_result.append(tmp)
 | 
			
		||||
        
 | 
			
		||||
        result['updated_at'] = datetime.datetime.now()
 | 
			
		||||
        result['data'] = {
 | 
			
		||||
            'tags':tag_names,
 | 
			
		||||
            'peers':peers_result,
 | 
			
		||||
            'tag_colors':json.dumps(tag_colors)
 | 
			
		||||
        }
 | 
			
		||||
        result['data'] = json.dumps(result['data'])
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    else:
 | 
			
		||||
        postdata = json.loads(request.body.decode())
 | 
			
		||||
        data = postdata.get('data', '')
 | 
			
		||||
        data = {} if data=='' else json.loads(data)
 | 
			
		||||
        tagnames = data.get('tags', [])
 | 
			
		||||
        tag_colors = data.get('tag_colors', '')
 | 
			
		||||
        tag_colors = {} if tag_colors=='' else json.loads(tag_colors)
 | 
			
		||||
        peers = data.get('peers', [])
 | 
			
		||||
        
 | 
			
		||||
        if tagnames:
 | 
			
		||||
            # Delete the old tag
 | 
			
		||||
            RustDeskTag.objects.filter(uid=token.uid).delete()
 | 
			
		||||
            # Increase
 | 
			
		||||
            newlist = []
 | 
			
		||||
            for name in tagnames:
 | 
			
		||||
                tag = RustDeskTag(
 | 
			
		||||
                    uid=token.uid,
 | 
			
		||||
                    tag_name=name,
 | 
			
		||||
                    tag_color=tag_colors.get(name, '')
 | 
			
		||||
                )
 | 
			
		||||
                newlist.append(tag)
 | 
			
		||||
            RustDeskTag.objects.bulk_create(newlist)
 | 
			
		||||
        if peers:
 | 
			
		||||
            RustDeskPeer.objects.filter(uid=token.uid).delete()
 | 
			
		||||
            newlist = []
 | 
			
		||||
            for one in peers:
 | 
			
		||||
                peer = RustDeskPeer(
 | 
			
		||||
                    uid=token.uid,
 | 
			
		||||
                    rid=one['id'],
 | 
			
		||||
                    username=one['username'],
 | 
			
		||||
                    hostname=one['hostname'],
 | 
			
		||||
                    alias=one['alias'],
 | 
			
		||||
                    platform=one['platform'],
 | 
			
		||||
                    tags=','.join(one['tags']),
 | 
			
		||||
                    rhash=one['hash'],
 | 
			
		||||
                    
 | 
			
		||||
                    
 | 
			
		||||
                )
 | 
			
		||||
                newlist.append(peer)
 | 
			
		||||
            RustDeskPeer.objects.bulk_create(newlist)
 | 
			
		||||
 | 
			
		||||
    result = {
 | 
			
		||||
    'code':102,
 | 
			
		||||
    'data':_('更新地址簿有误')
 | 
			
		||||
    }
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
def ab_get(request):
 | 
			
		||||
    # 兼容 x86-sciter 版客户端,此版客户端通过访问 "POST /api/ab/get" 来获取地址簿
 | 
			
		||||
    request.method = 'GET'
 | 
			
		||||
    return ab(request)
 | 
			
		||||
 | 
			
		||||
def sysinfo(request):
 | 
			
		||||
    # 客户端注册服务后,才会发送设备信息
 | 
			
		||||
    result = {}
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        result['error'] = _('错误的提交方式!')
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    
 | 
			
		||||
    client_ip = get_client_ip(request)
 | 
			
		||||
    postdata = json.loads(request.body)
 | 
			
		||||
    device = RustDesDevice.objects.filter(Q(rid=postdata['id']) & Q(uuid=postdata['uuid']) ).first()
 | 
			
		||||
    if not device:
 | 
			
		||||
        device = RustDesDevice(
 | 
			
		||||
            rid=postdata['id'],
 | 
			
		||||
            cpu=postdata['cpu'],
 | 
			
		||||
            hostname=postdata['hostname'],
 | 
			
		||||
            memory=postdata['memory'],
 | 
			
		||||
            os=postdata['os'],
 | 
			
		||||
            username=postdata.get('username', '-'),
 | 
			
		||||
            uuid=postdata['uuid'],
 | 
			
		||||
            version=postdata['version'],
 | 
			
		||||
            ip=client_ip,
 | 
			
		||||
        )
 | 
			
		||||
        device.save()
 | 
			
		||||
    else:
 | 
			
		||||
        postdata2 = copy.copy(postdata)
 | 
			
		||||
        postdata2['rid'] = postdata2['id']
 | 
			
		||||
        postdata2.pop('id')
 | 
			
		||||
        postdata2['ip'] = client_ip
 | 
			
		||||
        RustDesDevice.objects.filter(Q(rid=postdata['id']) & Q(uuid=postdata['uuid']) ).update(**postdata2)
 | 
			
		||||
    result['data'] = 'ok'
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
def heartbeat(request):
 | 
			
		||||
    postdata = json.loads(request.body)
 | 
			
		||||
    device = RustDesDevice.objects.filter(Q(rid=postdata['id']) & Q(uuid=postdata['uuid']) ).first()
 | 
			
		||||
    if device:
 | 
			
		||||
        device.save()
 | 
			
		||||
    # token保活
 | 
			
		||||
    create_time = datetime.datetime.now() + datetime.timedelta(seconds=EFFECTIVE_SECONDS)
 | 
			
		||||
    RustDeskToken.objects.filter(Q(rid=postdata['id']) & Q(uuid=postdata['uuid']) ).update(create_time=create_time)
 | 
			
		||||
    result = {}
 | 
			
		||||
    result['data'] = _('在线')
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
def get_client_ip(request):
 | 
			
		||||
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
 | 
			
		||||
    if x_forwarded_for:
 | 
			
		||||
        ip = x_forwarded_for.split(',')[0]
 | 
			
		||||
    else:
 | 
			
		||||
        ip = request.META.get('REMOTE_ADDR')
 | 
			
		||||
    return ip
 | 
			
		||||
 | 
			
		||||
def convert_filesize(size_bytes):
 | 
			
		||||
    if size_bytes == 0:
 | 
			
		||||
        return "0B"
 | 
			
		||||
    size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
 | 
			
		||||
    i = int(math.floor(math.log(size_bytes, 1024)))
 | 
			
		||||
    p = math.pow(1024, i)
 | 
			
		||||
    s = round(size_bytes / p, 2)
 | 
			
		||||
    return "%s %s" % (s, size_name[i])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def audit(request):
 | 
			
		||||
    postdata = json.loads(request.body)
 | 
			
		||||
    #print(postdata)
 | 
			
		||||
    audit_type = postdata['action'] if 'action' in postdata else ''
 | 
			
		||||
    if audit_type == 'new':
 | 
			
		||||
        new_conn_log = ConnLog(
 | 
			
		||||
            action=postdata['action'] if 'action' in postdata else '',
 | 
			
		||||
            conn_id=postdata['conn_id'] if 'conn_id' in postdata else 0,
 | 
			
		||||
            from_ip=postdata['ip'] if 'ip' in postdata else '',
 | 
			
		||||
            from_id='',
 | 
			
		||||
            rid=postdata['id'] if 'id' in postdata else '',
 | 
			
		||||
            conn_start=datetime.datetime.now(),
 | 
			
		||||
            session_id=postdata['session_id'] if 'session_id' in postdata else 0,
 | 
			
		||||
            uuid=postdata['uuid'] if 'uuid' in postdata else '',
 | 
			
		||||
        )
 | 
			
		||||
        new_conn_log.save()
 | 
			
		||||
    elif audit_type =="close":
 | 
			
		||||
        ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(conn_end=datetime.datetime.now())
 | 
			
		||||
    elif 'is_file' in postdata:
 | 
			
		||||
        print(postdata)
 | 
			
		||||
        files = json.loads(postdata['info'])['files']
 | 
			
		||||
        filesize = convert_filesize(int(files[0][1]))
 | 
			
		||||
        new_file_log = FileLog(
 | 
			
		||||
            file=postdata['path'],
 | 
			
		||||
            user_id=postdata['peer_id'],
 | 
			
		||||
            user_ip=json.loads(postdata['info'])['ip'],
 | 
			
		||||
            remote_id=postdata['id'],
 | 
			
		||||
            filesize=filesize,
 | 
			
		||||
            direction=postdata['type'],
 | 
			
		||||
            logged_at=datetime.datetime.now(),
 | 
			
		||||
        )
 | 
			
		||||
        new_file_log.save()
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            peer = postdata['peer']
 | 
			
		||||
            ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(session_id=postdata['session_id'])
 | 
			
		||||
            ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(from_id=peer[0])
 | 
			
		||||
        except:
 | 
			
		||||
            print(postdata)
 | 
			
		||||
 | 
			
		||||
    result = {
 | 
			
		||||
    'code':1,
 | 
			
		||||
    'data':'ok'
 | 
			
		||||
    }
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
    
 | 
			
		||||
def users(request):
 | 
			
		||||
    result = {
 | 
			
		||||
    'code':1,
 | 
			
		||||
    'data':_('好的')
 | 
			
		||||
    }
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
    
 | 
			
		||||
def peers(request):
 | 
			
		||||
    result = {
 | 
			
		||||
    'code':1,
 | 
			
		||||
    'data':'ok'
 | 
			
		||||
    }
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
							
								
								
									
										719
									
								
								api/views_front.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										719
									
								
								api/views_front.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,719 @@
 | 
			
		||||
# cython:language_level=3
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.http import HttpResponseRedirect
 | 
			
		||||
from django.contrib.auth.hashers import make_password
 | 
			
		||||
from django.http import JsonResponse
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.contrib import auth
 | 
			
		||||
from api.models import RustDeskPeer, RustDesDevice, UserProfile, ShareLink, ConnLog, FileLog
 | 
			
		||||
from django.forms.models import model_to_dict
 | 
			
		||||
from django.core.paginator import Paginator
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from itertools import chain
 | 
			
		||||
from django.db.models.fields import DateTimeField, DateField, CharField, TextField
 | 
			
		||||
import datetime
 | 
			
		||||
from django.db.models import Model
 | 
			
		||||
import json
 | 
			
		||||
import time
 | 
			
		||||
import hashlib
 | 
			
		||||
import sys
 | 
			
		||||
from dateutil import tz
 | 
			
		||||
import platform
 | 
			
		||||
import psutil
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
import xlwt
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from .forms import AddPeerForm, EditPeerForm, AssignPeerForm
 | 
			
		||||
 | 
			
		||||
BASE_DIR = Path(__file__).resolve().parent.parent
 | 
			
		||||
salt = 'xiaomo'
 | 
			
		||||
EFFECTIVE_SECONDS = 7200
 | 
			
		||||
 | 
			
		||||
def getStrMd5(s):
 | 
			
		||||
    if not isinstance(s, (str,)):
 | 
			
		||||
        s = str(s)
 | 
			
		||||
 | 
			
		||||
    myHash = hashlib.md5()
 | 
			
		||||
    myHash.update(s.encode())
 | 
			
		||||
 | 
			
		||||
    return myHash.hexdigest()
 | 
			
		||||
 | 
			
		||||
def model_to_dict2(instance, fields=None, exclude=None, replace=None, default=None):
 | 
			
		||||
    """
 | 
			
		||||
    :params instance: Model object, not the QuerySet data set
 | 
			
		||||
    :params fields: Specify the field data to be displayed,('Field 1','Field 2')
 | 
			
		||||
    :params exclude: Specify the field data that is eliminated,('Field 1','Field 2')
 | 
			
		||||
    :params replace: Modify the field name to the required name,{'Database field name':'Front -end display name'}
 | 
			
		||||
    :params default: Added no existing field data,{'Field':'data'}
 | 
			
		||||
    """
 | 
			
		||||
    # 对传递进来的模型对象校验
 | 
			
		||||
    if not isinstance(instance, Model):
 | 
			
		||||
        raise Exception(_('model_to_dict接收的参数必须是模型对象'))
 | 
			
		||||
    # 对替换数据库字段名字校验
 | 
			
		||||
    if replace and type(replace) == dict:
 | 
			
		||||
        for replace_field in replace.values():
 | 
			
		||||
            if hasattr(instance, replace_field):
 | 
			
		||||
                raise Exception(_(f'model_to_dict,要替换成{replace_field}字段已经存在了'))
 | 
			
		||||
    # 对要新增的默认值进行校验
 | 
			
		||||
    if default and type(default) == dict:
 | 
			
		||||
        for default_key in default.keys():
 | 
			
		||||
            if hasattr(instance, default_key):
 | 
			
		||||
                raise Exception(_(f'model_to_dict,要新增默认值,但字段{default_key}已经存在了'))
 | 
			
		||||
    opts = instance._meta
 | 
			
		||||
    data = {}
 | 
			
		||||
    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
 | 
			
		||||
        # 源码下:这块代码会将时间字段剔除掉,我加上一层判断,让其不再剔除时间字段
 | 
			
		||||
        if not getattr(f, 'editable', False):
 | 
			
		||||
            if type(f) == DateField or type(f) == DateTimeField:
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                continue
 | 
			
		||||
        # 如果fields参数传递了,要进行判断
 | 
			
		||||
        if fields is not None and f.name not in fields:
 | 
			
		||||
            continue
 | 
			
		||||
        # 如果exclude 传递了,要进行判断
 | 
			
		||||
        if exclude and f.name in exclude:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        key = f.name
 | 
			
		||||
        # 获取字段对应的数据
 | 
			
		||||
        if type(f) == DateTimeField:
 | 
			
		||||
            # 字段类型是,DateTimeFiled 使用自己的方式操作
 | 
			
		||||
            value = getattr(instance, key)
 | 
			
		||||
            value = datetime.datetime.strftime(value, '%Y-%m-%d %H:%M')
 | 
			
		||||
        elif type(f) == DateField:
 | 
			
		||||
            # 字段类型是,DateFiled 使用自己的方式操作
 | 
			
		||||
            value = getattr(instance, key)
 | 
			
		||||
            value = datetime.datetime.strftime(value, '%Y-%m-%d')
 | 
			
		||||
        elif type(f) == CharField or type(f) == TextField:
 | 
			
		||||
            # 字符串数据是否可以进行序列化,转成python结构数据
 | 
			
		||||
            value = getattr(instance, key)
 | 
			
		||||
            try:
 | 
			
		||||
                value = json.loads(value)
 | 
			
		||||
            except Exception as _:
 | 
			
		||||
                value = value
 | 
			
		||||
        else:#其他类型的字段
 | 
			
		||||
            # value = getattr(instance, key)
 | 
			
		||||
            key = f.name
 | 
			
		||||
            value = f.value_from_object(instance)
 | 
			
		||||
            # data[f.name] = f.value_from_object(instance)
 | 
			
		||||
        # 1、替换字段名字
 | 
			
		||||
        if replace and key in replace.keys():
 | 
			
		||||
            key = replace.get(key)
 | 
			
		||||
        data[key] = value
 | 
			
		||||
    #2、新增默认的字段数据
 | 
			
		||||
    if default:
 | 
			
		||||
        data.update(default)
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def index(request):
 | 
			
		||||
    print('sdf',sys.argv)
 | 
			
		||||
    if request.user and request.user.username!='AnonymousUser':
 | 
			
		||||
        return HttpResponseRedirect('/api/work')
 | 
			
		||||
    return HttpResponseRedirect('/api/user_action?action=login')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def user_action(request):
 | 
			
		||||
    action = request.GET.get('action', '')
 | 
			
		||||
    if action == 'login':
 | 
			
		||||
        return user_login(request)
 | 
			
		||||
    elif action == 'register':
 | 
			
		||||
        return user_register(request)
 | 
			
		||||
    elif action == 'logout':
 | 
			
		||||
        return user_logout(request)
 | 
			
		||||
    else:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
def user_login(request):
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        return render(request, 'login.html')
 | 
			
		||||
 | 
			
		||||
    username = request.POST.get('account', '')
 | 
			
		||||
    password = request.POST.get('password', '')
 | 
			
		||||
    if not username or not password:
 | 
			
		||||
        return JsonResponse({'code':0, 'msg':_('出了点问题,未获取用户名或密码。')})
 | 
			
		||||
 | 
			
		||||
    user = auth.authenticate(username=username,password=password)
 | 
			
		||||
    if user:
 | 
			
		||||
        auth.login(request, user)
 | 
			
		||||
        return JsonResponse({'code':1, 'url':'/api/work'})
 | 
			
		||||
    else:
 | 
			
		||||
        return JsonResponse({'code':0, 'msg':_('帐号或密码错误!')})
 | 
			
		||||
 | 
			
		||||
def user_register(request):
 | 
			
		||||
    info = ''
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        return render(request, 'reg.html')
 | 
			
		||||
    ALLOW_REGISTRATION = settings.ALLOW_REGISTRATION
 | 
			
		||||
    result = {
 | 
			
		||||
        'code':0,
 | 
			
		||||
        'msg':''
 | 
			
		||||
    }
 | 
			
		||||
    if not ALLOW_REGISTRATION:
 | 
			
		||||
        result['msg'] = _('当前未开放注册,请联系管理员!')
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
    username = request.POST.get('user', '')
 | 
			
		||||
    password1 = request.POST.get('pwd', '')
 | 
			
		||||
 | 
			
		||||
    if len(username) <= 3:
 | 
			
		||||
        info = _('用户名不得小于3位')
 | 
			
		||||
        result['msg'] = info
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
    if len(password1)<8 or len(password1)>20:
 | 
			
		||||
        info = _('密码长度不符合要求, 应在8~20位。')
 | 
			
		||||
        result['msg'] = info
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
    user = UserProfile.objects.filter(Q(username=username)).first()
 | 
			
		||||
    if user:
 | 
			
		||||
        info = _('用户名已存在。')
 | 
			
		||||
        result['msg'] = info
 | 
			
		||||
        return JsonResponse(result)
 | 
			
		||||
    user = UserProfile(
 | 
			
		||||
        username=username,
 | 
			
		||||
        password=make_password(password1),
 | 
			
		||||
        is_admin = True if UserProfile.objects.count()==0 else False,
 | 
			
		||||
        is_superuser = True if UserProfile.objects.count()==0 else False,
 | 
			
		||||
        is_active = True
 | 
			
		||||
    )
 | 
			
		||||
    user.save()
 | 
			
		||||
    result['msg'] = info
 | 
			
		||||
    result['code'] = 1
 | 
			
		||||
    return JsonResponse(result)
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def user_logout(request):
 | 
			
		||||
    info = ''
 | 
			
		||||
    auth.logout(request)
 | 
			
		||||
    return HttpResponseRedirect('/api/user_action?action=login')
 | 
			
		||||
        
 | 
			
		||||
def get_single_info(uid):
 | 
			
		||||
    online_count = 0
 | 
			
		||||
    peers = RustDeskPeer.objects.filter(Q(uid=uid))
 | 
			
		||||
    rids = [x.rid for x in peers]
 | 
			
		||||
    peers = {x.rid:model_to_dict(x) for x in peers}
 | 
			
		||||
    #print(peers)
 | 
			
		||||
    devices = RustDesDevice.objects.filter(rid__in=rids)
 | 
			
		||||
    devices = {x.rid:x for x in devices}
 | 
			
		||||
 | 
			
		||||
    for rid in peers.keys():
 | 
			
		||||
        peers[rid]['has_rhash'] = _('yes') if len(peers[rid]['rhash'])>1 else _('no')
 | 
			
		||||
        peers[rid]['status'] = _('X')
 | 
			
		||||
 | 
			
		||||
    now = datetime.datetime.now()
 | 
			
		||||
    for rid, device in devices.items():
 | 
			
		||||
        peers[rid]['create_time'] = device.create_time.strftime('%Y-%m-%d')
 | 
			
		||||
        peers[rid]['update_time'] = device.update_time.strftime('%Y-%m-%d %H:%M')
 | 
			
		||||
        peers[rid]['version'] = device.version
 | 
			
		||||
        peers[rid]['memory'] = device.memory
 | 
			
		||||
        peers[rid]['cpu'] = device.cpu
 | 
			
		||||
        peers[rid]['os'] = device.os
 | 
			
		||||
        peers[rid]['ip'] = device.ip
 | 
			
		||||
        if (now-device.update_time).seconds <=120:
 | 
			
		||||
            peers[rid]['status'] = _('Online')
 | 
			
		||||
            online_count += 1
 | 
			
		||||
        else:
 | 
			
		||||
            peers[rid]['status'] = _('X')
 | 
			
		||||
 | 
			
		||||
    sorted_peers = sorted(peers.items(), key=custom_sort, reverse=True)
 | 
			
		||||
    new_ordered_dict = {}
 | 
			
		||||
    for key, peer in sorted_peers:
 | 
			
		||||
        new_ordered_dict[key] = peer
 | 
			
		||||
 | 
			
		||||
    #return ([v for k,v in peers.items()], online_count)
 | 
			
		||||
    return ([v for k,v in new_ordered_dict.items()], online_count)
 | 
			
		||||
 | 
			
		||||
def get_all_info():
 | 
			
		||||
    online_count = 0
 | 
			
		||||
    devices = RustDesDevice.objects.all()
 | 
			
		||||
    peers = RustDeskPeer.objects.all()
 | 
			
		||||
    devices = {x.rid:model_to_dict2(x) for x in devices}
 | 
			
		||||
    now = datetime.datetime.now()
 | 
			
		||||
    for peer in peers:
 | 
			
		||||
        user = UserProfile.objects.filter(Q(id=peer.uid)).first()
 | 
			
		||||
        device = devices.get(peer.rid, None)
 | 
			
		||||
        if device:
 | 
			
		||||
            devices[peer.rid]['rust_user'] = user.username
 | 
			
		||||
    
 | 
			
		||||
    for k, v in devices.items():
 | 
			
		||||
        if (now-datetime.datetime.strptime(v['update_time'], '%Y-%m-%d %H:%M')).seconds <=120:
 | 
			
		||||
            devices[k]['status'] = _('Online')
 | 
			
		||||
            online_count += 1
 | 
			
		||||
        else: 
 | 
			
		||||
           devices[k]['status'] = _('X')
 | 
			
		||||
 | 
			
		||||
    sorted_devices = sorted(devices.items(), key=custom_sort, reverse=True)
 | 
			
		||||
    new_ordered_dict = {}
 | 
			
		||||
    for key, device in sorted_devices:
 | 
			
		||||
        new_ordered_dict[key] = device
 | 
			
		||||
    return ([v for k,v in new_ordered_dict.items()], online_count)
 | 
			
		||||
 | 
			
		||||
def custom_sort(item):
 | 
			
		||||
    status = item[1]['status']
 | 
			
		||||
    if status == 'Online':
 | 
			
		||||
        return 1
 | 
			
		||||
    else:
 | 
			
		||||
        return 0
 | 
			
		||||
    
 | 
			
		||||
def get_conn_log():
 | 
			
		||||
    logs = ConnLog.objects.all()
 | 
			
		||||
    logs = {x.id:model_to_dict(x) for x in logs}
 | 
			
		||||
    
 | 
			
		||||
    for k, v in logs.items():
 | 
			
		||||
        try:
 | 
			
		||||
            peer = RustDeskPeer.objects.get(rid=v['rid'])
 | 
			
		||||
            logs[k]['alias'] = peer.alias
 | 
			
		||||
        except:
 | 
			
		||||
            logs[k]['alias'] = 'UNKNOWN'
 | 
			
		||||
        try:
 | 
			
		||||
            peer = RustDeskPeer.objects.get(rid=v['from_id'])
 | 
			
		||||
            logs[k]['from_alias'] = peer.alias
 | 
			
		||||
        except:
 | 
			
		||||
            logs[k]['from_alias'] = 'UNKNOWN'
 | 
			
		||||
        #from_zone = tz.tzutc()
 | 
			
		||||
        #to_zone = tz.tzlocal()
 | 
			
		||||
        #utc = logs[k]['logged_at']
 | 
			
		||||
        #utc = utc.replace(tzinfo=from_zone)
 | 
			
		||||
        #logs[k]['logged_at'] = utc.astimezone(to_zone)
 | 
			
		||||
        try:
 | 
			
		||||
            duration = round((logs[k]['conn_end'] - logs[k]['conn_start']).total_seconds())
 | 
			
		||||
            m, s = divmod(duration, 60)
 | 
			
		||||
            h, m = divmod(m, 60)
 | 
			
		||||
            #d, h = divmod(h, 24)
 | 
			
		||||
            logs[k]['duration'] = f'{h:02d}:{m:02d}:{s:02d}'
 | 
			
		||||
        except:
 | 
			
		||||
            logs[k]['duration'] = -1
 | 
			
		||||
 | 
			
		||||
    sorted_logs = sorted(logs.items(), key=lambda x: x[1]['conn_start'], reverse=True)
 | 
			
		||||
    new_ordered_dict = {}
 | 
			
		||||
    for key, alog in sorted_logs:
 | 
			
		||||
        new_ordered_dict[key] = alog
 | 
			
		||||
 | 
			
		||||
    return [v for k, v in new_ordered_dict.items()]
 | 
			
		||||
 | 
			
		||||
def get_file_log():
 | 
			
		||||
    logs = FileLog.objects.all()
 | 
			
		||||
    logs = {x.id:model_to_dict(x) for x in logs}
 | 
			
		||||
 | 
			
		||||
    for k, v in logs.items():
 | 
			
		||||
        try:
 | 
			
		||||
            peer_remote = RustDeskPeer.objects.get(rid=v['remote_id'])
 | 
			
		||||
            logs[k]['remote_alias'] = peer_remote.alias
 | 
			
		||||
        except:
 | 
			
		||||
            logs[k]['remote_alias'] = 'UNKNOWN'
 | 
			
		||||
        try:
 | 
			
		||||
            peer_user = RustDeskPeer.objects.get(rid=v['user_id'])
 | 
			
		||||
            logs[k]['user_alias'] = peer_user.alias
 | 
			
		||||
        except:
 | 
			
		||||
            logs[k]['user_alias'] = 'UNKNOWN'
 | 
			
		||||
 | 
			
		||||
    sorted_logs = sorted(logs.items(), key=lambda x: x[1]['logged_at'], reverse=True)
 | 
			
		||||
    new_ordered_dict = {}
 | 
			
		||||
    for key, alog in sorted_logs:
 | 
			
		||||
        new_ordered_dict[key] = alog
 | 
			
		||||
 | 
			
		||||
    return [v for k, v in new_ordered_dict.items()]
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def sys_info(request):
 | 
			
		||||
    hostname = platform.node()
 | 
			
		||||
    cpu_usage = psutil.cpu_percent()
 | 
			
		||||
    memory_usage = psutil.virtual_memory().percent
 | 
			
		||||
    disk_usage = psutil.disk_usage('/').percent
 | 
			
		||||
    print(cpu_usage, memory_usage, disk_usage)
 | 
			
		||||
    return render(request, 'show_sys_info.html', {'hostname':hostname, 'cpu_usage':cpu_usage, 'memory_usage':memory_usage, 'disk_usage':disk_usage, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def clients(request):
 | 
			
		||||
    basedir = os.path.join('clients')
 | 
			
		||||
    androidaarch64 = os.path.join(basedir,'android','aarch64')
 | 
			
		||||
    androidarmv7 = os.path.join(basedir,'android','armv7')
 | 
			
		||||
    linuxaarch64 = os.path.join(basedir,'linux','aarch64')
 | 
			
		||||
    linuxx86_64 = os.path.join(basedir,'linux','x86_64')
 | 
			
		||||
    mocos = os.path.join(basedir,'macOS')
 | 
			
		||||
    sciter = os.path.join(basedir,'sciter')
 | 
			
		||||
    custom = os.path.join(basedir,'custom')
 | 
			
		||||
    client_files = {}
 | 
			
		||||
    client_custom_files = {}
 | 
			
		||||
    if os.path.exists(basedir):
 | 
			
		||||
        for file in os.listdir(basedir):
 | 
			
		||||
            if (file.endswith(".exe") or file.endswith(".msi")):
 | 
			
		||||
                filepath = os.path.join(basedir,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': basedir
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(androidaarch64):
 | 
			
		||||
        for file in os.listdir(androidaarch64):
 | 
			
		||||
            if file.endswith(".apk"):
 | 
			
		||||
                filepath = os.path.join(androidaarch64,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': androidaarch64
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(androidarmv7):
 | 
			
		||||
        for file in os.listdir(androidarmv7):
 | 
			
		||||
            if file.endswith(".apk"):
 | 
			
		||||
                filepath = os.path.join(androidarmv7,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': androidarmv7
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(linuxaarch64):
 | 
			
		||||
        for file in os.listdir(linuxaarch64):
 | 
			
		||||
            if (file.endswith(".rpm") or file.endswith(".deb")):
 | 
			
		||||
                filepath = os.path.join(linuxaarch64,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': linuxaarch64
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(linuxx86_64):
 | 
			
		||||
        for file in os.listdir(linuxx86_64):
 | 
			
		||||
            if (file.endswith(".rpm") or file.endswith(".deb")):
 | 
			
		||||
                filepath = os.path.join(linuxx86_64,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': linuxx86_64
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(mocos):
 | 
			
		||||
        for file in os.listdir(mocos):
 | 
			
		||||
            if file.endswith(".dmg"):
 | 
			
		||||
                filepath = os.path.join(mocos,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': mocos
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(sciter):
 | 
			
		||||
        for file in os.listdir(sciter):
 | 
			
		||||
            if file.endswith(".exe"):
 | 
			
		||||
                filepath = os.path.join(sciter,file)
 | 
			
		||||
                modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
                client_files[file] = {
 | 
			
		||||
                    'file': file,
 | 
			
		||||
                    'modified': modified,
 | 
			
		||||
                    'path': sciter
 | 
			
		||||
                }
 | 
			
		||||
    if os.path.exists(custom):
 | 
			
		||||
        for file in os.listdir(custom):
 | 
			
		||||
            #if file.endswith(".exe"):
 | 
			
		||||
            filepath = os.path.join(custom,file)
 | 
			
		||||
            modified = datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %I:%M:%S %p')
 | 
			
		||||
            client_custom_files[file] = {
 | 
			
		||||
                'file': file,
 | 
			
		||||
                'modified': modified,
 | 
			
		||||
                'path': custom
 | 
			
		||||
            }
 | 
			
		||||
    return render(request, 'clients.html', {'client_files': client_files, 'client_custom_files': client_custom_files, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
def download_file(request, filename, path):
 | 
			
		||||
    file_path = os.path.join(str(BASE_DIR),path,filename)
 | 
			
		||||
    with open(file_path, 'rb') as file:
 | 
			
		||||
        response = HttpResponse(file, headers={
 | 
			
		||||
            'Content-Type': 'application/x-binary',
 | 
			
		||||
            'Content-Disposition': f'attachment; filename="{filename}"'
 | 
			
		||||
        })
 | 
			
		||||
    return response
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def download(request):
 | 
			
		||||
    filename = request.GET['filename']
 | 
			
		||||
    path = request.GET['path']
 | 
			
		||||
    return download_file(request, filename, path)
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_cation?action=login')
 | 
			
		||||
def delete_file(request):
 | 
			
		||||
    filename = request.GET['filename']
 | 
			
		||||
    path = request.GET['path']
 | 
			
		||||
    file_path = os.path.join(str(BASE_DIR),path,filename)
 | 
			
		||||
    if os.path.isfile(file_path):
 | 
			
		||||
        os.remove(file_path)
 | 
			
		||||
    return HttpResponseRedirect('/api/clients')
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def add_peer(request):
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        form = AddPeerForm(request.POST)
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            rid = form.cleaned_data['clientID']
 | 
			
		||||
            uid = request.user.id
 | 
			
		||||
            username = form.cleaned_data['username']
 | 
			
		||||
            hostname = form.cleaned_data['hostname']
 | 
			
		||||
            plat = form.cleaned_data['platform']
 | 
			
		||||
            alias = form.cleaned_data['alias']
 | 
			
		||||
            tags = form.cleaned_data['tags']
 | 
			
		||||
            ip = form.cleaned_data['ip']
 | 
			
		||||
 | 
			
		||||
            peer = RustDeskPeer(
 | 
			
		||||
                uid = uid,
 | 
			
		||||
                rid = rid,
 | 
			
		||||
                username = username,
 | 
			
		||||
                hostname = hostname,
 | 
			
		||||
                platform = plat,
 | 
			
		||||
                alias = alias,
 | 
			
		||||
                tags = tags,
 | 
			
		||||
                ip = ip
 | 
			
		||||
            )
 | 
			
		||||
            peer.save()
 | 
			
		||||
            return HttpResponseRedirect('/api/work')
 | 
			
		||||
    else:
 | 
			
		||||
        rid = request.GET.get('rid','')
 | 
			
		||||
        form = AddPeerForm()
 | 
			
		||||
    return render(request, 'add_peer.html', {'form': form, 'rid': rid, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def edit_peer(request):
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        form = EditPeerForm(request.POST)
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            rid = form.cleaned_data['clientID']
 | 
			
		||||
            uid = request.user.id
 | 
			
		||||
            username = form.cleaned_data['username']
 | 
			
		||||
            hostname = form.cleaned_data['hostname']
 | 
			
		||||
            plat = form.cleaned_data['platform']
 | 
			
		||||
            alias = form.cleaned_data['alias']
 | 
			
		||||
            tags = form.cleaned_data['tags']
 | 
			
		||||
 | 
			
		||||
            updated_peer = RustDeskPeer.objects.get(rid=rid,uid=uid)
 | 
			
		||||
            updated_peer.username=username
 | 
			
		||||
            updated_peer.hostname=hostname
 | 
			
		||||
            updated_peer.platform=plat
 | 
			
		||||
            updated_peer.alias=alias
 | 
			
		||||
            updated_peer.tags=tags
 | 
			
		||||
            updated_peer.save()
 | 
			
		||||
 | 
			
		||||
            return HttpResponseRedirect('/api/work')
 | 
			
		||||
        else:
 | 
			
		||||
            print(form.errors)
 | 
			
		||||
    else:
 | 
			
		||||
        rid = request.GET.get('rid','')
 | 
			
		||||
        peer = RustDeskPeer.objects.get(rid=rid)
 | 
			
		||||
        initial_data = {
 | 
			
		||||
            'clientID': rid,
 | 
			
		||||
            'alias': peer.alias,
 | 
			
		||||
            'tags': peer.tags,
 | 
			
		||||
            'username': peer.username,
 | 
			
		||||
            'hostname': peer.hostname,
 | 
			
		||||
            'platform': peer.platform,
 | 
			
		||||
            'ip': peer.ip
 | 
			
		||||
        }
 | 
			
		||||
        form = EditPeerForm(initial=initial_data)
 | 
			
		||||
        return render(request, 'edit_peer.html', {'form': form, 'peer': peer, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
    
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def assign_peer(request):
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        form = AssignPeerForm(request.POST)
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            rid = form.cleaned_data['clientID']
 | 
			
		||||
            uid = form.cleaned_data['uid']
 | 
			
		||||
            username = form.cleaned_data['username']
 | 
			
		||||
            hostname = form.cleaned_data['hostname']
 | 
			
		||||
            plat = form.cleaned_data['platform']
 | 
			
		||||
            alias = form.cleaned_data['alias']
 | 
			
		||||
            tags = form.cleaned_data['tags']
 | 
			
		||||
            ip = form.cleaned_data['ip']
 | 
			
		||||
 | 
			
		||||
            peer = RustDeskPeer(
 | 
			
		||||
                uid = uid.id,
 | 
			
		||||
                rid = rid,
 | 
			
		||||
                username = username,
 | 
			
		||||
                hostname = hostname,
 | 
			
		||||
                platform = plat,
 | 
			
		||||
                alias = alias,
 | 
			
		||||
                tags = tags,
 | 
			
		||||
                ip = ip
 | 
			
		||||
            )
 | 
			
		||||
            peer.save()
 | 
			
		||||
            return HttpResponseRedirect('/api/work')
 | 
			
		||||
        else:
 | 
			
		||||
            print(form.errors)
 | 
			
		||||
    else:
 | 
			
		||||
        rid = request.GET.get('rid')
 | 
			
		||||
        form = AssignPeerForm()
 | 
			
		||||
        #get list of users from the database
 | 
			
		||||
        return render(request, 'assign_peer.html', {'form':form, 'rid': rid})
 | 
			
		||||
    
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def delete_peer(request):
 | 
			
		||||
    rid = request.GET.get('rid')
 | 
			
		||||
    peer = RustDeskPeer.objects.filter(Q(uid=request.user.id) & Q(rid=rid))
 | 
			
		||||
    peer.delete()
 | 
			
		||||
    return HttpResponseRedirect('/api/work')
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def conn_log(request):
 | 
			
		||||
    paginator = Paginator(get_conn_log(), 20)
 | 
			
		||||
    page_number = request.GET.get('page')
 | 
			
		||||
    page_obj = paginator.get_page(page_number)
 | 
			
		||||
    return render(request, 'show_conn_log.html', {'page_obj':page_obj, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def file_log(request):
 | 
			
		||||
    paginator = Paginator(get_file_log(), 20)
 | 
			
		||||
    page_number = request.GET.get('page')
 | 
			
		||||
    page_obj = paginator.get_page(page_number)
 | 
			
		||||
    return render(request, 'show_file_log.html', {'page_obj':page_obj, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def work(request):
 | 
			
		||||
    username = request.user
 | 
			
		||||
    u = UserProfile.objects.get(username=username)
 | 
			
		||||
    
 | 
			
		||||
    show_type = request.GET.get('show_type', '')
 | 
			
		||||
    show_all = True if show_type == 'admin' and u.is_admin else False
 | 
			
		||||
    all_info, online_count_all = get_all_info()
 | 
			
		||||
    single_info, online_count_single = get_single_info(u.id)
 | 
			
		||||
    paginator = Paginator(all_info, 100) if show_type == 'admin' and u.is_admin else Paginator(single_info, 100)
 | 
			
		||||
    page_number = request.GET.get('page')
 | 
			
		||||
    page_obj = paginator.get_page(page_number)
 | 
			
		||||
    return render(request, 'show_work.html', {'u':u, 'show_all':show_all, 'page_obj':page_obj, 'online_count_single':online_count_single, 'online_count_all':online_count_all, 'phone_or_desktop': is_mobile(request)})
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def down_peers(request):
 | 
			
		||||
    username = request.user
 | 
			
		||||
    u = UserProfile.objects.get(username=username)
 | 
			
		||||
 | 
			
		||||
    if not u.is_admin:
 | 
			
		||||
        print(u.is_admin)
 | 
			
		||||
        return HttpResponseRedirect('/api/work')
 | 
			
		||||
    
 | 
			
		||||
    all_info = get_all_info()
 | 
			
		||||
    f = xlwt.Workbook(encoding='utf-8')
 | 
			
		||||
    sheet1 = f.add_sheet(_(u'设备信息表'), cell_overwrite_ok=True)
 | 
			
		||||
    all_fields = [x.name for x in RustDesDevice._meta.get_fields()]
 | 
			
		||||
    all_fields.append('rust_user')
 | 
			
		||||
    for i, one in enumerate(all_info):
 | 
			
		||||
        for j, name in enumerate(all_fields):
 | 
			
		||||
            if i == 0:
 | 
			
		||||
                # 写入列名
 | 
			
		||||
                sheet1.write(i, j, name)
 | 
			
		||||
            sheet1.write(i+1, j, one.get(name, '-'))
 | 
			
		||||
 | 
			
		||||
    sio = BytesIO()
 | 
			
		||||
    f.save(sio)
 | 
			
		||||
    sio.seek(0)
 | 
			
		||||
    response = HttpResponse(sio.getvalue(), content_type='application/vnd.ms-excel')
 | 
			
		||||
    response['Content-Disposition'] = 'attachment; filename=DeviceInfo.xls'
 | 
			
		||||
    response.write(sio.getvalue())
 | 
			
		||||
    return response
 | 
			
		||||
    
 | 
			
		||||
def check_sharelink_expired(sharelink):
 | 
			
		||||
    now = datetime.datetime.now()
 | 
			
		||||
    if sharelink.create_time > now:
 | 
			
		||||
        return False
 | 
			
		||||
    if (now - sharelink.create_time).seconds <15 * 60:
 | 
			
		||||
        return False
 | 
			
		||||
    else:
 | 
			
		||||
        sharelink.is_expired = True
 | 
			
		||||
        sharelink.save()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required(login_url='/api/user_action?action=login')
 | 
			
		||||
def share(request):
 | 
			
		||||
    peers = RustDeskPeer.objects.filter(Q(uid=request.user.id))
 | 
			
		||||
    sharelinks = ShareLink.objects.filter(Q(uid=request.user.id) & Q(is_used=False) & Q(is_expired=False))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # 省资源:处理已过期请求,不主动定时任务轮询请求,在任意地方请求时,检查是否过期,过期则保存。
 | 
			
		||||
    now = datetime.datetime.now()
 | 
			
		||||
    for sl in sharelinks:
 | 
			
		||||
        check_sharelink_expired(sl)
 | 
			
		||||
    sharelinks = ShareLink.objects.filter(Q(uid=request.user.id) & Q(is_used=False) & Q(is_expired=False))
 | 
			
		||||
    peers = [{'id':ix+1, 'name':f'{p.rid}|{p.alias}'} for ix, p in enumerate(peers)]
 | 
			
		||||
    sharelinks = [{'shash':s.shash, 'is_used':s.is_used, 'is_expired':s.is_expired, 'create_time':s.create_time, 'peers':s.peers} for ix, s in enumerate(sharelinks)]
 | 
			
		||||
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        url = request.build_absolute_uri()
 | 
			
		||||
        if url.endswith('share'):
 | 
			
		||||
            return render(request, 'share.html', {'peers':peers, 'sharelinks':sharelinks})
 | 
			
		||||
        else:
 | 
			
		||||
            shash = url.split('/')[-1]
 | 
			
		||||
            sharelink = ShareLink.objects.filter(Q(shash=shash))
 | 
			
		||||
            msg = ''
 | 
			
		||||
            title = 'success'
 | 
			
		||||
            if not sharelink:
 | 
			
		||||
                title = 'mistake'
 | 
			
		||||
                msg = f'Link{url}:<br>Share the link does not exist or have failed.'
 | 
			
		||||
            else:
 | 
			
		||||
                sharelink = sharelink[0]
 | 
			
		||||
                if str(request.user.id) == str(sharelink.uid):
 | 
			
		||||
                    title = 'mistake'
 | 
			
		||||
                    msg = f'Link{url}:<br><br>Lets say, you cant share the link to yourself, right?Intersection'
 | 
			
		||||
                else:
 | 
			
		||||
                    sharelink.is_used = True
 | 
			
		||||
                    sharelink.save()
 | 
			
		||||
                    peers = sharelink.peers
 | 
			
		||||
                    peers = peers.split(',')
 | 
			
		||||
                    # 自己的peers若重叠,需要跳过
 | 
			
		||||
                    peers_self_ids = [x.rid for x in RustDeskPeer.objects.filter(Q(uid=request.user.id))]
 | 
			
		||||
                    peers_share = RustDeskPeer.objects.filter(Q(rid__in=peers) & Q(uid=sharelink.uid))
 | 
			
		||||
                    peers_share_ids = [x.rid for x in peers_share]
 | 
			
		||||
 | 
			
		||||
                    for peer in peers_share:
 | 
			
		||||
                        if peer.rid in peers_self_ids:
 | 
			
		||||
                            continue
 | 
			
		||||
                        #peer = RustDeskPeer.objects.get(rid=peer.rid)
 | 
			
		||||
                        peer_f = RustDeskPeer.objects.filter(Q(rid=peer.rid) & Q(uid=sharelink.uid))
 | 
			
		||||
                        if not peer_f:
 | 
			
		||||
                            msg += f"{peer.rid}existed,"
 | 
			
		||||
                            continue
 | 
			
		||||
                        
 | 
			
		||||
                        if len(peer_f) > 1:
 | 
			
		||||
                             msg += f'{peer.rid}There are multiple,Has skipped. '
 | 
			
		||||
                             continue
 | 
			
		||||
                        peer = peer_f[0]
 | 
			
		||||
                        peer.id = None
 | 
			
		||||
                        peer.uid = request.user.id
 | 
			
		||||
                        peer.save()
 | 
			
		||||
                        msg += f"{peer.rid},"
 | 
			
		||||
 | 
			
		||||
                    msg += 'Has been successfully obtained.'
 | 
			
		||||
 | 
			
		||||
            title = _(title)
 | 
			
		||||
            msg = _(msg)
 | 
			
		||||
            return render(request, 'msg.html', {'title':msg, 'msg':msg})
 | 
			
		||||
    else:
 | 
			
		||||
        data = request.POST.get('data', '[]')
 | 
			
		||||
 | 
			
		||||
        data = json.loads(data)
 | 
			
		||||
        if not data:
 | 
			
		||||
            return JsonResponse({'code':0, 'msg':_('数据为空。')})
 | 
			
		||||
        rustdesk_ids = [x['title'].split('|')[0] for x in data]
 | 
			
		||||
        rustdesk_ids = ','.join(rustdesk_ids)
 | 
			
		||||
        sharelink = ShareLink(
 | 
			
		||||
            uid=request.user.id,
 | 
			
		||||
            shash = getStrMd5(str(time.time())+salt),
 | 
			
		||||
            peers=rustdesk_ids,
 | 
			
		||||
        )
 | 
			
		||||
        sharelink.save()
 | 
			
		||||
 | 
			
		||||
        return JsonResponse({'code':1, 'shash':sharelink.shash})
 | 
			
		||||
 | 
			
		||||
def is_mobile(request):
 | 
			
		||||
    user_agent = request.META['HTTP_USER_AGENT']
 | 
			
		||||
    if 'Mobile' in user_agent or 'Android' in user_agent or 'iPhone' in user_agent:
 | 
			
		||||
        return 'base_phone.html'
 | 
			
		||||
    else:
 | 
			
		||||
        return 'base.html'
 | 
			
		||||
							
								
								
									
										343
									
								
								api/views_generator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								api/views_generator.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user