7 Commits

Author SHA1 Message Date
Markus F.J. Busche
fd5ff20743 Add disclaimer 2025-09-20 19:31:50 +02:00
Markus F.J. Busche
87449641f6 chore: remove scripts/upload_asset_direct.sh (deprecated) 2025-09-20 19:29:49 +02:00
Markus F.J. Busche
731cd207dd chore: stage remaining changes 2025-09-20 18:29:54 +02:00
Markus F.J. Busche
a5489de212 docs: korrigiere Screenshot-Dateiname auf .png 2025-09-20 18:24:47 +02:00
Markus F.J. Busche
c0d4448dc1 docs: symlink docs/ -> octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs 2025-09-20 18:24:24 +02:00
Markus F.J. Busche
0d43e181c8 docs: Screenshot der Funnel-Einstellungen in README verlinkt 2025-09-20 18:18:37 +02:00
Markus F.J. Busche
f60068b01f release: update scripts/release_gitea.sh to jq-based flow; skip existing assets 2025-09-20 18:13:22 +02:00
5 changed files with 21 additions and 192 deletions

1
docs Symbolic link
View File

@@ -0,0 +1 @@
octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs

View File

@@ -2,6 +2,8 @@
This plugin makes your OctoPrint instance accessible from anywhere via Tailscale Funnel, without needing to configure port forwarding, dynamic DNS, or complex firewall settings.
Disclaimer: *This plugin was partially vibe-coded*.
## Features
* Enable/disable Tailscale Funnel access directly from OctoPrint's settings
@@ -9,6 +11,10 @@ This plugin makes your OctoPrint instance accessible from anywhere via Tailscale
* Display the public URL for accessing OctoPrint remotely
* Configure the port to expose via Funnel
## Screenshot
![Tailscale Funnel Settings](octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs/screenshots/funnel-settings.png)
## Requirements
* OctoPrint 1.3.0 or higher

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -54,31 +54,11 @@ fi
# Try to fetch existing release by tag first
get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}" || true)
rel_id=$(python3 - <<'PY'
import sys, json
data=sys.stdin.read().strip()
try:
obj=json.loads(data) if data else {}
print(obj.get('id',''))
except Exception:
print('')
PY
<<<"$get_resp")
rel_id=$(echo "$get_resp" | jq -r '.id // empty')
if [ -z "$rel_id" ]; then
# Build minimal JSON payload (use existing tag)
create_payload=$(REL_TAG="$TAG" REL_NAME="$NAME" REL_BODY_TXT="$BODY" python3 - <<'PY'
import json, os
payload = {
"tag_name": os.environ["REL_TAG"],
"name": os.environ["REL_NAME"],
"body": os.environ["REL_BODY_TXT"],
"draft": False,
"prerelease": False
}
print(json.dumps(payload))
PY
)
create_payload=$(jq -n --arg tag "$TAG" --arg name "$NAME" --arg body "$BODY" '{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:false}')
create_resp=$(curl -sS -X POST \
-H 'Content-Type: application/json' \
@@ -87,35 +67,13 @@ PY
"${API_URL}/repos/${OWNER}/${REPO}/releases" || true)
# Extract id from create response
rel_id=$(python3 - <<'PY'
import sys, json
data=sys.stdin.read().strip()
try:
obj=json.loads(data) if data else {}
print(obj.get('id',''))
except Exception:
print('')
PY
<<<"$create_resp")
rel_id=$(echo "$create_resp" | jq -r '.id // empty')
fi
# Fallback: search releases list for matching tag if still empty
if [ -z "$rel_id" ]; then
list_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases?limit=100")
rel_id=$(python3 - <<'PY'
import sys, json, os
data=sys.stdin.read().strip()
try:
arr=json.loads(data) if data else []
tag=os.environ.get('TAG')
for rel in arr:
if rel.get('tag_name')==tag:
print(rel.get('id',''))
break
except Exception:
pass
PY
<<<"$list_resp")
rel_id=$(echo "$list_resp" | jq -r --arg tag "$TAG" '[.[] | select(.tag_name==$tag)][0].id // empty')
fi
if [ -z "$rel_id" ]; then
@@ -124,37 +82,18 @@ if [ -z "$rel_id" ]; then
exit 1
fi
# Delete existing asset with same name (if any)
# Check if asset exists (skip upload if present)
assets_json=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets")
asset_id=$(python3 - <<'PY'
import sys, json, os
name=os.environ['ASSET_NAME']
try:
arr=json.loads(sys.stdin.read())
for a in arr:
if a.get('name')==name:
print(a.get('id',''))
break
except Exception:
pass
PY
<<<"$assets_json")
asset_exists=$(echo "$assets_json" | jq -r --arg name "$ASSET_NAME" 'any(.[]; .name==$name)')
if [ -n "$asset_id" ]; then
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets/${asset_id}" >/dev/null || true
if [ "$asset_exists" = "true" ]; then
echo "Asset already exists, skipping upload: ${ASSET_NAME}"
else
echo "Uploading asset: ${ASSET_NAME}"
upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=${ASSET_NAME}")
html_url=$(echo "$upload_resp" | jq -r '.browser_download_url // empty')
echo "Asset uploaded: ${html_url}"
fi
# Upload asset
upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=${ASSET_NAME}")
html_url=$(python3 - <<'PY'
import sys, json
try:
print(json.loads(sys.stdin.read()).get('browser_download_url',''))
except Exception:
print("")
PY
<<<"$upload_resp")
echo "Release ${TAG} created (id=${rel_id}). Asset uploaded: ${html_url}"
echo "Release ${TAG} ready (id=${rel_id})."

