Compare commits

...

19 Commits

Author SHA1 Message Date
ljm42
7b767f7bdf Feat: write Flash Backup keepalive file weekly 2024-05-02 11:05:24 -07:00
Zack Spear
31a5413643 feat(web): registration page array status messaging 2024-05-01 12:21:24 -07:00
Zack Spear
a95fc5ed07 chore: lint fix 2024-05-01 12:21:24 -07:00
Zack Spear
fcd7bb790e refactor(web): ineligible release messaging 2024-05-01 12:21:24 -07:00
Zack Spear
008e10948e fix: prevent local dev from throwing ssl error 2024-05-01 12:21:24 -07:00
Zack Spear
c97a4f1268 feat: registration page server error heading + subheading 2024-05-01 12:21:24 -07:00
Zack Spear
3eba95b8cc feat: array state on registration page 2024-05-01 12:21:24 -07:00
Zack Spear
2bf8f0b937 fix(api): readme discord url 2024-04-30 17:34:46 -07:00
Zack Spear
9ae45d1258 fix(web): discord url 2024-04-30 17:34:46 -07:00
Zack Spear
1835a4cf3f chore(plg): comment explain web component downgrade prevention 2024-04-30 17:12:33 -07:00
Zack Spear
2ab44b894d feat(plg): plg install prevent web component downgrade 2024-04-30 17:12:33 -07:00
Zack Spear
1108f49b07 feat: postbuild script to add timestamp to web component manifest 2024-04-30 17:12:33 -07:00
ljm42
cc69213beb Feat: Flash Backup requires connection to mothership (#868)
* fix: branding

* feat: flash backup requires connection to mothership

* feat: flash backup requires connection to mothership
2024-04-26 12:01:42 -04:00
ljm42
460e557dd8 Flash Backup: exclude large files from repo (#866) 2024-04-23 21:21:01 -04:00
Zack Spear
05e29468d2 refactor: trial messaging replace pro with unleashed (#865)
* refactor: trial messaging replace pro with unleashed

* fix: trial messaging grammar

* refactor: web component translations trial messaging
2024-04-03 13:46:25 -04:00
ljm42
4d3a311fb4 Feat: add support for outgoing proxies (#863) 2024-03-27 15:14:18 -07:00
Zack Spear
bc62d210ec refactor: config error messages (#862) 2024-03-26 12:30:34 -04:00
Eli Bosley
43d3ea6553 chore(release): 3.6.0 2024-03-26 10:19:31 -04:00
Zack Spear
882e3e1ef4 feat: server config enum message w/ ineligible support (#861)
* test: serverState local components data tweaks

* feat: server config enum message w/ ineligible support

* refactor: config error messages

* chore: lint
2024-03-26 09:57:04 -04:00
27 changed files with 878 additions and 143 deletions

View File

@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [3.6.0](https://github.com/unraid/api/compare/v3.5.3...v3.6.0) (2024-03-26)
### Features
* server config enum message w/ ineligible support ([#861](https://github.com/unraid/api/issues/861)) ([4d3a351](https://github.com/unraid/api/commit/4d3a3510777090788573f4cee83694a0dc6f8df5))
### [3.5.3](https://github.com/unraid/api/compare/v3.5.2...v3.5.3) (2024-03-25)

58
api/README.md Normal file
View File

@@ -0,0 +1,58 @@
# @unraid/api
## Installation
Install the production plugin via the apps tab (search for "my servers") on Unraid 6.9.2 or later.
## CLI
If you're on a unraid v6.9.2 or later machine this should be available by running `unraid-api` in any directory.
```bash
root@Devon:~# unraid-api
Unraid API
Thanks for using the official Unraid API
Usage:
$ unraid-api command <options>
Commands:
start/stop/restart/version/status/report/switch-env
Options:
-h, --help Prints this usage guide.
-d, --debug Enabled debug mode.
-p, --port string Set the graphql port.
--environment production/staging/development Set the working environment.
--log-level ALL/TRACE/DEBUG/INFO/WARN/ERROR/FATAL/MARK/OFF Set the log level.
Copyright © 2022 Lime Technology, Inc.
```
## Report
To view the current status of the unraid-api and its connection to mothership, run:
```
unraid-api report
```
To view verbose data (anonymized), run:
```
unraid-api report -v
```
To view non-anonymized verbose data, run:
```
unraid-api report -vv
```
## Secrets
If you found this file you're likely a developer. If you'd like to know more about the API and when it's available please join [our discord](https://discord.unraid.net/).
## License
Copyright 2019-2022 Lime Technology Inc. All rights reserved.

4
api/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@unraid/api",
"version": "3.5.3",
"version": "3.6.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@unraid/api",
"version": "3.5.3",
"version": "3.6.0",
"license": "UNLICENSED",
"dependencies": {
"@apollo/client": "^3.8.9",

View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/api",
"version": "3.5.3",
"version": "3.6.0",
"main": "dist/index.js",
"bin": "dist/unraid-api.cjs",
"type": "module",

View File

@@ -502,24 +502,94 @@ if test -f "${FILE}" && grep -q "top.Shadowbox" "${FILE}" &>/dev/null; then
sed -i 's/top.Shadowbox/parent.Shadowbox/gm' "${FILE}"
fi
# ensure _var() is defined
# brings older versions of Unraid in sync with 6.12.0
# ensure _var() is defined, brings older versions of Unraid in sync with 6.12.0
FILE=/usr/local/emhttp/plugins/dynamix/include/Wrappers.php
if test -f "${FILE}" && ! grep -q "_var" "${FILE}" &>/dev/null; then
TEXT=$(
if test -f "${FILE}" && ! grep -q "function _var" "${FILE}" &>/dev/null; then
ADDTEXT1=$(
cat <<'END_HEREDOC'
// backported by Unraid Connect
function _var(&$name, $key=null, $default='') {
return is_null($key) ? ($name ?? $default) : ($name[$key] ?? $default);
}
?>
END_HEREDOC
)
fi
# ensure my_logger() is defined, brings older versions of Unraid in sync with 6.13.0
if test -f "${FILE}" && ! grep -q "function my_logger" "${FILE}" &>/dev/null; then
ADDTEXT2=$(
cat <<'END_HEREDOC'
// backported by Unraid Connect
// ensure params passed to logger are properly escaped
function my_logger($message, $logger='webgui') {
exec('logger -t '.escapeshellarg($logger).' -- '.escapeshellarg($message));
}
END_HEREDOC
)
fi
# ensure http_get_contents() is defined, brings older versions of Unraid in sync with 6.13.0
if test -f "${FILE}" && ! grep -q "function http_get_contents" "${FILE}" &>/dev/null; then
ADDTEXT3=$(
cat <<'END_HEREDOC'
// backported by Unraid Connect
// Original PHP code by Chirp Internet: www.chirpinternet.eu
// Please acknowledge use of this code by including this header.
// https://www.the-art-of-web.com/php/http-get-contents/
// Modified for Unraid
/**
* Fetches URL and returns content
* @param string $url The URL to fetch
* @param array $opts Array of options to pass to curl_setopt()
* @param array $getinfo Empty array passed by reference, will contain results of curl_getinfo and curl_error
* @return string|false $out The fetched content
*/
function http_get_contents(string $url, array $opts = [], array &$getinfo = NULL) {
$ch = curl_init();
if(isset($getinfo)) {
curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_TIMEOUT, 45);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_REFERER, "");
curl_setopt($ch, CURLOPT_FAILONERROR, true);
if(is_array($opts) && $opts) {
foreach($opts as $key => $val) {
curl_setopt($ch, $key, $val);
}
}
$out = curl_exec($ch);
if(isset($getinfo)) {
$getinfo = curl_getinfo($ch);
}
if (curl_errno($ch)) {
$msg = curl_error($ch) . " {$url}";
if(isset($getinfo)) {
$getinfo['error'] = $msg;
}
my_logger($msg, "http_get_contents");
}
return $out;
}
END_HEREDOC
)
fi
if [[ -n "${ADDTEXT1}" || -n "${ADDTEXT2}" || -n "${ADDTEXT3}" ]]; then
TMP="$FILE.$RANDOM"
cp -f "$FILE" "$TMP"
cp -f "$FILE" "$FILE-"
# delete last line of the file if it contains `?>`
if test $( tail -n 1 "${FILE}" ) = '?>' ; then
sed -i '$ d' "${FILE}"
if test $( tail -n 1 "${TMP}" ) = '?>' ; then
sed -i '$ d' "${TMP}"
fi
echo "${TEXT}" >>"${FILE}"
[[ -n "${ADDTEXT1}" ]] && echo "${ADDTEXT1}" >>"${TMP}"
[[ -n "${ADDTEXT2}" ]] && echo "${ADDTEXT2}" >>"${TMP}"
[[ -n "${ADDTEXT3}" ]] && echo "${ADDTEXT3}" >>"${TMP}"
echo "?>" >>"${TMP}"
mv "${TMP}" "${FILE}"
fi
# install the main txz
@@ -613,6 +683,29 @@ if [[ "${CHANGED}" == "yes" ]]; then
fi
fi
# Prevent web component file downgrade if the webgui version is newer than the plugin version
plgWebComponentPath="/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components"
backupWebComponentPath="/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components-"
# Function to extract "ts" value from JSON file
extract_ts() {
local filepath="$1"
local ts_value=null
ts_value=$(jq -r '.ts' "$filepath" 2>/dev/null)
echo "$ts_value"
}
# Extract "ts" values from both files
plgManifestTs=$(extract_ts "$plgWebComponentPath/manifest.json")
webguiManifestTs=$(extract_ts "$backupWebComponentPath/manifest.json")
# Compare the "ts" values and return the file path of the higher value
if [[ "$webguiManifestTs" -gt "$plgManifestTs" ]]; then
rm -rf "$plgWebComponentPath"
mv "$backupWebComponentPath" "$plgWebComponentPath"
echo "♻️ Reverted to stock web components"
fi
# start background process to install/start the api and flash backup
echo
if [ -f /var/local/emhttp/var.ini ]; then

View File

@@ -0,0 +1,230 @@
#!/bin/bash
# This file is /etc/rc.d/rc.flash_backup
# use at queue "f" for flash backup
QUEUE=" -q f "
TASKNAME="/etc/rc.d/rc.flash_backup watch"
TASKACTION="/usr/local/emhttp/plugins/dynamix.my.servers/scripts/UpdateFlashBackup update"
last=$(date +%s)
# set GIT_OPTIONAL_LOCKS=0 globally to reduce/eliminate writes to /boot
export GIT_OPTIONAL_LOCKS=0
FAST=1 # 1 second delay when waiting for git
SLOW=10 # 10 second delay when waiting for git
# wait for existing git commands to complete
# $1 is the time in seconds to sleep when waiting. SLOW or FAST
_waitforgit() {
while [[ $(pgrep -f '^git -C /boot' -c) -ne 0 ]]; do
sleep "$1"
done
}
# log to syslog, then wait for existing git commands to complete
# $1 is the time in seconds to sleep when waiting. SLOW or FAST
_waitforgitlog() {
if [[ $(pgrep -f '^git -C /boot' -c) -ne 0 ]]; then
logger "waiting for current backup to complete" --tag flash_backup
_waitforgit "$1"
fi
}
status() {
_connected && CONNECTED="system is connected to Unraid Connect Cloud." || CONNECTED="system is not connected to Unraid Connect Cloud."
if _watching; then
echo "flash backup monitor is running. ${CONNECTED}"
_hasqueue && echo "changes detected, backup queued."
exit 0
else
if _enabled; then
echo "flash backup is enabled but the monitor is not running. ${CONNECTED}"
else
echo "flash backup is disabled so the monitor is disabled. ${CONNECTED}"
fi
exit 1
fi
}
start() {
_start
exit 0
}
stop() {
_stop
exit 0
}
reload() {
_start
sleep 1
status
}
_start() {
# Note: can start if not signed in, but watcher loop will not process until signed in
# only run if flash_backup is enabled
if ! _enabled; then
logger "flash backup disabled, exiting" --tag flash_backup
exit 1
fi
_stop
# start watcher loop as background process
exec ${TASKNAME} &>/dev/null &
}
_stop() {
if _watching; then
logger "stop watching for file changes" --tag flash_backup
# terminate watcher loop/process
pkill --full "${TASKNAME}" &>/dev/null
fi
# do not flush. better to have unsaved changes than to corrupt the backup during shutdown
# note that an existing git process could still be running
}
flush() {
# remove any queued jobs
_removequeue
# wait for existing git commands to finish before flushing
_waitforgitlog "${FAST}"
logger "flush: ${TASKACTION}" --tag flash_backup
# if _connected, push any changes ad-hoc
if _connected; then
# shellcheck disable=SC2086
echo "${TASKACTION}_nolimit &>/dev/null" | at ${QUEUE} -M now &>/dev/null
fi
}
_watching() {
local flash_backup_pid
flash_backup_pid=$(pgrep --full "${TASKNAME}")
if [[ ${flash_backup_pid} ]]; then
return 0
fi
return 1
}
_watch() {
# safely clean up git *.lock files
_clearlocks
# update a file in the repo to keep this repo alive
_keepalive
# flush: if connected, start the watch loop with a clean repo
flush
# wait for flush to complete
sleep 3
_waitforgitlog "${FAST}"
logger "start watching for file changes" --tag flash_backup
# start watcher loop
timer=0
incr=60
max=$((60*60*24*7)) # one week
while true; do
# if system is connected to Unraid Connect Cloud, see if there are updates to process
_connected && _f1
if ((timer>max)); then
# update a file in the repo once per max seconds to keep this repo alive
_keepalive
timer=0
fi
timer=$((timer+incr))
sleep $incr
done
}
_f1() {
# wait for existing git commands to finish before checking for updates
_waitforgit "${SLOW}"
if [ "$(git -C /boot status -s)" ]; then
_hasqueue || _f2
elif _haserror && _beenawhile; then
# we are in an error state and it has been 3 hours since we last tried submitting. run the task now.
_runtaskaction
fi
}
_f2() {
if ! _haserror || [[ $(($(date +"%M") % 10)) -eq 0 ]]; then
logger "adding task: ${TASKACTION}" --tag flash_backup
fi
sed -i "s@uptodate=yes@uptodate=no@" /var/local/emhttp/flashbackup.ini &>/dev/null
_runtaskaction
}
_hasqueue() {
# returns false if the queue is empty, true otherwise
# shellcheck disable=SC2086
if [ -z "$(atq ${QUEUE})" ]; then
return 1
fi
return 0
}
_removequeue() {
# delete any at jobs in queue f
# @TODO shellcheck SC2162
# shellcheck disable=SC2086
atq ${QUEUE} | while read line; do
id=$(echo ${line} | cut -d " " -f 1)
atrm ${id}
done
}
_runtaskaction() {
# shellcheck disable=SC2086
echo "${TASKACTION} &>/dev/null" | at ${QUEUE} -M now +1 minute &>/dev/null
last=$(date +%s)
}
_enabled() {
local output
output=$(git -C /boot config --get remote.origin.url 2>&1)
if [[ "${output}" == *"backup.unraid.net"* ]]; then
return 0
fi
return 1
}
_connected() {
CFG=/var/local/emhttp/myservers.cfg
[[ ! -f "${CFG}" ]] && return 1
# shellcheck disable=SC1090
source <(sed -nr '/\[connectionStatus\]/,/\[/{/minigraph/p}' "${CFG}" 2>/dev/null)
if [[ -z "${minigraph}" || "${minigraph}" != "CONNECTED" ]]; then
return 1
fi
return 0
}
_haserror() {
errorstring=$(awk -F "=" '/error/ {print $2}' /var/local/emhttp/flashbackup.ini 2>&1 || echo '')
if [ ${#errorstring} -le 2 ]; then
return 1
fi
return 0
}
_beenawhile() {
now=$(date +%s)
age=$((now - last))
maxage=$((3 * 60 * 60)) # three hours
[[ $age -gt $maxage ]] && return 0
return 1
}
# wait for git commands to end, then delete any stale lock files
_clearlocks() {
_waitforgitlog "${FAST}"
find /boot/.git -type f -name '*.lock' -delete
}
# this function updates a file on the flash drive to trigger a git update
# do this regardless of whether the system is _connected
_keepalive() {
file=/boot/config/plugins/dynamix.my.servers/fb_keepalive
[[ ! -d "$(dirname "${file}")" ]] && return 1
logger "trigger flash backup keepalive" --tag flash_backup
date +'%s' > "${file}" 2>/dev/null
return 0
}
case "$1" in
'status')
status
;;
'start')
start
;;
'stop')
stop
;;
'reload')
reload
;;
'flush')
flush
;;
'watch')
_watch
;;
*)
echo "usage $0 status|start|stop|reload|flush"
;;
esac

View File

@@ -0,0 +1,182 @@
#!/bin/bash
# unraid-api-handler
flash="/boot/config/plugins/dynamix.my.servers"
[[ ! -d "${flash}" ]] && echo "Please reinstall the Unraid Connect plugin" && exit 1
[[ ! -f "${flash}/env" ]] && echo 'env=production' >"${flash}/env"
# define env to avoid shellcheck SC2154. Will be overridden by the source command below
env=production
# shellcheck disable=SC1091
source "${flash}/env"
api_base_directory="/usr/local/bin"
# Only allow specific envs
if [ "${env}" != "staging" ] && [ "${env}" != "production" ]; then
echo "\"${env}\" is an unsupported env. Please use \"staging\" or \"production\"."
exit 1
fi
switchenv() {
stop
# Get current environment from file
local envFile="${flash}/env"
local currentEnv
currentEnv=$(
# shellcheck disable=SC1090
source "${envFile}"
echo "${env}"
)
if [[ "${currentEnv}" = "production" ]]; then
echo "Switching from production to staging"
echo 'env="staging"' >"${envFile}"
cp "${api_base_directory}/unraid-api/.env.staging" "${api_base_directory}/unraid-api/.env"
elif [[ "${currentEnv}" = "staging" ]]; then
echo "Switching from staging to production"
echo 'env="production"' >"${envFile}"
cp "${api_base_directory}/unraid-api/.env.production" "${api_base_directory}/unraid-api/.env"
fi
echo "Run \"unraid-api start\" to start the API."
}
raiseloglevel() {
kill -s SIGUSR2 "$(pidof unraid-api)"
}
lowerloglevel() {
kill -s SIGUSR1 "$(pidof unraid-api)"
}
status() {
LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" status
}
start() {
LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" start 2>&1 | logger &
}
report() {
LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" report "$1" "$2"
}
startdebug() {
LOG_CONTEXT=true LOG_STACKTRACE=true LOG_TRACING=true LOG_LEVEL=debug "${api_base_directory}/unraid-api/unraid-api" start --debug
}
stop() {
LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" stop 2>/dev/null
}
reload() {
LOG_TYPE=raw "${api_base_directory}/unraid-api/unraid-api" restart
}
_install() {
# process file from commandline
if [[ -n "$1" ]]; then
file=$(realpath "${flash}/$1")
if [[ "${file}" == "${flash}"* ]] && [[ "${file}" == *".tgz" || "${file}" == *".zip" ]] && [[ -f "${file}" ]]; then
[[ "${file}" == *".tgz" ]] && ext=tgz || ext=zip
echo "installing $1"
cp "${file}" "${flash}/unraid-api.${ext}"
else
echo "invalid installation file: $1"
exit 1
fi
fi
# If this was downloaded from a Github action it'll be a zip with a tgz inside
# Let's extract the tgz and rename it for the next step
if [[ -f "${flash}/unraid-api.zip" ]]; then
for f in ${flash}/unraid-api.zip; do unzip -p "${f}" >"${flash}/${f%.zip}.tgz"; done
rm -f "${flash}/unraid-api.zip"
fi
# Ensure installation tgz exists
[[ ! -f "${flash}/unraid-api.tgz" ]] && echo "Please reinstall the Unraid Connect plugin" && exit 1
# Stop old process
[[ -f "${api_base_directory}/unraid-api/unraid-api" ]] && stop
# Install unraid-api
rm -rf "${api_base_directory}/unraid-api"
mkdir -p "${api_base_directory}/unraid-api"
tar -C "${api_base_directory}/unraid-api" -xzf "${flash}/unraid-api.tgz" --strip 1
# Reset permissions
rm -f "${flash}/data/permissions.json"
# Copy env file
cp "${api_base_directory}/unraid-api/.env.${env}" "${api_base_directory}/unraid-api/.env"
# Copy wc files from flash
if [ -f "${flash}/webComps/unraid.min.js" ]; then
rm -rf /usr/local/emhttp/webGui/webComps
mkdir -p /usr/local/emhttp/webGui/webComps
cp ${flash}/webComps/* /usr/local/emhttp/webGui/webComps
else
# not fatal, previous version of unraid.min.js should still exist in /usr/local/emhttp/webGui/webComps
echo "Note: ${flash}/webComps/unraid.min.js is missing"
fi
# bail if expected file does not exist
[[ ! -f "${api_base_directory}/unraid-api/unraid-api" ]] && echo "unraid-api install failed" && exit 1
}
install() {
# Install the files
_install "$1"
# if nginx is running, start the api. if not, it will be started by rc.nginx
if /etc/rc.d/rc.nginx status &>/dev/null; then
# Start new process
start
# Note: do not run another unraid-api command until you see "UNRAID API started successfully!" in syslog
sleep 3
echo "unraid-api installed and started"
else
echo "unraid-api installed"
fi
exit 0
}
uninstall() {
# Stop old process
[[ -f "${api_base_directory}/unraid-api/unraid-api" ]] && stop
# Remove all unraid-api files
rm -rf "${api_base_directory}/unraid-api"
rm -f /var/run/unraid-api.sock
}
case "$1" in
'status')
status
;;
'start')
start
;;
'report')
report "$2" "$3"
;;
'switch-env')
switchenv
;;
'start-debug')
startdebug
;;
'raise-log-level')
raiseloglevel
;;
'lower-log-level')
lowerloglevel
;;
'stop')
stop
;;
'reload')
reload
;;
'restart')
reload
;;
'install')
install "$2"
;;
'_install')
_install "$2"
;;
'uninstall')
uninstall
;;
*)
echo "usage $0 status|start|report|switch-env|start-debug|raise-log-level|lower-log-level|stop|reload|install|uninstall"
;;
esac

View File

@@ -14,6 +14,7 @@ Tag="globe"
* all copies or substantial portions of the Software.
*/
require_once "$docroot/plugins/dynamix.my.servers/include/state.php";
require_once "$docroot/webGui/include/Wrappers.php";
$serverState = new ServerState();
$keyfile = $serverState->keyfileBase64;
@@ -521,7 +522,7 @@ _(Allow Remote Access)_:
<?if(!$isRegistered): // NOTE: manually added close tags so the next section would not be indented ?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until you have signed in)_</span></dd></dl>
<?elseif(!$isMiniGraphConnected && $myServersFlashCfg['remote']['wanaccess']!="yes"): // NOTE: manually added close tags so the next section would not be indented ?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until connected to Unraid Connect Cloud)_</span></dd></dl>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until connected to Unraid Connect Cloud - try reloading the page)_</span></dd></dl>
<?elseif(!$hasMyUnraidNetCert): // NOTE: manually added close tags so the next section would not be indented ?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until you Provision a myunraid.net SSL Cert)_</span><input type="hidden" id="wanport" value="0"></dd></dl>
<?elseif(!$boolWebUIAuth): // NOTE: manually added close tags so the next section would not be indented ?>
@@ -546,7 +547,7 @@ _(Allow Remote Access)_:
<?endif?>
&nbsp;
: <unraid-i18n-host><unraid-wan-ip-check php-wan-ip="<?=@file_get_contents('https://wanip4.unraid.net/')?>"></unraid-wan-ip-check></unraid-i18n-host>
: <unraid-i18n-host><unraid-wan-ip-check php-wan-ip="<?=http_get_contents('https://wanip4.unraid.net/')?>"></unraid-wan-ip-check></unraid-i18n-host>
<div markdown="1" id="wanpanel" style="display:'none'">
@@ -626,8 +627,8 @@ _(Enable Transparent 2FA for Local Access)_<!-- do not index -->:
_(Flash backup)_:
<?if(!$isRegistered):?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until you have signed in)_</span>
<?elseif(!$isMiniGraphConnected && empty($flashbackup_status['activated'])):?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until connected to Unraid Connect Cloud)_</span>
<?elseif(!$isMiniGraphConnected):?>
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until connected to Unraid Connect Cloud - try reloading the page)_</span>
<?else: // begin show flash backup form ?>
: <span id='flashbackuptext'><span class='blue p0'>_(Loading)_ <i class="fa fa-spinner fa-spin" aria-hidden="true"></i></span></span>

View File

@@ -70,8 +70,8 @@ function save_flash_backup_state($loading='') {
rename($flashbackup_tmp, $flashbackup_ini);
}
function load_flash_backup_state() {
global $arrState,$flashbackup_ini,$isRegistered;
function default_flash_backup_state() {
global $arrState;
$arrState = [
'activated' => 'no',
@@ -80,6 +80,12 @@ function load_flash_backup_state() {
'error' => '',
'remoteerror' => ''
];
}
function load_flash_backup_state() {
global $arrState,$flashbackup_ini,$isRegistered;
default_flash_backup_state();
$arrNewState = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashbackup_ini) : [];
if ($arrNewState) {
@@ -277,7 +283,14 @@ if ($pgrep_output[0] != "0") {
// check if signed-in
if (!$isRegistered) {
response_complete(406, array('error' => 'Must be signed in to My Servers to use Flash Backup'));
default_flash_backup_state();
response_complete(406, array('error' => 'Must be signed in to Unraid Connect to use Flash Backup'));
}
// check if connected to Unraid Connect Cloud
if (!$isConnected) {
default_flash_backup_state();
response_complete(406, array('error' => 'Must be connected to Unraid Connect Cloud to use Flash Backup'));
}
// keyfile
@@ -422,29 +435,29 @@ if (!file_exists('/boot/.git/info/exclude')) {
}
// setup a nice git description
$gitdesc_text='Unraid flash drive for '.$var['NAME']."\n";
$gitdesc_file='/boot/.git/description';
if (!file_exists($gitdesc_file) || strpos(file_get_contents($gitdesc_file),$var['NAME']) === false) {
file_put_contents($gitdesc_file, 'Unraid flash drive for '.$var['NAME']."\n");
if (!file_exists($gitdesc_file) || (file_get_contents($gitdesc_file) != $gitdesc_text)) {
file_put_contents($gitdesc_file, $gitdesc_text);
}
// configure git to use the noprivatekeys filter
set_git_config('filter.noprivatekeys.clean', '/usr/local/emhttp/plugins/dynamix.my.servers/scripts/git-noprivatekeys-clean');
// configure git to apply the noprivatekeys filter to wireguard config files
$gitattributes_file='/boot/.gitattributes';
if (!file_exists($gitattributes_file) || strpos(file_get_contents($gitattributes_file),'noprivatekeys') === false) {
file_put_contents($gitattributes_file, '# file managed by Unraid, do not modify
$gitattributes_text='# file managed by Unraid, do not modify
config/wireguard/*.cfg filter=noprivatekeys
config/wireguard/*.conf filter=noprivatekeys
config/wireguard/peers/*.conf filter=noprivatekeys
');
';
$gitattributes_file='/boot/.gitattributes';
if (!file_exists($gitattributes_file) || (file_get_contents($gitattributes_file) != $gitattributes_text)) {
file_put_contents($gitattributes_file, $gitattributes_text);
}
// setup git ignore for files we dont need in the flash backup
$gitexclude_file='/boot/.git/info/exclude';
if (!file_exists($gitexclude_file) || strpos(file_get_contents($gitexclude_file),'# version 1.0') === false) {
file_put_contents($gitexclude_file, '# file managed by Unraid, do not modify
# version 1.0
// setup master git exclude file to specify what to include/exclude from repo
$gitexclude_text = '# file managed by Unraid, do not modify
# version 1.1
# Blacklist everything
/*
@@ -480,7 +493,18 @@ config/plugins-error
config/plugins-old-versions
config/plugins/dockerMan/images
config/wireguard/peers/*.png
');
';
// find large files to exclude from flash backup
$oversize_files = $return_var = null;
exec('find /boot/config -type f -size +30M 2>/dev/null | sed "s|^/boot/||g" 2>/dev/null', $oversize_files, $return_var);
if ($oversize_files && is_array($oversize_files)) {
$gitexclude_text .= "\n# Blacklist large files on this system\n".implode("\n", $oversize_files)."\n";
}
$gitexclude_file='/boot/.git/info/exclude';
if (!file_exists($gitexclude_file) || (file_get_contents($gitexclude_file) != $gitexclude_text)) {
file_put_contents($gitexclude_file, $gitexclude_text);
}
// ensure git user is configured
@@ -529,7 +553,7 @@ if (empty($SSH_PORT)) {
} else {
$arrState['loading'] = '';
if (stripos(implode($ssh_output),'permission denied') !== false) {
$arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to My Servers Cloud';
$arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to Unraid Connect Cloud';
} else {
$arrState['error'] = 'Unable to connect to backup.unraid.net:22';
}
@@ -557,7 +581,7 @@ if ($command == 'activate') {
exec_log('git -C /boot checkout -B master origin/master');
// establish status
exec_log('git -C /boot status --porcelain 2>&1', $status_output, $return_var);
exec_log('git -C /boot status --porcelain', $status_output, $return_var);
if ($return_var != 0) {
// detect git submodule
@@ -600,7 +624,7 @@ if ($command == 'activate') {
}
// detect corruption #1
exec_log('git -C /boot show --summary 2>&1', $show_output, $return_var);
exec_log('git -C /boot show --summary', $show_output, $return_var);
if ($return_var != 0) {
if (stripos(implode($show_output),'fatal: your current branch appears to be broken') !== false) {
$arrState['error'] = 'Error: Backup corrupted';
@@ -616,11 +640,21 @@ if ($command == 'activate') {
} // end check for ($command == 'activate')
if ($command == 'update' || $command == 'activate') {
// note: this section only runs if there are changes detected
if ($arrState['uptodate'] == 'no') {
// increment git commit counter
appendToFile($commitCountFile, $time."\n");
// find files that are in repo but should not be, according to /boot/.git/info/exclude and various .gitignore files
$invalid_files = $return_var = null;
exec_log('git -C /boot ls-files --cached --ignored --exclude-standard', $invalid_files, $return_var);
foreach ((array) $invalid_files as $invalid_file) {
// remove each of these files from the repo
// this prevents future changes from being tracked but does not remove the file from history.
exec_log("git -C /boot rm --cached --ignore-unmatch '$invalid_file'");
}
// add and commit all file changes
exec_log('git -C /boot add -A');
exec_log('git -C /boot commit -m ' . escapeshellarg($commitmsg));
@@ -633,7 +667,7 @@ if ($command == 'update' || $command == 'activate') {
if ($return_var != 0) {
// check for permission denied
if (stripos(implode($push_output),'permission denied') !== false) {
$arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to My Servers Cloud';
$arrState['error'] = ($isConnected) ? 'Permission Denied' : 'Permission Denied, ensure you are connected to Unraid Connect Cloud';
} elseif (stripos(implode($push_output),'fatal: loose object') !== false && stripos(implode($push_output),'is corrupt') !== false) {
// detect corruption #2
$arrState['error'] = 'Error: Backup corrupted';

View File

@@ -47,6 +47,7 @@ class ServerState
private $connectPluginVersion;
private $configErrorEnum = [
"error" => 'UNKNOWN_ERROR',
"ineligible" => 'INELIGIBLE',
"invalid" => 'INVALID',
"nokeyserver" => 'NO_KEY_SERVER',
"withdrawn" => 'WITHDRAWN',
@@ -235,13 +236,17 @@ class ServerState
public function getServerState()
{
$serverState = [
"array" => [
"state" => @$this->getWebguiGlobal('var', 'fsState'),
"progress" => @$this->getWebguiGlobal('var', 'fsProgress'),
],
"apiKey" => $this->apiKey,
"apiVersion" => $this->apiVersion,
"avatar" => $this->avatar,
"caseModel" => $this->caseModel,
"config" => [
'valid' => ($this->var['configValid'] === 'yes'),
'error' => isset($this->configErrorEnum[$this->var['configValid']]) ? $this->configErrorEnum[$this->var['configValid']] : 'UNKNOWN_ERROR',
'error' => isset($this->configErrorEnum[$this->var['configValid']]) ? $this->configErrorEnum[$this->var['configValid']] : null,
],
"connectPluginInstalled" => $this->connectPluginInstalled,
"connectPluginVersion" => $this->connectPluginVersion,

View File

@@ -72,13 +72,13 @@ class WebComponentTranslations
'<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>' => '<p>' . _('To continue using Unraid OS you may purchase a license key.') . ' ' . _('Alternately, you may request a Trial extension.') . '</p>',
'<p>To support more storage devices as your server grows, click Upgrade Key.</p>' => '<p>' . _('To support more storage devices as your server grows, click Upgrade Key.') . '</p>',
'<p>You have used all your Trial extensions. To continue using Unraid OS you may purchase a license key.</p>' => '<p>' . _('You have used all your Trial extensions.') . ' ' . _('To continue using Unraid OS you may purchase a license key.') . '</p>',
'<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>' => '<p>' . _('Your **Trial** key includes all the functionality and device support of a **Pro** key') . '</p><p>' . _('After your **Trial** has reached expiration, your server *still functions normally* until the next time you Stop the array or reboot your server') . '</p><p>' . _('At that point you may either purchase a license key or request a *Trial* extension.') . '</p>',
'<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>' => '<p>' . _('Your **Trial** key includes all the functionality and device support of an **Unleashed** key') . '</p><p>' . _('After your **Trial** has reached expiration, your server *still functions normally* until the next time you Stop the array or reboot your server') . '</p><p>' . _('At that point you may either purchase a license key or request a *Trial* extension.') . '</p>',
'<p>Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>If you do not have a backup copy of your license key file you may attempt to recover your key.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>' => '<p>' . _('Your license key file is corrupted or missing.') . ' ' . _('The key file should be located in the /config directory on your USB Flash boot device') . '</p><p>' . _('If you do not have a backup copy of your license key file you may attempt to recover your key with your Unraid.net account') . '</p><p>' . _('If this was an expired Trial installation, you may purchase a license key.') . '</p>',
'<p>Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.</p><p>You may attempt to recover your key with your Unraid.net account.</p><p>If this was an expired Trial installation, you may purchase a license key.</p>' => '<p>' . _('Your license key file is corrupted or missing.') . ' ' . _('The key file should be located in the /config directory on your USB Flash boot device') . '</p><p>' . _('If you do not have a backup copy of your license key file you may attempt to recover your key with your Unraid.net account') . '</p><p>' . _('If this was an expired Trial installation, you may purchase a license key.') . '</p>',
'<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class="list-disc pl-16px"><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>' => '<p>' . _('Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key.') . ' ' . _('A <em>Trial</em> key provides all the functionality of a Pro Registration key.') . '</p><p>' . _('Registration keys are bound to your USB Flash boot device serial number (GUID).') . ' ' . _('Please use a high quality name brand device at least 1GB in size.') . '</p><p>' . _('Note: USB memory card readers are generally not supported because most do not present unique serial numbers.') . '</p><p>' . _('*Important:*') . '</p><ul class="list-disc pl-16px"><li>' . _('Please make sure your server time is accurate to within 5 minutes') . '</li><li>' . _('Please make sure there is a DNS server specified') . '</li>>' . '</ul>',
'<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class="list-disc pl-16px"><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>' => '<p>' . _('Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key.') . ' ' . _('A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.') . '</p><p>' . _('Registration keys are bound to your USB Flash boot device serial number (GUID).') . ' ' . _('Please use a high quality name brand device at least 1GB in size.') . '</p><p>' . _('Note: USB memory card readers are generally not supported because most do not present unique serial numbers.') . '</p><p>' . _('*Important:*') . '</p><ul class="list-disc pl-16px"><li>' . _('Please make sure your server time is accurate to within 5 minutes') . '</li><li>' . _('Please make sure there is a DNS server specified') . '</li>>' . '</ul>',
'<p>Your Trial key requires an internet connection.</p><p><a href="/Settings/NetworkSettings" class="underline">Please check Settings > Network</a></p>' => '<p>' . _('Your Trial key requires an internet connection') . '</p><p><a href="/Settings/NetworkSettings" class="underline">' . _('Please check Settings > Network') . '</a></p>',
'<p>Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.</p>' => '<p>' . _('Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.') . '</p>',
'A Trial key provides all the functionality of a Pro Registration key' => _('A Trial key provides all the functionality of a Pro Registration key'),
'A Trial key provides all the functionality of an Unleashed Registration key' => _('A Trial key provides all the functionality of an Unleashed Registration key'),
'Acklowledge that you have made a Flash Backup to enable this action' => _('Acklowledge that you have made a Flash Backup to enable this action'),
'ago' => _('ago'),
'All you need is an active internet connection, an Unraid.net account, and the Connect plugin. Get started by installing the plugin.' => _('All you need is an active internet connection, an Unraid.net account, and the Connect plugin.') . ' ' . _('Get started by installing the plugin.'),
@@ -339,7 +339,7 @@ class WebComponentTranslations
'You may still update to releases dated prior to your update expiration date.' => _('You may still update to releases dated prior to your update expiration date.'),
'You\'re one step closer to enhancing your Unraid experience' => _('You\'re one step closer to enhancing your Unraid experience'),
'Your {0} Key has been replaced!' => sprintf(_('Your %s Key has been replaced!'), '{0}'),
'Your free Trial key provides all the functionality of a Pro Registration key' => _('Your free Trial key provides all the functionality of a Pro Registration key'),
'Your free Trial key provides all the functionality of an Unleashed Registration key' => _('Your free Trial key provides all the functionality of an Unleashed Registration key'),
'Your Trial has expired' => _('Your Trial has expired'),
'Your Trial key has been extended!' => _('Your Trial key has been extended!'),
];

View File

@@ -11,6 +11,7 @@
$cli = php_sapi_name() == 'cli';
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
/**
* @name response_complete
@@ -85,7 +86,7 @@ switch ($command) {
response_complete(200, array('result' => $output), $output);
break;
case 'wanip':
$wanip = trim(@file_get_contents("https://wanip4.unraid.net/"));
$wanip = trim(http_get_contents("https://wanip4.unraid.net/"));
response_complete(200, array('result' => $wanip), $wanip);
break;
case 'none':

View File

@@ -1,6 +1,6 @@
<?php
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
@@ -108,6 +108,7 @@ class UnraidOsCheck
function _($text) {return $text;}
}
// this command will set the $notify array
extract(parse_plugin_cfg('dynamix', true));
$var = (array)@parse_ini_file('/var/local/emhttp/var.ini');
@@ -123,21 +124,12 @@ class UnraidOsCheck
if ($parsedAltUrl) $params['altUrl'] = $parsedAltUrl;
$urlbase = $parsedAltUrl ?? $defaultUrl;
$url = $urlbase.'?'.http_build_query($params);
$response = "";
// use error handler to convert warnings from file_get_contents to errors so they can be captured
function warning_as_error($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
$url = $urlbase.'?'.http_build_query($params);
$curlinfo = [];
$response = http_get_contents($url,[],$curlinfo);
if (array_key_exists('error', $curlinfo)) {
$response = json_encode(array('error' => $curlinfo['error']), JSON_PRETTY_PRINT);
}
set_error_handler("warning_as_error");
try {
$response = file_get_contents($url);
} catch (Exception $e) {
$response = json_encode(array('error' => $e->getMessage()), JSON_PRETTY_PRINT);
}
restore_error_handler();
$responseMutated = json_decode($response, true);
if (!$responseMutated) {
$response = json_encode(array('error' => 'Invalid response from '.$urlbase), JSON_PRETTY_PRINT);
@@ -159,14 +151,17 @@ class UnraidOsCheck
// send notification if a newer version is available and not ignored
$isNewerVersion = array_key_exists('isNewer',$responseMutated) ? $responseMutated['isNewer'] : false;
$isReleaseIgnored = in_array($responseMutated['version'], $this->getIgnoredReleases());
$isReleaseIgnored = array_key_exists('version',$responseMutated) ? in_array($responseMutated['version'], $this->getIgnoredReleases()) : false;
if ($responseMutated && $isNewerVersion && !$isReleaseIgnored) {
$output = _var($notify,'plugin');
$server = strtoupper(_var($var,'NAME','server'));
$newver = (array_key_exists('version',$responseMutated) && $responseMutated['version']) ? $responseMutated['version'] : 'unknown';
$script = '/usr/local/emhttp/webGui/scripts/notify';
exec("$script -e ".escapeshellarg("System - Unraid [$newver]")." -s ".escapeshellarg("Notice [$server] - Version update $newver")." -d ".escapeshellarg("A new version of Unraid is available")." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
$event = "System - Unraid [$newver]";
$subject = "Notice [$server] - Version update $newver";
$description = "A new version of Unraid is available";
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x");
}
exit(0);

View File

@@ -24,8 +24,32 @@ import type {
// return result;
// }
// ENOKEYFILE
// TRIAL
// BASIC
// PLUS
// PRO
// STARTER
// UNLEASHED
// LIFETIME
// EEXPIRED
// EGUID
// EGUID1
// ETRIAL
// ENOKEYFILE2
// ENOKEYFILE1
// ENOFLASH
// EBLACKLISTED
// EBLACKLISTED1
// EBLACKLISTED2
// ENOCONN
// '1111-1111-5GDB-123412341234' Starter.key = TkJCrVyXMLWWGKZF6TCEvf0C86UYI9KfUDSOm7JoFP19tOMTMgLKcJ6QIOt9_9Psg_t0yF-ANmzSgZzCo94ljXoPm4BESFByR0K7nyY9KVvU8szLEUcBUT3xC2adxLrAXFNxiPeK-mZqt34n16uETKYvLKL_Sr5_JziG5L5lJFBqYZCPmfLMiguFo1vp0xL8pnBH7q8bYoBnePrAcAVb9mAGxFVPEInSPkMBfC67JLHz7XY1Y_K5bYIq3go9XPtLltJ53_U4BQiMHooXUBJCKXodpqoGxq0eV0IhNEYdauAhnTsG90qmGZig0hZalQ0soouc4JZEMiYEcZbn9mBxPg
const staticGuid = '1111-1111-5GDB-123412341234';
const state: ServerState = 'TRIAL';
const currentFlashGuid = '1111-1111-NUIK-TEST1234ZACK'; // this is the flash drive that's been booted from
const regGuid = '1111-1111-NUIK-TEST1234ZACK'; // this guid is registered in key server
const keyfileBase64 = ''; // @todo raycast download key to base64
// const randomGuid = `1111-1111-${makeid(4)}-123412341234`; // this guid is registered in key server
// const newGuid = `1234-1234-${makeid(4)}-123412341234`; // this is a new USB, not registered
@@ -42,23 +66,6 @@ const oneHourFromNow = Date.now() + 60 * 60 * 1000; // 1 hour from now
let expireTime = 0;
let regExp: number | undefined;
// ENOKEYFILE
// TRIAL
// BASIC
// PLUS
// PRO
// EEXPIRED
// EGUID
// EGUID1
// ETRIAL
// ENOKEYFILE2
// ENOKEYFILE1
// ENOFLASH
// EBLACKLISTED
// EBLACKLISTED1
// EBLACKLISTED2
// ENOCONN
const state: ServerState = 'TRIAL';
let regDevs = 0;
let regTy = '';
switch (state) {
@@ -101,10 +108,10 @@ switch (state) {
break;
}
const connectPluginInstalled = 'dynamix.unraid.net.staging.plg';
// const connectPluginInstalled = '';
// const connectPluginInstalled = 'dynamix.unraid.net.staging.plg';
const connectPluginInstalled = '';
const osVersion = '6.12.5';
const osVersion = '6.12.8';
const osVersionBranch = 'stable';
// const parsedRegExp = regExp ? dayjs(regExp).format('YYYY-MM-DD') : undefined;
@@ -133,8 +140,8 @@ export const serverState: Server = {
apiKey: 'unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810',
avatar: 'https://source.unsplash.com/300x300/?portrait',
config: {
// error: 'INVALID',
valid: true,
error: null,
valid: false,
},
connectPluginInstalled,
description: 'DevServer9000',
@@ -143,11 +150,11 @@ export const serverState: Server = {
flashBackupActivated: !!connectPluginInstalled,
flashProduct: 'SanDisk_3.2Gen1',
flashVendor: 'USB',
guid: staticGuid,
guid: currentFlashGuid,
// "guid": "0781-5583-8355-81071A2B0211",
inIframe: false,
// keyfile: 'DUMMY_KEYFILE',
keyfile: 'TkJCrVyXMLWWGKZF6TCEvf0C86UYI9KfUDSOm7JoFP19tOMTMgLKcJ6QIOt9_9Psg_t0yF-ANmzSgZzCo94ljXoPm4BESFByR0K7nyY9KVvU8szLEUcBUT3xC2adxLrAXFNxiPeK-mZqt34n16uETKYvLKL_Sr5_JziG5L5lJFBqYZCPmfLMiguFo1vp0xL8pnBH7q8bYoBnePrAcAVb9mAGxFVPEInSPkMBfC67JLHz7XY1Y_K5bYIq3go9XPtLltJ53_U4BQiMHooXUBJCKXodpqoGxq0eV0IhNEYdauAhnTsG90qmGZig0hZalQ0soouc4JZEMiYEcZbn9mBxPg',
keyfile: keyfileBase64,
lanIp: '192.168.254.36',
license: '',
locale: 'en_US', // en_US, ja
@@ -161,7 +168,7 @@ export const serverState: Server = {
regTo: 'Zack Spear',
regTy,
regExp,
// "regGuid": "0781-5583-8355-81071A2B0211",
regGuid,
site: 'http://localhost:4321',
state,
theme: {
@@ -173,15 +180,15 @@ export const serverState: Server = {
name: 'white',
textColor: ''
},
updateOsResponse: {
version: '6.12.6',
name: 'Unraid 6.12.6',
date: '2023-12-13',
isNewer: true,
isEligible: false,
changelog: 'https://docs.unraid.net/unraid-os/release-notes/6.12.6/',
sha256: '2f5debaf80549029cf6dfab0db59180e7e3391c059e6521aace7971419c9c4bf',
},
// updateOsResponse: {
// version: '6.12.6',
// name: 'Unraid 6.12.6',
// date: '2023-12-13',
// isNewer: true,
// isEligible: false,
// changelog: 'https://docs.unraid.net/unraid-os/release-notes/6.12.6/',
// sha256: '2f5debaf80549029cf6dfab0db59180e7e3391c059e6521aace7971419c9c4bf',
// },
uptime,
username: 'zspearmint',
wanFQDN: ''

View File

@@ -7,6 +7,8 @@ Tag="globe"
/**
* @todo create web component env switcher liker upcEnv(). If we utilize manifest.json then we'll be switching its path.
*/
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
require_once "$docroot/webGui/include/Wrappers.php";
$myservers_flash_cfg_path='/boot/config/plugins/dynamix.my.servers/myservers.cfg';
$myservers = file_exists($myservers_flash_cfg_path) ? @parse_ini_file($myservers_flash_cfg_path,true) : [];
// print_r($mystatus);
@@ -142,7 +144,7 @@ if ($display['theme'] === 'black' || $display['theme'] === 'azure') {
<unraid-key-actions></connect-key-actions>
</div>
<div class="ComponentWrapper">
<unraid-wan-ip-check php-wan-ip="<?=@file_get_contents('https://wanip4.unraid.net/')?>"></connect-wan-ip-check>
<unraid-wan-ip-check php-wan-ip="<?=http_get_contents('https://wanip4.unraid.net/')?>"></connect-wan-ip-check>
</div>
<script>

View File

@@ -25,6 +25,7 @@ import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import useDateTimeHelper from '~/composables/dateTime';
import { useReplaceRenewStore } from '~/store/replaceRenew';
import { useServerStore } from '~/store/server';
import type { RegistrationItemProps } from '~/types/registration';
@@ -39,6 +40,8 @@ const { t } = useI18n();
const replaceRenewCheckStore = useReplaceRenewStore();
const serverStore = useServerStore();
const {
computedArray,
arrayWarning,
authAction,
dateTimeFormat,
deviceCount,
@@ -54,6 +57,7 @@ const {
regTy,
regExp,
regUpdatesExpired,
serverErrors,
state,
stateData,
stateDataError,
@@ -76,6 +80,24 @@ watch(regTm, (_newV) => {
});
onBeforeMount(() => {
setFormattedRegTm();
/** automatically check for replacement and renewal eligibility…will prompt user if eligible for a renewal / key re-roll for legacy keys */
if (guid.value && keyfile.value) {
replaceRenewCheckStore.check();
}
});
const headingIcon = computed(() => serverErrors.value.length ? ShieldExclamationIcon : ShieldCheckIcon);
const heading = computed(() => {
if (serverErrors.value.length) { // It's rare to have multiple errors but for the time being only show the first error
return serverErrors.value[0]?.heading;
}
return stateData.value.heading;
});
const subheading = computed(() => {
if (serverErrors.value.length) { // It's rare to have multiple errors but for the time being only show the first error
return serverErrors.value[0]?.message;
}
return stateData.value.message;
});
const showTrialExpiration = computed((): boolean => state.value === 'TRIAL' || state.value === 'EEXPIRED');
@@ -87,6 +109,13 @@ const showFilteredKeyActions = computed((): boolean => !!(keyActions.value && ke
const items = computed((): RegistrationItemProps[] => {
return [
...(computedArray.value
? [{
label: t('Array status'),
text: computedArray.value,
warning: arrayWarning.value,
}]
: []),
...(regTy.value
? [{
label: t('License key type'),
@@ -153,7 +182,7 @@ const items = computed((): RegistrationItemProps[] => {
: []),
...(keyInstalled.value
? [{
error: !!tooManyDevices.value,
error: tooManyDevices.value,
label: t('Attached Storage Devices'),
text: tooManyDevices.value
? t('{0} out of {1} allowed devices upgrade your key to support more devices', [deviceCount.value, computedRegDevs.value])
@@ -167,6 +196,7 @@ const items = computed((): RegistrationItemProps[] => {
componentProps: { t },
}]
: []),
...(showFilteredKeyActions.value
? [{
component: KeyActions,
@@ -178,13 +208,6 @@ const items = computed((): RegistrationItemProps[] => {
: []),
];
});
onBeforeMount(() => {
/** automatically check for replacement and renewal eligibility…will prompt user if eligible for a renewal / key re-roll for legacy keys */
if (guid.value && keyfile.value) {
replaceRenewCheckStore.check();
}
});
</script>
<template>
@@ -194,17 +217,17 @@ onBeforeMount(() => {
<header class="flex flex-col gap-y-16px">
<h3
class="text-20px md:text-24px font-semibold leading-normal flex flex-row items-center gap-8px"
:class="stateDataError ? 'text-unraid-red' : 'text-green-500'"
:class="serverErrors.length ? 'text-unraid-red' : 'text-green-500'"
>
<component :is="stateDataError ? ShieldExclamationIcon : ShieldCheckIcon" class="w-24px h-24px" />
<component :is="headingIcon" class="w-24px h-24px" />
<span>
{{ stateData.heading }}
{{ heading }}
</span>
</h3>
<div
v-if="stateData.message"
v-if="subheading"
class="prose text-16px leading-relaxed whitespace-normal opacity-75"
v-html="stateData.message"
v-html="subheading"
/>
<span v-if="authAction" class="grow-0">
<BrandButton

View File

@@ -25,9 +25,9 @@ const evenBgColor = computed(() => {
error && 'text-white bg-unraid-red',
warning && 'text-black bg-yellow-100',
]"
class="text-16px p-12px grid grid-cols-1 gap-4px sm:px-20px sm:grid-cols-5 sm:gap-16px items-start rounded"
class="text-16px p-12px grid grid-cols-1 gap-4px sm:px-20px sm:grid-cols-5 sm:gap-16px items-baseline rounded"
>
<dt v-if="label" class="font-semibold sm:col-span-2 flex flex-row sm:justify-end sm:text-right items-center gap-x-8px">
<dt v-if="label" class="font-semibold leading-normal sm:col-span-2 flex flex-row sm:justify-end sm:text-right items-center gap-x-8px">
<ShieldExclamationIcon v-if="error" class="w-16px h-16px fill-current" />
<span v-html="label" />
</dt>

View File

@@ -102,7 +102,7 @@ const subheading = computed(() => {
if (keyActionType.value === 'purchase') { return props.t('Thank you for purchasing an Unraid {0} Key!', [keyType.value]); }
if (keyActionType.value === 'replace') { return props.t('Your {0} Key has been replaced!', [keyType.value]); }
if (keyActionType.value === 'trialExtend') { return props.t('Your Trial key has been extended!'); }
if (keyActionType.value === 'trialStart') { return props.t('Your free Trial key provides all the functionality of a Pro Registration key'); }
if (keyActionType.value === 'trialStart') { return props.t('Your free Trial key provides all the functionality of an Unleashed Registration key'); }
if (keyActionType.value === 'upgrade') { return props.t('Thank you for upgrading to an Unraid {0} Key!', [keyType.value]); }
return '';
}

View File

@@ -242,6 +242,7 @@ export type Config = {
};
export enum ConfigErrorState {
Ineligible = 'INELIGIBLE',
Invalid = 'INVALID',
NoKeyServer = 'NO_KEY_SERVER',
UnknownError = 'UNKNOWN_ERROR',

View File

@@ -9,7 +9,7 @@ const CONNECT_DOCS = new URL('category/unraid-connect', DOCS);
const CONNECT_DASHBOARD = new URL(import.meta.env.VITE_CONNECT ?? 'https://connect.myunraid.net');
const CONNECT_FORUMS = new URL('/forum/94-connect-plugin-support/', FORUMS);
const CONTACT = new URL('/contact', UNRAID_NET);
const DISCORD = new URL('https://discord.gg/unraid');
const DISCORD = new URL('https://discord.unraid.net');
const PURCHASE_CALLBACK = new URL('/c', UNRAID_NET);
const WEBGUI = new URL(import.meta.env.VITE_WEBGUI ?? window.location.origin);

View File

@@ -126,10 +126,10 @@
"Learn More": "",
"No Keyfile": "",
"Let's Unleash your Hardware!": "",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "",
"Trial": "",
"Thank you for choosing Unraid OS!": "",
"<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "",
"<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "",
"Trial Expired": "",
"Your Trial has expired": "",
"<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>": "",
@@ -186,7 +186,7 @@
"Starting your free 30 day trial": "",
"Trial Key Created": "",
"Please wait while the page reloads to install your trial key": "",
"A Trial key provides all the functionality of a Pro Registration key": "",
"A Trial key provides all the functionality of an Unleashed Registration key": "",
"Extension Installed": "",
"Recovered": "",
"Replaced": "",
@@ -196,5 +196,5 @@
"Install Extended": "",
"Install Recovered": "",
"Install Replaced": "",
"Your free Trial key provides all the functionality of a Pro Registration key": ""
"Your free Trial key provides all the functionality of an Unleashed Registration key": ""
}

View File

@@ -129,10 +129,10 @@
"Learn More": "Learn More",
"No Keyfile": "No Keyfile",
"Let's Unleash your Hardware!": "Let's Unleash your Hardware!",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>",
"Trial": "Trial",
"Thank you for choosing Unraid OS!": "Thank you for choosing Unraid OS!",
"<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>",
"<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>",
"Trial Expired": "Trial Expired",
"Your Trial has expired": "Your Trial has expired",
"<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>": "<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>",
@@ -189,7 +189,7 @@
"Starting your free 30 day trial": "Starting your free 30 day trial",
"Trial Key Created": "Trial Key Created",
"Please wait while the page reloads to install your trial key": "Please wait while the page reloads to install your trial key",
"A Trial key provides all the functionality of a Pro Registration key": "A Trial key provides all the functionality of a Pro Registration key.",
"A Trial key provides all the functionality of an Unleashed Registration key": "A Trial key provides all the functionality of an Unleashed Registration key.",
"Extension Installed": "Extension Installed",
"Recovered": "Recovered",
"Replaced": "Replaced",
@@ -199,7 +199,7 @@
"Install Extended": "Install Extended",
"Install Recovered": "Install Recovered",
"Install Replaced": "Install Replaced",
"Your free Trial key provides all the functionality of a Pro Registration key": "Your free Trial key provides all the functionality of a Pro Registration key",
"Your free Trial key provides all the functionality of an Unleashed Registration key": "Your free Trial key provides all the functionality of an Unleashed Registration key",
"Calculating trial expiration…": "Calculating trial expiration…",
"Signing In": "Signing In",
"Signing Out": "Signing Out",

View File

@@ -126,10 +126,10 @@
"Learn More": "もっと詳しく知る",
"No Keyfile": "キーファイルがありません",
"Let's Unleash your Hardware!": "ハードウェアを解き放ちましょう!",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of a Pro Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "<p>登録キーを購入するか、30 日間の無料の<em>トライアル</em>キーをインストールするまで、サーバーは使用できません。 \n<em>トライアル</em> キーは、Pro 登録キーのすべての機能を提供します。</p><p>登録キーは、USB フラッシュ ブート デバイスのシリアル番号 (GUID) にバインドされています。\nサイズが少なくとも 1 GB の高品質の有名ブランドのデバイスを使用してください。</p><p>注: USB メモリ カード リーダーのほとんどは固有のシリアル番号を提示していないため、通常はサポートされていません。</p><p><strong>\n重要:</strong></p><ul class='list-disc pl-16px'><li>サーバー時間が 5 分以内であることを確認してください。</li><li>\n指定された DNS サーバー</li></ul>",
"<p>Your server will not be usable until you purchase a Registration key or install a free 30 day <em>Trial</em> key. A <em>Trial</em> key provides all the functionality of an Unleashed Registration key.</p><p>Registration keys are bound to your USB Flash boot device serial number (GUID). Please use a high quality name brand device at least 1GB in size.</p><p>Note: USB memory card readers are generally not supported because most do not present unique serial numbers.</p><p><strong>Important:</strong></p><ul class='list-disc pl-16px'><li>Please make sure your server time is accurate to within 5 minutes</li><li>Please make sure there is a DNS server specified</li></ul>": "<p>登録キーを購入するか、30 日間の無料の<em>トライアル</em>キーをインストールするまで、サーバーは使用できません。 \n<em>トライアル</em> キーは、Pro 登録キーのすべての機能を提供します。</p><p>登録キーは、USB フラッシュ ブート デバイスのシリアル番号 (GUID) にバインドされています。\nサイズが少なくとも 1 GB の高品質の有名ブランドのデバイスを使用してください。</p><p>注: USB メモリ カード リーダーのほとんどは固有のシリアル番号を提示していないため、通常はサポートされていません。</p><p><strong>\n重要:</strong></p><ul class='list-disc pl-16px'><li>サーバー時間が 5 分以内であることを確認してください。</li><li>\n指定された DNS サーバー</li></ul>",
"Trial": "トライアル",
"Thank you for choosing Unraid OS!": "Unraid OS をお選びいただきありがとうございます。",
"<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "<p><em>トライアル</em> キーには、<em>プロ</em> キーのすべての機能とデバイス サポートが含まれています。</p><p><em>トライアル</em> の終了後\n有効期限に達しても、次回アレイを停止するかサーバーを再起動するまで、 サーバーは<strong>通常どおり機能</strong>します。</p><p>その時点で、ライセンス キーを購入するか、<em>ライセンス キーをリクエストすることができます。 \n> トライアル</em>拡張機能。</p>",
"<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>": "<p><em>トライアル</em> キーには、<em>プロ</em> キーのすべての機能とデバイス サポートが含まれています。</p><p><em>トライアル</em> の終了後\n有効期限に達しても、次回アレイを停止するかサーバーを再起動するまで、 サーバーは<strong>通常どおり機能</strong>します。</p><p>その時点で、ライセンス キーを購入するか、<em>ライセンス キーをリクエストすることができます。 \n> トライアル</em>拡張機能。</p>",
"Trial Expired": "トライアル期間が終了しました",
"Your Trial has expired": "トライアル版の有効期限が切れました",
"<p>To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.</p>": "<p>Unraid OS を引き続き使用するには、ライセンス キーを購入することができます。\nあるいは、トライアルの延長をリクエストすることもできます。</p>",
@@ -186,7 +186,7 @@
"Starting your free 30 day trial": "30 日間の無料トライアルを開始する",
"Trial Key Created": "トライアルキーが作成されました",
"Please wait while the page reloads to install your trial key": "試用版キーをインストールするには、ページがリロードされるまでお待ちください。",
"A Trial key provides all the functionality of a Pro Registration key": "トライアル キーは、Pro 登録キーのすべての機能を提供します。",
"A Trial key provides all the functionality of an Unleashed Registration key": "トライアル キーは、Pro 登録キーのすべての機能を提供します。",
"Extension Installed": "拡張機能がインストールされました",
"Recovered": "回復しました",
"Replaced": "交換されました",
@@ -196,5 +196,5 @@
"Install Extended": "拡張インストール",
"Install Recovered": "インストールが回復しました",
"Install Replaced": "インストールと置き換え",
"Your free Trial key provides all the functionality of a Pro Registration key": "無料のトライアル キーは、プロ登録キーのすべての機能を提供します"
"Your free Trial key provides all the functionality of an Unleashed Registration key": "無料のトライアル キーは、プロ登録キーのすべての機能を提供します"
}

View File

@@ -5,11 +5,12 @@
"dev": "nuxt dev",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"type-check": "nuxi typecheck",
"build:dev": "npm run lint && npm run type-check && nuxt build && npm run deploy-wc:dev",
"build:webgui": "npm run lint && npm run type-check && nuxt build && npm run copy-to-webgui-repo",
"build": "npm run type-check && nuxt build",
"build:dev": "npm run lint && npm run type-check && nuxt build && npm run manifest-ts && npm run deploy-wc:dev",
"build:webgui": "npm run lint && npm run type-check && nuxt build && npm run manifest-ts && npm run copy-to-webgui-repo",
"build": "npm run type-check && nuxt build && npm run manifest-ts",
"deploy-wc:dev": "./scripts/deploy-dev.sh",
"copy-to-webgui-repo": "./scripts/copy-to-webgui-repo.sh",
"manifest-ts": "node ./scripts/add-timestamp-webcomponent-manifest.js",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",

View File

@@ -0,0 +1,15 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('fs');
// Read the JSON file
const filePath = '../web/.nuxt/nuxt-custom-elements/dist/unraid-components/manifest.json';
const jsonData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
// Add timestamp (ts) to the JSON data
const timestamp = Math.floor(new Date().getTime() / 1000); // Current timestamp epoch
jsonData.ts = timestamp;
// Write the updated JSON back to the file
fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2));
console.log(`Timestamp: ${timestamp} added to the web component manifest.`);

View File

@@ -5,6 +5,7 @@ import dayjs from 'dayjs';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import prerelease from 'semver/functions/prerelease';
import {
ArrowPathIcon,
ArrowRightOnRectangleIcon,
CogIcon,
GlobeAltIcon,
@@ -17,7 +18,11 @@ import { useQuery } from '@vue/apollo-composable';
import { SERVER_CLOUD_FRAGMENT, SERVER_STATE_QUERY } from './server.fragment';
import { useFragment } from '~/composables/gql/fragment-masking';
import { WebguiState, WebguiUpdateIgnore } from '~/composables/services/webgui';
import { WEBGUI_SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls';
import {
WEBGUI_SETTINGS_MANAGMENT_ACCESS,
WEBGUI_TOOLS_REGISTRATION,
WEBGUI_TOOLS_UPDATE,
} from '~/helpers/urls';
import { useAccountStore } from '~/store/account';
import { useErrorsStore, type Error } from '~/store/errors';
import { usePurchaseStore } from '~/store/purchase';
@@ -36,6 +41,7 @@ import type {
ServerconnectPluginInstalled,
ServerDateTimeFormat,
ServerStateDataKeyActions,
ServerStateArray,
ServerOsVersionBranch,
ServerUpdateOsResponse,
} from '~/types/server';
@@ -65,6 +71,18 @@ export const useServerStore = defineStore('server', () => {
}
});
const apiVersion = ref<string>('');
const array = ref<ServerStateArray | undefined>();
// helps to display warning next to array status
const arrayWarning = computed(() => !!(stateDataError.value || serverConfigError.value));
const computedArray = computed(() => {
if (arrayWarning.value) {
if (array.value?.state === 'Stopped') {
return 'Stopped. The Array will not start until the above issue is resolved.';
}
return 'Started. If stopped, the Array will not restart until the above issue is resolved.';
}
return array.value?.state;
});
const avatar = ref<string>(''); // @todo potentially move to a user store
const caseModel = ref<string>('');
const cloud = ref<PartialCloudFragment | undefined>();
@@ -165,6 +183,7 @@ export const useServerStore = defineStore('server', () => {
return {
apiKey: apiKey.value,
apiVersion: apiVersion.value,
array: array.value,
avatar: avatar.value,
connectPluginVersion: connectPluginVersion.value,
connectPluginInstalled: connectPluginInstalled.value,
@@ -451,7 +470,7 @@ export const useServerStore = defineStore('server', () => {
],
humanReadable: 'Trial',
heading: 'Thank you for choosing Unraid OS!',
message: '<p>Your <em>Trial</em> key includes all the functionality and device support of a <em>Pro</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>',
message: '<p>Your <em>Trial</em> key includes all the functionality and device support of an <em>Unleashed</em> key.</p><p>After your <em>Trial</em> has reached expiration, your server <strong>still functions normally</strong> until the next time you Stop the array or reboot your server.</p><p>At that point you may either purchase a license key or request a <em>Trial</em> extension.</p>',
};
case 'EEXPIRED':
return {
@@ -683,24 +702,73 @@ export const useServerStore = defineStore('server', () => {
});
const trialExtensionEligible = computed(() => !regGen.value || regGen.value < 2);
const tooManyDevices = computed((): Error | undefined => {
if ((deviceCount.value !== 0 && computedRegDevs.value > 0 && deviceCount.value > computedRegDevs.value) ||
(!config.value?.valid && config.value?.error === 'INVALID')) {
return {
heading: 'Too Many Devices',
level: 'error',
message: 'You have exceeded the number of devices allowed for your license. Please remove a device before adding another.',
ref: 'tooManyDevices',
type: 'server',
};
const serverConfigError = computed((): Error | undefined => {
if (!config.value?.valid && config.value?.error) {
switch (config.value?.error) {
// case 'UNKNOWN_ERROR':
// return {
// heading: 'Unknown Error',
// level: 'error',
// message: 'An unknown internal error occurred.',
// ref: 'configError',
// type: 'server',
// };
case 'INELIGIBLE':
return {
heading: 'Ineligible for OS Version',
level: 'error',
message: 'Your License Key does not support this OS Version. OS build date greater than key expiration. Please consider extending your registration key.',
actions: [{
href: WEBGUI_TOOLS_REGISTRATION.toString(),
icon: CogIcon,
text: 'Learn More at Tools > Registration',
}],
ref: 'configError',
type: 'server',
};
case 'INVALID':
return {
heading: 'Too Many Devices',
level: 'error',
message: 'You have exceeded the number of devices allowed for your license. Please remove a device to start the array, or upgrade your key to support more devices.',
ref: 'configError',
type: 'server',
};
case 'NO_KEY_SERVER':
return {
heading: 'Check Network Connection',
level: 'error',
message: 'Unable to validate your trial key. Please check your network connection.',
ref: 'configError',
type: 'server',
};
case 'WITHDRAWN':
return {
heading: 'OS Version Withdrawn',
level: 'error',
message: 'This OS release should not be run. OS Update Required.',
actions: [{
href: WEBGUI_TOOLS_UPDATE.toString(),
icon: ArrowPathIcon,
text: 'Check for Update',
}],
ref: 'configError',
type: 'server',
};
}
return undefined;
}
return undefined;
});
watch(tooManyDevices, (newVal, oldVal) => {
watch(serverConfigError, (newVal, oldVal) => {
if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); }
if (newVal) { errorsStore.setError(newVal); }
});
const tooManyDevices = computed((): boolean => {
return ((deviceCount.value !== 0 && computedRegDevs.value > 0 && deviceCount.value > computedRegDevs.value) ||
(!config.value?.valid && config.value?.error === 'INVALID'));
});
const pluginInstallFailed = computed((): Error | undefined => {
if (connectPluginInstalled.value && connectPluginInstalled.value.includes('_installFailed')) {
return {
@@ -730,7 +798,7 @@ export const useServerStore = defineStore('server', () => {
* Deprecation warning for [hash].unraid.net SSL certs. Deprecation started 2023-01-01
*/
const deprecatedUnraidSSL = ref<Error | undefined>(
(window.location.hostname.includes('localhost')
(window.location.hostname.includes('localhost') && window.location.port !== '4321'
? {
actions: [
{
@@ -797,18 +865,20 @@ export const useServerStore = defineStore('server', () => {
const serverErrors = computed(() => {
return [
stateDataError.value,
tooManyDevices.value,
serverConfigError.value,
pluginInstallFailed.value,
deprecatedUnraidSSL.value,
cloudError.value,
].filter(Boolean);
});
/**
* Actions
*/
const setServer = (data: Server) => {
console.debug('[setServer]', data);
if (typeof data?.apiKey !== 'undefined') { apiKey.value = data.apiKey; }
if (typeof data?.array !== 'undefined') { array.value = data.array; }
if (typeof data?.apiVersion !== 'undefined') { apiVersion.value = data.apiVersion; }
if (typeof data?.avatar !== 'undefined') { avatar.value = data.avatar; }
if (typeof data?.caseModel !== 'undefined') { caseModel.value = data.caseModel; }
@@ -1020,6 +1090,7 @@ export const useServerStore = defineStore('server', () => {
return {
// state
apiKey,
array,
avatar,
cloud,
config,
@@ -1077,6 +1148,9 @@ export const useServerStore = defineStore('server', () => {
stateDataError,
serverErrors,
tooManyDevices,
serverConfigError,
arrayWarning,
computedArray,
// actions
setServer,
setUpdateOsResponse,

View File

@@ -60,9 +60,15 @@ export interface ServerUpdateOsResponse {
sha256: string | null;
}
export interface ServerStateArray {
state: 'Stopped' | 'Started' | 'Starting' | 'Stopping';
progress: string;
}
export interface Server {
apiKey?: string;
apiVersion?: string;
array?: ServerStateArray;
avatar?: string;
caseModel?: string;
cloud?: PartialCloudFragment | undefined;