Compare commits
8 Commits
v0.1.6.1
...
fd5ff20743
Author | SHA1 | Date | |
---|---|---|---|
|
fd5ff20743 | ||
|
87449641f6 | ||
|
731cd207dd | ||
|
a5489de212 | ||
|
c0d4448dc1 | ||
|
0d43e181c8 | ||
|
f60068b01f | ||
|
4d484c5023 |
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.
|
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
|
## Features
|
||||||
|
|
||||||
* Enable/disable Tailscale Funnel access directly from OctoPrint's settings
|
* 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
|
* Display the public URL for accessing OctoPrint remotely
|
||||||
* Configure the port to expose via Funnel
|
* Configure the port to expose via Funnel
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
* OctoPrint 1.3.0 or higher
|
* OctoPrint 1.3.0 or higher
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
@@ -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.1"
|
plugin_version = "0.1.6.2"
|
||||||
|
|
||||||
# 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
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1.6.1"
|
"version": "0.1.6.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,54 +44,36 @@ if [ -z "$TOKEN" ]; then
|
|||||||
echo "GITEA_API_TOKEN not set (in .env)." >&2; exit 1
|
echo "GITEA_API_TOKEN not set (in .env)." >&2; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Derive asset name early for later use
|
||||||
|
ASSET_NAME="$(basename "$ASSET_PATH")"
|
||||||
|
|
||||||
BODY="Tailscale Funnel Plugin ${TAG}\n\nAutomated release."
|
BODY="Tailscale Funnel Plugin ${TAG}\n\nAutomated release."
|
||||||
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
|
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
|
||||||
BODY=$(cat "$BODY_FILE")
|
BODY=$(cat "$BODY_FILE")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build JSON payload via python (robust quoting)
|
# Try to fetch existing release by tag first
|
||||||
create_payload=$(REL_TAG="$TAG" REL_NAME="$NAME" REL_BODY_TXT="$BODY" python3 - <<'PY'
|
get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}" || true)
|
||||||
import json, os
|
rel_id=$(echo "$get_resp" | jq -r '.id // empty')
|
||||||
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_resp=$(curl -sS -X POST \
|
if [ -z "$rel_id" ]; then
|
||||||
|
# Build minimal JSON payload (use existing tag)
|
||||||
|
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' \
|
-H 'Content-Type: application/json' \
|
||||||
-H "Authorization: token ${TOKEN}" \
|
-H "Authorization: token ${TOKEN}" \
|
||||||
-d "$create_payload" \
|
-d "$create_payload" \
|
||||||
"${API_URL}/repos/${OWNER}/${REPO}/releases" || true)
|
"${API_URL}/repos/${OWNER}/${REPO}/releases" || true)
|
||||||
|
|
||||||
# Extract id; if missing, fetch by tag
|
# Extract id from create response
|
||||||
rel_id=$(python3 - <<'PY'
|
rel_id=$(echo "$create_resp" | jq -r '.id // empty')
|
||||||
import sys, json
|
fi
|
||||||
data=sys.stdin.read().strip()
|
|
||||||
if not data:
|
|
||||||
print("")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
obj=json.loads(data)
|
|
||||||
print(obj.get('id',''))
|
|
||||||
except Exception:
|
|
||||||
print("")
|
|
||||||
PY
|
|
||||||
<<<"$create_resp")
|
|
||||||
|
|
||||||
|
# Fallback: search releases list for matching tag if still empty
|
||||||
if [ -z "$rel_id" ]; then
|
if [ -z "$rel_id" ]; then
|
||||||
get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}")
|
list_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases?limit=100")
|
||||||
rel_id=$(python3 - <<'PY'
|
rel_id=$(echo "$list_resp" | jq -r --arg tag "$TAG" '[.[] | select(.tag_name==$tag)][0].id // empty')
|
||||||
import sys, json
|
|
||||||
obj=json.loads(sys.stdin.read())
|
|
||||||
print(obj.get('id',''))
|
|
||||||
PY
|
|
||||||
<<<"$get_resp")
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$rel_id" ]; then
|
if [ -z "$rel_id" ]; then
|
||||||
@@ -100,37 +82,18 @@ if [ -z "$rel_id" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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")
|
assets_json=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets")
|
||||||
asset_id=$(python3 - <<'PY'
|
asset_exists=$(echo "$assets_json" | jq -r --arg name "$ASSET_NAME" 'any(.[]; .name==$name)')
|
||||||
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
|
|
||||||
ASSET_NAME="$(basename "$ASSET_PATH")" <<<"$assets_json")
|
|
||||||
|
|
||||||
if [ -n "$asset_id" ]; then
|
if [ "$asset_exists" = "true" ]; then
|
||||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets/${asset_id}" >/dev/null || true
|
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
|
fi
|
||||||
|
|
||||||
# Upload asset
|
echo "Release ${TAG} ready (id=${rel_id})."
|
||||||
upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=$(basename "$ASSET_PATH")")
|
|
||||||
|
|
||||||
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}"
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user