diff --git a/farmq-admin/check_existing_data.py b/farmq-admin/check_existing_data.py new file mode 100644 index 0000000..8d34faf --- /dev/null +++ b/farmq-admin/check_existing_data.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +""" +기존 Headscale 데이터 확인 스크립트 +""" + +import sqlite3 +import os +from datetime import datetime + +def check_headscale_data(): + """Headscale 데이터베이스의 기존 데이터 확인""" + db_path = '/srv/headscale-setup/data/db.sqlite' + + if not os.path.exists(db_path): + print(f"❌ Database file not found: {db_path}") + return + + print(f"📊 Checking Headscale database: {db_path}") + print("=" * 60) + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + try: + # 테이블 목록 확인 + cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") + tables = cursor.fetchall() + print(f"📋 Available tables: {[table[0] for table in tables]}") + print() + + # Users 테이블 확인 + try: + cursor.execute("SELECT * FROM users") + users = cursor.fetchall() + print(f"👥 Users ({len(users)} records):") + if users: + cursor.execute("PRAGMA table_info(users)") + columns = [col[1] for col in cursor.fetchall()] + print(f" Columns: {columns}") + for user in users: + print(f" - {dict(zip(columns, user))}") + print() + except Exception as e: + print(f" ❌ Error reading users: {e}") + + # Nodes 테이블 확인 + try: + cursor.execute("SELECT * FROM nodes") + nodes = cursor.fetchall() + print(f"💻 Nodes ({len(nodes)} records):") + if nodes: + cursor.execute("PRAGMA table_info(nodes)") + columns = [col[1] for col in cursor.fetchall()] + print(f" Columns: {columns}") + for node in nodes: + node_dict = dict(zip(columns, node)) + print(f" - ID: {node_dict.get('id')}, Given Name: {node_dict.get('given_name')}, " + f"Hostname: {node_dict.get('hostname')}, IPv4: {node_dict.get('ipv4')}, " + f"User ID: {node_dict.get('user_id')}") + print() + except Exception as e: + print(f" ❌ Error reading nodes: {e}") + + # PreAuthKeys 테이블 확인 + try: + cursor.execute("SELECT * FROM pre_auth_keys") + keys = cursor.fetchall() + print(f"🔑 PreAuth Keys ({len(keys)} records):") + if keys: + cursor.execute("PRAGMA table_info(pre_auth_keys)") + columns = [col[1] for col in cursor.fetchall()] + print(f" Columns: {columns}") + for key in keys: + key_dict = dict(zip(columns, key)) + print(f" - ID: {key_dict.get('id')}, Key: {key_dict.get('key')[:20]}..., " + f"Used: {key_dict.get('used')}, User ID: {key_dict.get('user_id')}") + print() + except Exception as e: + print(f" ❌ Error reading pre_auth_keys: {e}") + + # API Keys 테이블 확인 + try: + cursor.execute("SELECT * FROM api_keys") + api_keys = cursor.fetchall() + print(f"🗝️ API Keys ({len(api_keys)} records):") + if api_keys: + cursor.execute("PRAGMA table_info(api_keys)") + columns = [col[1] for col in cursor.fetchall()] + print(f" Columns: {columns}") + for api_key in api_keys: + key_dict = dict(zip(columns, api_key)) + print(f" - ID: {key_dict.get('id')}, Prefix: {key_dict.get('prefix')}, " + f"Created: {key_dict.get('created_at')}") + print() + except Exception as e: + print(f" ❌ Error reading api_keys: {e}") + + except Exception as e: + print(f"❌ Database connection error: {e}") + finally: + conn.close() + +if __name__ == '__main__': + check_headscale_data() \ No newline at end of file diff --git a/farmq-admin/debug_machine.py b/farmq-admin/debug_machine.py new file mode 100644 index 0000000..12bf93b --- /dev/null +++ b/farmq-admin/debug_machine.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +머신 상세 정보 디버깅 +""" + +from utils.database import init_database, get_session, get_machine_with_details +from models import Node, MachineSpecs, MonitoringData, PharmacyInfo + +def debug_machine_detail(machine_id=1): + """머신 상세 정보 디버깅""" + print(f"🔍 Debugging machine detail for ID: {machine_id}") + + session = get_session() + + try: + # 1. 머신 정보 확인 + machine = session.query(Node).filter_by(id=machine_id).first() + print(f"📱 Machine: {machine}") + if machine: + print(f" - ID: {machine.id}") + print(f" - Hostname: {machine.hostname}") + print(f" - Given Name: {machine.given_name}") + print(f" - User ID: {machine.user_id}") + print(f" - IPv4: {machine.ipv4}") + print(f" - Last Seen: {machine.last_seen}") + + # is_online 메서드 테스트 + try: + online_status = machine.is_online() + print(f" - Online Status: {online_status}") + except Exception as e: + print(f" - Online Status Error: {e}") + + # 2. 머신 스펙 확인 + specs = session.query(MachineSpecs).filter_by(machine_id=machine_id).first() + print(f"💾 Specs: {specs}") + if specs: + print(f" - CPU: {specs.cpu_model}") + print(f" - RAM: {specs.ram_gb}GB") + print(f" - Storage: {specs.storage_gb}GB") + + # 3. 모니터링 데이터 확인 + monitoring = session.query(MonitoringData).filter_by( + machine_id=machine_id + ).order_by(MonitoringData.collected_at.desc()).first() + print(f"📊 Latest Monitoring: {monitoring}") + if monitoring: + print(f" - CPU Usage: {monitoring.cpu_usage}%") + print(f" - Temperature: {monitoring.cpu_temperature}°C") + print(f" - Collected at: {monitoring.collected_at}") + + # 4. get_machine_with_details 함수 테스트 + print(f"\n🧪 Testing get_machine_with_details function...") + try: + details = get_machine_with_details(machine_id) + print(f" ✅ Success: {details is not None}") + if details: + print(f" - Machine: {details['machine'].hostname if details['machine'] else None}") + print(f" - Specs: {details['specs'] is not None}") + print(f" - Monitoring: {details['latest_monitoring'] is not None}") + print(f" - Online: {details['is_online']}") + print(f" - Pharmacy: {details['pharmacy']}") + except Exception as e: + print(f" ❌ Error: {e}") + import traceback + traceback.print_exc() + + except Exception as e: + print(f"❌ Database error: {e}") + import traceback + traceback.print_exc() + + finally: + session.close() + +if __name__ == '__main__': + # 데이터베이스 초기화 + init_database('sqlite:///srv/headscale-setup/data/db.sqlite') + debug_machine_detail(1) \ No newline at end of file diff --git a/farmq-admin/flask-venv/bin/Activate.ps1 b/farmq-admin/flask-venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/farmq-admin/flask-venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/farmq-admin/flask-venv/bin/activate b/farmq-admin/flask-venv/bin/activate new file mode 100644 index 0000000..d38c261 --- /dev/null +++ b/farmq-admin/flask-venv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath /srv/headscale-setup/farmq-admin/flask-venv) +else + # use the path as-is + export VIRTUAL_ENV=/srv/headscale-setup/farmq-admin/flask-venv +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"bin":$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1='(flask-venv) '"${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT='(flask-venv) ' + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/farmq-admin/flask-venv/bin/activate.csh b/farmq-admin/flask-venv/bin/activate.csh new file mode 100644 index 0000000..286ae62 --- /dev/null +++ b/farmq-admin/flask-venv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV /srv/headscale-setup/farmq-admin/flask-venv + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/"bin":$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = '(flask-venv) '"$prompt" + setenv VIRTUAL_ENV_PROMPT '(flask-venv) ' +endif + +alias pydoc python -m pydoc + +rehash diff --git a/farmq-admin/flask-venv/bin/activate.fish b/farmq-admin/flask-venv/bin/activate.fish new file mode 100644 index 0000000..0105ff7 --- /dev/null +++ b/farmq-admin/flask-venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV /srv/headscale-setup/farmq-admin/flask-venv + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/"bin $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) '(flask-venv) ' (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT '(flask-venv) ' +end diff --git a/farmq-admin/flask-venv/bin/flask b/farmq-admin/flask-venv/bin/flask new file mode 100755 index 0000000..fe49310 --- /dev/null +++ b/farmq-admin/flask-venv/bin/flask @@ -0,0 +1,8 @@ +#!/srv/headscale-setup/farmq-admin/flask-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from flask.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/farmq-admin/flask-venv/bin/normalizer b/farmq-admin/flask-venv/bin/normalizer new file mode 100755 index 0000000..d31749f --- /dev/null +++ b/farmq-admin/flask-venv/bin/normalizer @@ -0,0 +1,8 @@ +#!/srv/headscale-setup/farmq-admin/flask-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from charset_normalizer.cli import cli_detect +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli_detect()) diff --git a/farmq-admin/flask-venv/bin/pip b/farmq-admin/flask-venv/bin/pip new file mode 100755 index 0000000..185bba9 --- /dev/null +++ b/farmq-admin/flask-venv/bin/pip @@ -0,0 +1,8 @@ +#!/srv/headscale-setup/farmq-admin/flask-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/farmq-admin/flask-venv/bin/pip3 b/farmq-admin/flask-venv/bin/pip3 new file mode 100755 index 0000000..185bba9 --- /dev/null +++ b/farmq-admin/flask-venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/srv/headscale-setup/farmq-admin/flask-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/farmq-admin/flask-venv/bin/pip3.12 b/farmq-admin/flask-venv/bin/pip3.12 new file mode 100755 index 0000000..185bba9 --- /dev/null +++ b/farmq-admin/flask-venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/srv/headscale-setup/farmq-admin/flask-venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/farmq-admin/flask-venv/bin/python b/farmq-admin/flask-venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/farmq-admin/flask-venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/farmq-admin/flask-venv/bin/python3 b/farmq-admin/flask-venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/farmq-admin/flask-venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/farmq-admin/flask-venv/bin/python3.12 b/farmq-admin/flask-venv/bin/python3.12 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/farmq-admin/flask-venv/bin/python3.12 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/farmq-admin/flask-venv/include/site/python3.12/greenlet/greenlet.h b/farmq-admin/flask-venv/include/site/python3.12/greenlet/greenlet.h new file mode 100644 index 0000000..d02a16e --- /dev/null +++ b/farmq-admin/flask-venv/include/site/python3.12/greenlet/greenlet.h @@ -0,0 +1,164 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +#ifndef GREENLET_MODULE +#define implementation_ptr_t void* +#endif + +typedef struct _greenlet { + PyObject_HEAD + PyObject* weakreflist; + PyObject* dict; + implementation_ptr_t pimpl; +} PyGreenlet; + +#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) + + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 12 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#define PyGreenlet_MAIN_NUM 8 +#define PyGreenlet_STARTED_NUM 9 +#define PyGreenlet_ACTIVE_NUM 10 +#define PyGreenlet_GET_PARENT_NUM 11 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* + * PyGreenlet_GetParent(PyObject* greenlet) + * + * return greenlet.parent; + * + * This could return NULL even if there is no exception active. + * If it does not return NULL, you are responsible for decrementing the + * reference count. + */ +# define PyGreenlet_GetParent \ + (*(PyGreenlet* (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) + +/* + * deprecated, undocumented alias. + */ +# define PyGreenlet_GET_PARENT PyGreenlet_GetParent + +# define PyGreenlet_MAIN \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_MAIN_NUM]) + +# define PyGreenlet_STARTED \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_STARTED_NUM]) + +# define PyGreenlet_ACTIVE \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) + + + + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/farmq-admin/flask-venv/lib64 b/farmq-admin/flask-venv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/farmq-admin/flask-venv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/farmq-admin/flask-venv/pyvenv.cfg b/farmq-admin/flask-venv/pyvenv.cfg new file mode 100644 index 0000000..abb3386 --- /dev/null +++ b/farmq-admin/flask-venv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.12.3 +executable = /usr/bin/python3.12 +command = /usr/bin/python3 -m venv /srv/headscale-setup/farmq-admin/flask-venv diff --git a/farmq-admin/init_sample_data.py b/farmq-admin/init_sample_data.py new file mode 100644 index 0000000..137190e --- /dev/null +++ b/farmq-admin/init_sample_data.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +""" +FARMQ 관리 시스템 - 샘플 데이터 초기화 스크립트 +""" + +import os +import sys +from datetime import datetime, timedelta +import random +from sqlalchemy import text + +# 현재 디렉토리를 파이썬 경로에 추가 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, current_dir) + +from config import config +from utils.database import init_database, get_session, close_session +from models import PharmacyInfo, MachineSpecs, MonitoringData, Alert, User, Node + +def init_sample_data(): + """샘플 데이터 초기화""" + print("🔧 Initializing sample data for FARMQ Admin System...") + + # 데이터베이스 세션 획득 + session = get_session() + + try: + # 1. 기존 샘플 데이터 정리 + print("📝 Cleaning existing sample data...") + session.query(Alert).delete() + session.query(MonitoringData).delete() + session.query(MachineSpecs).delete() + session.query(PharmacyInfo).delete() + + # 2. 샘플 약국 데이터 생성 + print("🏥 Creating sample pharmacy data...") + pharmacies = [ + { + 'pharmacy_name': '서울약국', + 'business_number': '123-45-67890', + 'manager_name': '김철수', + 'phone': '02-1234-5678', + 'address': '서울특별시 강남구 테헤란로 123', + 'proxmox_host': '192.168.1.100', + 'user_id': 'seoul-pharmacy' + }, + { + 'pharmacy_name': '부산메디컬약국', + 'business_number': '987-65-43210', + 'manager_name': '이영희', + 'phone': '051-2345-6789', + 'address': '부산광역시 해운대구 센텀대로 456', + 'proxmox_host': '192.168.1.101', + 'user_id': 'busan-medical' + }, + { + 'pharmacy_name': '대구건강약국', + 'business_number': '456-78-91234', + 'manager_name': '박민수', + 'phone': '053-3456-7890', + 'address': '대구광역시 중구 동성로 789', + 'proxmox_host': '192.168.1.102', + 'user_id': 'daegu-health' + }, + { + 'pharmacy_name': '인천바다약국', + 'business_number': '321-54-87695', + 'manager_name': '최수진', + 'phone': '032-4567-8901', + 'address': '인천광역시 연수구 송도대로 321', + 'proxmox_host': '192.168.1.103', + 'user_id': 'incheon-sea' + }, + { + 'pharmacy_name': '광주햇살약국', + 'business_number': '654-32-10987', + 'manager_name': '정태영', + 'phone': '062-5678-9012', + 'address': '광주광역시 서구 상무대로 654', + 'proxmox_host': '192.168.1.104', + 'user_id': 'gwangju-sunshine' + } + ] + + pharmacy_objects = [] + for pharmacy_data in pharmacies: + pharmacy = PharmacyInfo(**pharmacy_data) + session.add(pharmacy) + pharmacy_objects.append(pharmacy) + + session.flush() # ID 생성을 위해 flush + + # 3. 기존 Headscale 사용자와 연결 + print("👥 Linking with existing Headscale users...") + existing_users = session.query(User).all() + existing_nodes = session.query(Node).all() + + print(f"📊 Found {len(existing_users)} users and {len(existing_nodes)} nodes in Headscale") + + # 4. 머신 스펙 데이터 생성 + print("💻 Creating machine specifications...") + cpu_models = [ + 'Intel Core i5-11400', + 'Intel Core i7-10700', + 'AMD Ryzen 5 5600X', + 'AMD Ryzen 7 5700G', + 'Intel Core i3-10100' + ] + + machine_specs = [] + for node in existing_nodes: + specs = MachineSpecs( + machine_id=node.id, + cpu_model=random.choice(cpu_models), + cpu_cores=random.choice([4, 6, 8]), + ram_gb=random.choice([8, 16, 32]), + storage_gb=random.choice([256, 512, 1024]), + network_speed=random.choice([100, 1000]), + os_info=f"Ubuntu 22.04 LTS", + created_at=datetime.now() + ) + session.add(specs) + machine_specs.append(specs) + + # 5. 모니터링 데이터 생성 (최근 24시간) + print("📈 Creating monitoring data...") + for spec in machine_specs: + # 각 머신별로 최근 24시간 데이터 생성 + base_time = datetime.now() - timedelta(hours=24) + + for i in range(144): # 10분 간격으로 24시간 = 144개 데이터 + monitoring_time = base_time + timedelta(minutes=i * 10) + + # 시간대별로 사실적인 패턴 생성 + hour = monitoring_time.hour + base_cpu = 20 if 6 <= hour <= 22 else 10 # 업무시간 vs 야간 + + monitoring = MonitoringData( + machine_id=spec.machine_id, + cpu_usage=max(0, min(100, base_cpu + random.gauss(0, 10))), + memory_usage=max(10, min(90, 30 + random.gauss(0, 15))), + disk_usage=max(20, min(80, 45 + random.gauss(0, 5))), + cpu_temperature=max(35, min(85, 55 + random.gauss(0, 8))), + network_in_bytes=random.randint(1000, 50000), + network_out_bytes=random.randint(500, 25000), + collected_at=monitoring_time + ) + session.add(monitoring) + + # 6. 알림 데이터 생성 + print("🚨 Creating alert data...") + alert_types = ['HIGH_CPU', 'HIGH_MEMORY', 'HIGH_TEMPERATURE', 'DISK_SPACE', 'NETWORK_ISSUE'] + alert_levels = ['INFO', 'WARNING', 'ERROR', 'CRITICAL'] + + for i in range(15): # 15개의 알림 생성 + alert_time = datetime.now() - timedelta(hours=random.randint(1, 72)) + machine_id = random.choice([spec.machine_id for spec in machine_specs]) + + alert_type = random.choice(alert_types) + level = random.choice(alert_levels) + + messages = { + 'HIGH_CPU': f'CPU 사용률 80% 초과', + 'HIGH_MEMORY': f'메모리 사용률 85% 초과', + 'HIGH_TEMPERATURE': f'CPU 온도 75°C 초과', + 'DISK_SPACE': f'디스크 사용률 80% 초과', + 'NETWORK_ISSUE': f'네트워크 연결 불안정' + } + + alert = Alert( + machine_id=machine_id, + alert_type=alert_type, + level=level, + message=messages[alert_type], + created_at=alert_time, + is_resolved=random.choice([True, False]) + ) + session.add(alert) + + # 커밋 + session.commit() + + print("✅ Sample data initialization completed!") + print(f" - {len(pharmacy_objects)} pharmacies created") + print(f" - {len(machine_specs)} machine specifications created") + print(f" - {144 * len(machine_specs)} monitoring records created") + print(f" - 15 alerts created") + + # 통계 출력 + print("\n📊 Database Statistics:") + print(f" - Total Users: {session.query(User).count()}") + print(f" - Total Nodes: {session.query(Node).count()}") + print(f" - Total Pharmacies: {session.query(PharmacyInfo).count()}") + print(f" - Total Monitoring Records: {session.query(MonitoringData).count()}") + print(f" - Active Alerts: {session.query(Alert).filter_by(is_resolved=False).count()}") + + except Exception as e: + print(f"❌ Error initializing sample data: {e}") + session.rollback() + raise + finally: + close_session() + +if __name__ == '__main__': + # 데이터베이스 초기화 + init_database('/srv/headscale-setup/headscale-data/db.sqlite') + + # 샘플 데이터 생성 + init_sample_data() \ No newline at end of file diff --git a/farmq-admin/static/novnc/AUTHORS b/farmq-admin/static/novnc/AUTHORS new file mode 100644 index 0000000..e8fb124 --- /dev/null +++ b/farmq-admin/static/novnc/AUTHORS @@ -0,0 +1,13 @@ +maintainers: +- Samuel Mannehed for Cendio AB (@samhed) +- Pierre Ossman for Cendio AB (@CendioOssman) +maintainersEmeritus: +- Joel Martin (@kanaka) +- Solly Ross (@directxman12) +- @astrand +contributors: +# There are a bunch of people that should be here. +# If you want to be on this list, feel free send a PR +# to add yourself. +- jalf +- NTT corp. diff --git a/farmq-admin/static/novnc/LICENSE.txt b/farmq-admin/static/novnc/LICENSE.txt new file mode 100644 index 0000000..37efdcd --- /dev/null +++ b/farmq-admin/static/novnc/LICENSE.txt @@ -0,0 +1,62 @@ +noVNC is Copyright (C) 2022 The noVNC Authors +(./AUTHORS) + +The noVNC core library files are licensed under the MPL 2.0 (Mozilla +Public License 2.0). The noVNC core library is composed of the +Javascript code necessary for full noVNC operation. This includes (but +is not limited to): + + core/**/*.js + app/*.js + test/playback.js + +The HTML, CSS, font and images files that included with the noVNC +source distibution (or repository) are not considered part of the +noVNC core library and are licensed under more permissive licenses. +The intent is to allow easy integration of noVNC into existing web +sites and web applications. + +The HTML, CSS, font and image files are licensed as follows: + + *.html : 2-Clause BSD license + + app/styles/*.css : 2-Clause BSD license + + app/styles/Orbitron* : SIL Open Font License 1.1 + (Copyright 2009 Matt McInerney) + + app/images/ : Creative Commons Attribution-ShareAlike + http://creativecommons.org/licenses/by-sa/3.0/ + +Some portions of noVNC are copyright to their individual authors. +Please refer to the individual source files and/or to the noVNC commit +history: https://github.com/novnc/noVNC/commits/master + +The are several files and projects that have been incorporated into +the noVNC core library. Here is a list of those files and the original +licenses (all MPL 2.0 compatible): + + core/base64.js : MPL 2.0 + + core/des.js : Various BSD style licenses + + vendor/pako/ : MIT + +Any other files not mentioned above are typically marked with +a copyright/license header at the top of the file. The default noVNC +license is MPL-2.0. + +The following license texts are included: + + docs/LICENSE.MPL-2.0 + docs/LICENSE.OFL-1.1 + docs/LICENSE.BSD-3-Clause (New BSD) + docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD) + vendor/pako/LICENSE (MIT) + +Or alternatively the license texts may be found here: + + http://www.mozilla.org/MPL/2.0/ + http://scripts.sil.org/OFL + http://en.wikipedia.org/wiki/BSD_licenses + https://opensource.org/licenses/MIT diff --git a/farmq-admin/static/novnc/README.md b/farmq-admin/static/novnc/README.md new file mode 100644 index 0000000..a771cb4 --- /dev/null +++ b/farmq-admin/static/novnc/README.md @@ -0,0 +1,230 @@ +## noVNC: HTML VNC Client Library and Application + +[![Test Status](https://github.com/novnc/noVNC/workflows/Test/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ATest) +[![Lint Status](https://github.com/novnc/noVNC/workflows/Lint/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ALint) + +### Description + +noVNC is both a HTML VNC client JavaScript library and an application built on +top of that library. noVNC runs well in any modern browser including mobile +browsers (iOS and Android). + +Many companies, projects and products have integrated noVNC including +[OpenStack](http://www.openstack.org), +[OpenNebula](http://opennebula.org/), +[LibVNCServer](http://libvncserver.sourceforge.net), and +[ThinLinc](https://cendio.com/thinlinc). See +[the Projects and Companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC) +for a more complete list with additional info and links. + +### Table of Contents + +- [News/help/contact](#newshelpcontact) +- [Features](#features) +- [Screenshots](#screenshots) +- [Browser Requirements](#browser-requirements) +- [Server Requirements](#server-requirements) +- [Quick Start](#quick-start) +- [Installation from Snap Package](#installation-from-snap-package) +- [Integration and Deployment](#integration-and-deployment) +- [Authors/Contributors](#authorscontributors) + +### News/help/contact + +The project website is found at [novnc.com](http://novnc.com). +Notable commits, announcements and news are posted to +[@noVNC](http://www.twitter.com/noVNC). + +If you are a noVNC developer/integrator/user (or want to be) please join the +[noVNC discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc). + +Bugs and feature requests can be submitted via +[github issues](https://github.com/novnc/noVNC/issues). If you have questions +about using noVNC then please first use the +[discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc). +We also have a [wiki](https://github.com/novnc/noVNC/wiki/) with lots of +helpful information. + +If you are looking for a place to start contributing to noVNC, a good place to +start would be the issues that are marked as +["patchwelcome"](https://github.com/novnc/noVNC/issues?labels=patchwelcome). +Please check our +[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) though. + +If you want to show appreciation for noVNC you could donate to a great non- +profits such as: +[Compassion International](http://www.compassion.com/), +[SIL](http://www.sil.org), +[Habitat for Humanity](http://www.habitat.org), +[Electronic Frontier Foundation](https://www.eff.org/), +[Against Malaria Foundation](http://www.againstmalaria.com/), +[Nothing But Nets](http://www.nothingbutnets.net/), etc. +Please tweet [@noVNC](http://www.twitter.com/noVNC) if you do. + + +### Features + +* Supports all modern browsers including mobile (iOS, Android) +* Supported authentication methods: none, classical VNC, RealVNC's + RSA-AES, Tight, VeNCrypt Plain, XVP, Apple's Diffie-Hellman, + UltraVNC's MSLogonII +* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG, + ZRLE, JPEG +* Supports scaling, clipping and resizing the desktop +* Local cursor rendering +* Clipboard copy/paste with full Unicode support +* Translations +* Touch gestures for emulating common mouse actions +* Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see + [the license document](LICENSE.txt) for details + +### Screenshots + +Running in Firefox before and after connecting: + +  + + +See more screenshots +[here](http://novnc.com/screenshots.html). + + +### Browser Requirements + +noVNC uses many modern web technologies so a formal requirement list is +not available. However these are the minimum versions we are currently +aware of: + +* Chrome 64, Firefox 79, Safari 13.4, Opera 51, Edge 79 + + +### Server Requirements + +noVNC follows the standard VNC protocol, but unlike other VNC clients it does +require WebSockets support. Many servers include support (e.g. +[x11vnc/libvncserver](http://libvncserver.sourceforge.net/), +[QEMU](http://www.qemu.org/), and +[MobileVNC](http://www.smartlab.at/mobilevnc/)), but for the others you need to +use a WebSockets to TCP socket proxy. noVNC has a sister project +[websockify](https://github.com/novnc/websockify) that provides a simple such +proxy. + + +### Quick Start + +* Use the `novnc_proxy` script to automatically download and start websockify, which + includes a mini-webserver and the WebSockets proxy. The `--vnc` option is + used to specify the location of a running VNC server: + + `./utils/novnc_proxy --vnc localhost:5901` + +* If you don't need to expose the web server to public internet, you can + bind to localhost: + + `./utils/novnc_proxy --vnc localhost:5901 --listen localhost:6081` + +* Point your browser to the cut-and-paste URL that is output by the `novnc_proxy` + script. Hit the Connect button, enter a password if the VNC server has one + configured, and enjoy! + +### Installation from Snap Package +Running the command below will install the latest release of noVNC from Snap: + +`sudo snap install novnc` + +#### Running noVNC from Snap Directly + +You can run the Snap-package installed novnc directly with, for example: + +`novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH` + +If you want to use certificate files, due to standard Snap confinement restrictions you need to have them in the /home/\/snap/novnc/current/ directory. If your username is jsmith an example command would be: + + `novnc --listen 8443 --cert ~jsmith/snap/novnc/current/self.crt --key ~jsmith/snap/novnc/current/self.key --vnc ubuntu.example.com:5901` + +#### Running noVNC from Snap as a Service (Daemon) +The Snap package also has the capability to run a 'novnc' service which can be +configured to listen on multiple ports connecting to multiple VNC servers +(effectively a service runing multiple instances of novnc). +Instructions (with example values): + +List current services (out-of-box this will be blank): + +``` +sudo snap get novnc services +Key Value +services.n6080 {...} +services.n6081 {...} +``` + +Create a new service that listens on port 6082 and connects to the VNC server +running on port 5902 on localhost: + +`sudo snap set novnc services.n6082.listen=6082 services.n6082.vnc=localhost:5902` + +(Any services you define with 'snap set' will be automatically started) +Note that the name of the service, 'n6082' in this example, can be anything +as long as it doesn't start with a number or contain spaces/special characters. + +View the configuration of the service just created: + +``` +sudo snap get novnc services.n6082 +Key Value +services.n6082.listen 6082 +services.n6082.vnc localhost:5902 +``` + +Disable a service (note that because of a limitation in Snap it's currently not +possible to unset config variables, setting them to blank values is the way +to disable a service): + +`sudo snap set novnc services.n6082.listen='' services.n6082.vnc=''` + +(Any services you set to blank with 'snap set' like this will be automatically stopped) + +Verify that the service is disabled (blank values): + +``` +sudo snap get novnc services.n6082 +Key Value +services.n6082.listen +services.n6082.vnc +``` + +### Integration and Deployment + +Please see our other documents for how to integrate noVNC in your own software, +or deploying the noVNC application in production environments: + +* [Embedding](docs/EMBEDDING.md) - For the noVNC application +* [Library](docs/LIBRARY.md) - For the noVNC JavaScript library + + +### Authors/Contributors + +See [AUTHORS](AUTHORS) for a (full-ish) list of authors. If you're not on +that list and you think you should be, feel free to send a PR to fix that. + +* Core team: + * [Samuel Mannehed](https://github.com/samhed) (Cendio) + * [Pierre Ossman](https://github.com/CendioOssman) (Cendio) + +* Previous core contributors: + * [Joel Martin](https://github.com/kanaka) (Project founder) + * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) + +* Notable contributions: + * UI and Icons : Pierre Ossman, Chris Gordon + * Original Logo : Michael Sersen + * tight encoding : Michael Tinglof (Mercuri.ca) + * RealVNC RSA AES authentication : USTC Vlab Team + +* Included libraries: + * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net) + * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs) + * Pako : Vitaly Puzrin (https://github.com/nodeca/pako) + +Do you want to be on this list? Check out our +[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) and +start hacking! diff --git a/farmq-admin/static/novnc/app/error-handler.js b/farmq-admin/static/novnc/app/error-handler.js new file mode 100644 index 0000000..67b6372 --- /dev/null +++ b/farmq-admin/static/novnc/app/error-handler.js @@ -0,0 +1,79 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2019 The noVNC Authors + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +// Fallback for all uncought errors +function handleError(event, err) { + try { + const msg = document.getElementById('noVNC_fallback_errormsg'); + + // Work around Firefox bug: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1685038 + if (event.message === "ResizeObserver loop completed with undelivered notifications.") { + return false; + } + + // Only show the initial error + if (msg.hasChildNodes()) { + return false; + } + + let div = document.createElement("div"); + div.classList.add('noVNC_message'); + div.appendChild(document.createTextNode(event.message)); + msg.appendChild(div); + + if (event.filename) { + div = document.createElement("div"); + div.className = 'noVNC_location'; + let text = event.filename; + if (event.lineno !== undefined) { + text += ":" + event.lineno; + if (event.colno !== undefined) { + text += ":" + event.colno; + } + } + div.appendChild(document.createTextNode(text)); + msg.appendChild(div); + } + + if (err && err.stack) { + div = document.createElement("div"); + div.className = 'noVNC_stack'; + div.appendChild(document.createTextNode(err.stack)); + msg.appendChild(div); + } + + document.getElementById('noVNC_fallback_error') + .classList.add("noVNC_open"); + + } catch (exc) { + document.write("noVNC encountered an error."); + } + + // Try to disable keyboard interaction, best effort + try { + // Remove focus from the currently focused element in order to + // prevent keyboard interaction from continuing + if (document.activeElement) { document.activeElement.blur(); } + + // Don't let any element be focusable when showing the error + let keyboardFocusable = 'a[href], button, input, textarea, select, details, [tabindex]'; + document.querySelectorAll(keyboardFocusable).forEach((elem) => { + elem.setAttribute("tabindex", "-1"); + }); + } catch (exc) { + // Do nothing + } + + // Don't return true since this would prevent the error + // from being printed to the browser console. + return false; +} + +window.addEventListener('error', evt => handleError(evt, evt.error)); +window.addEventListener('unhandledrejection', evt => handleError(evt.reason, evt.reason)); diff --git a/farmq-admin/static/novnc/app/images/alt.svg b/farmq-admin/static/novnc/app/images/alt.svg new file mode 100644 index 0000000..e5bb461 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/alt.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/clipboard.svg b/farmq-admin/static/novnc/app/images/clipboard.svg new file mode 100644 index 0000000..79af275 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/clipboard.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/connect.svg b/farmq-admin/static/novnc/app/images/connect.svg new file mode 100644 index 0000000..56cde41 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/connect.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/ctrl.svg b/farmq-admin/static/novnc/app/images/ctrl.svg new file mode 100644 index 0000000..856e939 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/ctrl.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/ctrlaltdel.svg b/farmq-admin/static/novnc/app/images/ctrlaltdel.svg new file mode 100644 index 0000000..d7744ea --- /dev/null +++ b/farmq-admin/static/novnc/app/images/ctrlaltdel.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/disconnect.svg b/farmq-admin/static/novnc/app/images/disconnect.svg new file mode 100644 index 0000000..6be7d18 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/disconnect.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/drag.svg b/farmq-admin/static/novnc/app/images/drag.svg new file mode 100644 index 0000000..139caf9 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/drag.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/error.svg b/farmq-admin/static/novnc/app/images/error.svg new file mode 100644 index 0000000..8356d3f --- /dev/null +++ b/farmq-admin/static/novnc/app/images/error.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/esc.svg b/farmq-admin/static/novnc/app/images/esc.svg new file mode 100644 index 0000000..830152b --- /dev/null +++ b/farmq-admin/static/novnc/app/images/esc.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/expander.svg b/farmq-admin/static/novnc/app/images/expander.svg new file mode 100644 index 0000000..e163535 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/expander.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/fullscreen.svg b/farmq-admin/static/novnc/app/images/fullscreen.svg new file mode 100644 index 0000000..29bd05d --- /dev/null +++ b/farmq-admin/static/novnc/app/images/fullscreen.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/handle.svg b/farmq-admin/static/novnc/app/images/handle.svg new file mode 100644 index 0000000..4a7a126 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/handle.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/handle_bg.svg b/farmq-admin/static/novnc/app/images/handle_bg.svg new file mode 100644 index 0000000..7579c42 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/handle_bg.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/icons/Makefile b/farmq-admin/static/novnc/app/images/icons/Makefile new file mode 100644 index 0000000..03eaed0 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/icons/Makefile @@ -0,0 +1,42 @@ +BROWSER_SIZES := 16 24 32 48 64 +#ANDROID_SIZES := 72 96 144 192 +# FIXME: The ICO is limited to 8 icons due to a Chrome bug: +# https://bugs.chromium.org/p/chromium/issues/detail?id=1381393 +ANDROID_SIZES := 96 144 192 +WEB_ICON_SIZES := $(BROWSER_SIZES) $(ANDROID_SIZES) + +#IOS_1X_SIZES := 20 29 40 76 # No such devices exist anymore +IOS_2X_SIZES := 40 58 80 120 152 167 +IOS_3X_SIZES := 60 87 120 180 +ALL_IOS_SIZES := $(IOS_1X_SIZES) $(IOS_2X_SIZES) $(IOS_3X_SIZES) + +ALL_ICONS := \ + $(ALL_IOS_SIZES:%=novnc-ios-%.png) \ + novnc.ico + +all: $(ALL_ICONS) + +# Our testing shows that the ICO file need to be sorted in largest to +# smallest to get the apporpriate behviour +WEB_ICON_SIZES_REVERSE := $(shell echo $(WEB_ICON_SIZES) | tr ' ' '\n' | sort -nr | tr '\n' ' ') +WEB_BASE_ICONS := $(WEB_ICON_SIZES_REVERSE:%=novnc-%.png) +.INTERMEDIATE: $(WEB_BASE_ICONS) + +novnc.ico: $(WEB_BASE_ICONS) + convert $(WEB_BASE_ICONS) "$@" + +# General conversion +novnc-%.png: novnc-icon.svg + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# iOS icons use their own SVG +novnc-ios-%.png: novnc-ios-icon.svg + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# The smallest sizes are generated using a different SVG +novnc-16.png novnc-24.png novnc-32.png: novnc-icon-sm.svg + +clean: + rm -f *.png diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-icon-sm.svg b/farmq-admin/static/novnc/app/images/icons/novnc-icon-sm.svg new file mode 100644 index 0000000..aa1c6f1 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/icons/novnc-icon-sm.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-icon.svg b/farmq-admin/static/novnc/app/images/icons/novnc-icon.svg new file mode 100644 index 0000000..1efff91 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/icons/novnc-icon.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-120.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-120.png new file mode 100644 index 0000000..8da7bab Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-120.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-152.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-152.png new file mode 100644 index 0000000..60b2bce Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-152.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-167.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-167.png new file mode 100644 index 0000000..98fade2 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-167.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-180.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-180.png new file mode 100644 index 0000000..5d24df7 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-180.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-40.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-40.png new file mode 100644 index 0000000..cf14894 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-40.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-58.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-58.png new file mode 100644 index 0000000..f6dfbeb Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-58.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-60.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-60.png new file mode 100644 index 0000000..8cda295 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-60.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-80.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-80.png new file mode 100644 index 0000000..6c417c4 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-80.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-87.png b/farmq-admin/static/novnc/app/images/icons/novnc-ios-87.png new file mode 100644 index 0000000..4377d87 Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc-ios-87.png differ diff --git a/farmq-admin/static/novnc/app/images/icons/novnc-ios-icon.svg b/farmq-admin/static/novnc/app/images/icons/novnc-ios-icon.svg new file mode 100644 index 0000000..009452a --- /dev/null +++ b/farmq-admin/static/novnc/app/images/icons/novnc-ios-icon.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/icons/novnc.ico b/farmq-admin/static/novnc/app/images/icons/novnc.ico new file mode 100644 index 0000000..c3bc58e Binary files /dev/null and b/farmq-admin/static/novnc/app/images/icons/novnc.ico differ diff --git a/farmq-admin/static/novnc/app/images/info.svg b/farmq-admin/static/novnc/app/images/info.svg new file mode 100644 index 0000000..557b772 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/info.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/keyboard.svg b/farmq-admin/static/novnc/app/images/keyboard.svg new file mode 100644 index 0000000..137b350 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/keyboard.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/power.svg b/farmq-admin/static/novnc/app/images/power.svg new file mode 100644 index 0000000..4925d3e --- /dev/null +++ b/farmq-admin/static/novnc/app/images/power.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/settings.svg b/farmq-admin/static/novnc/app/images/settings.svg new file mode 100644 index 0000000..dbb2e80 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/tab.svg b/farmq-admin/static/novnc/app/images/tab.svg new file mode 100644 index 0000000..1ccb322 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/tab.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/toggleextrakeys.svg b/farmq-admin/static/novnc/app/images/toggleextrakeys.svg new file mode 100644 index 0000000..b578c0d --- /dev/null +++ b/farmq-admin/static/novnc/app/images/toggleextrakeys.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/warning.svg b/farmq-admin/static/novnc/app/images/warning.svg new file mode 100644 index 0000000..7114f9b --- /dev/null +++ b/farmq-admin/static/novnc/app/images/warning.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/farmq-admin/static/novnc/app/images/windows.svg b/farmq-admin/static/novnc/app/images/windows.svg new file mode 100644 index 0000000..ad5eec3 --- /dev/null +++ b/farmq-admin/static/novnc/app/images/windows.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml + + + \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/README b/farmq-admin/static/novnc/app/locale/README new file mode 100644 index 0000000..ca4f548 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/README @@ -0,0 +1 @@ +DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES. diff --git a/farmq-admin/static/novnc/app/locale/cs.json b/farmq-admin/static/novnc/app/locale/cs.json new file mode 100644 index 0000000..589145e --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/cs.json @@ -0,0 +1,71 @@ +{ + "Connecting...": "Připojení...", + "Disconnecting...": "Odpojení...", + "Reconnecting...": "Obnova připojení...", + "Internal error": "Vnitřní chyba", + "Must set host": "Hostitel musí být nastavení", + "Connected (encrypted) to ": "Připojení (šifrované) k ", + "Connected (unencrypted) to ": "Připojení (nešifrované) k ", + "Something went wrong, connection is closed": "Něco se pokazilo, odpojeno", + "Failed to connect to server": "Chyba připojení k serveru", + "Disconnected": "Odpojeno", + "New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ", + "New connection has been rejected": "Nové připojení bylo odmítnuto", + "Password is required": "Je vyžadováno heslo", + "noVNC encountered an error:": "noVNC narazilo na chybu:", + "Hide/Show the control bar": "Skrýt/zobrazit ovládací panel", + "Move/Drag Viewport": "Přesunout/přetáhnout výřez", + "viewport drag": "přesun výřezu", + "Active Mouse Button": "Aktivní tlačítka myši", + "No mousebutton": "Žádné", + "Left mousebutton": "Levé tlačítko myši", + "Middle mousebutton": "Prostřední tlačítko myši", + "Right mousebutton": "Pravé tlačítko myši", + "Keyboard": "Klávesnice", + "Show Keyboard": "Zobrazit klávesnici", + "Extra keys": "Extra klávesy", + "Show Extra Keys": "Zobrazit extra klávesy", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Přepnout Ctrl", + "Alt": "Alt", + "Toggle Alt": "Přepnout Alt", + "Send Tab": "Odeslat tabulátor", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Odeslat Esc", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del", + "Shutdown/Reboot": "Vypnutí/Restart", + "Shutdown/Reboot...": "Vypnutí/Restart...", + "Power": "Napájení", + "Shutdown": "Vypnout", + "Reboot": "Restart", + "Reset": "Reset", + "Clipboard": "Schránka", + "Clear": "Vymazat", + "Fullscreen": "Celá obrazovka", + "Settings": "Nastavení", + "Shared Mode": "Sdílený režim", + "View Only": "Pouze prohlížení", + "Clip to Window": "Přizpůsobit oknu", + "Scaling Mode:": "Přizpůsobení velikosti", + "None": "Žádné", + "Local Scaling": "Místní", + "Remote Resizing": "Vzdálené", + "Advanced": "Pokročilé", + "Repeater ID:": "ID opakovače", + "WebSocket": "WebSocket", + "Encrypt": "Šifrování:", + "Host:": "Hostitel:", + "Port:": "Port:", + "Path:": "Cesta", + "Automatic Reconnect": "Automatická obnova připojení", + "Reconnect Delay (ms):": "Zpoždění připojení (ms)", + "Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši", + "Logging:": "Logování:", + "Disconnect": "Odpojit", + "Connect": "Připojit", + "Password:": "Heslo", + "Send Password": "Odeslat heslo", + "Cancel": "Zrušit" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/de.json b/farmq-admin/static/novnc/app/locale/de.json new file mode 100644 index 0000000..62e7336 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/de.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "Verbinden...", + "Disconnecting...": "Verbindung trennen...", + "Reconnecting...": "Verbindung wiederherstellen...", + "Internal error": "Interner Fehler", + "Must set host": "Richten Sie den Server ein", + "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ", + "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ", + "Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt", + "Disconnected": "Verbindung zum Server getrennt", + "New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ", + "New connection has been rejected": "Verbindung wurde abgelehnt", + "Password is required": "Passwort ist erforderlich", + "noVNC encountered an error:": "Ein Fehler ist aufgetreten:", + "Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen", + "Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen", + "viewport drag": "Ansichtsfenster ziehen", + "Active Mouse Button": "Aktive Maustaste", + "No mousebutton": "Keine Maustaste", + "Left mousebutton": "Linke Maustaste", + "Middle mousebutton": "Mittlere Maustaste", + "Right mousebutton": "Rechte Maustaste", + "Keyboard": "Tastatur", + "Show Keyboard": "Tastatur anzeigen", + "Extra keys": "Zusatztasten", + "Show Extra Keys": "Zusatztasten anzeigen", + "Ctrl": "Strg", + "Toggle Ctrl": "Strg umschalten", + "Alt": "Alt", + "Toggle Alt": "Alt umschalten", + "Send Tab": "Tab senden", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Escape senden", + "Ctrl+Alt+Del": "Strg+Alt+Entf", + "Send Ctrl-Alt-Del": "Strg+Alt+Entf senden", + "Shutdown/Reboot": "Herunterfahren/Neustarten", + "Shutdown/Reboot...": "Herunterfahren/Neustarten...", + "Power": "Energie", + "Shutdown": "Herunterfahren", + "Reboot": "Neustarten", + "Reset": "Zurücksetzen", + "Clipboard": "Zwischenablage", + "Clear": "Löschen", + "Fullscreen": "Vollbild", + "Settings": "Einstellungen", + "Shared Mode": "Geteilter Modus", + "View Only": "Nur betrachten", + "Clip to Window": "Auf Fenster begrenzen", + "Scaling Mode:": "Skalierungsmodus:", + "None": "Keiner", + "Local Scaling": "Lokales skalieren", + "Remote Resizing": "Serverseitiges skalieren", + "Advanced": "Erweitert", + "Repeater ID:": "Repeater ID:", + "WebSocket": "WebSocket", + "Encrypt": "Verschlüsselt", + "Host:": "Server:", + "Port:": "Port:", + "Path:": "Pfad:", + "Automatic Reconnect": "Automatisch wiederverbinden", + "Reconnect Delay (ms):": "Wiederverbindungsverzögerung (ms):", + "Logging:": "Protokollierung:", + "Disconnect": "Verbindung trennen", + "Connect": "Verbinden", + "Password:": "Passwort:", + "Cancel": "Abbrechen", + "Canvas not supported.": "Canvas nicht unterstützt." +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/el.json b/farmq-admin/static/novnc/app/locale/el.json new file mode 100644 index 0000000..f801251 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/el.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "Συνδέεται...", + "Disconnecting...": "Aποσυνδέεται...", + "Reconnecting...": "Επανασυνδέεται...", + "Internal error": "Εσωτερικό σφάλμα", + "Must set host": "Πρέπει να οριστεί ο διακομιστής", + "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ", + "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ", + "Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε", + "Disconnected": "Αποσυνδέθηκε", + "New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ", + "New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ", + "Password is required": "Απαιτείται ο κωδικός πρόσβασης", + "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:", + "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου", + "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου", + "viewport drag": "σύρσιμο θεατού πεδίου", + "Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού", + "No mousebutton": "Χωρίς Πλήκτρο Ποντικιού", + "Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού", + "Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού", + "Right mousebutton": "Δεξί Πλήκτρο Ποντικιού", + "Keyboard": "Πληκτρολόγιο", + "Show Keyboard": "Εμφάνιση Πληκτρολογίου", + "Extra keys": "Επιπλέον πλήκτρα", + "Show Extra Keys": "Εμφάνιση Επιπλέον Πλήκτρων", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Εναλλαγή Ctrl", + "Alt": "Alt", + "Toggle Alt": "Εναλλαγή Alt", + "Send Tab": "Αποστολή Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Αποστολή Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Αποστολή Ctrl-Alt-Del", + "Shutdown/Reboot": "Κλείσιμο/Επανεκκίνηση", + "Shutdown/Reboot...": "Κλείσιμο/Επανεκκίνηση...", + "Power": "Απενεργοποίηση", + "Shutdown": "Κλείσιμο", + "Reboot": "Επανεκκίνηση", + "Reset": "Επαναφορά", + "Clipboard": "Πρόχειρο", + "Clear": "Καθάρισμα", + "Fullscreen": "Πλήρης Οθόνη", + "Settings": "Ρυθμίσεις", + "Shared Mode": "Κοινόχρηστη Λειτουργία", + "View Only": "Μόνο Θέαση", + "Clip to Window": "Αποκοπή στο όριο του Παράθυρου", + "Scaling Mode:": "Λειτουργία Κλιμάκωσης:", + "None": "Καμία", + "Local Scaling": "Τοπική Κλιμάκωση", + "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους", + "Advanced": "Για προχωρημένους", + "Repeater ID:": "Repeater ID:", + "WebSocket": "WebSocket", + "Encrypt": "Κρυπτογράφηση", + "Host:": "Όνομα διακομιστή:", + "Port:": "Πόρτα διακομιστή:", + "Path:": "Διαδρομή:", + "Automatic Reconnect": "Αυτόματη επανασύνδεση", + "Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):", + "Logging:": "Καταγραφή:", + "Disconnect": "Αποσύνδεση", + "Connect": "Σύνδεση", + "Password:": "Κωδικός Πρόσβασης:", + "Cancel": "Ακύρωση", + "Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/es.json b/farmq-admin/static/novnc/app/locale/es.json new file mode 100644 index 0000000..b9e663a --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/es.json @@ -0,0 +1,68 @@ +{ + "Connecting...": "Conectando...", + "Connected (encrypted) to ": "Conectado (con encriptación) a", + "Connected (unencrypted) to ": "Conectado (sin encriptación) a", + "Disconnecting...": "Desconectando...", + "Disconnected": "Desconectado", + "Must set host": "Se debe configurar el host", + "Reconnecting...": "Reconectando...", + "Password is required": "La contraseña es obligatoria", + "Disconnect timeout": "Tiempo de desconexión agotado", + "noVNC encountered an error:": "noVNC ha encontrado un error:", + "Hide/Show the control bar": "Ocultar/Mostrar la barra de control", + "Move/Drag Viewport": "Mover/Arrastrar la ventana", + "viewport drag": "Arrastrar la ventana", + "Active Mouse Button": "Botón activo del ratón", + "No mousebutton": "Ningún botón del ratón", + "Left mousebutton": "Botón izquierdo del ratón", + "Middle mousebutton": "Botón central del ratón", + "Right mousebutton": "Botón derecho del ratón", + "Keyboard": "Teclado", + "Show Keyboard": "Mostrar teclado", + "Extra keys": "Teclas adicionales", + "Show Extra Keys": "Mostrar Teclas Adicionales", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Pulsar/Soltar Ctrl", + "Alt": "Alt", + "Toggle Alt": "Pulsar/Soltar Alt", + "Send Tab": "Enviar Tabulación", + "Tab": "Tabulación", + "Esc": "Esc", + "Send Escape": "Enviar Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del", + "Shutdown/Reboot": "Apagar/Reiniciar", + "Shutdown/Reboot...": "Apagar/Reiniciar...", + "Power": "Encender", + "Shutdown": "Apagar", + "Reboot": "Reiniciar", + "Reset": "Restablecer", + "Clipboard": "Portapapeles", + "Clear": "Vaciar", + "Fullscreen": "Pantalla Completa", + "Settings": "Configuraciones", + "Encrypt": "Encriptar", + "Shared Mode": "Modo Compartido", + "View Only": "Solo visualización", + "Clip to Window": "Recortar al tamaño de la ventana", + "Scaling Mode:": "Modo de escalado:", + "None": "Ninguno", + "Local Scaling": "Escalado Local", + "Local Downscaling": "Reducción de escala local", + "Remote Resizing": "Cambio de tamaño remoto", + "Advanced": "Avanzado", + "Local Cursor": "Cursor Local", + "Repeater ID:": "ID del Repetidor:", + "WebSocket": "WebSocket", + "Host:": "Host:", + "Port:": "Puerto:", + "Path:": "Ruta:", + "Automatic Reconnect": "Reconexión automática", + "Reconnect Delay (ms):": "Retraso en la reconexión (ms):", + "Logging:": "Registrando:", + "Disconnect": "Desconectar", + "Connect": "Conectar", + "Password:": "Contraseña:", + "Cancel": "Cancelar", + "Canvas not supported.": "Canvas no soportado." +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/fr.json b/farmq-admin/static/novnc/app/locale/fr.json new file mode 100644 index 0000000..22531f7 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/fr.json @@ -0,0 +1,78 @@ +{ + "HTTPS is required for full functionality": "", + "Connecting...": "En cours de connexion...", + "Disconnecting...": "Déconnexion en cours...", + "Reconnecting...": "Reconnexion en cours...", + "Internal error": "Erreur interne", + "Must set host": "Doit définir l'hôte", + "Connected (encrypted) to ": "Connecté (chiffré) à ", + "Connected (unencrypted) to ": "Connecté (non chiffré) à ", + "Something went wrong, connection is closed": "Quelque chose s'est mal passé, la connexion a été fermée", + "Failed to connect to server": "Échec de connexion au serveur", + "Disconnected": "Déconnecté", + "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec motif : ", + "New connection has been rejected": "Une nouvelle connexion a été rejetée", + "Credentials are required": "Les identifiants sont requis", + "noVNC encountered an error:": "noVNC a rencontré une erreur :", + "Hide/Show the control bar": "Masquer/Afficher la barre de contrôle", + "Drag": "Faire glisser", + "Move/Drag Viewport": "Déplacer/faire glisser le Viewport", + "Keyboard": "Clavier", + "Show Keyboard": "Afficher le clavier", + "Extra keys": "Touches supplémentaires", + "Show Extra Keys": "Afficher les touches supplémentaires", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Basculer Ctrl", + "Alt": "Alt", + "Toggle Alt": "Basculer Alt", + "Toggle Windows": "Basculer Windows", + "Windows": "Windows", + "Send Tab": "Envoyer l'onglet", + "Tab": "l'onglet", + "Esc": "Esc", + "Send Escape": "Envoyer Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Envoyer Ctrl-Alt-Del", + "Shutdown/Reboot": "Arrêter/Redémarrer", + "Shutdown/Reboot...": "Arrêter/Redémarrer...", + "Power": "Alimentation", + "Shutdown": "Arrêter", + "Reboot": "Redémarrer", + "Reset": "Réinitialiser", + "Clipboard": "Presse-papiers", + "Edit clipboard content in the textarea below.": "", + "Settings": "Paramètres", + "Shared Mode": "Mode partagé", + "View Only": "Afficher uniquement", + "Clip to Window": "Clip à fenêtre", + "Scaling Mode:": "Mode mise à l'échelle :", + "None": "Aucun", + "Local Scaling": "Mise à l'échelle locale", + "Remote Resizing": "Redimensionnement à distance", + "Advanced": "Avancé", + "Quality:": "Qualité :", + "Compression level:": "Niveau de compression :", + "Repeater ID:": "ID Répéteur :", + "WebSocket": "WebSocket", + "Encrypt": "Chiffrer", + "Host:": "Hôte :", + "Port:": "Port :", + "Path:": "Chemin :", + "Automatic Reconnect": "Reconnecter automatiquemen", + "Reconnect Delay (ms):": "Délai de reconnexion (ms) :", + "Show Dot when No Cursor": "Afficher le point lorsqu'il n'y a pas de curseur", + "Logging:": "Se connecter :", + "Version:": "Version :", + "Disconnect": "Déconnecter", + "Connect": "Connecter", + "Server identity": "", + "The server has provided the following identifying information:": "", + "Fingerprint:": "", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "", + "Approve": "", + "Reject": "", + "Username:": "Nom d'utilisateur :", + "Password:": "Mot de passe :", + "Send Credentials": "Envoyer les identifiants", + "Cancel": "Annuler" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/it.json b/farmq-admin/static/novnc/app/locale/it.json new file mode 100644 index 0000000..6fd2570 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/it.json @@ -0,0 +1,72 @@ +{ + "Connecting...": "Connessione in corso...", + "Disconnecting...": "Disconnessione...", + "Reconnecting...": "Riconnessione...", + "Internal error": "Errore interno", + "Must set host": "Devi impostare l'host", + "Connected (encrypted) to ": "Connesso (crittografato) a ", + "Connected (unencrypted) to ": "Connesso (non crittografato) a", + "Something went wrong, connection is closed": "Qualcosa è andato storto, la connessione è stata chiusa", + "Failed to connect to server": "Impossibile connettersi al server", + "Disconnected": "Disconnesso", + "New connection has been rejected with reason: ": "La nuova connessione è stata rifiutata con motivo: ", + "New connection has been rejected": "La nuova connessione è stata rifiutata", + "Credentials are required": "Le credenziali sono obbligatorie", + "noVNC encountered an error:": "noVNC ha riscontrato un errore:", + "Hide/Show the control bar": "Nascondi/Mostra la barra di controllo", + "Drag": "", + "Move/Drag Viewport": "", + "Keyboard": "Tastiera", + "Show Keyboard": "Mostra tastiera", + "Extra keys": "Tasti Aggiuntivi", + "Show Extra Keys": "Mostra Tasti Aggiuntivi", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Tieni premuto Ctrl", + "Alt": "Alt", + "Toggle Alt": "Tieni premuto Alt", + "Toggle Windows": "Tieni premuto Windows", + "Windows": "Windows", + "Send Tab": "Invia Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Invia Esc", + "Ctrl+Alt+Del": "Ctrl+Alt+Canc", + "Send Ctrl-Alt-Del": "Invia Ctrl-Alt-Canc", + "Shutdown/Reboot": "Spegnimento/Riavvio", + "Shutdown/Reboot...": "Spegnimento/Riavvio...", + "Power": "Alimentazione", + "Shutdown": "Spegnimento", + "Reboot": "Riavvio", + "Reset": "Reset", + "Clipboard": "Clipboard", + "Clear": "Pulisci", + "Fullscreen": "Schermo intero", + "Settings": "Impostazioni", + "Shared Mode": "Modalità condivisa", + "View Only": "Sola Visualizzazione", + "Clip to Window": "", + "Scaling Mode:": "Modalità di ridimensionamento:", + "None": "Nessuna", + "Local Scaling": "Ridimensionamento Locale", + "Remote Resizing": "Ridimensionamento Remoto", + "Advanced": "Avanzate", + "Quality:": "Qualità:", + "Compression level:": "Livello Compressione:", + "Repeater ID:": "ID Ripetitore:", + "WebSocket": "WebSocket", + "Encrypt": "Crittografa", + "Host:": "Host:", + "Port:": "Porta:", + "Path:": "Percorso:", + "Automatic Reconnect": "Riconnessione Automatica", + "Reconnect Delay (ms):": "Ritardo Riconnessione (ms):", + "Show Dot when No Cursor": "Mostra Punto quando Nessun Cursore", + "Logging:": "", + "Version:": "Versione:", + "Disconnect": "Disconnetti", + "Connect": "Connetti", + "Username:": "Utente:", + "Password:": "Password:", + "Send Credentials": "Invia Credenziale", + "Cancel": "Annulla" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/ja.json b/farmq-admin/static/novnc/app/locale/ja.json new file mode 100644 index 0000000..43fc5bf --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/ja.json @@ -0,0 +1,72 @@ +{ + "Connecting...": "接続しています...", + "Disconnecting...": "切断しています...", + "Reconnecting...": "再接続しています...", + "Internal error": "内部エラー", + "Must set host": "ホストを設定する必要があります", + "Connected (encrypted) to ": "接続しました (暗号化済み): ", + "Connected (unencrypted) to ": "接続しました (暗号化されていません): ", + "Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました", + "Failed to connect to server": "サーバーへの接続に失敗しました", + "Disconnected": "切断しました", + "New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ", + "New connection has been rejected": "新規接続は拒否されました", + "Credentials are required": "資格情報が必要です", + "noVNC encountered an error:": "noVNC でエラーが発生しました:", + "Hide/Show the control bar": "コントロールバーを隠す/表示する", + "Drag": "ドラッグ", + "Move/Drag Viewport": "ビューポートを移動/ドラッグ", + "Keyboard": "キーボード", + "Show Keyboard": "キーボードを表示", + "Extra keys": "追加キー", + "Show Extra Keys": "追加キーを表示", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Ctrl キーを切り替え", + "Alt": "Alt", + "Toggle Alt": "Alt キーを切り替え", + "Toggle Windows": "Windows キーを切り替え", + "Windows": "Windows", + "Send Tab": "Tab キーを送信", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Escape キーを送信", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信", + "Shutdown/Reboot": "シャットダウン/再起動", + "Shutdown/Reboot...": "シャットダウン/再起動...", + "Power": "電源", + "Shutdown": "シャットダウン", + "Reboot": "再起動", + "Reset": "リセット", + "Clipboard": "クリップボード", + "Clear": "クリア", + "Fullscreen": "全画面表示", + "Settings": "設定", + "Shared Mode": "共有モード", + "View Only": "表示のみ", + "Clip to Window": "ウィンドウにクリップ", + "Scaling Mode:": "スケーリングモード:", + "None": "なし", + "Local Scaling": "ローカルスケーリング", + "Remote Resizing": "リモートでリサイズ", + "Advanced": "高度", + "Quality:": "品質:", + "Compression level:": "圧縮レベル:", + "Repeater ID:": "リピーター ID:", + "WebSocket": "WebSocket", + "Encrypt": "暗号化", + "Host:": "ホスト:", + "Port:": "ポート:", + "Path:": "パス:", + "Automatic Reconnect": "自動再接続", + "Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):", + "Show Dot when No Cursor": "カーソルがないときにドットを表示", + "Logging:": "ロギング:", + "Version:": "バージョン:", + "Disconnect": "切断", + "Connect": "接続", + "Username:": "ユーザー名:", + "Password:": "パスワード:", + "Send Credentials": "資格情報を送信", + "Cancel": "キャンセル" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/ko.json b/farmq-admin/static/novnc/app/locale/ko.json new file mode 100644 index 0000000..e4ecddc --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/ko.json @@ -0,0 +1,70 @@ +{ + "Connecting...": "연결중...", + "Disconnecting...": "연결 해제중...", + "Reconnecting...": "재연결중...", + "Internal error": "내부 오류", + "Must set host": "호스트는 설정되어야 합니다.", + "Connected (encrypted) to ": "다음과 (암호화되어) 연결되었습니다:", + "Connected (unencrypted) to ": "다음과 (암호화 없이) 연결되었습니다:", + "Something went wrong, connection is closed": "무언가 잘못되었습니다, 연결이 닫혔습니다.", + "Failed to connect to server": "서버에 연결하지 못했습니다.", + "Disconnected": "연결이 해제되었습니다.", + "New connection has been rejected with reason: ": "새 연결이 다음 이유로 거부되었습니다:", + "New connection has been rejected": "새 연결이 거부되었습니다.", + "Password is required": "비밀번호가 필요합니다.", + "noVNC encountered an error:": "noVNC에 오류가 발생했습니다:", + "Hide/Show the control bar": "컨트롤 바 숨기기/보이기", + "Move/Drag Viewport": "움직이기/드래그 뷰포트", + "viewport drag": "뷰포트 드래그", + "Active Mouse Button": "마우스 버튼 활성화", + "No mousebutton": "마우스 버튼 없음", + "Left mousebutton": "왼쪽 마우스 버튼", + "Middle mousebutton": "중간 마우스 버튼", + "Right mousebutton": "오른쪽 마우스 버튼", + "Keyboard": "키보드", + "Show Keyboard": "키보드 보이기", + "Extra keys": "기타 키들", + "Show Extra Keys": "기타 키들 보이기", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Ctrl 켜기/끄기", + "Alt": "Alt", + "Toggle Alt": "Alt 켜기/끄기", + "Send Tab": "Tab 보내기", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Esc 보내기", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Ctrl+Alt+Del 보내기", + "Shutdown/Reboot": "셧다운/리붓", + "Shutdown/Reboot...": "셧다운/리붓...", + "Power": "전원", + "Shutdown": "셧다운", + "Reboot": "리붓", + "Reset": "리셋", + "Clipboard": "클립보드", + "Clear": "지우기", + "Fullscreen": "전체화면", + "Settings": "설정", + "Shared Mode": "공유 모드", + "View Only": "보기 전용", + "Clip to Window": "창에 클립", + "Scaling Mode:": "스케일링 모드:", + "None": "없음", + "Local Scaling": "로컬 스케일링", + "Remote Resizing": "원격 크기 조절", + "Advanced": "고급", + "Repeater ID:": "중계 ID", + "WebSocket": "웹소켓", + "Encrypt": "암호화", + "Host:": "호스트:", + "Port:": "포트:", + "Path:": "위치:", + "Automatic Reconnect": "자동 재연결", + "Reconnect Delay (ms):": "재연결 지연 시간 (ms)", + "Logging:": "로깅", + "Disconnect": "연결 해제", + "Connect": "연결", + "Password:": "비밀번호:", + "Send Password": "비밀번호 전송", + "Cancel": "취소" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/nl.json b/farmq-admin/static/novnc/app/locale/nl.json new file mode 100644 index 0000000..0cdcc92 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/nl.json @@ -0,0 +1,73 @@ +{ + "Connecting...": "Verbinden...", + "Disconnecting...": "Verbinding verbreken...", + "Reconnecting...": "Opnieuw verbinding maken...", + "Internal error": "Interne fout", + "Must set host": "Host moeten worden ingesteld", + "Connected (encrypted) to ": "Verbonden (versleuteld) met ", + "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ", + "Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken", + "Failed to connect to server": "Verbinding maken met server is mislukt", + "Disconnected": "Verbinding verbroken", + "New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ", + "New connection has been rejected": "Nieuwe verbinding is geweigerd", + "Password is required": "Wachtwoord is vereist", + "noVNC encountered an error:": "noVNC heeft een fout bemerkt:", + "Hide/Show the control bar": "Verberg/Toon de bedieningsbalk", + "Move/Drag Viewport": "Verplaats/Versleep Kijkvenster", + "viewport drag": "kijkvenster slepen", + "Active Mouse Button": "Actieve Muisknop", + "No mousebutton": "Geen muisknop", + "Left mousebutton": "Linker muisknop", + "Middle mousebutton": "Middelste muisknop", + "Right mousebutton": "Rechter muisknop", + "Keyboard": "Toetsenbord", + "Show Keyboard": "Toon Toetsenbord", + "Extra keys": "Extra toetsen", + "Show Extra Keys": "Toon Extra Toetsen", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Ctrl omschakelen", + "Alt": "Alt", + "Toggle Alt": "Alt omschakelen", + "Toggle Windows": "Windows omschakelen", + "Windows": "Windows", + "Send Tab": "Tab Sturen", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Escape Sturen", + "Ctrl+Alt+Del": "Ctrl-Alt-Del", + "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Sturen", + "Shutdown/Reboot": "Uitschakelen/Herstarten", + "Shutdown/Reboot...": "Uitschakelen/Herstarten...", + "Power": "Systeem", + "Shutdown": "Uitschakelen", + "Reboot": "Herstarten", + "Reset": "Resetten", + "Clipboard": "Klembord", + "Clear": "Wissen", + "Fullscreen": "Volledig Scherm", + "Settings": "Instellingen", + "Shared Mode": "Gedeelde Modus", + "View Only": "Alleen Kijken", + "Clip to Window": "Randen buiten venster afsnijden", + "Scaling Mode:": "Schaalmodus:", + "None": "Geen", + "Local Scaling": "Lokaal Schalen", + "Remote Resizing": "Op Afstand Formaat Wijzigen", + "Advanced": "Geavanceerd", + "Repeater ID:": "Repeater ID:", + "WebSocket": "WebSocket", + "Encrypt": "Versleutelen", + "Host:": "Host:", + "Port:": "Poort:", + "Path:": "Pad:", + "Automatic Reconnect": "Automatisch Opnieuw Verbinden", + "Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):", + "Show Dot when No Cursor": "Geef stip weer indien geen cursor", + "Logging:": "Logmeldingen:", + "Disconnect": "Verbinding verbreken", + "Connect": "Verbinden", + "Password:": "Wachtwoord:", + "Send Password": "Verzend Wachtwoord:", + "Cancel": "Annuleren" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/pl.json b/farmq-admin/static/novnc/app/locale/pl.json new file mode 100644 index 0000000..006ac7a --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/pl.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "Łączenie...", + "Disconnecting...": "Rozłączanie...", + "Reconnecting...": "Łączenie...", + "Internal error": "Błąd wewnętrzny", + "Must set host": "Host i port są wymagane", + "Connected (encrypted) to ": "Połączenie (szyfrowane) z ", + "Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ", + "Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte", + "Disconnected": "Rozłączony", + "New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ", + "New connection has been rejected": "Nowe połączenie zostało odrzucone", + "Password is required": "Hasło jest wymagane", + "noVNC encountered an error:": "noVNC napotkało błąd:", + "Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień", + "Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport", + "viewport drag": "przeciągnij viewport", + "Active Mouse Button": "Aktywny Przycisk Myszy", + "No mousebutton": "Brak przycisku myszy", + "Left mousebutton": "Lewy przycisk myszy", + "Middle mousebutton": "Środkowy przycisk myszy", + "Right mousebutton": "Prawy przycisk myszy", + "Keyboard": "Klawiatura", + "Show Keyboard": "Pokaż klawiaturę", + "Extra keys": "Przyciski dodatkowe", + "Show Extra Keys": "Pokaż przyciski dodatkowe", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Przełącz Ctrl", + "Alt": "Alt", + "Toggle Alt": "Przełącz Alt", + "Send Tab": "Wyślij Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Wyślij Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Wyślij Ctrl-Alt-Del", + "Shutdown/Reboot": "Wyłącz/Uruchom ponownie", + "Shutdown/Reboot...": "Wyłącz/Uruchom ponownie...", + "Power": "Włączony", + "Shutdown": "Wyłącz", + "Reboot": "Uruchom ponownie", + "Reset": "Resetuj", + "Clipboard": "Schowek", + "Clear": "Wyczyść", + "Fullscreen": "Pełny ekran", + "Settings": "Ustawienia", + "Shared Mode": "Tryb Współdzielenia", + "View Only": "Tylko Podgląd", + "Clip to Window": "Przytnij do Okna", + "Scaling Mode:": "Tryb Skalowania:", + "None": "Brak", + "Local Scaling": "Skalowanie lokalne", + "Remote Resizing": "Skalowanie zdalne", + "Advanced": "Zaawansowane", + "Repeater ID:": "ID Repeatera:", + "WebSocket": "WebSocket", + "Encrypt": "Szyfrowanie", + "Host:": "Host:", + "Port:": "Port:", + "Path:": "Ścieżka:", + "Automatic Reconnect": "Automatycznie wznawiaj połączenie", + "Reconnect Delay (ms):": "Opóźnienie wznawiania (ms):", + "Logging:": "Poziom logowania:", + "Disconnect": "Rozłącz", + "Connect": "Połącz", + "Password:": "Hasło:", + "Cancel": "Anuluj", + "Canvas not supported.": "Element Canvas nie jest wspierany." +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/pt_BR.json b/farmq-admin/static/novnc/app/locale/pt_BR.json new file mode 100644 index 0000000..aa130f7 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/pt_BR.json @@ -0,0 +1,72 @@ +{ + "Connecting...": "Conectando...", + "Disconnecting...": "Desconectando...", + "Reconnecting...": "Reconectando...", + "Internal error": "Erro interno", + "Must set host": "É necessário definir o host", + "Connected (encrypted) to ": "Conectado (com criptografia) a ", + "Connected (unencrypted) to ": "Conectado (sem criptografia) a ", + "Something went wrong, connection is closed": "Algo deu errado. A conexão foi encerrada.", + "Failed to connect to server": "Falha ao conectar-se ao servidor", + "Disconnected": "Desconectado", + "New connection has been rejected with reason: ": "A nova conexão foi rejeitada pelo motivo: ", + "New connection has been rejected": "A nova conexão foi rejeitada", + "Credentials are required": "Credenciais são obrigatórias", + "noVNC encountered an error:": "O noVNC encontrou um erro:", + "Hide/Show the control bar": "Esconder/mostrar a barra de controles", + "Drag": "Arrastar", + "Move/Drag Viewport": "Mover/arrastar a janela", + "Keyboard": "Teclado", + "Show Keyboard": "Mostrar teclado", + "Extra keys": "Teclas adicionais", + "Show Extra Keys": "Mostar teclas adicionais", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Pressionar/soltar Ctrl", + "Alt": "Alt", + "Toggle Alt": "Pressionar/soltar Alt", + "Toggle Windows": "Pressionar/soltar Windows", + "Windows": "Windows", + "Send Tab": "Enviar Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Enviar Esc", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Enviar Ctrl-Alt-Del", + "Shutdown/Reboot": "Desligar/reiniciar", + "Shutdown/Reboot...": "Desligar/reiniciar...", + "Power": "Ligar", + "Shutdown": "Desligar", + "Reboot": "Reiniciar", + "Reset": "Reiniciar (forçado)", + "Clipboard": "Área de transferência", + "Clear": "Limpar", + "Fullscreen": "Tela cheia", + "Settings": "Configurações", + "Shared Mode": "Modo compartilhado", + "View Only": "Apenas visualizar", + "Clip to Window": "Recortar à janela", + "Scaling Mode:": "Modo de dimensionamento:", + "None": "Nenhum", + "Local Scaling": "Local", + "Remote Resizing": "Remoto", + "Advanced": "Avançado", + "Quality:": "Qualidade:", + "Compression level:": "Nível de compressão:", + "Repeater ID:": "ID do repetidor:", + "WebSocket": "WebSocket", + "Encrypt": "Criptografar", + "Host:": "Host:", + "Port:": "Porta:", + "Path:": "Caminho:", + "Automatic Reconnect": "Reconexão automática", + "Reconnect Delay (ms):": "Atraso da reconexão (ms)", + "Show Dot when No Cursor": "Mostrar ponto quando não há cursor", + "Logging:": "Registros:", + "Version:": "Versão:", + "Disconnect": "Desconectar", + "Connect": "Conectar", + "Username:": "Nome de usuário:", + "Password:": "Senha:", + "Send Credentials": "Enviar credenciais", + "Cancel": "Cancelar" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/ru.json b/farmq-admin/static/novnc/app/locale/ru.json new file mode 100644 index 0000000..cab9739 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/ru.json @@ -0,0 +1,72 @@ +{ + "Connecting...": "Подключение...", + "Disconnecting...": "Отключение...", + "Reconnecting...": "Переподключение...", + "Internal error": "Внутренняя ошибка", + "Must set host": "Задайте имя сервера или IP", + "Connected (encrypted) to ": "Подключено (с шифрованием) к ", + "Connected (unencrypted) to ": "Подключено (без шифрования) к ", + "Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано", + "Failed to connect to server": "Ошибка подключения к серверу", + "Disconnected": "Отключено", + "New connection has been rejected with reason: ": "Новое соединение отклонено по причине: ", + "New connection has been rejected": "Новое соединение отклонено", + "Credentials are required": "Требуются учетные данные", + "noVNC encountered an error:": "Ошибка noVNC: ", + "Hide/Show the control bar": "Скрыть/Показать контрольную панель", + "Drag": "Переместить", + "Move/Drag Viewport": "Переместить окно", + "Keyboard": "Клавиатура", + "Show Keyboard": "Показать клавиатуру", + "Extra keys": "Дополнительные Кнопки", + "Show Extra Keys": "Показать Дополнительные Кнопки", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Переключение нажатия Ctrl", + "Alt": "Alt", + "Toggle Alt": "Переключение нажатия Alt", + "Toggle Windows": "Переключение вкладок", + "Windows": "Вкладка", + "Send Tab": "Передать нажатие Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Передать нажатие Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del", + "Shutdown/Reboot": "Выключить/Перезагрузить", + "Shutdown/Reboot...": "Выключить/Перезагрузить...", + "Power": "Питание", + "Shutdown": "Выключить", + "Reboot": "Перезагрузить", + "Reset": "Сброс", + "Clipboard": "Буфер обмена", + "Clear": "Очистить", + "Fullscreen": "Во весь экран", + "Settings": "Настройки", + "Shared Mode": "Общий режим", + "View Only": "Только Просмотр", + "Clip to Window": "В окно", + "Scaling Mode:": "Масштаб:", + "None": "Нет", + "Local Scaling": "Локльный масштаб", + "Remote Resizing": "Удаленная перенастройка размера", + "Advanced": "Дополнительно", + "Quality:": "Качество", + "Compression level:": "Уровень Сжатия", + "Repeater ID:": "Идентификатор ID:", + "WebSocket": "WebSocket", + "Encrypt": "Шифрование", + "Host:": "Сервер:", + "Port:": "Порт:", + "Path:": "Путь:", + "Automatic Reconnect": "Автоматическое переподключение", + "Reconnect Delay (ms):": "Задержка переподключения (мс):", + "Show Dot when No Cursor": "Показать точку вместо курсора", + "Logging:": "Лог:", + "Version:": "Версия", + "Disconnect": "Отключение", + "Connect": "Подключение", + "Username:": "Имя Пользователя", + "Password:": "Пароль:", + "Send Credentials": "Передача Учетных Данных", + "Cancel": "Выход" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/sv.json b/farmq-admin/static/novnc/app/locale/sv.json new file mode 100644 index 0000000..077ef42 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/sv.json @@ -0,0 +1,80 @@ +{ + "HTTPS is required for full functionality": "HTTPS krävs för full funktionalitet", + "Connecting...": "Ansluter...", + "Disconnecting...": "Kopplar ner...", + "Reconnecting...": "Återansluter...", + "Internal error": "Internt fel", + "Must set host": "Du måste specifiera en värd", + "Connected (encrypted) to ": "Ansluten (krypterat) till ", + "Connected (unencrypted) to ": "Ansluten (okrypterat) till ", + "Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades", + "Failed to connect to server": "Misslyckades att ansluta till servern", + "Disconnected": "Frånkopplad", + "New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ", + "New connection has been rejected": "Ny anslutning har blivit nekad", + "Credentials are required": "Användaruppgifter krävs", + "noVNC encountered an error:": "noVNC stötte på ett problem:", + "Hide/Show the control bar": "Göm/Visa kontrollbaren", + "Drag": "Dra", + "Move/Drag Viewport": "Flytta/Dra Vyn", + "Keyboard": "Tangentbord", + "Show Keyboard": "Visa Tangentbord", + "Extra keys": "Extraknappar", + "Show Extra Keys": "Visa Extraknappar", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Växla Ctrl", + "Alt": "Alt", + "Toggle Alt": "Växla Alt", + "Toggle Windows": "Växla Windows", + "Windows": "Windows", + "Send Tab": "Skicka Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Skicka Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del", + "Shutdown/Reboot": "Stäng av/Boota om", + "Shutdown/Reboot...": "Stäng av/Boota om...", + "Power": "Ström", + "Shutdown": "Stäng av", + "Reboot": "Boota om", + "Reset": "Återställ", + "Clipboard": "Urklipp", + "Edit clipboard content in the textarea below.": "Redigera urklippets innehåll i fältet nedan.", + "Full Screen": "Fullskärm", + "Settings": "Inställningar", + "Shared Mode": "Delat Läge", + "View Only": "Endast Visning", + "Clip to Window": "Begränsa till Fönster", + "Scaling Mode:": "Skalningsläge:", + "None": "Ingen", + "Local Scaling": "Lokal Skalning", + "Remote Resizing": "Ändra Storlek", + "Advanced": "Avancerat", + "Quality:": "Kvalitet:", + "Compression level:": "Kompressionsnivå:", + "Repeater ID:": "Repeater-ID:", + "WebSocket": "WebSocket", + "Encrypt": "Kryptera", + "Host:": "Värd:", + "Port:": "Port:", + "Path:": "Sökväg:", + "Automatic Reconnect": "Automatisk Återanslutning", + "Reconnect Delay (ms):": "Fördröjning (ms):", + "Show Dot when No Cursor": "Visa prick när ingen muspekare finns", + "Logging:": "Loggning:", + "Version:": "Version:", + "Disconnect": "Koppla från", + "Connect": "Anslut", + "Server identity": "Server-identitet", + "The server has provided the following identifying information:": "Servern har gett följande identifierande information:", + "Fingerprint:": "Fingeravtryck:", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck annars \"Neka\".", + "Approve": "Godkänn", + "Reject": "Neka", + "Credentials": "Användaruppgifter", + "Username:": "Användarnamn:", + "Password:": "Lösenord:", + "Send Credentials": "Skicka Användaruppgifter", + "Cancel": "Avbryt" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/tr.json b/farmq-admin/static/novnc/app/locale/tr.json new file mode 100644 index 0000000..451c1b8 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/tr.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "Bağlanıyor...", + "Disconnecting...": "Bağlantı kesiliyor...", + "Reconnecting...": "Yeniden bağlantı kuruluyor...", + "Internal error": "İç hata", + "Must set host": "Sunucuyu kur", + "Connected (encrypted) to ": "Bağlı (şifrelenmiş)", + "Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)", + "Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi", + "Disconnected": "Bağlantı kesildi", + "New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ", + "New connection has been rejected": "Bağlantı reddedildi", + "Password is required": "Şifre gerekli", + "noVNC encountered an error:": "Bir hata oluştu:", + "Hide/Show the control bar": "Denetim masasını Gizle/Göster", + "Move/Drag Viewport": "Görünümü Taşı/Sürükle", + "viewport drag": "Görüntü penceresini sürükle", + "Active Mouse Button": "Aktif Fare Düğmesi", + "No mousebutton": "Fare düğmesi yok", + "Left mousebutton": "Farenin sol düğmesi", + "Middle mousebutton": "Farenin orta düğmesi", + "Right mousebutton": "Farenin sağ düğmesi", + "Keyboard": "Klavye", + "Show Keyboard": "Klavye Düzenini Göster", + "Extra keys": "Ekstra tuşlar", + "Show Extra Keys": "Ekstra tuşları göster", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Ctrl Değiştir ", + "Alt": "Alt", + "Toggle Alt": "Alt Değiştir", + "Send Tab": "Sekme Gönder", + "Tab": "Sekme", + "Esc": "Esc", + "Send Escape": "Boşluk Gönder", + "Ctrl+Alt+Del": "Ctrl + Alt + Del", + "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder", + "Shutdown/Reboot": "Kapat/Yeniden Başlat", + "Shutdown/Reboot...": "Kapat/Yeniden Başlat...", + "Power": "Güç", + "Shutdown": "Kapat", + "Reboot": "Yeniden Başlat", + "Reset": "Sıfırla", + "Clipboard": "Pano", + "Clear": "Temizle", + "Fullscreen": "Tam Ekran", + "Settings": "Ayarlar", + "Shared Mode": "Paylaşım Modu", + "View Only": "Sadece Görüntüle", + "Clip to Window": "Pencereye Tıkla", + "Scaling Mode:": "Ölçekleme Modu:", + "None": "Bilinmeyen", + "Local Scaling": "Yerel Ölçeklendirme", + "Remote Resizing": "Uzaktan Yeniden Boyutlandırma", + "Advanced": "Gelişmiş", + "Repeater ID:": "Tekralayıcı ID:", + "WebSocket": "WebSocket", + "Encrypt": "Şifrele", + "Host:": "Ana makine:", + "Port:": "Port:", + "Path:": "Yol:", + "Automatic Reconnect": "Otomatik Yeniden Bağlan", + "Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):", + "Logging:": "Giriş yapılıyor:", + "Disconnect": "Bağlantıyı Kes", + "Connect": "Bağlan", + "Password:": "Parola:", + "Cancel": "Vazgeç", + "Canvas not supported.": "Tuval desteklenmiyor." +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/zh_CN.json b/farmq-admin/static/novnc/app/locale/zh_CN.json new file mode 100644 index 0000000..f0aea9a --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/zh_CN.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "连接中...", + "Disconnecting...": "正在断开连接...", + "Reconnecting...": "重新连接中...", + "Internal error": "内部错误", + "Must set host": "请提供主机名", + "Connected (encrypted) to ": "已连接到(加密)", + "Connected (unencrypted) to ": "已连接到(未加密)", + "Something went wrong, connection is closed": "发生错误,连接已关闭", + "Failed to connect to server": "无法连接到服务器", + "Disconnected": "已断开连接", + "New connection has been rejected with reason: ": "连接被拒绝,原因:", + "New connection has been rejected": "连接被拒绝", + "Password is required": "请提供密码", + "noVNC encountered an error:": "noVNC 遇到一个错误:", + "Hide/Show the control bar": "显示/隐藏控制栏", + "Move/Drag Viewport": "拖放显示范围", + "viewport drag": "显示范围拖放", + "Active Mouse Button": "启动鼠标按鍵", + "No mousebutton": "禁用鼠标按鍵", + "Left mousebutton": "鼠标左鍵", + "Middle mousebutton": "鼠标中鍵", + "Right mousebutton": "鼠标右鍵", + "Keyboard": "键盘", + "Show Keyboard": "显示键盘", + "Extra keys": "额外按键", + "Show Extra Keys": "显示额外按键", + "Ctrl": "Ctrl", + "Toggle Ctrl": "切换 Ctrl", + "Alt": "Alt", + "Toggle Alt": "切换 Alt", + "Send Tab": "发送 Tab 键", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "发送 Escape 键", + "Ctrl+Alt+Del": "Ctrl-Alt-Del", + "Send Ctrl-Alt-Del": "发送 Ctrl-Alt-Del 键", + "Shutdown/Reboot": "关机/重新启动", + "Shutdown/Reboot...": "关机/重新启动...", + "Power": "电源", + "Shutdown": "关机", + "Reboot": "重新启动", + "Reset": "重置", + "Clipboard": "剪贴板", + "Clear": "清除", + "Fullscreen": "全屏", + "Settings": "设置", + "Shared Mode": "分享模式", + "View Only": "仅查看", + "Clip to Window": "限制/裁切窗口大小", + "Scaling Mode:": "缩放模式:", + "None": "无", + "Local Scaling": "本地缩放", + "Remote Resizing": "远程调整大小", + "Advanced": "高级", + "Repeater ID:": "中继站 ID", + "WebSocket": "WebSocket", + "Encrypt": "加密", + "Host:": "主机:", + "Port:": "端口:", + "Path:": "路径:", + "Automatic Reconnect": "自动重新连接", + "Reconnect Delay (ms):": "重新连接间隔 (ms):", + "Logging:": "日志级别:", + "Disconnect": "中断连接", + "Connect": "连接", + "Password:": "密码:", + "Cancel": "取消" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/locale/zh_TW.json b/farmq-admin/static/novnc/app/locale/zh_TW.json new file mode 100644 index 0000000..8ddf813 --- /dev/null +++ b/farmq-admin/static/novnc/app/locale/zh_TW.json @@ -0,0 +1,69 @@ +{ + "Connecting...": "連線中...", + "Disconnecting...": "正在中斷連線...", + "Reconnecting...": "重新連線中...", + "Internal error": "內部錯誤", + "Must set host": "請提供主機資訊", + "Connected (encrypted) to ": "已加密連線到", + "Connected (unencrypted) to ": "未加密連線到", + "Something went wrong, connection is closed": "發生錯誤,連線已關閉", + "Failed to connect to server": "無法連線到伺服器", + "Disconnected": "連線已中斷", + "New connection has been rejected with reason: ": "連線被拒絕,原因:", + "New connection has been rejected": "連線被拒絕", + "Password is required": "請提供密碼", + "noVNC encountered an error:": "noVNC 遇到一個錯誤:", + "Hide/Show the control bar": "顯示/隱藏控制列", + "Move/Drag Viewport": "拖放顯示範圍", + "viewport drag": "顯示範圍拖放", + "Active Mouse Button": "啟用滑鼠按鍵", + "No mousebutton": "無滑鼠按鍵", + "Left mousebutton": "滑鼠左鍵", + "Middle mousebutton": "滑鼠中鍵", + "Right mousebutton": "滑鼠右鍵", + "Keyboard": "鍵盤", + "Show Keyboard": "顯示鍵盤", + "Extra keys": "額外按鍵", + "Show Extra Keys": "顯示額外按鍵", + "Ctrl": "Ctrl", + "Toggle Ctrl": "切換 Ctrl", + "Alt": "Alt", + "Toggle Alt": "切換 Alt", + "Send Tab": "送出 Tab 鍵", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "送出 Escape 鍵", + "Ctrl+Alt+Del": "Ctrl-Alt-Del", + "Send Ctrl-Alt-Del": "送出 Ctrl-Alt-Del 快捷鍵", + "Shutdown/Reboot": "關機/重新啟動", + "Shutdown/Reboot...": "關機/重新啟動...", + "Power": "電源", + "Shutdown": "關機", + "Reboot": "重新啟動", + "Reset": "重設", + "Clipboard": "剪貼簿", + "Clear": "清除", + "Fullscreen": "全螢幕", + "Settings": "設定", + "Shared Mode": "分享模式", + "View Only": "僅檢視", + "Clip to Window": "限制/裁切視窗大小", + "Scaling Mode:": "縮放模式:", + "None": "無", + "Local Scaling": "本機縮放", + "Remote Resizing": "遠端調整大小", + "Advanced": "進階", + "Repeater ID:": "中繼站 ID", + "WebSocket": "WebSocket", + "Encrypt": "加密", + "Host:": "主機:", + "Port:": "連接埠:", + "Path:": "路徑:", + "Automatic Reconnect": "自動重新連線", + "Reconnect Delay (ms):": "重新連線間隔 (ms):", + "Logging:": "日誌級別:", + "Disconnect": "中斷連線", + "Connect": "連線", + "Password:": "密碼:", + "Cancel": "取消" +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/app/localization.js b/farmq-admin/static/novnc/app/localization.js new file mode 100644 index 0000000..84341da --- /dev/null +++ b/farmq-admin/static/novnc/app/localization.js @@ -0,0 +1,179 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2018 The noVNC Authors + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/* + * Localization Utilities + */ + +export class Localizer { + constructor() { + // Currently configured language + this.language = 'en'; + + // Current dictionary of translations + this.dictionary = undefined; + } + + // Configure suitable language based on user preferences + setup(supportedLanguages) { + this.language = 'en'; // Default: US English + + /* + * Navigator.languages only available in Chrome (32+) and FireFox (32+) + * Fall back to navigator.language for other browsers + */ + let userLanguages; + if (typeof window.navigator.languages == 'object') { + userLanguages = window.navigator.languages; + } else { + userLanguages = [navigator.language || navigator.userLanguage]; + } + + for (let i = 0;i < userLanguages.length;i++) { + const userLang = userLanguages[i] + .toLowerCase() + .replace("_", "-") + .split("-"); + + // Built-in default? + if ((userLang[0] === 'en') && + ((userLang[1] === undefined) || (userLang[1] === 'us'))) { + return; + } + + // First pass: perfect match + for (let j = 0; j < supportedLanguages.length; j++) { + const supLang = supportedLanguages[j] + .toLowerCase() + .replace("_", "-") + .split("-"); + + if (userLang[0] !== supLang[0]) { + continue; + } + if (userLang[1] !== supLang[1]) { + continue; + } + + this.language = supportedLanguages[j]; + return; + } + + // Second pass: fallback + for (let j = 0;j < supportedLanguages.length;j++) { + const supLang = supportedLanguages[j] + .toLowerCase() + .replace("_", "-") + .split("-"); + + if (userLang[0] !== supLang[0]) { + continue; + } + if (supLang[1] !== undefined) { + continue; + } + + this.language = supportedLanguages[j]; + return; + } + } + } + + // Retrieve localised text + get(id) { + if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) { + return this.dictionary[id]; + } else { + return id; + } + } + + // Traverses the DOM and translates relevant fields + // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate + translateDOM() { + const self = this; + + function process(elem, enabled) { + function isAnyOf(searchElement, items) { + return items.indexOf(searchElement) !== -1; + } + + function translateString(str) { + // We assume surrounding whitespace, and whitespace around line + // breaks is just for source formatting + str = str.split("\n").map(s => s.trim()).join(" ").trim(); + return self.get(str); + } + + function translateAttribute(elem, attr) { + const str = translateString(elem.getAttribute(attr)); + elem.setAttribute(attr, str); + } + + function translateTextNode(node) { + const str = translateString(node.data); + node.data = str; + } + + if (elem.hasAttribute("translate")) { + if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) { + enabled = true; + } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) { + enabled = false; + } + } + + if (enabled) { + if (elem.hasAttribute("abbr") && + elem.tagName === "TH") { + translateAttribute(elem, "abbr"); + } + if (elem.hasAttribute("alt") && + isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) { + translateAttribute(elem, "alt"); + } + if (elem.hasAttribute("download") && + isAnyOf(elem.tagName, ["A", "AREA"])) { + translateAttribute(elem, "download"); + } + if (elem.hasAttribute("label") && + isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", + "OPTION", "TRACK"])) { + translateAttribute(elem, "label"); + } + // FIXME: Should update "lang" + if (elem.hasAttribute("placeholder") && + isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) { + translateAttribute(elem, "placeholder"); + } + if (elem.hasAttribute("title")) { + translateAttribute(elem, "title"); + } + if (elem.hasAttribute("value") && + elem.tagName === "INPUT" && + isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) { + translateAttribute(elem, "value"); + } + } + + for (let i = 0; i < elem.childNodes.length; i++) { + const node = elem.childNodes[i]; + if (node.nodeType === node.ELEMENT_NODE) { + process(node, enabled); + } else if (node.nodeType === node.TEXT_NODE && enabled) { + translateTextNode(node); + } + } + } + + process(document.body, true); + } +} + +export const l10n = new Localizer(); +export default l10n.get.bind(l10n); diff --git a/farmq-admin/static/novnc/app/sounds/CREDITS b/farmq-admin/static/novnc/app/sounds/CREDITS new file mode 100644 index 0000000..ec1fb55 --- /dev/null +++ b/farmq-admin/static/novnc/app/sounds/CREDITS @@ -0,0 +1,4 @@ +bell + Copyright: Dr. Richard Boulanger et al + URL: http://www.archive.org/details/Berklee44v12 + License: CC-BY Attribution 3.0 Unported diff --git a/farmq-admin/static/novnc/app/sounds/bell.mp3 b/farmq-admin/static/novnc/app/sounds/bell.mp3 new file mode 100644 index 0000000..fdbf149 Binary files /dev/null and b/farmq-admin/static/novnc/app/sounds/bell.mp3 differ diff --git a/farmq-admin/static/novnc/app/sounds/bell.oga b/farmq-admin/static/novnc/app/sounds/bell.oga new file mode 100644 index 0000000..144d2b3 Binary files /dev/null and b/farmq-admin/static/novnc/app/sounds/bell.oga differ diff --git a/farmq-admin/static/novnc/app/styles/Orbitron700.ttf b/farmq-admin/static/novnc/app/styles/Orbitron700.ttf new file mode 100644 index 0000000..e28729d Binary files /dev/null and b/farmq-admin/static/novnc/app/styles/Orbitron700.ttf differ diff --git a/farmq-admin/static/novnc/app/styles/Orbitron700.woff b/farmq-admin/static/novnc/app/styles/Orbitron700.woff new file mode 100644 index 0000000..61db630 Binary files /dev/null and b/farmq-admin/static/novnc/app/styles/Orbitron700.woff differ diff --git a/farmq-admin/static/novnc/app/styles/base.css b/farmq-admin/static/novnc/app/styles/base.css new file mode 100644 index 0000000..06e736a --- /dev/null +++ b/farmq-admin/static/novnc/app/styles/base.css @@ -0,0 +1,922 @@ +/* + * noVNC base CSS + * Copyright (C) 2019 The noVNC Authors + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +/* + * Z index layers: + * + * 0: Main screen + * 10: Control bar + * 50: Transition blocker + * 60: Connection popups + * 100: Status bar + * ... + * 1000: Javascript crash + * ... + * 10000: Max (used for polyfills) + */ + +/* + * State variables (set on :root): + * + * noVNC_loading: Page is still loading + * noVNC_connecting: Connecting to server + * noVNC_reconnecting: Re-establishing a connection + * noVNC_connected: Connected to server (most common state) + * noVNC_disconnecting: Disconnecting from server + */ + +:root { + font-family: sans-serif; +} + +body { + margin:0; + padding:0; + /*Background image with light grey curve.*/ + background-color:#494949; + background-repeat:no-repeat; + background-position:right bottom; + height:100%; + touch-action: none; +} + +html { + height:100%; +} + +.noVNC_only_touch.noVNC_hidden { + display: none; +} + +.noVNC_disabled { + color: rgb(128, 128, 128); +} + +/* ---------------------------------------- + * Spinner + * ---------------------------------------- + */ + +.noVNC_spinner { + position: relative; +} +.noVNC_spinner, .noVNC_spinner::before, .noVNC_spinner::after { + width: 10px; + height: 10px; + border-radius: 2px; + box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); + animation: noVNC_spinner 1.0s linear infinite; +} +.noVNC_spinner::before { + content: ""; + position: absolute; + left: 0px; + top: 0px; + animation-delay: -0.1s; +} +.noVNC_spinner::after { + content: ""; + position: absolute; + top: 0px; + left: 0px; + animation-delay: 0.1s; +} +@keyframes noVNC_spinner { + 0% { box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); width: 20px; } + 25% { box-shadow: 20px 10px 0 rgba(255, 255, 255, 1); width: 10px; } + 50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; } +} + +/* ---------------------------------------- + * WebKit centering hacks + * ---------------------------------------- + */ + +.noVNC_center { + /* + * This is a workaround because webkit misrenders transforms and + * uses non-integer coordinates, resulting in blurry content. + * Ideally we'd use "top: 50%; transform: translateY(-50%);" on + * the objects instead. + */ + display: flex; + align-items: center; + justify-content: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; +} +.noVNC_center > * { + pointer-events: auto; +} +.noVNC_vcenter { + display: flex !important; + flex-direction: column; + justify-content: center; + position: fixed; + top: 0; + left: 0; + height: 100%; + margin: 0 !important; + padding: 0 !important; + pointer-events: none; +} +.noVNC_vcenter > * { + pointer-events: auto; +} + +/* ---------------------------------------- + * Layering + * ---------------------------------------- + */ + +.noVNC_connect_layer { + z-index: 60; +} + +/* ---------------------------------------- + * Fallback error + * ---------------------------------------- + */ + +#noVNC_fallback_error { + z-index: 1000; + visibility: hidden; + /* Put a dark background in front of everything but the error, + and don't let mouse events pass through */ + background: rgba(0, 0, 0, 0.8); + pointer-events: all; +} +#noVNC_fallback_error.noVNC_open { + visibility: visible; +} + +#noVNC_fallback_error > div { + max-width: calc(100vw - 30px - 30px); + max-height: calc(100vh - 30px - 30px); + overflow: auto; + + padding: 15px; + + transition: 0.5s ease-in-out; + + transform: translateY(-50px); + opacity: 0; + + text-align: center; + font-weight: bold; + color: #fff; + + border-radius: 10px; + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); + background: rgba(200,55,55,0.8); +} +#noVNC_fallback_error.noVNC_open > div { + transform: translateY(0); + opacity: 1; +} + +#noVNC_fallback_errormsg { + font-weight: normal; +} + +#noVNC_fallback_errormsg .noVNC_message { + display: inline-block; + text-align: left; + font-family: monospace; + white-space: pre-wrap; +} + +#noVNC_fallback_error .noVNC_location { + font-style: italic; + font-size: 0.8em; + color: rgba(255, 255, 255, 0.8); +} + +#noVNC_fallback_error .noVNC_stack { + padding: 10px; + margin: 10px; + font-size: 0.8em; + text-align: left; + font-family: monospace; + white-space: pre; + border: 1px solid rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, 0.2); + overflow: auto; +} + +/* ---------------------------------------- + * Control Bar + * ---------------------------------------- + */ + +#noVNC_control_bar_anchor { + /* The anchor is needed to get z-stacking to work */ + position: fixed; + z-index: 10; + + transition: 0.5s ease-in-out; + + /* Edge misrenders animations wihthout this */ + transform: translateX(0); +} +:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle { + opacity: 0.8; +} +#noVNC_control_bar_anchor.noVNC_right { + left: auto; + right: 0; +} + +#noVNC_control_bar { + position: relative; + left: -100%; + + transition: 0.5s ease-in-out; + + background-color: rgb(110, 132, 163); + border-radius: 0 10px 10px 0; + + user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; /* Disable iOS image long-press popup */ +} +#noVNC_control_bar.noVNC_open { + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); + left: 0; +} +#noVNC_control_bar::before { + /* This extra element is to get a proper shadow */ + content: ""; + position: absolute; + z-index: -1; + height: 100%; + width: 30px; + left: -30px; + transition: box-shadow 0.5s ease-in-out; +} +#noVNC_control_bar.noVNC_open::before { + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +.noVNC_right #noVNC_control_bar { + left: 100%; + border-radius: 10px 0 0 10px; +} +.noVNC_right #noVNC_control_bar.noVNC_open { + left: 0; +} +.noVNC_right #noVNC_control_bar::before { + visibility: hidden; +} + +#noVNC_control_bar_handle { + position: absolute; + left: -15px; + top: 0; + transform: translateY(35px); + width: calc(100% + 30px); + height: 50px; + z-index: -1; + cursor: pointer; + border-radius: 5px; + background-color: rgb(83, 99, 122); + background-image: url("../images/handle_bg.svg"); + background-repeat: no-repeat; + background-position: right; + box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5); +} +#noVNC_control_bar_handle:after { + content: ""; + transition: transform 0.5s ease-in-out; + background: url("../images/handle.svg"); + position: absolute; + top: 22px; /* (50px-6px)/2 */ + right: 5px; + width: 5px; + height: 6px; +} +#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { + transform: translateX(1px) rotate(180deg); +} +:root:not(.noVNC_connected) #noVNC_control_bar_handle { + display: none; +} +.noVNC_right #noVNC_control_bar_handle { + background-position: left; +} +.noVNC_right #noVNC_control_bar_handle:after { + left: 5px; + right: 0; + transform: translateX(1px) rotate(180deg); +} +.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { + transform: none; +} +/* Larger touch area for the handle, used when a touch screen is available */ +#noVNC_control_bar_handle div { + position: absolute; + right: -35px; + top: 0; + width: 50px; + height: 100%; + display: none; +} +@media (any-pointer: coarse) { + #noVNC_control_bar_handle div { + display: initial; + } +} +.noVNC_right #noVNC_control_bar_handle div { + left: -35px; + right: auto; +} + +#noVNC_control_bar > .noVNC_scroll { + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + padding: 0 10px; +} + +#noVNC_control_bar > .noVNC_scroll > * { + display: block; + margin: 10px auto; +} + +/* Control bar hint */ +#noVNC_hint_anchor { + position: fixed; + right: -50px; + left: auto; +} +#noVNC_control_bar_anchor.noVNC_right + #noVNC_hint_anchor { + left: -50px; + right: auto; +} +#noVNC_control_bar_hint { + position: relative; + transform: scale(0); + width: 100px; + height: 50%; + max-height: 600px; + + visibility: hidden; + opacity: 0; + transition: 0.2s ease-in-out; + background: transparent; + box-shadow: 0 0 10px black, inset 0 0 10px 10px rgba(110, 132, 163, 0.8); + border-radius: 10px; + transition-delay: 0s; +} +#noVNC_control_bar_hint.noVNC_active { + visibility: visible; + opacity: 1; + transition-delay: 0.2s; + transform: scale(1); +} +#noVNC_control_bar_hint.noVNC_notransition { + transition: none !important; +} + +/* Control bar buttons */ +#noVNC_control_bar .noVNC_button { + padding: 4px 4px; + vertical-align: middle; + border:1px solid rgba(255, 255, 255, 0.2); + border-radius: 6px; + background-color: transparent; + background-image: unset; /* we don't want the gradiant from input.css */ +} +#noVNC_control_bar .noVNC_button.noVNC_selected { + border-color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, 0.5); +} +#noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.2); +} +#noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: rgba(255, 255, 255, 0.2); +} +#noVNC_control_bar .noVNC_button:not(:disabled):active { + padding-top: 5px; + padding-bottom: 3px; +} +#noVNC_control_bar .noVNC_button.noVNC_hidden { + display: none !important; +} + +/* Android browsers don't properly update hover state if touch events are + * intercepted, like they are when clicking on the remote screen. */ +@media (any-pointer: coarse) { + #noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: transparent; + } + #noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, 0.5); + } +} + + +/* Panels */ +.noVNC_panel { + transform: translateX(25px); + + transition: 0.5s ease-in-out; + + box-sizing: border-box; /* so max-width don't have to care about padding */ + max-width: calc(100vw - 75px - 25px); /* minus left and right margins */ + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + + visibility: hidden; + opacity: 0; + + padding: 15px; + + background: #fff; + border-radius: 10px; + color: #000; + border: 2px solid #E0E0E0; + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +.noVNC_panel.noVNC_open { + visibility: visible; + opacity: 1; + transform: translateX(75px); +} +.noVNC_right .noVNC_vcenter { + left: auto; + right: 0; +} +.noVNC_right .noVNC_panel { + transform: translateX(-25px); +} +.noVNC_right .noVNC_panel.noVNC_open { + transform: translateX(-75px); +} + +.noVNC_panel > * { + display: block; + margin: 10px auto; +} +.noVNC_panel > *:first-child { + margin-top: 0 !important; +} +.noVNC_panel > *:last-child { + margin-bottom: 0 !important; +} + +.noVNC_panel hr { + border: none; + border-top: 1px solid rgb(192, 192, 192); +} + +.noVNC_panel label { + display: block; + white-space: nowrap; + margin: 5px; +} + +.noVNC_panel li { + margin: 5px; +} + +.noVNC_panel .noVNC_heading { + background-color: rgb(110, 132, 163); + border-radius: 5px; + padding: 5px; + /* Compensate for padding in image */ + padding-right: 8px; + color: white; + font-size: 20px; + white-space: nowrap; +} +.noVNC_panel .noVNC_heading img { + vertical-align: bottom; +} + +.noVNC_submit { + float: right; +} + +/* Expanders */ +.noVNC_expander { + cursor: pointer; +} +.noVNC_expander::before { + content: url("../images/expander.svg"); + display: inline-block; + margin-right: 5px; + transition: 0.2s ease-in-out; +} +.noVNC_expander.noVNC_open::before { + transform: rotateZ(90deg); +} +.noVNC_expander ~ * { + margin: 5px; + margin-left: 10px; + padding: 5px; + background: rgba(0, 0, 0, 0.05); + border-radius: 5px; +} +.noVNC_expander:not(.noVNC_open) ~ * { + display: none; +} + +/* Control bar content */ + +#noVNC_control_bar .noVNC_logo { + font-size: 13px; +} + +.noVNC_logo + hr { + /* Remove all but top border */ + border: none; + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + +:root:not(.noVNC_connected) #noVNC_view_drag_button { + display: none; +} + +/* noVNC Touch Device only buttons */ +:root:not(.noVNC_connected) #noVNC_mobile_buttons { + display: none; +} +@media not all and (any-pointer: coarse) { + /* FIXME: The button for the virtual keyboard is the only button in this + group of "mobile buttons". It is bad to assume that no touch + devices have physical keyboards available. Hopefully we can get + a media query for this: + https://github.com/w3c/csswg-drafts/issues/3871 */ + :root.noVNC_connected #noVNC_mobile_buttons { + display: none; + } +} + +/* Extra manual keys */ +:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button { + display: none; +} + +#noVNC_modifiers { + background-color: rgb(92, 92, 92); + border: none; + padding: 10px; +} + +/* Shutdown/Reboot */ +:root:not(.noVNC_connected) #noVNC_power_button { + display: none; +} +#noVNC_power { +} +#noVNC_power_buttons { + display: none; +} + +#noVNC_power input[type=button] { + width: 100%; +} + +/* Clipboard */ +:root:not(.noVNC_connected) #noVNC_clipboard_button { + display: none; +} +#noVNC_clipboard_text { + width: 360px; + min-width: 150px; + height: 160px; + min-height: 70px; + + box-sizing: border-box; + max-width: 100%; + /* minus approximate height of title, height of subtitle, and margin */ + max-height: calc(100vh - 10em - 25px); +} + +/* Settings */ +#noVNC_settings { +} +#noVNC_settings ul { + list-style: none; + padding: 0px; +} +#noVNC_setting_port { + width: 80px; +} +#noVNC_setting_path { + width: 100px; +} + +/* Version */ + +.noVNC_version_wrapper { + font-size: small; +} + +.noVNC_version { + margin-left: 1rem; +} + +/* Connection Controls */ +:root:not(.noVNC_connected) #noVNC_disconnect_button { + display: none; +} + +/* ---------------------------------------- + * Status Dialog + * ---------------------------------------- + */ + +#noVNC_status { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 100; + transform: translateY(-100%); + + cursor: pointer; + + transition: 0.5s ease-in-out; + + visibility: hidden; + opacity: 0; + + padding: 5px; + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + + line-height: 25px; + word-wrap: break-word; + color: #fff; + + border-bottom: 1px solid rgba(0, 0, 0, 0.9); +} +#noVNC_status.noVNC_open { + transform: translateY(0); + visibility: visible; + opacity: 1; +} + +#noVNC_status::before { + content: ""; + display: inline-block; + width: 25px; + height: 25px; + margin-right: 5px; +} + +#noVNC_status.noVNC_status_normal { + background: rgba(128,128,128,0.9); +} +#noVNC_status.noVNC_status_normal::before { + content: url("../images/info.svg") " "; +} +#noVNC_status.noVNC_status_error { + background: rgba(200,55,55,0.9); +} +#noVNC_status.noVNC_status_error::before { + content: url("../images/error.svg") " "; +} +#noVNC_status.noVNC_status_warn { + background: rgba(180,180,30,0.9); +} +#noVNC_status.noVNC_status_warn::before { + content: url("../images/warning.svg") " "; +} + +/* ---------------------------------------- + * Connect Dialog + * ---------------------------------------- + */ + +#noVNC_connect_dlg { + transition: 0.5s ease-in-out; + + transform: scale(0, 0); + visibility: hidden; + opacity: 0; +} +#noVNC_connect_dlg.noVNC_open { + transform: scale(1, 1); + visibility: visible; + opacity: 1; +} +#noVNC_connect_dlg .noVNC_logo { + transition: 0.5s ease-in-out; + padding: 10px; + margin-bottom: 10px; + + font-size: 80px; + text-align: center; + + border-radius: 5px; +} +@media (max-width: 440px) { + #noVNC_connect_dlg { + max-width: calc(100vw - 100px); + } + #noVNC_connect_dlg .noVNC_logo { + font-size: calc(25vw - 30px); + } +} +#noVNC_connect_dlg div { + padding: 12px; + + background-color: rgb(110, 132, 163); + border-radius: 12px; + text-align: center; + font-size: 20px; + + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +#noVNC_connect_button { + width: 100%; + padding: 5px 30px; + + cursor: pointer; + + border-color: rgb(83, 99, 122); + border-radius: 5px; + + background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147)); + color: white; + + /* This avoids it jumping around when :active */ + vertical-align: middle; +} +#noVNC_connect_button:hover { + background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155)); +} + +#noVNC_connect_button img { + vertical-align: bottom; + height: 1.3em; +} + +/* ---------------------------------------- + * Server verification Dialog + * ---------------------------------------- + */ + +#noVNC_verify_server_dlg { + position: relative; + + transform: translateY(-50px); +} +#noVNC_verify_server_dlg.noVNC_open { + transform: translateY(0); +} +#noVNC_fingerprint_block { + margin: 10px; +} + +/* ---------------------------------------- + * Password Dialog + * ---------------------------------------- + */ + +#noVNC_credentials_dlg { + position: relative; + + transform: translateY(-50px); +} +#noVNC_credentials_dlg.noVNC_open { + transform: translateY(0); +} +#noVNC_username_block.noVNC_hidden, +#noVNC_password_block.noVNC_hidden { + display: none; +} + + +/* ---------------------------------------- + * Main Area + * ---------------------------------------- + */ + +/* Transition screen */ +#noVNC_transition { + transition: 0.5s ease-in-out; + + display: flex; + opacity: 0; + visibility: hidden; + + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + + color: white; + background: rgba(0, 0, 0, 0.5); + z-index: 50; + + /*display: flex;*/ + align-items: center; + justify-content: center; + flex-direction: column; +} +:root.noVNC_loading #noVNC_transition, +:root.noVNC_connecting #noVNC_transition, +:root.noVNC_disconnecting #noVNC_transition, +:root.noVNC_reconnecting #noVNC_transition { + opacity: 1; + visibility: visible; +} +:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button { + display: none; +} +#noVNC_transition_text { + font-size: 1.5em; +} + +/* Main container */ +#noVNC_container { + width: 100%; + height: 100%; + background-color: #313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ + + /* If selection isn't disabled, long-pressing stuff in the sidebar + can accidentally select the container or the canvas. This can + happen when attempting to move the handle. */ + user-select: none; + -webkit-user-select: none; +} + +#noVNC_keyboardinput { + width: 1px; + height: 1px; + background-color: #fff; + color: #fff; + border: 0; + position: absolute; + left: -40px; + z-index: -1; + ime-mode: disabled; +} + +/*Default noVNC logo.*/ +/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 700; + src: local('?'), url('Orbitron700.woff') format('woff'), + url('Orbitron700.ttf') format('truetype'); +} + +.noVNC_logo { + color:yellow; + font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + line-height:90%; + text-shadow: 0.1em 0.1em 0 black; +} +.noVNC_logo span{ + color:green; +} + +#noVNC_bell { + display: none; +} + +/* ---------------------------------------- + * Media sizing + * ---------------------------------------- + */ + +@media screen and (max-width: 640px){ + #noVNC_logo { + font-size: 150px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { + #noVNC_logo { + font-size: 110px; + } +} + +@media screen and (max-width: 320px) { + #noVNC_logo { + font-size: 90px; + } +} diff --git a/farmq-admin/static/novnc/app/styles/input.css b/farmq-admin/static/novnc/app/styles/input.css new file mode 100644 index 0000000..eaf083c --- /dev/null +++ b/farmq-admin/static/novnc/app/styles/input.css @@ -0,0 +1,281 @@ +/* + * noVNC general input element CSS + * Copyright (C) 2022 The noVNC Authors + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +/* + * Common for all inputs + */ +input, input::file-selector-button, button, select, textarea { + /* Respect standard font settings */ + font: inherit; + + /* Disable default rendering */ + appearance: none; + background: none; + + padding: 5px; + border: 1px solid rgb(192, 192, 192); + border-radius: 5px; + color: black; + --bg-gradient: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); + background-image: var(--bg-gradient); +} + +/* + * Buttons + */ +input[type=button], +input[type=color], +input[type=image], +input[type=reset], +input[type=submit], +input::file-selector-button, +button, +select { + border-bottom-width: 2px; + + /* This avoids it jumping around when :active */ + vertical-align: middle; + margin-top: 0; + + padding-left: 20px; + padding-right: 20px; + + /* Disable Chrome's touch tap highlight */ + -webkit-tap-highlight-color: transparent; +} + +/* + * Select dropdowns + */ +select { + --select-arrow: url('data:image/svg+xml;utf8, \ + \ + \ + '); + background-image: var(--select-arrow), var(--bg-gradient); + background-position: calc(100% - 7px), left top; + background-repeat: no-repeat; + padding-right: calc(2*7px + 8px); + padding-left: 7px; +} +/* FIXME: :active isn't set when the   + Perftest:  + Realtime:   + +   + +