View File

@@ -1,117 +0,0 @@
#!/usr/bin/env bash
set -e
set +u
# Load .env
if [ -f "$(dirname "$0")/../.env" ]; then
# shellcheck disable=SC1091
. "$(dirname "$0")/../.env"
fi
API_URL=${GITEA_API_URL:-"https://gitea.elpatron.me/api/v1"}
OWNER=${GITEA_OWNER:-"elpatron"}
REPO=${GITEA_REPO:-"octo-funnel"}
TOKEN=${GITEA_API_TOKEN:-""}
TAG=${1:-v0.1.6.1}
ASSET_PATH=${2:-"$(pwd)/octoprint_tailscale_funnel/dist/OctoPrint-Tailscale-Funnel-0.1.6.1.zip"}
if [ -z "$TOKEN" ]; then
echo "GITEA_API_TOKEN not set" >&2
exit 1
fi
if [ ! -f "$ASSET_PATH" ]; then
echo "Asset not found: $ASSET_PATH" >&2
exit 1
fi
ASSET_NAME=$(basename "$ASSET_PATH")
# Get release by tag
REL_JSON=$(curl -sS -H "Authorization: token $TOKEN" "$API_URL/repos/$OWNER/$REPO/releases/tags/$TAG" || true)
REL_ID=$(python3 - <<'PY'
import sys, json
s=sys.stdin.read().strip()
try:
d=json.loads(s) if s else {}
print(d.get('id',''))
except Exception:
print('')
PY
<<<"$REL_JSON")
# Fallback list search
if [ -z "$REL_ID" ]; then
LIST=$(curl -sS -H "Authorization: token $TOKEN" "$API_URL/repos/$OWNER/$REPO/releases?limit=100")
REL_ID=$(TAG="$TAG" python3 - <<'PY'
import os, sys, json
tag=os.environ['TAG']
try:
arr=json.loads(sys.stdin.read())
for r in arr:
if r.get('tag_name')==tag:
print(r.get('id',''))
break
except Exception:
print('')
PY
<<<"$LIST")
fi
# Create release if still missing
if [ -z "$REL_ID" ]; then
PAYLOAD=$(TAG="$TAG" python3 - <<'PY'
import json, os
print(json.dumps({
'tag_name': os.environ['TAG'],
'name': os.environ['TAG'],
'body': 'Automated release'
}))
PY
)
CREATE=$(curl -sS -X POST -H 'Content-Type: application/json' -H "Authorization: token $TOKEN" -d "$PAYLOAD" "$API_URL/repos/$OWNER/$REPO/releases")
REL_ID=$(python3 - <<'PY'
import sys, json
try:
d=json.loads(sys.stdin.read())
print(d.get('id',''))
except Exception:
print('')
PY
<<<"$CREATE")
fi
if [ -z "$REL_ID" ]; then
echo "Failed to resolve release id for tag $TAG" >&2
exit 1
fi
# Check existing assets
ASSETS=$(curl -sS -H "Authorization: token $TOKEN" "$API_URL/repos/$OWNER/$REPO/releases/$REL_ID/assets")
HAS=$(NAME="$ASSET_NAME" python3 - <<'PY'
import os, sys, json
name=os.environ['NAME']
try:
arr=json.loads(sys.stdin.read())
print(any(a.get('name')==name for a in arr))
except Exception:
print(False)
PY
<<<"$ASSETS")
if [ "$HAS" = "True" ]; then
echo "Asset already exists: $ASSET_NAME"
else
echo "Uploading asset: $ASSET_NAME"
curl -sS -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/octet-stream" \
--data-binary @"$ASSET_PATH" \
"$API_URL/repos/$OWNER/$REPO/releases/$REL_ID/assets?name=$ASSET_NAME" >/dev/null
echo "Upload done."
fi
DL_URL="https://gitea.elpatron.me/$OWNER/$REPO/releases/download/$TAG/$ASSET_NAME"
CODE=$(curl -s -o /dev/null -w '%{http_code}' "$DL_URL")
echo "DOWNLOAD_URL=$DL_URL"
echo "HTTP_STATUS=$CODE"