mirror of
https://github.com/unraid/api.git
synced 2026-01-02 14:40:01 -06:00
Compare commits
18 Commits
v3.5.1
...
feat/serve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
533927c692 | ||
|
|
985ed5bad4 | ||
|
|
21123f39a6 | ||
|
|
fd6f4eccf4 | ||
|
|
2ce62bf789 | ||
|
|
b33c86c99c | ||
|
|
cd0248e4c9 | ||
|
|
ecb3ed5003 | ||
|
|
0569339a41 | ||
|
|
3e9faead43 | ||
|
|
6e700b2385 | ||
|
|
464fc4993c | ||
|
|
4316c72809 | ||
|
|
ce0cebe09c | ||
|
|
23b90a0d56 | ||
|
|
3f8b3536b5 | ||
|
|
0dcf785b45 | ||
|
|
8cf4aff622 |
@@ -1 +1 @@
|
||||
18.17.1
|
||||
18.19.1
|
||||
@@ -2,6 +2,23 @@
|
||||
|
||||
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.5.3](https://github.com/unraid/api/compare/v3.5.2...v3.5.3) (2024-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* regDevs usage to allow more flexibility for STARTER ([#860](https://github.com/unraid/api/issues/860)) ([92a9600](https://github.com/unraid/api/commit/92a9600f3a242c5f263f1672eab81054b9cf4fae))
|
||||
|
||||
### [3.5.2](https://github.com/unraid/api/compare/v3.5.1...v3.5.2) (2024-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency vue-i18n to v9.10.1 ([#813](https://github.com/unraid/api/issues/813)) ([69b599c](https://github.com/unraid/api/commit/69b599c5ed8d44864201a32b4d952427d454dc74))
|
||||
* **deps:** update dependency wretch to v2.8.0 ([#814](https://github.com/unraid/api/issues/814)) ([66900b4](https://github.com/unraid/api/commit/66900b495b82b923264897d38b1529a22b10aa1c))
|
||||
* update os check modal button conditionals ([282a836](https://github.com/unraid/api/commit/282a83625f417ccefe090b65cc6b73a084727a87))
|
||||
* update os check modal ineligible date format ([83083de](https://github.com/unraid/api/commit/83083de1e698f73a35635ae6047dcf49fd4b8114))
|
||||
|
||||
### [3.5.1](https://github.com/unraid/api/compare/v3.5.0...v3.5.1) (2024-02-29)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
###########################################################
|
||||
# Development/Build Image
|
||||
###########################################################
|
||||
FROM node:18.17.1-bookworm-slim As development
|
||||
FROM node:18.19.1-bookworm-slim As development
|
||||
|
||||
# Install build tools and dependencies
|
||||
RUN apt-get update -y && apt-get install -y \
|
||||
|
||||
4
api/package-lock.json
generated
4
api/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@unraid/api",
|
||||
"version": "3.5.1",
|
||||
"version": "3.5.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@unraid/api",
|
||||
"version": "3.5.1",
|
||||
"version": "3.5.3",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.8.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@unraid/api",
|
||||
"version": "3.5.1",
|
||||
"version": "3.5.3",
|
||||
"main": "dist/index.js",
|
||||
"bin": "dist/unraid-api.cjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -140,7 +140,7 @@ function registerServer(button) {
|
||||
button.form.submit();
|
||||
});
|
||||
<?else:?>
|
||||
// give the unraid-api time to call rc.nginx and UpdateDNS before refreshing the page
|
||||
// give the unraid-api time to call rc.nginx before refreshing the page
|
||||
const delay = 4000;
|
||||
setTimeout(function() {
|
||||
button.form.submit();
|
||||
|
||||
@@ -47,6 +47,7 @@ class ServerState
|
||||
private $connectPluginVersion;
|
||||
private $configErrorEnum = [
|
||||
"error" => 'UNKNOWN_ERROR',
|
||||
"ineligible" => 'INELIGIBLE',
|
||||
"invalid" => 'INVALID',
|
||||
"nokeyserver" => 'NO_KEY_SERVER',
|
||||
"withdrawn" => 'WITHDRAWN',
|
||||
@@ -241,7 +242,7 @@ class ServerState
|
||||
"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,
|
||||
@@ -270,7 +271,7 @@ class ServerState
|
||||
"osVersionBranch" => $this->osVersionBranch,
|
||||
"protocol" => _var($_SERVER, 'REQUEST_SCHEME'),
|
||||
"rebootType" => $this->rebootDetails->getRebootType(),
|
||||
"regDev" => @(int)$this->var['regDev'] ?? 0,
|
||||
"regDevs" => @(int)$this->var['regDevs'] ?? 0,
|
||||
"regGen" => @(int)$this->var['regGen'],
|
||||
"regGuid" => @$this->var['regGUID'] ?? '',
|
||||
"regTo" => @htmlspecialchars($this->var['regTo'], ENT_HTML5, 'UTF-8') ?? '',
|
||||
|
||||
@@ -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,
|
||||
@@ -11,429 +11,12 @@
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = 'settings';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
|
||||
function host_lookup_ip($host) {
|
||||
$result = @dns_get_record($host, DNS_A);
|
||||
$ip = ($result) ? $result[0]['ip']??'' : '';
|
||||
return($ip);
|
||||
}
|
||||
function rebindDisabled() {
|
||||
global $isLegacyCert;
|
||||
$rebindtesturl = $isLegacyCert ? "rebindtest.unraid.net" : "rebindtest.myunraid.net";
|
||||
// DNS Rebind Protection - this checks the server but clients could still have issues
|
||||
$validResponse = array("192.168.42.42", "fd42");
|
||||
$response = host_lookup_ip($rebindtesturl);
|
||||
return in_array(explode('::',$response)[0], $validResponse);
|
||||
}
|
||||
function format_port($port) {
|
||||
return ($port != 80 && $port != 443) ? ':'.$port : '';
|
||||
}
|
||||
function anonymize_host($host) {
|
||||
global $anon;
|
||||
if ($anon) {
|
||||
$host = preg_replace('/.*\.myunraid\.net/', '*.hash.myunraid.net', $host);
|
||||
$host = preg_replace('/.*\.unraid\.net/', 'hash.unraid.net', $host);
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
function anonymize_ip($ip) {
|
||||
global $anon;
|
||||
if ($anon && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
|
||||
$ip = "[redacted]";
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
function generate_internal_host($host, $ip) {
|
||||
if (strpos($host,'.myunraid.net') !== false) {
|
||||
$host = str_replace('*', str_replace('.', '-', $ip), $host);
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
function generate_external_host($host, $ip) {
|
||||
if (strpos($host,'.myunraid.net') !== false) {
|
||||
$host = str_replace('*', str_replace('.', '-', $ip), $host);
|
||||
} elseif (strpos($host,'.unraid.net') !== false) {
|
||||
$host = "www.".$host;
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
function verbose_output($httpcode, $result) {
|
||||
global $cli, $verbose, $anon, $plgversion, $post, $var, $isRegistered, $myservers, $reloadNginx, $nginx, $isLegacyCert;
|
||||
global $remoteaccess;
|
||||
global $icon_warn, $icon_ok;
|
||||
if (!$cli || !$verbose) return;
|
||||
|
||||
if ($anon) echo "(Output is anonymized, use '-vv' to see full details)".PHP_EOL;
|
||||
echo "Unraid OS {$var['version']}".((strpos($plgversion, "base-") === false) ? " with My Servers plugin version {$plgversion}" : '').PHP_EOL;
|
||||
echo ($isRegistered) ? "{$icon_ok}Signed in to Unraid.net as {$myservers['remote']['username']}".PHP_EOL : "{$icon_warn}Not signed in to Unraid.net".PHP_EOL ;
|
||||
echo "Use SSL is {$nginx['NGINX_USESSL']}".PHP_EOL;
|
||||
echo (rebindDisabled()) ? "{$icon_ok}Rebind protection is disabled" : "{$icon_warn}Rebind protection is enabled";
|
||||
echo " for ".($isLegacyCert ? "unraid.net" : "myunraid.net").PHP_EOL;
|
||||
if ($post) {
|
||||
$wanip = trim(@file_get_contents("https://wanip4.unraid.net/"));
|
||||
// check the data
|
||||
$certhostname = $nginx['NGINX_CERTNAME'];
|
||||
if ($certhostname) {
|
||||
// $certhostname is $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
|
||||
$certhostip = host_lookup_ip(generate_internal_host($certhostname, $post['internalip']));
|
||||
$certhosterr = ($certhostip != $post['internalip']);
|
||||
}
|
||||
if ($post['internalhostname'] != $certhostname) {
|
||||
// $post['internalhostname'] is $nginx['NGINX_LANMDNS'] (no cert, or Server_unraid_bundle.pem) || $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
|
||||
$internalhostip = host_lookup_ip(generate_internal_host($post['internalhostname'], $post['internalip']));
|
||||
$internalhosterr = ($internalhostip != $post['internalip']);
|
||||
}
|
||||
if (!empty($post['externalhostname'])) {
|
||||
// $post['externalhostname'] is $nginx['NGINX_CERTNAME'] (certificate_bundle.pem)
|
||||
$externalhostip = host_lookup_ip(generate_external_host($post['externalhostname'], $wanip));
|
||||
$externalhosterr = ($externalhostip != $wanip);
|
||||
}
|
||||
// anonymize data. no caclulations can be done with this data beyond this point.
|
||||
if ($anon) {
|
||||
if (!empty($certhostip)) $certhostip = anonymize_ip($certhostip);
|
||||
if (!empty($certhostname)) $certhostname = anonymize_host($certhostname);
|
||||
if (!empty($internalhostip)) $internalhostip = anonymize_ip($internalhostip);
|
||||
if (!empty($externalhostip)) $externalhostip = anonymize_ip($externalhostip);
|
||||
if (!empty($wanip)) $wanip = anonymize_ip($wanip);
|
||||
if (!empty($post['internalip'])) $post['internalip'] = anonymize_ip($post['internalip']);
|
||||
if (!empty($post['internalhostname'])) $post['internalhostname'] = anonymize_host($post['internalhostname']);
|
||||
if (!empty($post['externalhostname'])) $post['externalhostname'] = anonymize_host($post['externalhostname']);
|
||||
if (!empty($post['externalport'])) $post['externalport'] = "[redacted]";
|
||||
}
|
||||
// always anonymize the keyfile
|
||||
if (!empty($post['keyfile'])) $post['keyfile'] = "[redacted]";
|
||||
// output notes
|
||||
if (!empty($post['internalprotocol']) && !empty($post['internalhostname']) && !empty($post['internalport'])) {
|
||||
$localurl = $post['internalprotocol']."://".generate_internal_host($post['internalhostname'], $post['internalip']).format_port($post['internalport']);
|
||||
echo 'Local Access url: '.$localurl.PHP_EOL;
|
||||
if ($internalhostip) {
|
||||
// $internalhostip will not be defined for .local domains, ok to skip
|
||||
echo ($internalhosterr) ? $icon_warn : $icon_ok;
|
||||
echo generate_internal_host($post['internalhostname'], $post['internalip'])." resolves to {$internalhostip}";
|
||||
echo ($internalhosterr) ? ", it should resolve to {$post['internalip']}" : "";
|
||||
echo PHP_EOL;
|
||||
}
|
||||
if ($certhostname) {
|
||||
echo ($certhosterr) ? $icon_warn : $icon_ok;
|
||||
echo generate_internal_host($certhostname, $post['internalip']).' ';
|
||||
echo ($certhostip) ? "resolves to {$certhostip}" : "does not resolve to an IP address";
|
||||
echo ($certhosterr) ? ", it should resolve to {$post['internalip']}" : "";
|
||||
echo PHP_EOL;
|
||||
}
|
||||
if ($remoteaccess == 'yes' && !empty($post['externalprotocol']) && !empty($post['externalhostname']) && !empty($post['externalport'])) {
|
||||
$remoteurl = $post['externalprotocol']."://".generate_external_host($post['externalhostname'], $wanip).format_port($post['externalport']);
|
||||
echo 'Remote Access url: '.$remoteurl.PHP_EOL;
|
||||
echo ($externalhosterr) ? $icon_warn : $icon_ok;
|
||||
echo generate_external_host($post['externalhostname'], $wanip).' ';
|
||||
echo ($externalhosterr) ? "does not resolve to an IP address" : "resolves to {$externalhostip}";
|
||||
echo PHP_EOL;
|
||||
}
|
||||
if ($reloadNginx) {
|
||||
echo "IP address changes were detected, nginx was reloaded".PHP_EOL;
|
||||
}
|
||||
}
|
||||
// output post data
|
||||
echo PHP_EOL.'Request:'.PHP_EOL;
|
||||
echo @json_encode($post, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
}
|
||||
if ($result) {
|
||||
echo "Response (HTTP $httpcode):".PHP_EOL;
|
||||
$mutatedResult = is_array($result) ? json_encode($result) : $result;
|
||||
echo @json_encode(@json_decode($mutatedResult, true), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @name response_complete
|
||||
* @param {HTTP Response Status Code} $httpcode https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||
* @param {String|Array} $result - strings are assumed to be encoded JSON. Arrays will be encoded to JSON.
|
||||
* @param {String} $cli_success_msg
|
||||
*/
|
||||
function response_complete($httpcode, $result, $cli_success_msg='') {
|
||||
global $cli, $verbose;
|
||||
$mutatedResult = is_array($result) ? json_encode($result) : $result;
|
||||
if ($cli) {
|
||||
if ($verbose) verbose_output($httpcode, $result);
|
||||
$json = @json_decode($mutatedResult,true);
|
||||
if (!empty($json['error'])) {
|
||||
echo 'Error: '.$json['error'].PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
exit($cli_success_msg.PHP_EOL);
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
http_response_code($httpcode);
|
||||
exit((string)$mutatedResult);
|
||||
}
|
||||
|
||||
// This is a stub, does nothing but return success
|
||||
$cli = php_sapi_name()=='cli';
|
||||
$verbose = $anon = false;
|
||||
if ($cli && ($argc > 1) && $argv[1] == "-v") {
|
||||
$verbose = true;
|
||||
$anon = true;
|
||||
}
|
||||
if ($cli && ($argc > 1) && $argv[1] == "-vv") {
|
||||
$verbose = true;
|
||||
}
|
||||
$var = parse_ini_file('/var/local/emhttp/var.ini');
|
||||
$nginx = parse_ini_file('/var/local/emhttp/nginx.ini');
|
||||
$is69 = version_compare($var['version'],"6.9.9","<");
|
||||
$reloadNginx = false;
|
||||
$dnserr = false;
|
||||
$icon_warn = "⚠️ ";
|
||||
$icon_ok = "✅ ";
|
||||
|
||||
$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) : [];
|
||||
// ensure some vars are defined here so we don't have to test them later
|
||||
if (empty($myservers['remote']['apikey'])) {
|
||||
$myservers['remote']['apikey'] = "";
|
||||
}
|
||||
if (empty($myservers['remote']['wanaccess'])) {
|
||||
$myservers['remote']['wanaccess'] = "no";
|
||||
}
|
||||
if (empty($myservers['remote']['wanport'])) {
|
||||
$myservers['remote']['wanport'] = 443;
|
||||
}
|
||||
// remoteaccess, externalport
|
||||
if ($cli) {
|
||||
$remoteaccess = (empty($nginx['NGINX_WANFQDN'])) ? 'no' : 'yes';
|
||||
$externalport = $myservers['remote']['wanport'];
|
||||
} else {
|
||||
$remoteaccess = $_POST['remoteaccess']??'no';
|
||||
$externalport = intval($_POST['externalport']??443);
|
||||
|
||||
if ($remoteaccess != 'yes') {
|
||||
$remoteaccess = 'no';
|
||||
}
|
||||
|
||||
if ($externalport < 1 || $externalport > 65535) {
|
||||
$externalport = 443;
|
||||
}
|
||||
|
||||
if ($myservers['remote']['wanaccess'] != $remoteaccess) {
|
||||
// update the wanaccess ini value
|
||||
$orig = file_exists($myservers_flash_cfg_path) ? parse_ini_file($myservers_flash_cfg_path,true) : [];
|
||||
if (!$orig) {
|
||||
$orig = ['remote' => $myservers['remote']];
|
||||
}
|
||||
$orig['remote']['wanaccess'] = $remoteaccess;
|
||||
$text = '';
|
||||
foreach ($orig as $section => $block) {
|
||||
$pairs = "";
|
||||
foreach ($block as $key => $value) if (strlen($value)) $pairs .= "$key=\"$value\"\n";
|
||||
if ($pairs) $text .= "[$section]\n".$pairs;
|
||||
}
|
||||
if ($text) file_put_contents($myservers_flash_cfg_path, $text);
|
||||
// need nginx reload
|
||||
$reloadNginx = true;
|
||||
}
|
||||
exit("success".PHP_EOL);
|
||||
}
|
||||
$isRegistered = !empty($myservers['remote']['username']);
|
||||
|
||||
// protocols, hostnames, ports
|
||||
$internalprotocol = 'http';
|
||||
$internalport = $nginx['NGINX_PORT'];
|
||||
$internalhostname = $nginx['NGINX_LANMDNS'];
|
||||
$externalprotocol = 'https';
|
||||
// keyserver will expand *.hash.myunraid.net or add www to hash.unraid.net as needed
|
||||
$externalhostname = $nginx['NGINX_CERTNAME'];
|
||||
$isLegacyCert = preg_match('/.*\.unraid\.net$/', $nginx['NGINX_CERTNAME']);
|
||||
$isWildcardCert = preg_match('/.*\.myunraid\.net$/', $nginx['NGINX_CERTNAME']);
|
||||
$internalip = $nginx['NGINX_LANIP'];
|
||||
|
||||
if ($nginx['NGINX_USESSL']=='yes') {
|
||||
// When NGINX_USESSL is 'yes' in 6.9, it could be using either Server_unraid_bundle.pem or certificate_bundle.pem
|
||||
// When NGINX_USESSL is 'yes' in 6.10, it is is using Server_unraid_bundle.pem
|
||||
$internalprotocol = 'https';
|
||||
$internalport = $nginx['NGINX_PORTSSL'];
|
||||
if ($is69 && $nginx['NGINX_CERTNAME']) {
|
||||
// this is from certificate_bundle.pem
|
||||
$internalhostname = $nginx['NGINX_CERTNAME'];
|
||||
}
|
||||
}
|
||||
if ($nginx['NGINX_USESSL']=='auto') {
|
||||
// NGINX_USESSL cannot be 'auto' in 6.9, it is either 'yes' or 'no'
|
||||
// When NGINX_USESSL is 'auto' in 6.10, it is using certificate_bundle.pem
|
||||
$internalprotocol = 'https';
|
||||
$internalport = $nginx['NGINX_PORTSSL'];
|
||||
// keyserver will expand *.hash.myunraid.net as needed
|
||||
$internalhostname = $nginx['NGINX_CERTNAME'];
|
||||
}
|
||||
|
||||
// My Servers version
|
||||
$plgversion = file_exists("/var/log/plugins/dynamix.unraid.net.plg") ? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.plg 2>/dev/null'))
|
||||
: ( file_exists("/var/log/plugins/dynamix.unraid.net.staging.plg") ? trim(@exec('/usr/local/sbin/plugin version /var/log/plugins/dynamix.unraid.net.staging.plg 2>/dev/null'))
|
||||
: 'base-'.$var['version'] );
|
||||
|
||||
// only proceed when when signed in or when legacy unraid.net SSL certificate exists
|
||||
if (!$isRegistered && !$isLegacyCert) {
|
||||
response_complete(406, array('error' => _('Nothing to do')));
|
||||
}
|
||||
|
||||
// keyfile
|
||||
$keyfile = empty($var['regFILE']) ? false : @file_get_contents($var['regFILE']);
|
||||
if ($keyfile === false) {
|
||||
response_complete(406, array('error' => _('Registration key required')));
|
||||
}
|
||||
$keyfile = @base64_encode($keyfile);
|
||||
|
||||
// build post array
|
||||
$post = [
|
||||
'keyfile' => $keyfile,
|
||||
'plgversion' => $plgversion
|
||||
];
|
||||
if ($isLegacyCert) {
|
||||
// sign in not required to maintain local ddns for unraid.net cert
|
||||
// enable local ddns regardless of use_ssl value
|
||||
$post['internalip'] = $internalip;
|
||||
// if host.unraid.net does not resolve to the internalip and DNS Rebind Protection is disabled, disable caching
|
||||
if (host_lookup_ip(generate_internal_host($nginx['NGINX_CERTNAME'], $post['internalip'])) != $post['internalip'] && rebindDisabled()) $dnserr = true;
|
||||
}
|
||||
if ($isRegistered) {
|
||||
// if signed in, send data needed to maintain My Servers Dashboard
|
||||
$post['internalhostname'] = $internalhostname;
|
||||
$post['internalport'] = $internalport;
|
||||
$post['internalprotocol'] = $internalprotocol;
|
||||
$post['remoteaccess'] = $remoteaccess;
|
||||
$post['servercomment'] = $var['COMMENT'];
|
||||
$post['servername'] = $var['NAME'];
|
||||
if ($isWildcardCert) {
|
||||
// keyserver needs the internalip to generate the local access url
|
||||
$post['internalip'] = $internalip;
|
||||
}
|
||||
if ($remoteaccess == 'yes') {
|
||||
// include wanip in the cache file so we can track if it changes
|
||||
$post['_wanip'] = trim(@file_get_contents("https://wanip4.unraid.net/"));
|
||||
$post['externalhostname'] = $externalhostname;
|
||||
$post['externalport'] = $externalport;
|
||||
$post['externalprotocol'] = $externalprotocol;
|
||||
// if wanip.hash.myunraid.net or www.hash.unraid.net does not resolve to the wanip, disable caching
|
||||
if (host_lookup_ip(generate_external_host($post['externalhostname'], $post['_wanip'])) != $post['_wanip']) $dnserr = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Include unraid-api report
|
||||
$unraidreport = [];
|
||||
if (file_exists('/usr/local/sbin/unraid-api')) {
|
||||
$jsonString = trim(@exec("/usr/local/sbin/unraid-api report --json 2>/dev/null"));
|
||||
$unraidreport = @json_decode($jsonString, true);
|
||||
if ($unraidreport === false) {
|
||||
$post['unraidreport'] = $jsonString;
|
||||
} else {
|
||||
// remove fields we don't need to submit
|
||||
unset($unraidreport['servers']);
|
||||
}
|
||||
} elseif (strpos($plgversion, "base-") === false) {
|
||||
// The plugin is installed but the api doesn't exist. This is a failed install. Generate basic troubleshooting data.
|
||||
if (file_exists('/boot/config/plugins/dynamix.my.servers/env')) {
|
||||
@extract(parse_ini_file('/boot/config/plugins/dynamix.my.servers/env',true));
|
||||
}
|
||||
if (empty($env)) {
|
||||
$env = "production";
|
||||
}
|
||||
$unraidreport['os']['version'] = $var['version'];
|
||||
$unraidreport['api']['version'] = "failed install";
|
||||
$unraidreport['api']['status'] = "missing";
|
||||
$unraidreport['api']['environment'] = $env;
|
||||
$unraidreport['relay']['status'] = "disconnected";
|
||||
$unraidreport['minigraph']['status'] = "disconnected";
|
||||
if ($isRegistered) {
|
||||
$unraidreport['myServers']['status'] = "authenticated";
|
||||
$unraidreport['myServers']['myServersUsername'] = $myservers['remote']['username'];
|
||||
} else {
|
||||
$unraidreport['myServers']['status'] = "signed out";
|
||||
}
|
||||
$unraidreport['apiKey'] = (empty($myservers['remote']['apikey'])) ? "invalid" : "exists";
|
||||
}
|
||||
|
||||
if (!empty($unraidreport)) {
|
||||
// include unraid-api crash logs
|
||||
$crashLog = '/var/log/unraid-api/crash.json';
|
||||
$crashAge = 0;
|
||||
if (file_exists($crashLog)) {
|
||||
$crashTime = filemtime($crashLog);
|
||||
$crashAge = time() - $crashTime; // age of crashLog in seconds
|
||||
$crashDetails = @json_decode(@file_get_contents($crashLog), true);
|
||||
if (empty($crashDetails['apiVersion']) && $crashAge < 30*60) {
|
||||
// found a recent crash log without an apiVersion, assume was created by current version of api
|
||||
$crashDetails['apiVersion'] = $unraidreport['api']['version'];
|
||||
// overwrite the crash log so it will always have the apiVersion
|
||||
file_put_contents($crashLog, json_encode($crashDetails, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
// reset to original timestamp so crashAge remains accurate
|
||||
touch($crashLog, $crashTime);
|
||||
}
|
||||
$unraidreport['crashAge'] = $crashAge;
|
||||
$unraidreport['crashLogs'] = $crashDetails;
|
||||
}
|
||||
|
||||
// add flash backup status
|
||||
$flashbackup_ini = '/var/local/emhttp/flashbackup.ini';
|
||||
$flashbackup_status = (file_exists($flashbackup_ini)) ? @parse_ini_file($flashbackup_ini) : [];
|
||||
if (empty($flashbackup_status['activated'])) {
|
||||
$flashbackup_status['activated'] = "";
|
||||
}
|
||||
if (empty($flashbackup_status['error'])) {
|
||||
$flashbackup_status['error'] = "";
|
||||
}
|
||||
$unraidreport['flashbackup']['activated'] = ($flashbackup_status['activated']) ? "yes" : "no";
|
||||
$unraidreport['flashbackup']['error'] = ($flashbackup_status['error']) ? $flashbackup_status['error'] : "no";
|
||||
|
||||
// add unraidreport to payload
|
||||
$post['unraidreport'] = json_encode($unraidreport);
|
||||
|
||||
// if the api is stopped and there are no crashLogs, or any crashLogs are more than maxCrashAge, start the api
|
||||
$maxCrashAge = 1*60*60; // 1 hour
|
||||
if ($unraidreport['api']['status'] == 'stopped' && (empty($unraidreport['crashLogs']) || $crashAge > $maxCrashAge)) {
|
||||
exec("echo \"/usr/local/sbin/unraid-api start\" | at -M now >/dev/null 2>&1");
|
||||
}
|
||||
}
|
||||
|
||||
// if remoteaccess is enabled in 6.10.0-rc3+ and WANIP has changed since nginx started, reload nginx
|
||||
if (isset($post['_wanip']) && ($post['_wanip'] != $nginx['NGINX_WANIP']) && version_compare($var['version'],"6.10.0-rc2",">")) $reloadNginx = true;
|
||||
// if remoteaccess is currently disabled (perhaps because a wanip was not available when nginx was started)
|
||||
// BUT the system is configured to have it enabled AND a wanip is now available
|
||||
// then reload nginx
|
||||
if ($remoteaccess == 'no' && $nginx['NGINX_WANACCESS'] == 'yes' && !empty(trim(@file_get_contents("https://wanip4.unraid.net/")))) $reloadNginx = true;
|
||||
if ($reloadNginx) {
|
||||
exec("/etc/rc.d/rc.nginx reload &>/dev/null");
|
||||
}
|
||||
|
||||
// maxage is 36 hours
|
||||
$maxage = 36*60*60;
|
||||
if ($dnserr || $verbose) $maxage = 0;
|
||||
$datafile = "/tmp/UpdateDNS.txt";
|
||||
$datafiletmp = "/tmp/UpdateDNS.txt.new";
|
||||
$dataprev = @file_get_contents($datafile) ?: '';
|
||||
$datanew = implode("\n",$post)."\n";
|
||||
if ($datanew == $dataprev && (time()-filemtime($datafile) < $maxage)) {
|
||||
response_complete(204, null, _('No change to report'));
|
||||
}
|
||||
file_put_contents($datafiletmp,$datanew);
|
||||
rename($datafiletmp, $datafile);
|
||||
|
||||
// do not submit the wanip, it will be captured from the submission if needed for remote access
|
||||
unset($post['_wanip']);
|
||||
|
||||
// report necessary server details to limetech for DNS updates
|
||||
$ch = curl_init('https://keys.lime-technology.com/account/server/register');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$result = curl_exec($ch);
|
||||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ( ($result === false) || ($httpcode != "200") ) {
|
||||
// delete cache file to retry submission on next run
|
||||
@unlink($datafile);
|
||||
response_complete($httpcode ?? "500", array('error' => $error));
|
||||
}
|
||||
|
||||
response_complete($httpcode, $result, _('success'));
|
||||
header('Content-Type: application/json');
|
||||
http_response_code(204);
|
||||
exit(0);
|
||||
?>
|
||||
|
||||
@@ -1 +1 @@
|
||||
18.17.1
|
||||
18.19.1
|
||||
@@ -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 = 'STARTER';
|
||||
const currentFlashGuid = '1111-1111-SDDD-TEST1234ZACK '; // this is the flash drive that's been booted from
|
||||
const regGuid = '1111-1111-SDDD-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,24 +66,7 @@ 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 = 'STARTER';
|
||||
let regDev = 0;
|
||||
let regDevs = 0;
|
||||
let regTy = '';
|
||||
switch (state) {
|
||||
// @ts-ignore
|
||||
@@ -73,15 +80,15 @@ switch (state) {
|
||||
regTy = 'Trial';
|
||||
// @ts-ignore
|
||||
case 'BASIC':
|
||||
regDev = 6;
|
||||
regDevs = 6;
|
||||
// @ts-ignore
|
||||
case 'PLUS':
|
||||
regDev = 12;
|
||||
regDevs = 12;
|
||||
// @ts-ignore
|
||||
case 'PRO':
|
||||
// @ts-ignore
|
||||
case 'STARTER':
|
||||
regDev = 4;
|
||||
regDevs = 6;
|
||||
// regExp = oneHourFromNow;
|
||||
// regExp = oneDayFromNow;
|
||||
regExp = ninetyDaysAgo;
|
||||
@@ -96,15 +103,15 @@ switch (state) {
|
||||
// regExp = 1696363920000; // nori.local's expiration
|
||||
// @ts-ignore
|
||||
case 'LIFETIME':
|
||||
if (regDev === 0) { regDev = 99999; }
|
||||
if (regDevs === 0) { regDevs = -1; }
|
||||
if (regTy === '') { regTy = state.charAt(0).toUpperCase() + state.substring(1).toLowerCase(); } // title case
|
||||
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,21 +140,21 @@ 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',
|
||||
deviceCount: 3,
|
||||
deviceCount: 4,
|
||||
expireTime,
|
||||
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: ''
|
||||
|
||||
@@ -47,6 +47,7 @@ const {
|
||||
flashProduct,
|
||||
keyActions,
|
||||
keyfile,
|
||||
computedRegDevs,
|
||||
regGuid,
|
||||
regTm,
|
||||
regTo,
|
||||
@@ -56,6 +57,7 @@ const {
|
||||
state,
|
||||
stateData,
|
||||
stateDataError,
|
||||
tooManyDevices,
|
||||
} = storeToRefs(serverStore);
|
||||
|
||||
const formattedRegTm = ref<any>();
|
||||
@@ -76,24 +78,6 @@ onBeforeMount(() => {
|
||||
setFormattedRegTm();
|
||||
});
|
||||
|
||||
const devicesAvailable = computed((): number => {
|
||||
switch (regTy.value) {
|
||||
case 'Starter':
|
||||
return 4;
|
||||
case 'Basic':
|
||||
return 6;
|
||||
case 'Plus':
|
||||
return 12;
|
||||
case 'Unleashed':
|
||||
case 'Lifetime':
|
||||
case 'Pro':
|
||||
case 'Trial':
|
||||
return 9999;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
const showTrialExpiration = computed((): boolean => state.value === 'TRIAL' || state.value === 'EEXPIRED');
|
||||
const showUpdateEligibility = computed((): boolean => !!(regExp.value));
|
||||
const keyInstalled = computed((): boolean => !!(!stateDataError.value && state.value !== 'ENOKEYFILE'));
|
||||
@@ -169,11 +153,11 @@ const items = computed((): RegistrationItemProps[] => {
|
||||
: []),
|
||||
...(keyInstalled.value
|
||||
? [{
|
||||
error: deviceCount.value > devicesAvailable.value,
|
||||
error: tooManyDevices.value,
|
||||
label: t('Attached Storage Devices'),
|
||||
text: deviceCount.value > devicesAvailable.value
|
||||
? t('{0} out of {1} allowed devices – upgrade your key to support more devices', [deviceCount.value, devicesAvailable.value > 12 ? t('unlimited') : devicesAvailable.value])
|
||||
: t('{0} out of {1} devices', [deviceCount.value, devicesAvailable.value > 12 ? t('unlimited') : devicesAvailable.value]),
|
||||
text: tooManyDevices.value
|
||||
? t('{0} out of {1} allowed devices – upgrade your key to support more devices', [deviceCount.value, computedRegDevs.value])
|
||||
: t('{0} out of {1} devices', [deviceCount.value, computedRegDevs.value === -1 ? t('unlimited') : computedRegDevs.value]),
|
||||
}]
|
||||
: []),
|
||||
...(showTransferStatus.value
|
||||
|
||||
@@ -49,9 +49,20 @@ const {
|
||||
checkForUpdatesLoading,
|
||||
} = storeToRefs(updateOsStore);
|
||||
|
||||
const {
|
||||
outputDateTimeFormatted: formattedRegExp,
|
||||
} = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
/**
|
||||
* regExp may not have a value until we get a response from the refreshServerState action
|
||||
* So we need to watch for this value to be able to format it based on the user's date time preferences.
|
||||
*/
|
||||
const formattedRegExp = ref<any>();
|
||||
const setFormattedRegExp = () => { // ran in watch on regExp and onBeforeMount
|
||||
if (!regExp.value) { return; }
|
||||
|
||||
const { outputDateTimeFormatted } = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
formattedRegExp.value = outputDateTimeFormatted.value;
|
||||
};
|
||||
watch(regExp, (_newV) => {
|
||||
setFormattedRegExp();
|
||||
});
|
||||
|
||||
const ignoreThisRelease = ref(false);
|
||||
// if we had a release ignored and now we don't set ignoreThisRelease to false
|
||||
@@ -136,7 +147,8 @@ const actionButtons = computed((): ButtonProps[] | null => {
|
||||
const buttons: ButtonProps[] = [];
|
||||
|
||||
// update available but not stable branch - should link out to account update callback
|
||||
if (availableRequiresAuth.value) {
|
||||
// if availableWithRenewal.value is true, then we need to renew the license before we can update so don't show the verify button
|
||||
if (availableRequiresAuth.value && !availableWithRenewal.value) {
|
||||
buttons.push({
|
||||
click: async () => await accountStore.updateOs(),
|
||||
icon: IdentificationIcon,
|
||||
@@ -147,7 +159,7 @@ const actionButtons = computed((): ButtonProps[] | null => {
|
||||
}
|
||||
|
||||
// update available - open changelog to commence update
|
||||
if (available.value) {
|
||||
if (available.value && updateOsResponse.value?.changelog) {
|
||||
buttons.push({
|
||||
btnStyle: availableWithRenewal.value
|
||||
? 'outline'
|
||||
@@ -208,6 +220,7 @@ onBeforeMount(() => {
|
||||
if (availableReleaseDate.value) {
|
||||
setUserFormattedReleaseDate();
|
||||
}
|
||||
setFormattedRegExp();
|
||||
});
|
||||
|
||||
const modalWidth = computed(() => {
|
||||
|
||||
@@ -20,7 +20,10 @@ const showExpireTime = computed(() => (state.value === 'TRIAL' || state.value ==
|
||||
<div class="flex flex-col gap-y-24px w-full min-w-300px md:min-w-[500px] max-w-xl p-16px">
|
||||
<header>
|
||||
<h2 class="text-24px text-center font-semibold" v-html="t(stateData.heading)" />
|
||||
<div class="flex flex-col gap-y-8px" v-html="t(stateData.message)" />
|
||||
<div
|
||||
class="text-center prose text-16px leading-relaxed whitespace-normal opacity-75 gap-y-8px"
|
||||
v-html="t(stateData.message)"
|
||||
/>
|
||||
<UpcUptimeExpire
|
||||
v-if="showExpireTime"
|
||||
class="text-center opacity-75 mt-12px"
|
||||
|
||||
@@ -33,6 +33,22 @@ const timeFormatOptions: TimeFormatOption[] = [
|
||||
];
|
||||
|
||||
/**
|
||||
* the provided ref may not have a value until we get a response from the refreshServerState action
|
||||
* So we need to watch for this value to be able to format it based on the user's date time preferences.
|
||||
* @example below is how to use this composable
|
||||
* const formattedRegExp = ref<any>();
|
||||
* const setFormattedRegExp = () => { // ran in watch on regExp and onBeforeMount
|
||||
* if (!regExp.value) { return; }
|
||||
* const { outputDateTimeFormatted } = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
* formattedRegExp.value = outputDateTimeFormatted.value;
|
||||
* };
|
||||
* watch(regExp, (_newV) => {
|
||||
* setFormattedRegExp();
|
||||
* });
|
||||
* onBeforeMount(() => {
|
||||
* setFormattedRegExp();
|
||||
* });
|
||||
*
|
||||
* @param format provided by Unraid server's state.php and set in the server store
|
||||
* @param t translations
|
||||
* @param hideMinutesSeconds true will hide minutes and seconds from the output
|
||||
|
||||
@@ -242,6 +242,7 @@ export type Config = {
|
||||
};
|
||||
|
||||
export enum ConfigErrorState {
|
||||
Ineligible = 'INELIGIBLE',
|
||||
Invalid = 'INVALID',
|
||||
NoKeyServer = 'NO_KEY_SERVER',
|
||||
UnknownError = 'UNKNOWN_ERROR',
|
||||
|
||||
@@ -21,16 +21,6 @@ export const WebguiInstallKey = request.url('/webGui/include/InstallKey.php');
|
||||
* @param {string} username
|
||||
*/
|
||||
export const WebguiUpdate = request.url('/update.php');
|
||||
/**
|
||||
* @name WebguiUpdateDns
|
||||
* @dataForm formUrl
|
||||
* @description Used after Sign In to ensure URLs will work correctly
|
||||
* @note this request is delayed by 500ms to allow server to process key install fully
|
||||
* @todo potentially remove delay
|
||||
* @param csrf_token
|
||||
* @type POST
|
||||
*/
|
||||
export const WebguiUpdateDns = request.url('/webGui/include/UpdateDNS.php');
|
||||
/**
|
||||
* @name WebguiState
|
||||
* @description used to get current state of server via PHP rather than unraid-api
|
||||
|
||||
46
web/package-lock.json
generated
46
web/package-lock.json
generated
@@ -2984,12 +2984,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.5.tgz",
|
||||
"integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
|
||||
"version": "9.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.10.1.tgz",
|
||||
"integrity": "sha512-0+Wtjj04GIyglh5KKiNjRwgjpHrhqqGZhaKY/QVjjogWKZq5WHROrTi84pNVsRN18QynyPmjtsVUWqFKPQ45xQ==",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "9.6.5",
|
||||
"@intlify/shared": "9.6.5"
|
||||
"@intlify/message-compiler": "9.10.1",
|
||||
"@intlify/shared": "9.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
@@ -2999,11 +2999,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
|
||||
"integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
|
||||
"version": "9.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.10.1.tgz",
|
||||
"integrity": "sha512-b68UTmRhgZfswJZI7VAgW6BXZK5JOpoi5swMLGr4j6ss2XbFY13kiw+Hu+xYAfulMPSapcHzdWHnq21VGnMCnA==",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "9.6.5",
|
||||
"@intlify/shared": "9.10.1",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3014,9 +3014,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.5.tgz",
|
||||
"integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ==",
|
||||
"version": "9.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.10.1.tgz",
|
||||
"integrity": "sha512-liyH3UMoglHBUn70iCYcy9CQlInx/lp50W2aeSxqqrvmG+LDj/Jj7tBJhBoQL4fECkldGhbmW0g2ommHfL6Wmw==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
@@ -18353,9 +18353,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.24.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
|
||||
"integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
|
||||
"version": "5.28.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
|
||||
"integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
@@ -19666,12 +19666,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "9.6.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.5.tgz",
|
||||
"integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
|
||||
"version": "9.10.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.10.1.tgz",
|
||||
"integrity": "sha512-37HVJQZ/pZaRXGzFmmMomM1u1k7kndv3xCBPYHKEVfv5W3UVK67U/TpBug71ILYLNmjHLHdvTUPRF81pFT5fFg==",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "9.6.5",
|
||||
"@intlify/shared": "9.6.5",
|
||||
"@intlify/core-base": "9.10.1",
|
||||
"@intlify/shared": "9.10.1",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -20099,9 +20099,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wretch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/wretch/-/wretch-2.7.0.tgz",
|
||||
"integrity": "sha512-IOjqi9SlQ8FEWp1X3KJ74wLNqpDVBoJIJvC7ZDHxPhzriNJd84+7RAhFBTl2sHiqnzBhzfqs1sznaB0Ik/3Ngw==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/wretch/-/wretch-2.8.0.tgz",
|
||||
"integrity": "sha512-MNYh+lukFfFgcmVpvxv0KsQt2LtAPvKFfLNcLGpCMiHTD3ndSoQ3/k5fGOCPI1J7CgN1mYAbp9mhPFFUGTzUeg==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { defineStore, createPinia, setActivePinia } from 'pinia';
|
||||
import { delay } from 'wretch/middlewares';
|
||||
import { WebguiInstallKey, WebguiUpdateDns } from '~/composables/services/webgui';
|
||||
import { WebguiInstallKey } from '~/composables/services/webgui';
|
||||
import { useErrorsStore } from '~/store/errors';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import type { ExternalKeyActions } from '~/store/callback';
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
@@ -12,7 +10,6 @@ setActivePinia(createPinia());
|
||||
|
||||
export const useInstallKeyStore = defineStore('installKey', () => {
|
||||
const errorsStore = useErrorsStore();
|
||||
const serverStore = useServerStore();
|
||||
|
||||
const keyInstallStatus = ref<'failed' | 'installing' | 'ready' | 'success'>('ready');
|
||||
|
||||
@@ -45,18 +42,6 @@ export const useInstallKeyStore = defineStore('installKey', () => {
|
||||
console.log('[install] WebguiInstallKey installResponse', installResponse);
|
||||
|
||||
keyInstallStatus.value = 'success';
|
||||
|
||||
try {
|
||||
const updateDnsResponse = await WebguiUpdateDns
|
||||
.middlewares([
|
||||
delay(1500)
|
||||
])
|
||||
.formUrl({ csrf_token: serverStore.csrf })
|
||||
.post();
|
||||
console.log('[install] WebguiUpdateDns updateDnsResponse', updateDnsResponse);
|
||||
} catch (error) {
|
||||
console.error('[install] WebguiUpdateDns error', error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[install] WebguiInstallKey error', error);
|
||||
let errorMessage = 'Unknown error';
|
||||
|
||||
@@ -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';
|
||||
@@ -95,7 +100,27 @@ export const useServerStore = defineStore('server', () => {
|
||||
const rebootType = ref<'thirdPartyDriversDownloading' | 'downgrade' | 'update' | ''>('');
|
||||
const rebootVersion = ref<string | undefined>();
|
||||
const registered = ref<boolean>();
|
||||
const regDev = ref<number>(0);
|
||||
const regDevs = ref<number>(0); // use computedRegDevs to ensure it includes Basic, Plus, and Pro
|
||||
const computedRegDevs = computed(() => {
|
||||
if (regDevs.value > 0) {
|
||||
return regDevs.value;
|
||||
}
|
||||
|
||||
switch (regTy.value) {
|
||||
case 'Starter':
|
||||
case 'Basic':
|
||||
return 6;
|
||||
case 'Plus':
|
||||
return 12;
|
||||
case 'Unleashed':
|
||||
case 'Lifetime':
|
||||
case 'Pro':
|
||||
case 'Trial':
|
||||
return -1; // unlimited
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
const regGen = ref<number>(0);
|
||||
const regGuid = ref<string>('');
|
||||
const regTm = ref<number>(0);
|
||||
@@ -164,7 +189,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
osVersion: osVersion.value,
|
||||
osVersionBranch: osVersionBranch.value,
|
||||
registered: registered.value,
|
||||
regDev: regDev.value,
|
||||
regDevs: computedRegDevs.value,
|
||||
regGen: regGen.value,
|
||||
regGuid: regGuid.value,
|
||||
regExp: regExp.value,
|
||||
@@ -415,12 +440,12 @@ export const useServerStore = defineStore('server', () => {
|
||||
return {
|
||||
actions: [
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []),
|
||||
...([purchaseAction.value, redeemAction.value, trialStartAction.value, recoverAction.value]),
|
||||
...([trialStartAction.value, purchaseAction.value, redeemAction.value, recoverAction.value]),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []),
|
||||
],
|
||||
humanReadable: 'No Keyfile',
|
||||
heading: 'Let\'s Unleash your Hardware!',
|
||||
message: '<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>',
|
||||
heading: 'Let\'s Unleash Your Hardware',
|
||||
message: '<p>Choose an option below, then use our <a href="https://unraid.net/getting-started" target="_blank" rel="noreffer noopener">Getting Started Guide</a> to configure your array in less than 15 minutes.</p>',
|
||||
};
|
||||
case 'TRIAL':
|
||||
return {
|
||||
@@ -487,6 +512,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
actions: [
|
||||
...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []),
|
||||
...(regUpdatesExpired.value ? [renewAction.value] : []),
|
||||
...(state.value === 'UNLEASHED' ? [upgradeAction.value] : []),
|
||||
...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []),
|
||||
],
|
||||
humanReadable: state.value === 'PRO'
|
||||
@@ -662,24 +688,73 @@ export const useServerStore = defineStore('server', () => {
|
||||
});
|
||||
const trialExtensionEligible = computed(() => !regGen.value || regGen.value < 2);
|
||||
|
||||
const tooManyDevices = computed((): Error | undefined => {
|
||||
if ((deviceCount.value !== 0 && regDev.value !== 0 && deviceCount.value > regDev.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.',
|
||||
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 before starting 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 {
|
||||
@@ -776,7 +851,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
const serverErrors = computed(() => {
|
||||
return [
|
||||
stateDataError.value,
|
||||
tooManyDevices.value,
|
||||
serverConfigError.value,
|
||||
pluginInstallFailed.value,
|
||||
deprecatedUnraidSSL.value,
|
||||
cloudError.value,
|
||||
@@ -1022,7 +1097,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
rebootType,
|
||||
rebootVersion,
|
||||
registered,
|
||||
regDev,
|
||||
computedRegDevs,
|
||||
regGen,
|
||||
regGuid,
|
||||
regTm,
|
||||
@@ -1056,6 +1131,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
stateDataError,
|
||||
serverErrors,
|
||||
tooManyDevices,
|
||||
serverConfigError,
|
||||
// actions
|
||||
setServer,
|
||||
setUpdateOsResponse,
|
||||
|
||||
@@ -91,7 +91,7 @@ export interface Server {
|
||||
rebootType?: ServerRebootType;
|
||||
rebootVersion?: string;
|
||||
registered?: boolean;
|
||||
regDev?: number;
|
||||
regDevs?: number;
|
||||
regGen?: number;
|
||||
regGuid?: string;
|
||||
regTm?: number;
|
||||
|
||||
Reference in New Issue
Block a user