+ + Results:
+ + +

+ +
+
Loading
+
+ + diff --git a/farmq-admin/static/novnc/utils/.eslintrc b/farmq-admin/static/novnc/utils/.eslintrc new file mode 100644 index 0000000..b7dc129 --- /dev/null +++ b/farmq-admin/static/novnc/utils/.eslintrc @@ -0,0 +1,8 @@ +{ + "env": { + "node": true + }, + "rules": { + "no-console": 0 + } +} \ No newline at end of file diff --git a/farmq-admin/static/novnc/utils/README.md b/farmq-admin/static/novnc/utils/README.md new file mode 100644 index 0000000..05d75c9 --- /dev/null +++ b/farmq-admin/static/novnc/utils/README.md @@ -0,0 +1,14 @@ +## WebSockets Proxy/Bridge + +Websockify has been forked out into its own project. `novnc_proxy` will +automatically download it here if it is not already present and not +installed as system-wide. + +For more detailed description and usage information please refer to +the [websockify README](https://github.com/novnc/websockify/blob/master/README.md). + +The other versions of websockify (C, Node.js) and the associated test +programs have been moved to +[websockify](https://github.com/novnc/websockify). Websockify was +formerly named wsproxy. + diff --git a/farmq-admin/static/novnc/utils/b64-to-binary.pl b/farmq-admin/static/novnc/utils/b64-to-binary.pl new file mode 100755 index 0000000..280e28c --- /dev/null +++ b/farmq-admin/static/novnc/utils/b64-to-binary.pl @@ -0,0 +1,17 @@ +#!/usr/bin/env perl +use MIME::Base64; + +for (<>) { + unless (/^'([{}])(\d+)\1(.+?)',$/) { + print; + next; + } + + my ($dir, $amt, $b64) = ($1, $2, $3); + + my $decoded = MIME::Base64::decode($b64) or die "Could not base64-decode line `$_`"; + + my $decoded_escaped = join "", map { "\\x$_" } unpack("(H2)*", $decoded); + + print "'${dir}${amt}${dir}${decoded_escaped}',\n"; +} diff --git a/farmq-admin/static/novnc/utils/convert.js b/farmq-admin/static/novnc/utils/convert.js new file mode 100755 index 0000000..aeba49d --- /dev/null +++ b/farmq-admin/static/novnc/utils/convert.js @@ -0,0 +1,140 @@ +#!/usr/bin/env node + +const path = require('path'); +const program = require('commander'); +const fs = require('fs'); +const fse = require('fs-extra'); +const babel = require('@babel/core'); + +program + .option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ') + .option('--clean', 'clear the lib folder before building') + .parse(process.argv); + +// the various important paths +const paths = { + main: path.resolve(__dirname, '..'), + core: path.resolve(__dirname, '..', 'core'), + vendor: path.resolve(__dirname, '..', 'vendor'), + libDirBase: path.resolve(__dirname, '..', 'lib'), +}; + +// util.promisify requires Node.js 8.x, so we have our own +function promisify(original) { + return function promiseWrap() { + const args = Array.prototype.slice.call(arguments); + return new Promise((resolve, reject) => { + original.apply(this, args.concat((err, value) => { + if (err) return reject(err); + resolve(value); + })); + }); + }; +} + +const writeFile = promisify(fs.writeFile); + +const readdir = promisify(fs.readdir); +const lstat = promisify(fs.lstat); + +const ensureDir = promisify(fse.ensureDir); + +const babelTransformFile = promisify(babel.transformFile); + +// walkDir *recursively* walks directories trees, +// calling the callback for all normal files found. +function walkDir(basePath, cb, filter) { + return readdir(basePath) + .then((files) => { + const paths = files.map(filename => path.join(basePath, filename)); + return Promise.all(paths.map(filepath => lstat(filepath) + .then((stats) => { + if (filter !== undefined && !filter(filepath, stats)) return; + + if (stats.isSymbolicLink()) return; + if (stats.isFile()) return cb(filepath); + if (stats.isDirectory()) return walkDir(filepath, cb, filter); + }))); + }); +} + +function makeLibFiles(sourceMaps) { + // NB: we need to make a copy of babelOpts, since babel sets some defaults on it + const babelOpts = () => ({ + plugins: [], + presets: [ + [ '@babel/preset-env', + { modules: 'commonjs' } ] + ], + ast: false, + sourceMaps: sourceMaps, + }); + + fse.ensureDirSync(paths.libDirBase); + + const outFiles = []; + + const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve() + .then(() => { + const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename)); + + if (path.extname(filename) !== '.js') { + return; // skip non-javascript files + } + return Promise.resolve() + .then(() => ensureDir(path.dirname(outPath))) + .then(() => { + const opts = babelOpts(); + // Adjust for the fact that we move the core files relative + // to the vendor directory + if (vendorRewrite) { + opts.plugins.push(["import-redirect", + {"root": paths.libDirBase, + "redirect": { "vendor/(.+)": "./vendor/$1"}}]); + } + + return babelTransformFile(filename, opts) + .then((res) => { + console.log(`Writing ${outPath}`); + const {map} = res; + let {code} = res; + if (sourceMaps === true) { + // append URL for external source map + code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`; + } + outFiles.push(`${outPath}`); + return writeFile(outPath, code) + .then(() => { + if (sourceMaps === true || sourceMaps === 'both') { + console.log(` and ${outPath}.map`); + outFiles.push(`${outPath}.map`); + return writeFile(`${outPath}.map`, JSON.stringify(map)); + } + }); + }); + }); + }); + + Promise.resolve() + .then(() => { + const handler = handleDir.bind(null, false, paths.main); + return walkDir(paths.vendor, handler); + }) + .then(() => { + const handler = handleDir.bind(null, true, paths.core); + return walkDir(paths.core, handler); + }) + .catch((err) => { + console.error(`Failure converting modules: ${err}`); + process.exit(1); + }); +} + +let options = program.opts(); + +if (options.clean) { + console.log(`Removing ${paths.libDirBase}`); + fse.removeSync(paths.libDirBase); +} + +makeLibFiles(options.withSourceMaps); diff --git a/farmq-admin/static/novnc/utils/genkeysymdef.js b/farmq-admin/static/novnc/utils/genkeysymdef.js new file mode 100755 index 0000000..f539a0b --- /dev/null +++ b/farmq-admin/static/novnc/utils/genkeysymdef.js @@ -0,0 +1,127 @@ +#!/usr/bin/env node +/* + * genkeysymdef: X11 keysymdef.h to JavaScript converter + * Copyright (C) 2018 The noVNC Authors + * Licensed under MPL 2.0 (see LICENSE.txt) + */ + +"use strict"; + +const fs = require('fs'); + +let showHelp = process.argv.length === 2; +let filename; + +for (let i = 2; i < process.argv.length; ++i) { + switch (process.argv[i]) { + case "--help": + case "-h": + showHelp = true; + break; + case "--file": + case "-f": + default: + filename = process.argv[i]; + } +} + +if (!filename) { + showHelp = true; + console.log("Error: No filename specified\n"); +} + +if (showHelp) { + console.log("Parses a *nix keysymdef.h to generate Unicode code point mappings"); + console.log("Usage: node parse.js [options] filename:"); + console.log(" -h [ --help ] Produce this help message"); + console.log(" filename The keysymdef.h file to parse"); + process.exit(0); +} + +const buf = fs.readFileSync(filename); +const str = buf.toString('utf8'); + +const re = /^#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-fA-F]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/m; + +const arr = str.split('\n'); + +const codepoints = {}; + +for (let i = 0; i < arr.length; ++i) { + const result = re.exec(arr[i]); + if (result) { + const keyname = result[1]; + const keysym = parseInt(result[2], 16); + const remainder = result[3]; + + const unicodeRes = /U\+([0-9a-fA-F]+)/.exec(remainder); + if (unicodeRes) { + const unicode = parseInt(unicodeRes[1], 16); + // The first entry is the preferred one + if (!codepoints[unicode]) { + codepoints[unicode] = { keysym: keysym, name: keyname }; + } + } + } +} + +let out = +"/*\n" + +" * Mapping from Unicode codepoints to X11/RFB keysyms\n" + +" *\n" + +" * This file was automatically generated from keysymdef.h\n" + +" * DO NOT EDIT!\n" + +" */\n" + +"\n" + +"/* Functions at the bottom */\n" + +"\n" + +"const codepoints = {\n"; + +function toHex(num) { + let s = num.toString(16); + if (s.length < 4) { + s = ("0000" + s).slice(-4); + } + return "0x" + s; +} + +for (let codepoint in codepoints) { + codepoint = parseInt(codepoint); + + // Latin-1? + if ((codepoint >= 0x20) && (codepoint <= 0xff)) { + continue; + } + + // Handled by the general Unicode mapping? + if ((codepoint | 0x01000000) === codepoints[codepoint].keysym) { + continue; + } + + out += " " + toHex(codepoint) + ": " + + toHex(codepoints[codepoint].keysym) + + ", // XK_" + codepoints[codepoint].name + "\n"; +} + +out += +"};\n" + +"\n" + +"export default {\n" + +" lookup(u) {\n" + +" // Latin-1 is one-to-one mapping\n" + +" if ((u >= 0x20) && (u <= 0xff)) {\n" + +" return u;\n" + +" }\n" + +"\n" + +" // Lookup table (fairly random)\n" + +" const keysym = codepoints[u];\n" + +" if (keysym !== undefined) {\n" + +" return keysym;\n" + +" }\n" + +"\n" + +" // General mapping as final fallback\n" + +" return 0x01000000 | u;\n" + +" },\n" + +"};"; + +console.log(out); diff --git a/farmq-admin/static/novnc/utils/novnc_proxy b/farmq-admin/static/novnc/utils/novnc_proxy new file mode 100755 index 0000000..ea3ea70 --- /dev/null +++ b/farmq-admin/static/novnc/utils/novnc_proxy @@ -0,0 +1,214 @@ +#!/usr/bin/env bash + +# Copyright (C) 2018 The noVNC Authors +# Licensed under MPL 2.0 or any later version (see LICENSE.txt) + +usage() { + if [ "$*" ]; then + echo "$*" + echo + fi + echo "Usage: ${NAME} [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]" + echo + echo "Starts the WebSockets proxy and a mini-webserver and " + echo "provides a cut-and-paste URL to go to." + echo + echo " --listen PORT Port for proxy/webserver to listen on" + echo " Default: 6080" + echo " --vnc VNC_HOST:PORT VNC server host:port proxy target" + echo " Default: localhost:5900" + echo " --cert CERT Path to combined cert/key file, or just" + echo " the cert file if used with --key" + echo " Default: self.pem" + echo " --key KEY Path to key file, when not combined with cert" + echo " --web WEB Path to web files (e.g. vnc.html)" + echo " Default: ./" + echo " --ssl-only Disable non-https connections." + echo " " + echo " --file-only Disable directory listing in web server." + echo " " + echo " --record FILE Record traffic to FILE.session.js" + echo " " + echo " --syslog SERVER Can be local socket such as /dev/log, or a UDP host:port pair." + echo " " + echo " --heartbeat SEC send a ping to the client every SEC seconds" + echo " --timeout SEC after SEC seconds exit when not connected" + echo " --idle-timeout SEC server exits after SEC seconds if there are no" + echo " " + echo " --web-auth enable authentication" + echo " --auth-plugin CLASS authentication plugin to use" + echo " --auth-source ARG plugin configuration" + echo " " + echo " active connections" + echo " " + exit 2 +} + +NAME="$(basename $0)" +REAL_NAME="$(readlink -f $0)" +HERE="$(cd "$(dirname "$REAL_NAME")" && pwd)" +PORT="6080" +VNC_DEST="localhost:5900" +CERT="" +KEY="" +WEB="" +proxy_pid="" +SSLONLY="" +RECORD_ARG="" +SYSLOG_ARG="" +HEARTBEAT_ARG="" +IDLETIMEOUT_ARG="" +TIMEOUT_ARG="" +WEBAUTH_ARG="" +AUTHPLUGIN_ARG="" +AUTHSOURCE_ARG="" +FILEONLY_ARG="" + + +die() { + echo "$*" + exit 1 +} + +cleanup() { + trap - TERM QUIT INT EXIT + trap "true" CHLD # Ignore cleanup messages + echo + if [ -n "${proxy_pid}" ]; then + echo "Terminating WebSockets proxy (${proxy_pid})" + kill ${proxy_pid} + fi +} + +# Process Arguments + +# Arguments that only apply to chrooter itself +while [ "$*" ]; do + param=$1; shift; OPTARG=$1 + case $param in + --listen) PORT="${OPTARG}"; shift ;; + --vnc) VNC_DEST="${OPTARG}"; shift ;; + --cert) CERT="${OPTARG}"; shift ;; + --key) KEY="${OPTARG}"; shift ;; + --web) WEB="${OPTARG}"; shift ;; + --ssl-only) SSLONLY="--ssl-only" ;; + --file-only) FILEONLY_ARG="--file-only" ;; + --record) RECORD_ARG="--record ${OPTARG}"; shift ;; + --syslog) SYSLOG_ARG="--syslog ${OPTARG}"; shift ;; + --heartbeat) HEARTBEAT_ARG="--heartbeat ${OPTARG}"; shift ;; + --idle-timeout) IDLETIMEOUT_ARG="--idle-timeout ${OPTARG}"; shift ;; + --timeout) TIMEOUT_ARG="--timeout ${OPTARG}"; shift ;; + --web-auth) WEBAUTH_ARG="--web-auth" ;; + --auth-plugin) AUTHPLUGIN_ARG="--auth-plugin ${OPTARG}"; shift ;; + --auth-source) AUTHSOURCE_ARG="--auth-source ${OPTARG}"; shift ;; + -h|--help) usage ;; + -*) usage "Unknown chrooter option: ${param}" ;; + *) break ;; + esac +done + +# Sanity checks +if bash -c "exec 7<>/dev/tcp/localhost/${PORT}" &> /dev/null; then + exec 7<&- + exec 7>&- + die "Port ${PORT} in use. Try --listen PORT" +else + exec 7<&- + exec 7>&- +fi + +trap "cleanup" TERM QUIT INT EXIT + +# Find vnc.html +if [ -n "${WEB}" ]; then + if [ ! -e "${WEB}/vnc.html" ]; then + die "Could not find ${WEB}/vnc.html" + fi +elif [ -e "$(pwd)/vnc.html" ]; then + WEB=$(pwd) +elif [ -e "${HERE}/../vnc.html" ]; then + WEB=${HERE}/../ +elif [ -e "${HERE}/vnc.html" ]; then + WEB=${HERE} +elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then + WEB=${HERE}/../share/novnc/ +else + die "Could not find vnc.html" +fi + +# Find self.pem +if [ -n "${CERT}" ]; then + if [ ! -e "${CERT}" ]; then + die "Could not find ${CERT}" + fi +elif [ -e "$(pwd)/self.pem" ]; then + CERT="$(pwd)/self.pem" +elif [ -e "${HERE}/../self.pem" ]; then + CERT="${HERE}/../self.pem" +elif [ -e "${HERE}/self.pem" ]; then + CERT="${HERE}/self.pem" +else + echo "Warning: could not find self.pem" +fi + +# Check key file +if [ -n "${KEY}" ]; then + if [ ! -e "${KEY}" ]; then + die "Could not find ${KEY}" + fi +fi + +# try to find websockify (prefer local, try global, then download local) +if [[ -d ${HERE}/websockify ]]; then + WEBSOCKIFY=${HERE}/websockify/run + + if [[ ! -x $WEBSOCKIFY ]]; then + echo "The path ${HERE}/websockify exists, but $WEBSOCKIFY either does not exist or is not executable." + echo "If you intended to use an installed websockify package, please remove ${HERE}/websockify." + exit 1 + fi + + echo "Using local websockify at $WEBSOCKIFY" +else + WEBSOCKIFY_FROMSYSTEM=$(which websockify 2>/dev/null) + WEBSOCKIFY_FROMSNAP=${HERE}/../usr/bin/python2-websockify + [ -f $WEBSOCKIFY_FROMSYSTEM ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSYSTEM + [ -f $WEBSOCKIFY_FROMSNAP ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSNAP + + if [ ! -f "$WEBSOCKIFY" ]; then + echo "No installed websockify, attempting to clone websockify..." + WEBSOCKIFY=${HERE}/websockify/run + git clone https://github.com/novnc/websockify ${HERE}/websockify + + if [[ ! -e $WEBSOCKIFY ]]; then + echo "Unable to locate ${HERE}/websockify/run after downloading" + exit 1 + fi + + echo "Using local websockify at $WEBSOCKIFY" + else + echo "Using installed websockify at $WEBSOCKIFY" + fi +fi + +echo "Starting webserver and WebSockets proxy on port ${PORT}" +#${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} & +${WEBSOCKIFY} ${SYSLOG_ARG} ${SSLONLY} ${FILEONLY_ARG} --web ${WEB} ${CERT:+--cert ${CERT}} ${KEY:+--key ${KEY}} ${PORT} ${VNC_DEST} ${HEARTBEAT_ARG} ${IDLETIMEOUT_ARG} ${RECORD_ARG} ${TIMEOUT_ARG} ${WEBAUTH_ARG} ${AUTHPLUGIN_ARG} ${AUTHSOURCE_ARG} & +proxy_pid="$!" +sleep 1 +if [ -z "$proxy_pid" ] || ! ps -eo pid= | grep -w "$proxy_pid" > /dev/null; then + proxy_pid= + echo "Failed to start WebSockets proxy" + exit 1 +fi + +echo -e "\n\nNavigate to this URL:\n" +if [ "x$SSLONLY" == "x" ]; then + echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n" +else + echo -e " https://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n" +fi + +echo -e "Press Ctrl-C to exit\n\n" + +wait ${proxy_pid} diff --git a/farmq-admin/static/novnc/utils/u2x11 b/farmq-admin/static/novnc/utils/u2x11 new file mode 100755 index 0000000..fd3e4ba --- /dev/null +++ b/farmq-admin/static/novnc/utils/u2x11 @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Convert "U+..." commented entries in /usr/include/X11/keysymdef.h +# into JavaScript for use by noVNC. Note this is likely to produce +# a few duplicate properties with clashing values, that will need +# resolving manually. +# +# Colin Dean +# + +regex="^#define[ \t]+XK_[A-Za-z0-9_]+[ \t]+0x([0-9a-fA-F]+)[ \t]+\/\*[ \t]+U\+([0-9a-fA-F]+)[ \t]+[^*]+.[ \t]+\*\/[ \t]*$" +echo "unicodeTable = {" +while read line; do + if echo "${line}" | egrep -qs "${regex}"; then + + x11=$(echo "${line}" | sed -r "s/${regex}/\1/") + vnc=$(echo "${line}" | sed -r "s/${regex}/\2/") + + if echo "${vnc}" | egrep -qs "^00[2-9A-F][0-9A-F]$"; then + : # skip ISO Latin-1 (U+0020 to U+00FF) as 1-to-1 mapping + else + # note 1-to-1 is possible (e.g. for Euro symbol, U+20AC) + echo " 0x${vnc} : 0x${x11}," + fi + fi +done < /usr/include/X11/keysymdef.h | uniq +echo "};" + diff --git a/farmq-admin/static/novnc/utils/validate b/farmq-admin/static/novnc/utils/validate new file mode 100755 index 0000000..a6b5507 --- /dev/null +++ b/farmq-admin/static/novnc/utils/validate @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +RET=0 + +OUT=`mktemp` + +for fn in "$@"; do + echo "Validating $fn..." + echo + + case $fn in + *.html) + type="text/html" + ;; + *.css) + type="text/css" + ;; + *) + echo "Unknown format!" + echo + RET=1 + continue + ;; + esac + + curl --silent \ + --header "Content-Type: ${type}; charset=utf-8" \ + --data-binary @${fn} \ + https://validator.w3.org/nu/?out=text > $OUT + cat $OUT + echo + + # We don't fail the check for warnings as some warnings are + # not relevant for us, and we don't currently have a way to + # ignore just those + if grep -q -s -E "^Error:" $OUT; then + RET=1 + fi +done + +rm $OUT + +exit $RET diff --git a/farmq-admin/static/novnc/vendor/pako/LICENSE b/farmq-admin/static/novnc/vendor/pako/LICENSE new file mode 100644 index 0000000..d082ae3 --- /dev/null +++ b/farmq-admin/static/novnc/vendor/pako/LICENSE @@ -0,0 +1,21 @@ +(The MIT License) + +Copyright (C) 2014-2016 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/farmq-admin/static/novnc/vendor/pako/README.md b/farmq-admin/static/novnc/vendor/pako/README.md new file mode 100644 index 0000000..755df64 --- /dev/null +++ b/farmq-admin/static/novnc/vendor/pako/README.md @@ -0,0 +1,6 @@ +This is an ES6-modules-compatible version of +https://github.com/nodeca/pako, based on pako version 1.0.3. + +It's more-or-less a direct translation of the original, with unused parts +removed, and the dynamic support for non-typed arrays removed (since ES6 +modules don't work well with dynamic exports). diff --git a/farmq-admin/static/novnc/vnc.html b/farmq-admin/static/novnc/vnc.html new file mode 100644 index 0000000..24a118d --- /dev/null +++ b/farmq-admin/static/novnc/vnc.html @@ -0,0 +1,341 @@ + + + + + + noVNC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
noVNC encountered an error:
+
+
+
+
+ + +
+ +
+
+ +
+ +

