Merge branch 'navbar_step1_static': Navbar-Button/Status + Build-Skript + v0.1.6.3
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
include README.md
|
include README.md
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include requirements.txt
|
|
||||||
include setup.py
|
include setup.py
|
||||||
recursive-include static *
|
recursive-include octoprint_tailscale_funnel/static *
|
||||||
recursive-include templates *
|
recursive-include octoprint_tailscale_funnel/templates *
|
||||||
recursive-include tests *
|
recursive-include tests *
|
@@ -91,7 +91,7 @@ class TailscaleFunnelPlugin(octoprint.plugin.StartupPlugin,
|
|||||||
|
|
||||||
def get_assets(self):
|
def get_assets(self):
|
||||||
return dict(
|
return dict(
|
||||||
js=["js/tailscale_funnel.js"],
|
js=["js/tailscale_funnel.js", "js/tailscale_funnel_navbar.js"],
|
||||||
css=["css/tailscale_funnel.css"],
|
css=["css/tailscale_funnel.css"],
|
||||||
less=["less/tailscale_funnel.less"]
|
less=["less/tailscale_funnel.less"]
|
||||||
)
|
)
|
||||||
@@ -100,7 +100,8 @@ class TailscaleFunnelPlugin(octoprint.plugin.StartupPlugin,
|
|||||||
|
|
||||||
def get_template_configs(self):
|
def get_template_configs(self):
|
||||||
return [
|
return [
|
||||||
dict(type="settings", custom_bindings=True, template="tailscale_funnel_settings.jinja2")
|
dict(type="settings", custom_bindings=True, template="tailscale_funnel_settings.jinja2"),
|
||||||
|
dict(type="navbar", name="Funnel", custom_bindings=False, template="tailscale_funnel_navbar.jinja2")
|
||||||
]
|
]
|
||||||
|
|
||||||
##~~ BlueprintPlugin mixin
|
##~~ BlueprintPlugin mixin
|
||||||
|
@@ -38,4 +38,15 @@
|
|||||||
background-color: #fcf8e3;
|
background-color: #fcf8e3;
|
||||||
border-color: #faebcc;
|
border-color: #faebcc;
|
||||||
color: #8a6d3b;
|
color: #8a6d3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar Button Farbkennung */
|
||||||
|
#navbar_plugin_tailscale_funnel > a.tsf-enabled {
|
||||||
|
background-color: #5cb85c;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar_plugin_tailscale_funnel > a.tsf-disabled {
|
||||||
|
background-color: #777;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
@@ -0,0 +1,105 @@
|
|||||||
|
$(function() {
|
||||||
|
var lastEnabled = null;
|
||||||
|
|
||||||
|
function refreshNavbarStatus() {
|
||||||
|
var $status = $("#tsf_nav_status");
|
||||||
|
var $openLi = $("#tsf_nav_open_li");
|
||||||
|
var $open = $("#tsf_nav_open");
|
||||||
|
var $toggleText = $("#tsf_nav_toggle_text");
|
||||||
|
var $btn = $("#navbar_plugin_tailscale_funnel > a");
|
||||||
|
if ($status.length === 0) return;
|
||||||
|
$status.text("Checking...");
|
||||||
|
$.ajax({
|
||||||
|
url: PLUGIN_BASEURL + "tailscale_funnel/status",
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function(resp) {
|
||||||
|
if (resp && resp.status === "success") {
|
||||||
|
var enabled = !!(resp.data && resp.data.funnel_enabled);
|
||||||
|
var url = resp.data && resp.data.public_url ? resp.data.public_url : "";
|
||||||
|
$status.text(enabled ? "Enabled" : "Disabled");
|
||||||
|
$toggleText.text(enabled ? "Disable" : "Enable");
|
||||||
|
$btn.toggleClass('tsf-enabled', enabled).toggleClass('tsf-disabled', !enabled);
|
||||||
|
lastEnabled = enabled;
|
||||||
|
if (enabled && url) {
|
||||||
|
$open.attr("href", url);
|
||||||
|
$openLi.removeClass("hidden");
|
||||||
|
} else {
|
||||||
|
$open.attr("href", "#");
|
||||||
|
$openLi.addClass("hidden");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status.text("Error");
|
||||||
|
$toggleText.text("Enable");
|
||||||
|
$btn.removeClass('tsf-enabled tsf-disabled');
|
||||||
|
$openLi.addClass("hidden");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$status.text("Error");
|
||||||
|
$toggleText.text("Enable");
|
||||||
|
$btn.removeClass('tsf-enabled tsf-disabled');
|
||||||
|
$openLi.addClass("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh when dropdown opens, and on click of Refresh
|
||||||
|
$(document).on('show.bs.dropdown', '#navbar_plugin_tailscale_funnel', refreshNavbarStatus);
|
||||||
|
$(document).on('click', '#tsf_nav_refresh', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
refreshNavbarStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#tsf_nav_toggle', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var enable = !(lastEnabled === true);
|
||||||
|
// Optional simpler confirm: nur beim Aktivieren
|
||||||
|
if (enable) {
|
||||||
|
var c = window.confirm("Enabling Funnel will make your OctoPrint instance accessible from the public internet. Continue?");
|
||||||
|
if (!c) return;
|
||||||
|
}
|
||||||
|
var action = enable ? 'enable' : 'disable';
|
||||||
|
var $status = $("#tsf_nav_status");
|
||||||
|
var $toggleText = $("#tsf_nav_toggle_text");
|
||||||
|
var $btn = $("#navbar_plugin_tailscale_funnel > a");
|
||||||
|
$status.text(enable ? 'Enabling...' : 'Disabling...');
|
||||||
|
$.ajax({
|
||||||
|
url: PLUGIN_BASEURL + 'tailscale_funnel/' + action,
|
||||||
|
type: 'POST',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(resp) {
|
||||||
|
if (resp && resp.status === 'success') {
|
||||||
|
if (enable) {
|
||||||
|
$status.text('Enabled');
|
||||||
|
$toggleText.text('Disable');
|
||||||
|
$btn.addClass('tsf-enabled').removeClass('tsf-disabled');
|
||||||
|
var url = resp.data && resp.data.public_url ? resp.data.public_url : '';
|
||||||
|
if (url) {
|
||||||
|
$("#tsf_nav_open").attr('href', url);
|
||||||
|
$("#tsf_nav_open_li").removeClass('hidden');
|
||||||
|
}
|
||||||
|
lastEnabled = true;
|
||||||
|
} else {
|
||||||
|
$status.text('Disabled');
|
||||||
|
$toggleText.text('Enable');
|
||||||
|
$btn.addClass('tsf-disabled').removeClass('tsf-enabled');
|
||||||
|
$("#tsf_nav_open").attr('href', '#');
|
||||||
|
$("#tsf_nav_open_li").addClass('hidden');
|
||||||
|
lastEnabled = false;
|
||||||
|
}
|
||||||
|
// Finaler Abgleich mit Backendstatus
|
||||||
|
setTimeout(refreshNavbarStatus, 250);
|
||||||
|
} else {
|
||||||
|
$status.text('Error');
|
||||||
|
$btn.removeClass('tsf-enabled tsf-disabled');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$status.text('Error');
|
||||||
|
$btn.removeClass('tsf-enabled tsf-disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@@ -0,0 +1,14 @@
|
|||||||
|
<li id="navbar_plugin_tailscale_funnel" class="dropdown">
|
||||||
|
<a class="dropdown-toggle" href="#" data-toggle="dropdown" title="Tailscale Funnel">
|
||||||
|
<i class="fas fa-share-square"></i>
|
||||||
|
<span class="visible-lg">Funnel</span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li class="disabled"><a href="#"><strong>Status:</strong> <span id="tsf_nav_status">Checking...</span></a></li>
|
||||||
|
<li id="tsf_nav_open_li" class="hidden"><a id="tsf_nav_open" href="#" target="_blank" rel="noopener"><i class="fas fa-external-link-alt"></i> Open</a></li>
|
||||||
|
<li><a id="tsf_nav_toggle" href="#"><i class="fas fa-toggle-on"></i> <span id="tsf_nav_toggle_text">Enable</span></a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li><a id="tsf_nav_refresh" href="#"><i class="fas fa-sync"></i> Refresh</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
@@ -1 +1,2 @@
|
|||||||
|
# Runtime dependency for installation via OctoPrint's Plugin Manager
|
||||||
OctoPrint>=1.3.0
|
OctoPrint>=1.3.0
|
@@ -14,7 +14,7 @@ plugin_package = "octoprint_tailscale_funnel"
|
|||||||
plugin_name = "OctoPrint-Tailscale-Funnel"
|
plugin_name = "OctoPrint-Tailscale-Funnel"
|
||||||
|
|
||||||
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
|
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
|
||||||
plugin_version = "0.1.6.2"
|
plugin_version = "0.1.6.3"
|
||||||
|
|
||||||
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
|
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
|
||||||
# module
|
# module
|
||||||
@@ -88,7 +88,18 @@ setup_parameters = octoprint_setuptools.create_plugin_setup_parameters(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if len(additional_setup_parameters):
|
if len(additional_setup_parameters):
|
||||||
from octoprint.util import dict_merge
|
try:
|
||||||
|
from octoprint.util import dict_merge
|
||||||
|
except Exception:
|
||||||
|
# Fallback to allow building without the octoprint package installed
|
||||||
|
def dict_merge(a, b):
|
||||||
|
result = dict(a)
|
||||||
|
for k, v in b.items():
|
||||||
|
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
|
||||||
|
result[k] = dict_merge(result[k], v)
|
||||||
|
else:
|
||||||
|
result[k] = v
|
||||||
|
return result
|
||||||
setup_parameters = dict_merge(setup_parameters, additional_setup_parameters)
|
setup_parameters = dict_merge(setup_parameters, additional_setup_parameters)
|
||||||
|
|
||||||
setup(**setup_parameters)
|
setup(**setup_parameters)
|
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1.6.2"
|
"version": "0.1.6.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
setuptools>=68
|
||||||
|
wheel>=0.41
|
||||||
|
build>=1.0
|
||||||
|
packaging>=23
|
||||||
|
# Kompatible verfügbare Version laut Index
|
||||||
|
octoprint_setuptools==1.0.3
|
||||||
|
|
147
scripts/build_plugin.sh
Executable file
147
scripts/build_plugin.sh
Executable file
@@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Usage: scripts/build_plugin.sh [VERSION]
|
||||||
|
# - Läuft im Projekt-.venv
|
||||||
|
# - Optional: VERSION (z.B. 0.1.6.4). Wenn gesetzt, wird setup.py gepatcht.
|
||||||
|
# - Baut wheel + sdist und erstellt zusätzlich ein "normales" ZIP der sdist-Struktur.
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
|
||||||
|
PLUGIN_DIR="$ROOT_DIR/octoprint_tailscale_funnel"
|
||||||
|
DIST_DIR="$PLUGIN_DIR/dist"
|
||||||
|
VENV_DIR="$ROOT_DIR/.venv"
|
||||||
|
|
||||||
|
VERSION_INPUT="${1:-}"
|
||||||
|
|
||||||
|
echo "Project root: $ROOT_DIR"
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo "Venv dir: $VENV_DIR"
|
||||||
|
|
||||||
|
# Ensure venv
|
||||||
|
if [[ ! -x "$VENV_DIR/bin/python" ]]; then
|
||||||
|
echo "Creating venv at $VENV_DIR"
|
||||||
|
python3 -m venv "$VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$ROOT_DIR/requirements.txt" ]]; then
|
||||||
|
"$VENV_DIR/bin/pip" install -q --upgrade pip
|
||||||
|
"$VENV_DIR/bin/pip" install -q -r "$ROOT_DIR/requirements.txt"
|
||||||
|
else
|
||||||
|
"$VENV_DIR/bin/pip" install -q --upgrade pip setuptools wheel build packaging octoprint_setuptools
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional: bump version
|
||||||
|
if [[ -n "$VERSION_INPUT" ]]; then
|
||||||
|
echo "Setting version to $VERSION_INPUT"
|
||||||
|
"$VENV_DIR/bin/python" - "$VERSION_INPUT" "$PLUGIN_DIR/setup.py" <<'PY'
|
||||||
|
import sys, re, pathlib
|
||||||
|
ver = sys.argv[1]
|
||||||
|
setup_path = pathlib.Path(sys.argv[2])
|
||||||
|
text = setup_path.read_text()
|
||||||
|
new_text = re.sub(r'^(plugin_version\s*=\s*")([^"]*)(")', lambda m: m.group(1) + ver + m.group(3), text, flags=re.M)
|
||||||
|
setup_path.write_text(new_text)
|
||||||
|
print("Updated version to", ver)
|
||||||
|
PY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean dist
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
rm -f "$DIST_DIR"/*
|
||||||
|
|
||||||
|
# Build wheel + sdist
|
||||||
|
echo "Building (wheel + sdist)..."
|
||||||
|
"$VENV_DIR/bin/python" -m build --no-isolation "$PLUGIN_DIR"
|
||||||
|
|
||||||
|
# Create additional plain ZIP from sdist content (flat source zip)
|
||||||
|
SDIST_TGZ=$(ls -1 "$DIST_DIR"/*.tar.gz | tail -n1 || true)
|
||||||
|
if [[ -n "$SDIST_TGZ" ]]; then
|
||||||
|
echo "Creating plain ZIP from sdist: $SDIST_TGZ"
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
tar -xzf "$SDIST_TGZ" -C "$TMP_DIR"
|
||||||
|
SRC_DIR="$(find "$TMP_DIR" -maxdepth 1 -type d -name 'octoprint_tailscale_funnel-*' | head -n1)"
|
||||||
|
if [[ -n "$SRC_DIR" ]]; then
|
||||||
|
ZIP_NAME="$(basename "$SRC_DIR").zip"
|
||||||
|
(cd "$SRC_DIR" && zip -rq "$DIST_DIR/$ZIP_NAME" .)
|
||||||
|
echo "Created: $DIST_DIR/$ZIP_NAME"
|
||||||
|
else
|
||||||
|
echo "WARN: Could not find extracted sdist directory to zip"
|
||||||
|
fi
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
else
|
||||||
|
echo "WARN: No sdist .tar.gz found, skipping plain ZIP creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Artifacts in $DIST_DIR:"
|
||||||
|
ls -lah "$DIST_DIR"
|
||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Usage: scripts/build_plugin.sh [VERSION]
|
||||||
|
# - Läuft im Projekt-.venv
|
||||||
|
# - Optional: VERSION (z.B. 0.1.6.4). Wenn gesetzt, wird setup.py gepatcht.
|
||||||
|
# - Baut wheel + sdist und erstellt zusätzlich ein "normales" ZIP der sdist-Struktur.
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
|
||||||
|
PLUGIN_DIR="$ROOT_DIR/octoprint_tailscale_funnel"
|
||||||
|
DIST_DIR="$PLUGIN_DIR/dist"
|
||||||
|
VENV_DIR="$ROOT_DIR/.venv"
|
||||||
|
|
||||||
|
VERSION_INPUT="${1:-}"
|
||||||
|
|
||||||
|
echo "Project root: $ROOT_DIR"
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo "Venv dir: $VENV_DIR"
|
||||||
|
|
||||||
|
# Ensure venv
|
||||||
|
if [[ ! -x "$VENV_DIR/bin/python" ]]; then
|
||||||
|
echo "Creating venv at $VENV_DIR"
|
||||||
|
python3 -m venv "$VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$VENV_DIR/bin/pip" install -q --upgrade pip setuptools wheel build packaging octoprint_setuptools
|
||||||
|
|
||||||
|
# Optional: bump version
|
||||||
|
if [[ -n "$VERSION_INPUT" ]]; then
|
||||||
|
echo "Setting version to $VERSION_INPUT"
|
||||||
|
"$VENV_DIR/bin/python" - "$VERSION_INPUT" "$PLUGIN_DIR/setup.py" <<'PY'
|
||||||
|
import sys, re, pathlib
|
||||||
|
ver = sys.argv[1]
|
||||||
|
setup_path = pathlib.Path(sys.argv[2])
|
||||||
|
text = setup_path.read_text()
|
||||||
|
new_text = re.sub(r'^(plugin_version\s*=\s*")([^"]*)(")', lambda m: m.group(1) + ver + m.group(3), text, flags=re.M)
|
||||||
|
setup_path.write_text(new_text)
|
||||||
|
print("Updated version to", ver)
|
||||||
|
PY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean dist
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
rm -f "$DIST_DIR"/*
|
||||||
|
|
||||||
|
# Build wheel + sdist
|
||||||
|
echo "Building (wheel + sdist)..."
|
||||||
|
"$VENV_DIR/bin/python" -m build --no-isolation "$PLUGIN_DIR"
|
||||||
|
|
||||||
|
# Create additional plain ZIP from sdist content (flat source zip)
|
||||||
|
SDIST_TGZ=$(ls -1 "$DIST_DIR"/*.tar.gz | tail -n1 || true)
|
||||||
|
if [[ -n "$SDIST_TGZ" ]]; then
|
||||||
|
echo "Creating plain ZIP from sdist: $SDIST_TGZ"
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
tar -xzf "$SDIST_TGZ" -C "$TMP_DIR"
|
||||||
|
SRC_DIR="$(find "$TMP_DIR" -maxdepth 1 -type d -name 'octoprint_tailscale_funnel-*' | head -n1)"
|
||||||
|
if [[ -n "$SRC_DIR" ]]; then
|
||||||
|
ZIP_NAME="$(basename "$SRC_DIR").zip"
|
||||||
|
(cd "$SRC_DIR" && zip -rq "$DIST_DIR/$ZIP_NAME" .)
|
||||||
|
echo "Created: $DIST_DIR/$ZIP_NAME"
|
||||||
|
else
|
||||||
|
echo "WARN: Could not find extracted sdist directory to zip"
|
||||||
|
fi
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
else
|
||||||
|
echo "WARN: No sdist .tar.gz found, skipping plain ZIP creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Artifacts in $DIST_DIR:"
|
||||||
|
ls -lah "$DIST_DIR"
|
||||||
|
|
Reference in New Issue
Block a user