Compare commits
7 Commits
v0.1.6.2
...
fd5ff20743
Author | SHA1 | Date | |
---|---|---|---|
|
fd5ff20743 | ||
|
87449641f6 | ||
|
731cd207dd | ||
|
a5489de212 | ||
|
c0d4448dc1 | ||
|
0d43e181c8 | ||
|
f60068b01f |
1
docs
Symbolic link
1
docs
Symbolic link
@@ -0,0 +1 @@
|
||||
octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs
|
@@ -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
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
* OctoPrint 1.3.0 or higher
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
@@ -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})."
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
|
Reference in New Issue
Block a user