no
VNC

+ +
+ + + + + +
+ +
+ + + +
+
+ + + + + + +
+
+ + + +
+
+
+ Power +
+ + + +
+
+ + + +
+
+
+ Clipboard +
+

+ Edit clipboard content in the textarea below. +

+ +
+
+ + + + + + +
+
+
+ Settings +
+
    +
  • + +
  • +
  • + +
  • +

  • +
  • + +
  • +
  • + + +
  • +

  • +
  • +
    Advanced
    +
      +
    • + + +
    • +
    • + + +
    • +

    • +
    • + + +
    • +
    • +
      WebSocket
      +
        +
      • + +
      • +
      • + + +
      • +
      • + + +
      • +
      • + + +
      • +
      +
    • +

    • +
    • + +
    • +
    • + + +
    • +

    • +
    • + +
    • +

    • + +
    • + +
    • +
    +
  • +

  • +
  • + Version: + +
  • +
+
+
+ + + + +
+
+ +
+ +
+
+
+
+ + +
+ + +
+
+ +
+ +
+
+
+ + +
+
+
+ Server identity +
+
+ The server has provided the following identifying information: +
+
+ Fingerprint: + +
+
+ Please verify that the information is correct and press + "Approve". Otherwise press "Reject". +
+
+ + +
+
+
+ + +
+
+
+ Credentials +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
+ + +
+ + + + diff --git a/farmq-admin/static/novnc/vnc_lite.html b/farmq-admin/static/novnc/vnc_lite.html new file mode 100644 index 0000000..e725a2d --- /dev/null +++ b/farmq-admin/static/novnc/vnc_lite.html @@ -0,0 +1,187 @@ + + + + + + noVNC + + + + + + + +
+
Loading
+
Send CtrlAltDel
+
+
+ +
+ + diff --git a/farmq-admin/templates/base.html b/farmq-admin/templates/base.html index 29b1a99..96ed54e 100644 --- a/farmq-admin/templates/base.html +++ b/farmq-admin/templates/base.html @@ -146,7 +146,7 @@