Create MeshCentral workspace
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# meshpress-bridge Changelog
|
||||
|
||||
## Current
|
||||
- Fixed agent-link generation to pass the resolved group object instead of a boolean match flag.
|
||||
- Added selective WordPress event pushes for high-signal bridge lifecycle events so MeshPress can forward them into OmniLog.
|
||||
- Added bridge status visibility for WordPress event push configuration.
|
||||
|
||||
## 0.4.3 - 2026-06-12
|
||||
- Fixed agent-link generation to pass the resolved group object instead of a boolean match flag.
|
||||
- Added selective WordPress event pushes for high-signal bridge lifecycle events so MeshPress can forward them into OmniLog.
|
||||
- Added bridge status visibility for WordPress event push configuration.
|
||||
|
||||
## 0.4.2 - 2026-05-25
|
||||
- Added redacted MeshCentral config auditing for MySQL/MariaDB, framing, logging, backup, and maintenance posture.
|
||||
- Added real MeshCentral `settings.maintenanceMode` toggling with config backup and service restart support.
|
||||
- Replaced the data-only backup with a guarded full snapshot that includes MeshCentral files, config, manifest, checksum, and a MySQL/MariaDB dump when configured.
|
||||
- Added a configurable database dump timeout (`MESHPRESS_DB_DUMP_TIMEOUT_SECONDS`) so backup failures produce clear diagnostics instead of hanging.
|
||||
|
||||
## 0.4.1 - 2026-05-25
|
||||
- Resolved MeshCentral groups before generating agent links, so WordPress punctuation drift no longer breaks installer commands.
|
||||
- Generated Linux install commands from the canonical group short id, including MeshCentral `@` characters.
|
||||
- Returned the matched group record and group name with agent-link responses for clearer MeshPress output.
|
||||
- Returned normalized group `short_id` values so agent-link generation can preserve MeshCentral punctuation across the full path.
|
||||
- Added installer-shape diagnostics and an explicit warning when the resolved short Mesh ID looks too short.
|
||||
- Added a curl fallback command while keeping the native MeshCentral wget installer command as the primary Linux path.
|
||||
|
||||
## 0.4.0 - 2026-05-24
|
||||
- Resolved MeshCentral groups before generating agent links, so WordPress punctuation drift no longer breaks installer commands.
|
||||
- Generated Linux install commands from the canonical group short id, including MeshCentral `@` characters.
|
||||
- Returned the matched group record and group name with agent-link responses for clearer MeshPress output.
|
||||
|
||||
## 0.3.9 - 2026-05-24
|
||||
- Replaced the bridge updater with a staged, verified, rollback-capable update flow that does not delete PM2 before the new copy is ready.
|
||||
- Added catalog update checks and a runtime maintenance-mode toggle endpoint for MeshPress.
|
||||
- Made group-id resolution tolerant of MeshCentral punctuation drift, and switched Linux agent commands to MeshCentral's native install-script pattern.
|
||||
|
||||
## 0.3.8 - 2026-05-24
|
||||
- Group edits now verify the refreshed MeshCentral group record before returning success.
|
||||
- Added extra group edit diagnostics when MeshCtrl accepts a command but the group does not actually change.
|
||||
- Tried both `--desc` and `--description` edit forms for better MeshCtrl version compatibility.
|
||||
|
||||
## 0.3.7 - 2026-05-24
|
||||
- Added guarded MeshCentral maintenance endpoints for version/update checks, data backups, and update triggering.
|
||||
- Added agent uninstall diagnostics and richer copy-friendly bridge logging for lifecycle testing.
|
||||
- Hardened the bridge installer/updater so running from the live `/opt` target refuses instead of deleting itself.
|
||||
|
||||
## 0.3.6 - 2026-05-24
|
||||
- Rejected MeshCtrl failure text even when MeshCtrl exits with code 0, preventing false-success group edit responses.
|
||||
- Added richer group edit candidate attempts and copyable JSON diagnostics for invalid group identifiers.
|
||||
- Expanded agent link generation with Windows, Linux, macOS, Mesh Assistant, and Linux install command output.
|
||||
|
||||
## 0.3.5 - 2026-05-24
|
||||
- Changed MeshCentral session links to prefer the working `viewmode-first + gotonode` URL pattern.
|
||||
- Added short-node-id extraction for `gotonode` links so full `node//...` ids do not break direct console sessions.
|
||||
- Added `/api/session-diagnostics/:nodeId` plus guarded device rename and move endpoints for manual MeshPress testing.
|
||||
|
||||
## 0.3.4 - 2026-05-23
|
||||
- Added bridge runtime version reporting and a guarded manual update endpoint for MeshPress admin.
|
||||
- Added richer MeshCentral session-link generation for Desktop, Console, and Files with multiple direct-link candidates.
|
||||
- Added `console` and `shell` aliases for MeshCentral terminal sessions.
|
||||
|
||||
## 0.3.3 - 2026-05-23
|
||||
- Added `/api/version`, `/api/update-now`, and `/api/session-links/:nodeId`.
|
||||
- Added all-link candidate generation for MeshCentral session testing.
|
||||
- Restarted bridge updates with `pm2 --update-env` so env/config changes are picked up.
|
||||
|
||||
## 0.3.2 - 2026-05-23
|
||||
- Hardened bridge authentication for public-port exposure by requiring a configured bearer token on all endpoints.
|
||||
- Added optional exact-IP allowlisting and failed-auth rate limiting.
|
||||
- Moved WordPress fleet push authentication from query string to Authorization header.
|
||||
- Added startup security warnings for all-interface binding without app-layer allowlisting.
|
||||
|
||||
## 0.3.1 - 2026-05-23
|
||||
- Added bridge self-test, server-info, users, events, and device-info diagnostics for MeshPress.
|
||||
- Added configurable MeshCentral direct-link style with `MESHPRESS_LINK_MODE`.
|
||||
- Added guarded provisioning endpoints for group edit/remove, group user assignment, invite links, and simple device message/toast commands.
|
||||
- Added cache clearing and richer status output for faster troubleshooting.
|
||||
|
||||
## 0.3.0 - 2026-05-22
|
||||
- Added Phase 5.2 MeshCentral provisioning endpoints for Service Desk.
|
||||
- Added authenticated agent/provisioning link generation for MeshCentral device groups.
|
||||
- Added guarded device group creation through MeshCtrl `adddevicegroup`.
|
||||
- Required `MESHPRESS_ALLOW_COMMANDS=1` before the bridge will create MeshCentral groups, keeping provisioning actions opt-in.
|
||||
- Returned Windows x64, x86, ARM64, and interactive Windows agent links for linked MeshCentral groups.
|
||||
|
||||
<!-- GP-RECONSTRUCTED-CHANGELOG:START -->
|
||||
|
||||
## Reconstructed History
|
||||
|
||||
These entries were inferred from local distribution archives, module headers, and `.old` backups. They are intentionally conservative and may not capture the full intent of each change.
|
||||
|
||||
### 0.2.0 - reconstructed
|
||||
- Reconstructed from archive `meshpress-bridge-v0.2.0.zip` (2026-05-20).
|
||||
- Header description at this version: GracePress MeshPress Bridge. Pushes MeshCentral fleet snapshots to WordPress MeshPress.
|
||||
- Catalog/category marker: Hidden.
|
||||
|
||||
### 0.1.1 - reconstructed
|
||||
- Reconstructed from 2 file backup(s) in `tools/meshpress-bridge/.old`.
|
||||
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||
|
||||
### 0.1.0 - reconstructed
|
||||
- Reconstructed from 1 file backup(s) in `tools/meshpress-bridge/.old`.
|
||||
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||
|
||||
### VERSION, - reconstructed
|
||||
- Reconstructed from 2 file backup(s) in `tools/meshpress-bridge/.old`.
|
||||
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||
|
||||
<!-- GP-RECONSTRUCTED-CHANGELOG:END -->
|
||||
@@ -0,0 +1,184 @@
|
||||
# GracePress MeshPress Bridge
|
||||
|
||||
Small local HTTP bridge between WordPress/MeshPress and MeshCentral.
|
||||
|
||||
The bridge runs beside MeshCentral and uses MeshCentral's official `meshctrl` tool to read device groups and devices. WordPress talks to this bridge instead of trying to speak MeshCentral websocket protocol directly.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `GET /api/health`
|
||||
- `GET /api/status`
|
||||
- `GET /api/self-test`
|
||||
- `GET /api/groups`
|
||||
- `GET /api/users`
|
||||
- `GET /api/events?limit=50`
|
||||
- `GET /api/serverinfo`
|
||||
- `GET /api/devices`
|
||||
- `GET /api/device/:nodeId`
|
||||
- `GET /api/fleet`
|
||||
- `POST /api/push-now`
|
||||
- `POST /api/cache/clear`
|
||||
- `GET /api/link/desktop/:nodeId`
|
||||
- `GET /api/link/terminal/:nodeId`
|
||||
- `GET /api/link/files/:nodeId`
|
||||
- `GET /api/link/:mode/:nodeId?link_mode=both`
|
||||
- `GET /api/agent-links/:groupId`
|
||||
- `POST /api/groups/create`
|
||||
- `POST /api/groups/edit`
|
||||
- `POST /api/groups/remove`
|
||||
- `POST /api/groups/add-user`
|
||||
- `POST /api/groups/remove-user`
|
||||
- `POST /api/invite-link`
|
||||
- `POST /api/device/message`
|
||||
- `POST /api/device/toast`
|
||||
- `POST /api/action`
|
||||
|
||||
All endpoints require `Authorization: Bearer TOKEN`. If `MESHPRESS_BRIDGE_TOKEN` is empty, protected requests are rejected.
|
||||
|
||||
`/api/action` is intentionally locked down for now. It returns a clear unsupported-command response until each broad remote-command workflow is whitelisted and tested.
|
||||
|
||||
Provisioning and device-command endpoints require:
|
||||
|
||||
```bash
|
||||
MESHPRESS_ALLOW_COMMANDS="1"
|
||||
```
|
||||
|
||||
Without that flag the bridge will read MeshCentral state but refuse provisioning actions.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
cd /storage/meshcentral-workspace/bridge/meshpress-bridge
|
||||
sudo ./install.sh
|
||||
sudo nano /etc/gracepress/meshpress-bridge.env
|
||||
pm2 restart gp-meshpress-bridge
|
||||
```
|
||||
|
||||
## Required Config
|
||||
|
||||
```bash
|
||||
MESHPRESS_BRIDGE_PORT="3099"
|
||||
MESHPRESS_BRIDGE_BIND="127.0.0.1"
|
||||
MESHPRESS_BRIDGE_TOKEN="generated-secret"
|
||||
MESHPRESS_ALLOWED_IPS=""
|
||||
MESHPRESS_AUTH_WINDOW_SECONDS="900"
|
||||
MESHPRESS_AUTH_MAX_FAILURES="30"
|
||||
MESHCTRL_URL="wss://127.0.0.1"
|
||||
PUBLIC_MESH_URL="https://mesh.yourdomain.com"
|
||||
MESHCTRL_USER="admin"
|
||||
MESHCTRL_PASS=""
|
||||
MESHCTRL_DOMAIN=""
|
||||
MESHCTRL_LOGIN_KEY_FILE="/etc/gracepress/meshcentral-login.key"
|
||||
MESHCTRL_PATH=""
|
||||
MESHCTRL_IGNORE_CERT="0"
|
||||
MESHPRESS_LINK_HIDE=""
|
||||
MESHPRESS_LINK_MODE="gotonode"
|
||||
MESHPRESS_ALLOW_COMMANDS="0"
|
||||
WP_DOMAIN="https://christp2p.com"
|
||||
WP_API_KEY="same-token-used-in-meshpress"
|
||||
MESHPRESS_PUSH_INTERVAL_SECONDS="3600"
|
||||
MESHPRESS_PUSH_EVENTS="1"
|
||||
```
|
||||
|
||||
If auto-detection cannot find `meshctrl`, set `MESHCTRL_PATH` to the full path of `meshctrl.js`.
|
||||
|
||||
When `WP_DOMAIN` and `WP_API_KEY` are set, the bridge pushes fleet snapshots to WordPress at startup and then every hour.
|
||||
When `MESHPRESS_PUSH_EVENTS` is enabled, selected bridge lifecycle events are posted to MeshPress so WordPress can record them through OmniLog.
|
||||
|
||||
## Public Exposure Hardening
|
||||
|
||||
Do not expose port `3099` to the public internet without firewall/VPN controls. Preferred options:
|
||||
|
||||
1. Bind to localhost and proxy privately:
|
||||
|
||||
```bash
|
||||
MESHPRESS_BRIDGE_BIND="127.0.0.1"
|
||||
```
|
||||
|
||||
2. Bind to a Tailscale address only:
|
||||
|
||||
```bash
|
||||
MESHPRESS_BRIDGE_BIND="100.x.y.z"
|
||||
```
|
||||
|
||||
3. If the bridge must listen on all interfaces temporarily, restrict source IPs at both the firewall and app layer:
|
||||
|
||||
```bash
|
||||
MESHPRESS_BRIDGE_BIND="0.0.0.0"
|
||||
MESHPRESS_ALLOWED_IPS="WORDPRESS_SERVER_IP,YOUR_ADMIN_IP"
|
||||
```
|
||||
|
||||
The app-layer allowlist only accepts exact IP addresses. It is a backstop, not a replacement for firewall rules.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Use MeshPress > Diagnostics & Manual Actions first. It calls these bridge endpoints:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/health
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/version
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/status
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/self-test
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/groups?refresh=1
|
||||
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/devices?refresh=1
|
||||
```
|
||||
|
||||
`/api/self-test` verifies the bridge can find `meshctrl`, has an auth method, has a public MeshCentral URL, and can list device groups and devices.
|
||||
|
||||
For direct session links, test both URL styles:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer TOKEN" "http://127.0.0.1:3099/api/session-links/NODEID"
|
||||
curl -H "Authorization: Bearer TOKEN" "http://127.0.0.1:3099/api/link/console/NODEID?link_mode=all"
|
||||
```
|
||||
|
||||
To trigger a catalog-managed bridge update from MeshPress admin or curl:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/update-now
|
||||
```
|
||||
|
||||
To test the first provisioning path, enable commands and create a temporary group:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"MeshPress Test Group","description":"Manual bridge validation"}' \
|
||||
http://127.0.0.1:3099/api/groups/create
|
||||
```
|
||||
|
||||
## MeshCentral Auth
|
||||
|
||||
The bridge supports either a MeshCentral login key file or a dedicated integration username/password.
|
||||
|
||||
For username/password auth, set:
|
||||
|
||||
```bash
|
||||
MESHCTRL_USER="your-generated-integration-user"
|
||||
MESHCTRL_PASS="your-generated-integration-password"
|
||||
```
|
||||
|
||||
If `MESHCTRL_LOGIN_KEY_FILE` exists, the bridge will prefer it. If the file does not exist and `MESHCTRL_PASS` is set, it will use username/password auth.
|
||||
|
||||
## MeshCentral Login Key Option
|
||||
|
||||
On the MeshCentral server:
|
||||
|
||||
```bash
|
||||
cd /opt/meshcentral
|
||||
sudo node node_modules/meshcentral --loginTokenKey > /etc/gracepress/meshcentral-login.key
|
||||
sudo chmod 600 /etc/gracepress/meshcentral-login.key
|
||||
```
|
||||
|
||||
MeshCentral must have login tokens enabled in `meshcentral-data/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"allowLoginToken": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Restart MeshCentral after changing config.
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
COMPONENT="meshpress-bridge"
|
||||
TARGET_DIR="/opt/gracepress-meshpress-bridge"
|
||||
ENV_DIR="/etc/gracepress"
|
||||
ENV_FILE="$ENV_DIR/meshpress-bridge.env"
|
||||
CRON_FILE="/etc/cron.d/gracepress-meshpress-bridge"
|
||||
SERVICE_NAME="gp-meshpress-bridge"
|
||||
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VERSION="$(grep -m 1 '^# Version:' "$SOURCE_DIR/meshpress-bridge" | sed -E 's/^# Version:[[:space:]]*//')"
|
||||
SOURCE_REAL="$(realpath "$SOURCE_DIR")"
|
||||
TARGET_REAL="$(realpath -m "$TARGET_DIR")"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Please run as root: sudo ./install.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$SOURCE_REAL" = "$TARGET_REAL" ]; then
|
||||
echo "Same-directory install detected; delegating to safe in-place updater."
|
||||
exec "$TARGET_DIR/update.sh"
|
||||
fi
|
||||
|
||||
mkdir -p "$ENV_DIR"
|
||||
touch "$ENV_FILE"
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
rand_token() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex 32
|
||||
else
|
||||
date +%s%N | sha256sum | awk '{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_env() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if ! grep -q "^${key}=" "$ENV_FILE"; then
|
||||
echo "${key}=\"${value}\"" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_env "MESHPRESS_BRIDGE_PORT" "${MESHPRESS_BRIDGE_PORT:-3099}"
|
||||
ensure_env "MESHPRESS_BRIDGE_BIND" "${MESHPRESS_BRIDGE_BIND:-127.0.0.1}"
|
||||
ensure_env "MESHPRESS_BRIDGE_TOKEN" "${MESHPRESS_BRIDGE_TOKEN:-$(rand_token)}"
|
||||
ensure_env "MESHPRESS_ALLOWED_IPS" "${MESHPRESS_ALLOWED_IPS:-}"
|
||||
ensure_env "MESHPRESS_AUTH_WINDOW_SECONDS" "${MESHPRESS_AUTH_WINDOW_SECONDS:-900}"
|
||||
ensure_env "MESHPRESS_AUTH_MAX_FAILURES" "${MESHPRESS_AUTH_MAX_FAILURES:-30}"
|
||||
ensure_env "MESHCTRL_URL" "${MESHCTRL_URL:-wss://127.0.0.1}"
|
||||
ensure_env "PUBLIC_MESH_URL" "${PUBLIC_MESH_URL:-https://mesh.example.com}"
|
||||
ensure_env "MESHCTRL_USER" "${MESHCTRL_USER:-admin}"
|
||||
ensure_env "MESHCTRL_PASS" "${MESHCTRL_PASS:-}"
|
||||
ensure_env "MESHCTRL_DOMAIN" "${MESHCTRL_DOMAIN:-}"
|
||||
ensure_env "MESHCTRL_LOGIN_KEY_FILE" "${MESHCTRL_LOGIN_KEY_FILE:-/etc/gracepress/meshcentral-login.key}"
|
||||
ensure_env "MESHCTRL_PATH" "${MESHCTRL_PATH:-}"
|
||||
ensure_env "MESHCTRL_IGNORE_CERT" "${MESHCTRL_IGNORE_CERT:-0}"
|
||||
ensure_env "MESHPRESS_CACHE_TTL_SECONDS" "${MESHPRESS_CACHE_TTL_SECONDS:-60}"
|
||||
ensure_env "MESHPRESS_LINK_HIDE" "${MESHPRESS_LINK_HIDE:-}"
|
||||
ensure_env "MESHPRESS_ALLOW_COMMANDS" "${MESHPRESS_ALLOW_COMMANDS:-0}"
|
||||
ensure_env "MESHPRESS_ALLOW_MAINTENANCE" "${MESHPRESS_ALLOW_MAINTENANCE:-0}"
|
||||
ensure_env "MESHCENTRAL_DIR" "${MESHCENTRAL_DIR:-}"
|
||||
ensure_env "MESHCENTRAL_DATA_DIR" "${MESHCENTRAL_DATA_DIR:-}"
|
||||
ensure_env "MESHCENTRAL_SERVICE_NAME" "${MESHCENTRAL_SERVICE_NAME:-meshcentral}"
|
||||
ensure_env "MESHPRESS_BACKUP_DIR" "${MESHPRESS_BACKUP_DIR:-/var/backups/gracepress-meshcentral}"
|
||||
ensure_env "WP_DOMAIN" "${WP_DOMAIN:-}"
|
||||
ensure_env "WP_API_KEY" "${WP_API_KEY:-}"
|
||||
ensure_env "MESHPRESS_PUSH_INTERVAL_SECONDS" "${MESHPRESS_PUSH_INTERVAL_SECONDS:-3600}"
|
||||
ensure_env "MESHPRESS_PUSH_EVENTS" "${MESHPRESS_PUSH_EVENTS:-1}"
|
||||
ensure_env "AUTO_UPDATE" "${AUTO_UPDATE:-1}"
|
||||
|
||||
apt-get update -y >/dev/null
|
||||
apt-get install -y curl ca-certificates >/dev/null
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - >/dev/null
|
||||
apt-get install -y nodejs >/dev/null
|
||||
npm install -g pm2 >/dev/null
|
||||
|
||||
pm2 install pm2-logrotate >/dev/null 2>&1 || true
|
||||
pm2 set pm2-logrotate:max_size 25M >/dev/null 2>&1 || true
|
||||
pm2 set pm2-logrotate:retain 5 >/dev/null 2>&1 || true
|
||||
|
||||
STAGE_DIR="${TARGET_DIR}.installing.$$"
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR"
|
||||
cp -a "$SOURCE_DIR"/. "$STAGE_DIR"/
|
||||
echo "$VERSION" > "$STAGE_DIR/VERSION"
|
||||
cd "$STAGE_DIR"
|
||||
npm install --omit=dev >/dev/null
|
||||
node --check server.js >/dev/null
|
||||
chmod +x "$STAGE_DIR/update.sh" "$STAGE_DIR/meshpress-bridge"
|
||||
|
||||
pm2 delete "$SERVICE_NAME" >/dev/null 2>&1 || true
|
||||
if [ -d "$TARGET_DIR" ]; then
|
||||
BACKUP_DIR="${TARGET_DIR}.old.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$TARGET_DIR" "$BACKUP_DIR"
|
||||
echo "Previous install moved to $BACKUP_DIR"
|
||||
fi
|
||||
mv "$STAGE_DIR" "$TARGET_DIR"
|
||||
cd "$TARGET_DIR"
|
||||
|
||||
pm2 start server.js --name "$SERVICE_NAME" --time --max-memory-restart 512M >/dev/null
|
||||
pm2 save >/dev/null
|
||||
pm2 startup | grep "sudo" | bash >/dev/null 2>&1 || true
|
||||
|
||||
cat > "$CRON_FILE" <<EOF
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
17 * * * * root [ -f "$ENV_FILE" ] && . "$ENV_FILE"; [ "\${AUTO_UPDATE:-1}" = "1" ] && "$TARGET_DIR/update.sh" >/var/log/gracepress-meshpress-bridge-update.log 2>&1
|
||||
EOF
|
||||
chmod 644 "$CRON_FILE"
|
||||
|
||||
echo "GracePress MeshPress Bridge v$VERSION installed."
|
||||
echo "Config: $ENV_FILE"
|
||||
echo "Token: $(grep '^MESHPRESS_BRIDGE_TOKEN=' "$ENV_FILE" | cut -d= -f2- | sed 's/^\"//;s/\"$//')"
|
||||
echo "Next: edit $ENV_FILE and set MESHCTRL_URL, PUBLIC_MESH_URL, MESHCTRL_USER, MESHCTRL_PASS or MESHCTRL_LOGIN_KEY_FILE, MESHCTRL_PATH, WP_DOMAIN, and WP_API_KEY."
|
||||
echo "Then restart: pm2 restart $SERVICE_NAME --update-env"
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Description: GracePress MeshPress Bridge. Pushes MeshCentral fleet snapshots to WordPress MeshPress.
|
||||
# Category: Hidden
|
||||
# Version: 0.4.3
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec node "$DIR/server.js" "$@"
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "gracepress-meshpress-bridge",
|
||||
"version": "0.4.3",
|
||||
"private": true,
|
||||
"description": "GracePress bridge service for MeshCentral fleet sync and device links",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"check": "node --check server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.3"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
COMPONENT="meshpress-bridge"
|
||||
TARGET_DIR="/opt/gracepress-meshpress-bridge"
|
||||
ENV_FILE="/etc/gracepress/meshpress-bridge.env"
|
||||
SERVICE_NAME="gp-meshpress-bridge"
|
||||
LOG_PREFIX="[meshpress-bridge-update]"
|
||||
LOCK_FILE="/tmp/gracepress-meshpress-bridge-update.lock"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
STAGE_DIR=""
|
||||
BACKUP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
if [ -n "$STAGE_DIR" ] && [ -d "$STAGE_DIR" ]; then rm -rf "$STAGE_DIR"; fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
log() { echo "$LOG_PREFIX $(date -Is) $*"; }
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
log "Please run as root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec 9>"$LOCK_FILE"
|
||||
if command -v flock >/dev/null 2>&1; then
|
||||
flock -n 9 || { log "Another bridge update is already running."; exit 1; }
|
||||
fi
|
||||
|
||||
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
|
||||
CATALOG_URL="${GRACEPRESS_CATALOG_URL:-https://christit.com/gracepress/api.php?endpoint=manifest&slug=meshpress-bridge}"
|
||||
|
||||
apt-get update -y >/dev/null
|
||||
apt-get install -y curl unzip python3 ca-certificates nodejs npm >/dev/null
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
npm install -g pm2 >/dev/null
|
||||
fi
|
||||
|
||||
log "Checking catalog: $CATALOG_URL"
|
||||
curl -fsSL "$CATALOG_URL&nocache=$(date +%s)" -o "$TMP_DIR/manifest.json"
|
||||
DOWNLOAD_URL="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("download_url",""))' "$TMP_DIR/manifest.json")"
|
||||
VERSION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("version","0.0.0"))' "$TMP_DIR/manifest.json")"
|
||||
|
||||
if [ -z "$DOWNLOAD_URL" ]; then
|
||||
log "No download_url found in catalog manifest."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT="$(cat "$TARGET_DIR/VERSION" 2>/dev/null || echo 0.0.0)"
|
||||
if [ "$CURRENT" = "$VERSION" ]; then
|
||||
log "$COMPONENT is already current: $CURRENT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Updating $COMPONENT from $CURRENT to $VERSION"
|
||||
curl -fL "$DOWNLOAD_URL" -o "$TMP_DIR/package.zip"
|
||||
unzip -q "$TMP_DIR/package.zip" -d "$TMP_DIR/pkg"
|
||||
PKG_DIR="$(find "$TMP_DIR/pkg" -maxdepth 3 -type f -name server.js -printf '%h\n' | head -n 1)"
|
||||
if [ -z "$PKG_DIR" ] || [ ! -f "$PKG_DIR/package.json" ]; then
|
||||
log "Package does not contain a valid bridge payload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STAGE_DIR="${TARGET_DIR}.next.${VERSION}.$$"
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR"
|
||||
cp -a "$PKG_DIR"/. "$STAGE_DIR"/
|
||||
echo "$VERSION" > "$STAGE_DIR/VERSION"
|
||||
chmod +x "$STAGE_DIR/update.sh" "$STAGE_DIR/meshpress-bridge" 2>/dev/null || true
|
||||
|
||||
log "Installing node dependencies in staging directory."
|
||||
(cd "$STAGE_DIR" && npm install --omit=dev >/dev/null && node --check server.js >/dev/null)
|
||||
|
||||
BACKUP_DIR="${TARGET_DIR}.old.$(date +%Y%m%d%H%M%S)"
|
||||
if [ -d "$TARGET_DIR" ]; then
|
||||
mv "$TARGET_DIR" "$BACKUP_DIR"
|
||||
log "Previous install moved to $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
mv "$STAGE_DIR" "$TARGET_DIR"
|
||||
STAGE_DIR=""
|
||||
|
||||
rollback() {
|
||||
log "Update failed after swap; attempting rollback."
|
||||
local failed="${TARGET_DIR}.failed.$(date +%Y%m%d%H%M%S)"
|
||||
if [ -d "$TARGET_DIR" ]; then mv "$TARGET_DIR" "$failed"; fi
|
||||
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then mv "$BACKUP_DIR" "$TARGET_DIR"; fi
|
||||
if [ -f "$TARGET_DIR/server.js" ]; then
|
||||
pm2 start "$TARGET_DIR/server.js" --name "$SERVICE_NAME" --time --max-memory-restart 512M >/dev/null 2>&1 || pm2 restart "$SERVICE_NAME" --update-env >/dev/null 2>&1 || true
|
||||
pm2 save >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
if pm2 describe "$SERVICE_NAME" >/dev/null 2>&1; then
|
||||
if ! (cd "$TARGET_DIR" && pm2 restart "$SERVICE_NAME" --update-env >/dev/null); then
|
||||
rollback
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! (cd "$TARGET_DIR" && pm2 start server.js --name "$SERVICE_NAME" --time --max-memory-restart 512M >/dev/null); then
|
||||
rollback
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
pm2 save >/dev/null
|
||||
log "$COMPONENT updated to $VERSION"
|
||||
Reference in New Issue
Block a user