Adds dist.
This commit is contained in:
30
dist/.gitignore
vendored
Normal file
30
dist/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Local dev secrets
|
||||
.envrc
|
||||
|
||||
# Build artifacts
|
||||
build/
|
||||
dist/
|
||||
*.deb
|
||||
__debug*
|
||||
|
||||
# APT repository build artifacts
|
||||
.aptly/
|
||||
.aptly.conf
|
||||
|
||||
# Go build cache
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Test binary
|
||||
*.test
|
||||
|
||||
# Coverage
|
||||
*.out
|
||||
*.cover
|
||||
*.coverage
|
||||
|
||||
# Go workspace
|
||||
go.work
|
||||
go.work.sum
|
||||
gpg/wild-central.gpg
|
||||
164
dist/Makefile
vendored
Normal file
164
dist/Makefile
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
# Default target
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# Build configuration
|
||||
VERSION := $(shell cat ../VERSION 2>/dev/null || echo "0.0.0-dev")
|
||||
BUILD_DIR := build
|
||||
DIST_DIR := dist
|
||||
DEB_DIR := debian-package
|
||||
|
||||
# Git information
|
||||
GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
||||
EDGE_VERSION := 0.0.0~edge.$(shell date -u '+%Y%m%d%H%M%S').$(GIT_COMMIT)
|
||||
|
||||
# Source paths
|
||||
API_SOURCE := ..
|
||||
WEB_SOURCE := ../web
|
||||
|
||||
# Output files
|
||||
ARM64_BINARY := $(BUILD_DIR)/wild-central-arm64
|
||||
AMD64_BINARY := $(BUILD_DIR)/wild-central-amd64
|
||||
WEB_DIST_MARKER := $(BUILD_DIR)/.web-built
|
||||
ARM64_DEB := $(DIST_DIR)/packages/wild-central_$(VERSION)_arm64.deb
|
||||
AMD64_DEB := $(DIST_DIR)/packages/wild-central_$(VERSION)_amd64.deb
|
||||
|
||||
# Gitea release configuration
|
||||
GITEA_URL := https://git.civilsociety.dev
|
||||
GITEA_OWNER := wild-cloud
|
||||
GITEA_REPO := wild-central
|
||||
RELEASE_TAG := v$(VERSION)
|
||||
|
||||
.PHONY: help clean version build build-all package package-all repo deploy-repo release release-edge
|
||||
|
||||
help:
|
||||
@echo "Wild Central Package Builder"
|
||||
@echo ""
|
||||
@echo "This directory builds .deb packages for Wild Central."
|
||||
@echo "For development, use the parent directory:"
|
||||
@echo " - make dev (Go daemon)"
|
||||
@echo " - cd web && pnpm dev (React frontend)"
|
||||
@echo ""
|
||||
@echo "Build targets:"
|
||||
@echo " build - Build for current architecture (amd64)"
|
||||
@echo " build-all - Build all architectures"
|
||||
@echo ""
|
||||
@echo "Package targets:"
|
||||
@echo " package - Create .deb package (amd64)"
|
||||
@echo " package-all - Create all .deb packages"
|
||||
@echo ""
|
||||
@echo "Repository targets:"
|
||||
@echo " deploy-repo - Deploy repository to server"
|
||||
@echo ""
|
||||
@echo "Release targets:"
|
||||
@echo " release - Tag HEAD as v$$(cat ../VERSION), publish stable Gitea release + APT"
|
||||
@echo " release-edge - Publish rolling edge Gitea release + APT (no version bump needed)"
|
||||
@echo ""
|
||||
@echo "Utilities:"
|
||||
@echo " clean - Remove all build artifacts"
|
||||
@echo " version - Show build information"
|
||||
@echo ""
|
||||
@echo " Current stable version: $(VERSION) (from ../VERSION)"
|
||||
@echo " Current edge version: $(EDGE_VERSION)"
|
||||
@echo ""
|
||||
@echo "Environment variables:"
|
||||
@echo " GITEA_TOKEN - Gitea API token (required for release)"
|
||||
@echo " APTLY_URL - Aptly instance URL (required for APT deployment)"
|
||||
@echo " APTLY_PASS - Aptly API password"
|
||||
@echo " Run: source .envrc"
|
||||
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
@rm -rf $(BUILD_DIR) $(DIST_DIR) $(DEB_DIR)-* $(DEB_DIR)
|
||||
|
||||
version:
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "Git Commit: $(GIT_COMMIT)"
|
||||
@echo "Build Time: $(BUILD_TIME)"
|
||||
|
||||
# Web app build - track with marker file
|
||||
$(WEB_DIST_MARKER): $(shell find $(WEB_SOURCE)/src -type f 2>/dev/null | head -100) $(WEB_SOURCE)/package.json
|
||||
@echo "Building web application..."
|
||||
@ARCH=amd64 VERSION=$(VERSION) ./scripts/build-package.sh
|
||||
@touch $(WEB_DIST_MARKER)
|
||||
|
||||
# ARM64 binary
|
||||
$(ARM64_BINARY): $(shell find $(API_SOURCE) -name '*.go' -not -path '*/dist/*' -not -path '*/web/*' 2>/dev/null | head -100)
|
||||
@echo "Building arm64 binary..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@cd $(API_SOURCE) && GOOS=linux GOARCH=arm64 go build -ldflags="-X main.Version=$(VERSION)" -o dist/$(ARM64_BINARY) .
|
||||
|
||||
# AMD64 binary
|
||||
$(AMD64_BINARY): $(shell find $(API_SOURCE) -name '*.go' -not -path '*/dist/*' -not -path '*/web/*' 2>/dev/null | head -100)
|
||||
@echo "Building amd64 binary..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@cd $(API_SOURCE) && GOOS=linux GOARCH=amd64 go build -ldflags="-X main.Version=$(VERSION)" -o dist/$(AMD64_BINARY) .
|
||||
|
||||
# ARM64 package
|
||||
$(ARM64_DEB): $(ARM64_BINARY) $(WEB_DIST_MARKER)
|
||||
@echo "Creating arm64 .deb package..."
|
||||
@rm -f $(DIST_DIR)/packages/wild-central_*_arm64.deb
|
||||
@./scripts/package-deb.sh arm64 $(ARM64_BINARY) $(VERSION)
|
||||
|
||||
# AMD64 package
|
||||
$(AMD64_DEB): $(AMD64_BINARY) $(WEB_DIST_MARKER)
|
||||
@echo "Creating amd64 .deb package..."
|
||||
@rm -f $(DIST_DIR)/packages/wild-central_*_amd64.deb
|
||||
@./scripts/package-deb.sh amd64 $(AMD64_BINARY) $(VERSION)
|
||||
|
||||
# Convenience targets
|
||||
build: $(AMD64_BINARY) $(WEB_DIST_MARKER)
|
||||
|
||||
build-all: $(ARM64_BINARY) $(AMD64_BINARY) $(WEB_DIST_MARKER)
|
||||
|
||||
package: $(AMD64_DEB)
|
||||
|
||||
package-all: $(ARM64_DEB) $(AMD64_DEB)
|
||||
|
||||
deploy-repo: $(ARM64_DEB) $(AMD64_DEB)
|
||||
@echo "Deploying APT repository..."
|
||||
@if [ -z "$(APTLY_URL)" ] || [ -z "$(APTLY_PASS)" ]; then \
|
||||
echo "Error: APTLY_URL and APTLY_PASS must be set"; \
|
||||
echo " Run: source .envrc"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@./scripts/deploy-apt-repository.sh
|
||||
|
||||
release: $(ARM64_DEB) $(AMD64_DEB)
|
||||
@echo "Creating stable release $(RELEASE_TAG)..."
|
||||
@if [ -z "$(GITEA_TOKEN)" ]; then \
|
||||
echo "Error: GITEA_TOKEN environment variable not set"; \
|
||||
echo " Get a token from $(GITEA_URL)/user/settings/applications"; \
|
||||
echo " Or run: source .envrc"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if git tag | grep -q "^$(RELEASE_TAG)$$"; then \
|
||||
echo "Tag $(RELEASE_TAG) already exists — updating release assets only"; \
|
||||
else \
|
||||
echo "Tagging HEAD as $(RELEASE_TAG)..."; \
|
||||
git tag -a $(RELEASE_TAG) -m "Wild Central $(VERSION)"; \
|
||||
git push origin $(RELEASE_TAG); \
|
||||
fi
|
||||
@./scripts/create-gitea-release.sh "$(GITEA_URL)" "$(GITEA_OWNER)" "$(GITEA_REPO)" "$(RELEASE_TAG)" "$(VERSION)" "$(GITEA_TOKEN)" stable
|
||||
@if [ -n "$(APTLY_URL)" ] && [ -n "$(APTLY_PASS)" ]; then \
|
||||
./scripts/deploy-apt-repository.sh; \
|
||||
else \
|
||||
echo "Skipping APT deployment (APTLY_URL/APTLY_PASS not set)"; \
|
||||
fi
|
||||
|
||||
release-edge: $(ARM64_DEB) $(AMD64_DEB)
|
||||
@echo "Creating edge release ($(EDGE_VERSION))..."
|
||||
@if [ -z "$(GITEA_TOKEN)" ]; then \
|
||||
echo "Error: GITEA_TOKEN environment variable not set"; \
|
||||
echo " Or run: source .envrc"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "Force-pushing edge tag to HEAD..."
|
||||
@git tag -f edge
|
||||
@git push origin edge --force
|
||||
@./scripts/create-gitea-release.sh "$(GITEA_URL)" "$(GITEA_OWNER)" "$(GITEA_REPO)" "edge" "$(EDGE_VERSION)" "$(GITEA_TOKEN)" edge
|
||||
@if [ -n "$(APTLY_URL)" ] && [ -n "$(APTLY_PASS)" ]; then \
|
||||
DIST=edge ./scripts/deploy-apt-repository.sh; \
|
||||
else \
|
||||
echo "Skipping APT deployment (APTLY_URL/APTLY_PASS not set)"; \
|
||||
fi
|
||||
76
dist/README.md
vendored
Normal file
76
dist/README.md
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Packaging Wild Central
|
||||
|
||||
## Installation
|
||||
|
||||
### APT Repository (recommended)
|
||||
|
||||
```bash
|
||||
# Import the signing key
|
||||
curl -fsSL https://aptly.cloud2.payne.io/wild-central.gpg \
|
||||
| sudo tee /usr/share/keyrings/wild-central.gpg > /dev/null
|
||||
|
||||
# Add the repository
|
||||
sudo tee /etc/apt/sources.list.d/wild-central.sources <<EOF
|
||||
Types: deb
|
||||
URIs: https://aptly.cloud2.payne.io
|
||||
Suites: stable
|
||||
Components: main
|
||||
Signed-By: /usr/share/keyrings/wild-central.gpg
|
||||
EOF
|
||||
|
||||
sudo apt update && sudo apt install wild-central
|
||||
```
|
||||
|
||||
Use `Suites: edge` for rolling builds from HEAD.
|
||||
|
||||
### Manual .deb install
|
||||
|
||||
```bash
|
||||
sudo dpkg -i wild-central_*.deb
|
||||
sudo apt-get install -f
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Start the service**:
|
||||
```bash
|
||||
sudo systemctl enable --now wild-central
|
||||
```
|
||||
|
||||
2. **Access the web interface**: open `http://your-server-ip` in your browser
|
||||
|
||||
3. **Configure** (optional):
|
||||
```bash
|
||||
sudo cp /etc/wild-central/config.yaml.example /etc/wild-central/config.yaml
|
||||
sudo nano /etc/wild-central/config.yaml
|
||||
sudo systemctl restart wild-central
|
||||
```
|
||||
|
||||
## Release Artifacts
|
||||
|
||||
Each release includes:
|
||||
- `wild-central_{VERSION}_{arch}.deb` — full install (daemon + web UI)
|
||||
- `wild-central-{arch}` — standalone daemon binary
|
||||
- `SHA256SUMS` — checksums for all artifacts
|
||||
|
||||
## Developer Tooling
|
||||
|
||||
```bash
|
||||
make help # Show all targets and current version info
|
||||
make package-all # Build all .deb packages
|
||||
make release # Stable release: tag + Gitea + APT stable
|
||||
make release-edge # Edge release: force-tag + Gitea + APT edge
|
||||
make deploy-repo # Deploy packages to APT repository only
|
||||
```
|
||||
|
||||
Directory structure:
|
||||
|
||||
```
|
||||
build/ Intermediate build artifacts
|
||||
dist/bin/ Standalone binaries
|
||||
dist/packages/ .deb packages
|
||||
```
|
||||
|
||||
Environment variables (see `.envrc`):
|
||||
- `GITEA_TOKEN` — required for `make release` / `make release-edge`
|
||||
- `APTLY_URL` / `APTLY_PASS` — required for APT deployment
|
||||
12
dist/debian/DEBIAN/control
vendored
Normal file
12
dist/debian/DEBIAN/control
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Package: wild-central
|
||||
Version: VERSION_PLACEHOLDER
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: ARCH_PLACEHOLDER
|
||||
Homepage: https://mywildcloud.org
|
||||
Pre-Depends: dnsmasq, haproxy, nftables, wireguard-tools, certbot, python3-certbot-dns-cloudflare
|
||||
Depends: adduser, nginx
|
||||
Maintainer: Wild Cloud Team <paul@payne.io>
|
||||
Description: Wild Central - Networking and Coordination Platform
|
||||
Manages DNS, gateway routing, firewall, VPN, TLS certificates, and tunnels
|
||||
for Wild Cloud and other self-hosted services on the local network.
|
||||
262
dist/debian/DEBIAN/postinst
vendored
Normal file
262
dist/debian/DEBIAN/postinst
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
echo "Configuring wild-central..."
|
||||
|
||||
# Create wildcloud user if it doesn't exist
|
||||
if ! id wildcloud >/dev/null 2>&1; then
|
||||
useradd --system --home-dir /var/lib/wild-central --create-home --shell /bin/false wildcloud
|
||||
fi
|
||||
|
||||
# Create required directories
|
||||
mkdir -p /var/lib/wild-central
|
||||
mkdir -p /var/log/wild-central
|
||||
mkdir -p /etc/wild-central
|
||||
|
||||
# Set ownership
|
||||
chown -R wildcloud:wildcloud /var/lib/wild-central
|
||||
chown wildcloud:wildcloud /var/log/wild-central
|
||||
|
||||
# Set up dnsmasq directories and permissions
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
mkdir -p /etc/dnsmasq.d/wild-cloud-instances
|
||||
|
||||
# Give wildcloud group write access to dnsmasq.d directory
|
||||
chgrp wildcloud /etc/dnsmasq.d
|
||||
chmod 775 /etc/dnsmasq.d
|
||||
|
||||
# Create or fix ownership of wild-cloud.conf
|
||||
if [ ! -f /etc/dnsmasq.d/wild-cloud.conf ]; then
|
||||
touch /etc/dnsmasq.d/wild-cloud.conf
|
||||
echo "Created /etc/dnsmasq.d/wild-cloud.conf"
|
||||
else
|
||||
echo "Found existing /etc/dnsmasq.d/wild-cloud.conf - updating ownership"
|
||||
fi
|
||||
|
||||
chown wildcloud:wildcloud /etc/dnsmasq.d/wild-cloud.conf
|
||||
chmod 644 /etc/dnsmasq.d/wild-cloud.conf
|
||||
|
||||
# Set ownership and permissions for instance configs directory
|
||||
chown wildcloud:wildcloud /etc/dnsmasq.d/wild-cloud-instances
|
||||
chmod 755 /etc/dnsmasq.d/wild-cloud-instances
|
||||
echo "Created /etc/dnsmasq.d/wild-cloud-instances/ for per-instance DNS configs"
|
||||
|
||||
# Set up systemd-resolved configuration directory and file
|
||||
mkdir -p /etc/systemd/resolved.conf.d
|
||||
|
||||
if [ ! -f /etc/systemd/resolved.conf.d/wild-cloud.conf ]; then
|
||||
touch /etc/systemd/resolved.conf.d/wild-cloud.conf
|
||||
echo "Created /etc/systemd/resolved.conf.d/wild-cloud.conf"
|
||||
else
|
||||
echo "Found existing /etc/systemd/resolved.conf.d/wild-cloud.conf"
|
||||
fi
|
||||
chown wildcloud:wildcloud /etc/systemd/resolved.conf.d/wild-cloud.conf
|
||||
chmod 644 /etc/systemd/resolved.conf.d/wild-cloud.conf
|
||||
|
||||
# Ensure /etc/resolv.conf is a symlink to systemd-resolved stub
|
||||
# Skip in Docker/container environments where resolv.conf might be bind-mounted
|
||||
if [ ! -L /etc/resolv.conf ] || [ "$(readlink /etc/resolv.conf)" != "/run/systemd/resolve/stub-resolv.conf" ]; then
|
||||
echo "Configuring /etc/resolv.conf to use systemd-resolved stub mode"
|
||||
if [ -f /etc/resolv.conf ] && [ ! -L /etc/resolv.conf ]; then
|
||||
cp /etc/resolv.conf /etc/resolv.conf.wild-backup 2>/dev/null || echo "Note: Could not backup /etc/resolv.conf (may be read-only)"
|
||||
fi
|
||||
if rm -f /etc/resolv.conf 2>/dev/null; then
|
||||
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||
echo "Set /etc/resolv.conf to use systemd-resolved stub mode"
|
||||
else
|
||||
echo "Note: /etc/resolv.conf is read-only or busy (container environment?), skipping symlink setup"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set up HAProxy configuration directory
|
||||
mkdir -p /etc/haproxy
|
||||
if [ ! -f /etc/haproxy/haproxy.cfg ]; then
|
||||
touch /etc/haproxy/haproxy.cfg
|
||||
echo "Created /etc/haproxy/haproxy.cfg"
|
||||
fi
|
||||
chown wildcloud:wildcloud /etc/haproxy
|
||||
chown wildcloud:wildcloud /etc/haproxy/haproxy.cfg
|
||||
chmod 644 /etc/haproxy/haproxy.cfg
|
||||
|
||||
# Set up HAProxy TLS certs directory
|
||||
mkdir -p /etc/haproxy/certs
|
||||
chown wildcloud:wildcloud /etc/haproxy/certs
|
||||
chmod 700 /etc/haproxy/certs
|
||||
echo "Configured HAProxy for Wild Central management"
|
||||
|
||||
# Set up WireGuard configuration directory
|
||||
mkdir -p /etc/wireguard
|
||||
chown wildcloud:wildcloud /etc/wireguard
|
||||
chmod 700 /etc/wireguard
|
||||
if [ ! -f /etc/wireguard/wg0.conf ]; then
|
||||
touch /etc/wireguard/wg0.conf
|
||||
echo "Created /etc/wireguard/wg0.conf"
|
||||
fi
|
||||
chown wildcloud:wildcloud /etc/wireguard/wg0.conf
|
||||
chmod 600 /etc/wireguard/wg0.conf
|
||||
echo "Configured WireGuard for Wild Central management"
|
||||
|
||||
# Set up nftables configuration directory
|
||||
mkdir -p /etc/nftables.d
|
||||
if [ ! -f /etc/nftables.d/wild-cloud.nft ]; then
|
||||
touch /etc/nftables.d/wild-cloud.nft
|
||||
echo "Created /etc/nftables.d/wild-cloud.nft"
|
||||
fi
|
||||
chown wildcloud:wildcloud /etc/nftables.d/wild-cloud.nft
|
||||
chmod 644 /etc/nftables.d/wild-cloud.nft
|
||||
|
||||
# Ensure nftables.conf includes wild-cloud rules
|
||||
if [ -f /etc/nftables.conf ] && ! grep -q 'wild-cloud.nft' /etc/nftables.conf; then
|
||||
echo 'include "/etc/nftables.d/wild-cloud.nft"' >> /etc/nftables.conf
|
||||
echo "Added Wild Central nftables include to /etc/nftables.conf"
|
||||
fi
|
||||
echo "Configured nftables for Wild Central management"
|
||||
|
||||
# Install CrowdSec if not already installed
|
||||
if ! command -v cscli >/dev/null 2>&1; then
|
||||
echo "Installing CrowdSec..."
|
||||
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | os=ubuntu dist=noble bash
|
||||
apt-get install -y crowdsec
|
||||
echo "Installed CrowdSec"
|
||||
else
|
||||
echo "CrowdSec already installed: $(cscli version 2>/dev/null | head -1 || echo 'unknown')"
|
||||
fi
|
||||
|
||||
# Configure CrowdSec LAPI to listen on all interfaces
|
||||
CROWDSEC_CONFIG=/etc/crowdsec/config.yaml
|
||||
if [ -f "$CROWDSEC_CONFIG" ] && command -v yq >/dev/null 2>&1; then
|
||||
yq e -i '.api.server.listen_uri = "0.0.0.0:8080"' "$CROWDSEC_CONFIG"
|
||||
echo "Configured CrowdSec LAPI to listen on 0.0.0.0:8080"
|
||||
fi
|
||||
|
||||
# Ensure CrowdSec is running so the firewall bouncer can register with LAPI
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl start crowdsec 2>/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Install CrowdSec firewall bouncer
|
||||
if ! command -v crowdsec-firewall-bouncer >/dev/null 2>&1; then
|
||||
echo "Installing CrowdSec firewall bouncer..."
|
||||
apt-get install -y crowdsec-firewall-bouncer
|
||||
echo "Installed CrowdSec firewall bouncer"
|
||||
else
|
||||
echo "CrowdSec firewall bouncer already installed"
|
||||
fi
|
||||
|
||||
# Install Authelia if not already installed
|
||||
if ! command -v authelia >/dev/null 2>&1; then
|
||||
echo "Installing Authelia..."
|
||||
curl -fsSL https://apt.authelia.com/organization/signing.asc | \
|
||||
gpg --dearmor -o /usr/share/keyrings/authelia-archive-keyring.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/authelia-archive-keyring.gpg] \
|
||||
https://apt.authelia.com/integration/apt/repo/stable/debian debian main" \
|
||||
> /etc/apt/sources.list.d/authelia.list
|
||||
apt-get update -qq
|
||||
apt-get install -y authelia
|
||||
echo "Installed Authelia"
|
||||
else
|
||||
echo "Authelia already installed"
|
||||
fi
|
||||
|
||||
# Install HAProxy Lua scripts for Authelia auth-request forwarding
|
||||
mkdir -p /etc/haproxy/lua
|
||||
if [ ! -f /etc/haproxy/lua/haproxy-auth-request.lua ]; then
|
||||
curl -fsSL -o /etc/haproxy/lua/haproxy-auth-request.lua \
|
||||
https://raw.githubusercontent.com/TimWolla/haproxy-auth-request/main/auth-request.lua
|
||||
echo "Installed haproxy-auth-request.lua"
|
||||
fi
|
||||
if [ ! -f /etc/haproxy/lua/haproxy-lua-http.lua ]; then
|
||||
curl -fsSL -o /etc/haproxy/lua/haproxy-lua-http.lua \
|
||||
https://raw.githubusercontent.com/haproxytech/haproxy-lua-http/master/http.lua
|
||||
echo "Installed haproxy-lua-http.lua"
|
||||
fi
|
||||
apt-get install -y lua-json 2>/dev/null || true
|
||||
|
||||
# Configure Authelia systemd override to use Wild Central config path
|
||||
mkdir -p /etc/systemd/system/authelia.service.d
|
||||
cat > /etc/systemd/system/authelia.service.d/wild-central.conf << 'AUTHELIA_EOF'
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/authelia --config /var/lib/wild-central/authelia/configuration.yml
|
||||
AUTHELIA_EOF
|
||||
echo "Configured Authelia systemd override"
|
||||
|
||||
# Create Authelia data directory
|
||||
mkdir -p /var/lib/wild-central/authelia
|
||||
chown wildcloud:wildcloud /var/lib/wild-central/authelia
|
||||
chmod 700 /var/lib/wild-central/authelia
|
||||
|
||||
# Install sudoers rules for privileged operations
|
||||
cat > /etc/sudoers.d/wild-central << 'SUDOERS_EOF'
|
||||
wildcloud ALL=(ALL) NOPASSWD: /usr/sbin/nft -c -f *, /usr/sbin/nft list table inet wild-cloud, /usr/bin/cscli bouncers *, /usr/bin/cscli machines *, /usr/bin/cscli decisions *, /usr/bin/cscli alerts *, /usr/bin/cscli version, /usr/bin/wg-quick up wg0, /usr/bin/wg-quick down wg0, /usr/bin/wg show wg0, /usr/bin/certbot *, /usr/sbin/nginx -t, /usr/bin/systemctl reload nginx
|
||||
SUDOERS_EOF
|
||||
chmod 440 /etc/sudoers.d/wild-central
|
||||
echo "Installed sudoers rules for Wild Central"
|
||||
|
||||
# Install polkit rules for service management
|
||||
mkdir -p /etc/polkit-1/rules.d
|
||||
cat > /etc/polkit-1/rules.d/50-wild-central.rules << 'POLKIT_EOF'
|
||||
/* Allow wildcloud user to manage Wild Central services */
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||
(action.lookup("unit") == "dnsmasq.service" ||
|
||||
action.lookup("unit") == "systemd-resolved.service" ||
|
||||
action.lookup("unit") == "haproxy.service" ||
|
||||
action.lookup("unit") == "wild-central-nftables-reload.service" ||
|
||||
action.lookup("unit") == "crowdsec.service" ||
|
||||
action.lookup("unit") == "crowdsec-firewall-bouncer.service" ||
|
||||
action.lookup("unit") == "wg-quick@wg0.service" ||
|
||||
action.lookup("unit") == "cloudflared.service" ||
|
||||
action.lookup("unit") == "authelia.service" ||
|
||||
action.lookup("unit") == "wild-central.service") &&
|
||||
subject.user == "wildcloud") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
POLKIT_EOF
|
||||
chmod 644 /etc/polkit-1/rules.d/50-wild-central.rules
|
||||
echo "Installed polkit rules for service management"
|
||||
|
||||
# Enable nginx site (disable default site, enable wild-central)
|
||||
if [ -f /etc/nginx/sites-enabled/default ]; then
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
echo "Disabled default nginx site"
|
||||
fi
|
||||
if [ ! -L /etc/nginx/sites-enabled/wild-central ]; then
|
||||
ln -sf /etc/nginx/sites-available/wild-central /etc/nginx/sites-enabled/wild-central
|
||||
echo "Enabled wild-central nginx site"
|
||||
fi
|
||||
|
||||
# Reload nginx to pick up the new site
|
||||
if systemctl is-active --quiet nginx; then
|
||||
nginx -t && systemctl reload nginx
|
||||
echo "Reloaded nginx configuration"
|
||||
fi
|
||||
|
||||
# Enable and start the service
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload
|
||||
systemctl enable wild-central.service
|
||||
echo "wild-central configured successfully"
|
||||
echo "Start the service with: sudo systemctl start wild-central"
|
||||
echo "View logs with: sudo journalctl -u wild-central -f"
|
||||
else
|
||||
echo "Note: systemd not detected (container environment?), skipping service enable"
|
||||
echo "wild-central installed successfully"
|
||||
fi
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
80
dist/debian/DEBIAN/postrm
vendored
Normal file
80
dist/debian/DEBIAN/postrm
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
purge)
|
||||
echo "Purging wild-central configuration..."
|
||||
|
||||
# Remove polkit rules
|
||||
if [ -f /etc/polkit-1/rules.d/50-wild-central.rules ]; then
|
||||
rm -f /etc/polkit-1/rules.d/50-wild-central.rules
|
||||
echo "Removed polkit rules"
|
||||
fi
|
||||
|
||||
# Remove sudoers file
|
||||
if [ -f /etc/sudoers.d/wild-central ]; then
|
||||
rm -f /etc/sudoers.d/wild-central
|
||||
fi
|
||||
|
||||
# Remove dnsmasq configuration
|
||||
if [ -f /etc/dnsmasq.d/wild-cloud.conf ]; then
|
||||
rm -f /etc/dnsmasq.d/wild-cloud.conf
|
||||
echo "Removed dnsmasq configuration"
|
||||
fi
|
||||
|
||||
# Remove systemd-resolved configuration
|
||||
if [ -f /etc/systemd/resolved.conf.d/wild-cloud.conf ]; then
|
||||
rm -f /etc/systemd/resolved.conf.d/wild-cloud.conf
|
||||
echo "Removed systemd-resolved configuration"
|
||||
systemctl restart systemd-resolved 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Restore /etc/resolv.conf if we have a backup
|
||||
if [ -f /etc/resolv.conf.wild-backup ]; then
|
||||
echo "Restoring original /etc/resolv.conf from backup"
|
||||
rm -f /etc/resolv.conf
|
||||
mv /etc/resolv.conf.wild-backup /etc/resolv.conf
|
||||
fi
|
||||
|
||||
# Remove configuration directory
|
||||
if [ -d /etc/wild-central ]; then
|
||||
rm -rf /etc/wild-central
|
||||
fi
|
||||
|
||||
# Remove log directory
|
||||
if [ -d /var/log/wild-central ]; then
|
||||
rm -rf /var/log/wild-central
|
||||
fi
|
||||
|
||||
# Remove lib directory
|
||||
if [ -d /var/lib/wild-central ]; then
|
||||
rm -rf /var/lib/wild-central
|
||||
fi
|
||||
|
||||
# Remove nginx site
|
||||
if [ -L /etc/nginx/sites-enabled/wild-central ]; then
|
||||
rm -f /etc/nginx/sites-enabled/wild-central
|
||||
echo "Disabled wild-central nginx site"
|
||||
fi
|
||||
|
||||
# Only remove wildcloud user if wild-cloud package is not installed
|
||||
if ! dpkg -s wild-cloud >/dev/null 2>&1; then
|
||||
if id wildcloud >/dev/null 2>&1; then
|
||||
userdel wildcloud || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "wild-central purged successfully"
|
||||
;;
|
||||
|
||||
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postrm called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
28
dist/debian/DEBIAN/prerm
vendored
Normal file
28
dist/debian/DEBIAN/prerm
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
echo "Stopping wild-central service..."
|
||||
|
||||
# Stop and disable the service
|
||||
if systemctl is-active --quiet wild-central; then
|
||||
systemctl stop wild-central
|
||||
fi
|
||||
|
||||
if systemctl is-enabled --quiet wild-central; then
|
||||
systemctl disable wild-central
|
||||
fi
|
||||
;;
|
||||
|
||||
failed-upgrade)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "prerm called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
34
dist/debian/etc/nginx/sites-available/wild-central
vendored
Normal file
34
dist/debian/etc/nginx/sites-available/wild-central
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# Wild Central Management Interface
|
||||
root /var/www/html/wild-central;
|
||||
index index.html;
|
||||
|
||||
# API proxy to wild-central service
|
||||
location /api/ {
|
||||
proxy_pass http://localhost:5055;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# SSE events proxy (long-lived connections)
|
||||
location /api/v1/events {
|
||||
proxy_pass http://localhost:5055;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
|
||||
# Static files - SPA routing support
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
9
dist/debian/etc/systemd/system/wild-central-nftables-reload.service
vendored
Normal file
9
dist/debian/etc/systemd/system/wild-central-nftables-reload.service
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Apply Wild Central nftables rules
|
||||
Documentation=https://mywildcloud.org
|
||||
After=network.target nftables.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/wild-cloud.nft
|
||||
RemainAfterExit=no
|
||||
28
dist/debian/etc/systemd/system/wild-central.service
vendored
Normal file
28
dist/debian/etc/systemd/system/wild-central.service
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=Wild Central Service
|
||||
Documentation=https://mywildcloud.org
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=wildcloud
|
||||
Group=wildcloud
|
||||
ExecStart=/usr/bin/wild-central
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=WILD_CENTRAL_DATA_DIR=/var/lib/wild-central
|
||||
Environment=WILD_CENTRAL_STATIC_DIR=/var/www/html/wild-central
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=wild-central
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=full
|
||||
ProtectHome=yes
|
||||
ReadWritePaths=/etc/wild-central /var/lib/wild-central /var/log/wild-central /etc/dnsmasq.d /etc/systemd/resolved.conf.d /etc/haproxy /etc/nftables.d /etc/wireguard
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
42
dist/debian/etc/wild-central/config.yaml.example
vendored
Normal file
42
dist/debian/etc/wild-central/config.yaml.example
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
cloud:
|
||||
router:
|
||||
ip: 192.168.8.1
|
||||
dnsmasq:
|
||||
ip: 192.168.8.50
|
||||
interface: eth0
|
||||
# DHCP server (disabled by default — enable to take over DHCP from your router)
|
||||
# dhcp:
|
||||
# enabled: false
|
||||
# rangeStart: "192.168.8.100"
|
||||
# rangeEnd: "192.168.8.200"
|
||||
# leaseTime: "24h"
|
||||
# gateway: "" # defaults to cloud.router.ip
|
||||
# staticLeases: # managed via POST /api/v1/dnsmasq/dhcp/static
|
||||
# - mac: "aa:bb:cc:dd:ee:ff"
|
||||
# ip: "192.168.8.10"
|
||||
# hostname: "node1"
|
||||
|
||||
# HAProxy ingress proxy — routes external traffic by SNI.
|
||||
# Use POST /api/v1/haproxy/generate to apply after changing this config.
|
||||
haproxy:
|
||||
# Optional custom TCP routes for non-Wild Cloud services on your network.
|
||||
# customRoutes:
|
||||
# - name: civil_ssh
|
||||
# port: 2222 # external listen port
|
||||
# backend: 192.168.8.10:22
|
||||
|
||||
# nftables host firewall — tracks ports HAProxy is serving.
|
||||
# Set wanInterface to enable strict WAN filtering (drops all unrecognised inbound traffic).
|
||||
# Leave unset for default accept-all policy (safe during initial setup).
|
||||
# nftables:
|
||||
# wanInterface: eth0
|
||||
|
||||
# Dynamic DNS — updates DNS A records when your public IP changes.
|
||||
# API token goes in secrets.yaml under cloudflare.apiToken.
|
||||
# ddns:
|
||||
# enabled: false
|
||||
# provider: cloudflare
|
||||
# records:
|
||||
# - cloud.example.com
|
||||
# - cloud2.example.com
|
||||
# intervalMinutes: 5
|
||||
71
dist/scripts/build-package.sh
vendored
Executable file
71
dist/scripts/build-package.sh
vendored
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Build configuration
|
||||
VERSION="${VERSION:-0.1.0}"
|
||||
ARCH="${ARCH:-amd64}"
|
||||
BUILD_DIR="build"
|
||||
SRC_DIR="$BUILD_DIR/src"
|
||||
BINARY_NAME="wild-central"
|
||||
|
||||
# Source paths (relative to dist directory)
|
||||
API_SOURCE=".."
|
||||
WEBAPP_SOURCE="../web"
|
||||
|
||||
echo "Building Wild Central v${VERSION} for ${ARCH}"
|
||||
echo "================================================"
|
||||
|
||||
# Clean and create build directories
|
||||
echo "Preparing build directories..."
|
||||
rm -rf "$SRC_DIR"
|
||||
mkdir -p "$SRC_DIR"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
# Copy API source
|
||||
echo ""
|
||||
echo "Copying API source..."
|
||||
rsync -a --exclude='dist/' --exclude='web/' "$API_SOURCE/" "$SRC_DIR/api/"
|
||||
cd "$SRC_DIR/api"
|
||||
|
||||
# Build the daemon
|
||||
echo "Building daemon binary..."
|
||||
if [ "$ARCH" = "arm64" ]; then
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-X main.Version=$VERSION" -o "../../$BINARY_NAME" .
|
||||
elif [ "$ARCH" = "amd64" ]; then
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-X main.Version=$VERSION" -o "../../$BINARY_NAME" .
|
||||
else
|
||||
echo "Unsupported architecture: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd - > /dev/null
|
||||
echo "Daemon binary built: $BUILD_DIR/$BINARY_NAME"
|
||||
|
||||
# Copy web app source
|
||||
echo ""
|
||||
echo "Copying web app source..."
|
||||
cp -r "$WEBAPP_SOURCE" "$SRC_DIR/web"
|
||||
cd "$SRC_DIR/web"
|
||||
|
||||
# Remove any local env overrides
|
||||
rm -f .env.local .env.*.local
|
||||
|
||||
# Build the web app
|
||||
echo "Building web application..."
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# For production builds, use empty base URL so the app uses relative paths
|
||||
echo "VITE_API_BASE_URL=" > .env.production.local
|
||||
|
||||
pnpm run build --mode production || {
|
||||
echo "Build with type checking failed, trying without type check..."
|
||||
pnpm exec vite build
|
||||
}
|
||||
|
||||
cd - > /dev/null
|
||||
echo "Web app built: $SRC_DIR/web/dist/"
|
||||
|
||||
echo ""
|
||||
echo "Build complete!"
|
||||
echo " Binary: $BUILD_DIR/$BINARY_NAME"
|
||||
echo " Web App: $SRC_DIR/web/dist/"
|
||||
189
dist/scripts/create-gitea-release.sh
vendored
Executable file
189
dist/scripts/create-gitea-release.sh
vendored
Executable file
@@ -0,0 +1,189 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
GITEA_URL="$1"
|
||||
OWNER="$2"
|
||||
REPO="$3"
|
||||
TAG="$4"
|
||||
VERSION="$5"
|
||||
TOKEN="$6"
|
||||
CHANNEL="${7:-stable}" # "stable" or "edge"
|
||||
|
||||
API_URL="${GITEA_URL}/api/v1"
|
||||
PACKAGES_DIR="dist/packages"
|
||||
BINARIES_DIR="dist/bin"
|
||||
SKIP_RELEASE_CREATION=false
|
||||
|
||||
echo "Release configuration:"
|
||||
echo " Repository: ${OWNER}/${REPO}"
|
||||
echo " Tag: ${TAG}"
|
||||
echo " Version: ${VERSION}"
|
||||
echo ""
|
||||
|
||||
# Check if release already exists
|
||||
echo "Checking if release ${TAG} exists..."
|
||||
RELEASE_ID=$(curl -s -H "Authorization: token ${TOKEN}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}" \
|
||||
| jq -r '.id // empty')
|
||||
|
||||
if [ -n "$RELEASE_ID" ]; then
|
||||
echo "Release ${TAG} exists (ID: ${RELEASE_ID})"
|
||||
echo "Updating existing release assets..."
|
||||
|
||||
# Get existing assets
|
||||
EXISTING_ASSETS=$(curl -s -H "Authorization: token ${TOKEN}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases/${RELEASE_ID}/assets" \
|
||||
| jq -r '.[].id')
|
||||
|
||||
# Delete old assets
|
||||
for ASSET_ID in $EXISTING_ASSETS; do
|
||||
ASSET_NAME=$(curl -s -H "Authorization: token ${TOKEN}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases/${RELEASE_ID}/assets/${ASSET_ID}" \
|
||||
| jq -r '.name')
|
||||
|
||||
if [[ "$ASSET_NAME" == *.deb ]] || [[ "$ASSET_NAME" == wild-central-* ]] || [[ "$ASSET_NAME" == "SHA256SUMS" ]]; then
|
||||
echo " Deleting old asset: ${ASSET_NAME}"
|
||||
curl -s -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases/${RELEASE_ID}/assets/${ASSET_ID}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Cleaned up old package assets"
|
||||
SKIP_RELEASE_CREATION=true
|
||||
fi
|
||||
|
||||
# Extract changelog section for this version
|
||||
CHANGELOG_SECTION=""
|
||||
if [ -f "../CHANGELOG.md" ]; then
|
||||
CHANGELOG_SECTION=$(awk "/^## \[${VERSION}\]/{found=1; next} /^## \[/{if(found) exit} found{print}" ../CHANGELOG.md | sed '/^$/d')
|
||||
fi
|
||||
|
||||
INSTALL_DOCS="### Installation
|
||||
|
||||
#### Full Installation (.deb package)
|
||||
|
||||
Download the appropriate .deb package for your architecture:
|
||||
|
||||
- **arm64**: \`wild-central_${VERSION}_arm64.deb\` — Raspberry Pi 4/5, ARM servers
|
||||
- **amd64**: \`wild-central_${VERSION}_amd64.deb\` — x86_64 systems
|
||||
|
||||
\`\`\`bash
|
||||
wget ${GITEA_URL}/${OWNER}/${REPO}/releases/download/v${VERSION}/wild-central_${VERSION}_amd64.deb
|
||||
sudo dpkg -i wild-central_${VERSION}_amd64.deb
|
||||
sudo apt-get install -f
|
||||
sudo systemctl enable --now wild-central
|
||||
\`\`\`
|
||||
|
||||
#### Standalone Binary
|
||||
|
||||
- \`wild-central-{arch}\` — Daemon binary only (Docker/K8s)
|
||||
|
||||
#### Verification
|
||||
|
||||
\`\`\`bash
|
||||
wget ${GITEA_URL}/${OWNER}/${REPO}/releases/download/v${VERSION}/SHA256SUMS
|
||||
sha256sum -c SHA256SUMS
|
||||
\`\`\`"
|
||||
|
||||
if [ -n "$CHANGELOG_SECTION" ]; then
|
||||
BODY="${CHANGELOG_SECTION}
|
||||
|
||||
---
|
||||
|
||||
${INSTALL_DOCS}"
|
||||
else
|
||||
BODY="${INSTALL_DOCS}"
|
||||
fi
|
||||
|
||||
# Set release name and prerelease flag based on channel
|
||||
if [ "$CHANNEL" = "edge" ]; then
|
||||
RELEASE_NAME="Wild Central (edge) — ${VERSION}"
|
||||
IS_PRERELEASE=true
|
||||
else
|
||||
RELEASE_NAME="Wild Central ${VERSION}"
|
||||
IS_PRERELEASE=false
|
||||
fi
|
||||
|
||||
# Create new release if it doesn't exist
|
||||
if [ "$SKIP_RELEASE_CREATION" != "true" ]; then
|
||||
echo "Creating new release ${TAG}..."
|
||||
RELEASE_DATA=$(jq -n \
|
||||
--arg tag "${TAG}" \
|
||||
--arg name "${RELEASE_NAME}" \
|
||||
--arg body "${BODY}" \
|
||||
--argjson prerelease "${IS_PRERELEASE}" \
|
||||
'{"tag_name": $tag, "name": $name, "body": $body, "draft": false, "prerelease": $prerelease}')
|
||||
|
||||
RELEASE_RESPONSE=$(curl -s -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${RELEASE_DATA}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases")
|
||||
|
||||
RELEASE_ID=$(echo "${RELEASE_RESPONSE}" | jq -r '.id')
|
||||
|
||||
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
|
||||
echo "Failed to create release"
|
||||
echo "${RELEASE_RESPONSE}" | jq .
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Created release ${TAG} (ID: ${RELEASE_ID})"
|
||||
fi
|
||||
|
||||
# Generate checksums
|
||||
echo ""
|
||||
echo "Generating checksums..."
|
||||
cd dist
|
||||
sha256sum packages/*.deb bin/wild-central-* > SHA256SUMS
|
||||
echo "Created SHA256SUMS"
|
||||
cd ..
|
||||
|
||||
# Upload artifacts
|
||||
echo ""
|
||||
echo "Uploading release artifacts..."
|
||||
|
||||
upload_file() {
|
||||
local FILE="$1"
|
||||
local FILENAME=$(basename "$FILE")
|
||||
|
||||
echo " Uploading ${FILENAME}..."
|
||||
|
||||
UPLOAD_RESPONSE=$(curl -s -X POST \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-F "attachment=@${FILE}" \
|
||||
"${API_URL}/repos/${OWNER}/${REPO}/releases/${RELEASE_ID}/assets?name=${FILENAME}")
|
||||
|
||||
ASSET_ID=$(echo "${UPLOAD_RESPONSE}" | jq -r '.id')
|
||||
|
||||
if [ -z "$ASSET_ID" ] || [ "$ASSET_ID" = "null" ]; then
|
||||
echo " Failed to upload ${FILENAME}"
|
||||
echo "${UPLOAD_RESPONSE}" | jq .
|
||||
return 1
|
||||
else
|
||||
echo " Uploaded ${FILENAME}"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Upload .deb packages
|
||||
for DEB in ${PACKAGES_DIR}/wild-central_${VERSION}_*.deb; do
|
||||
if [ ! -f "$DEB" ]; then
|
||||
echo "No .deb files found for version ${VERSION} in ${PACKAGES_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
upload_file "$DEB"
|
||||
done
|
||||
|
||||
# Upload standalone daemon binaries
|
||||
for BINARY in ${BINARIES_DIR}/wild-central-*; do
|
||||
if [ -f "$BINARY" ]; then
|
||||
upload_file "$BINARY"
|
||||
fi
|
||||
done
|
||||
|
||||
# Upload checksums
|
||||
upload_file "dist/SHA256SUMS"
|
||||
|
||||
echo ""
|
||||
echo "Release complete!"
|
||||
echo "View at: ${GITEA_URL}/${OWNER}/${REPO}/releases/tag/${TAG}"
|
||||
138
dist/scripts/deploy-apt-repository.sh
vendored
Executable file
138
dist/scripts/deploy-apt-repository.sh
vendored
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Required environment variables:
|
||||
# APTLY_URL - Aptly server URL (e.g. https://aptly.cloud2.payne.io)
|
||||
# APTLY_PASS - Aptly API password
|
||||
# Optional:
|
||||
# APTLY_USER - API username (default: aptly)
|
||||
# APTLY_GPG_KEY - GPG key fingerprint for signing; if unset, signing is skipped
|
||||
|
||||
APTLY_URL="${APTLY_URL:?APTLY_URL is required}"
|
||||
APTLY_PASS="${APTLY_PASS:?APTLY_PASS is required}"
|
||||
APTLY_USER="${APTLY_USER:-aptly}"
|
||||
APTLY_GPG_KEY="${APTLY_GPG_KEY:-}"
|
||||
|
||||
VERSION=$(cat ../VERSION 2>/dev/null || echo "0.0.0-dev")
|
||||
REPO_NAME="wild-central"
|
||||
DIST="${DIST:-stable}"
|
||||
COMPONENT="main"
|
||||
PACKAGE_DIR="dist/packages"
|
||||
SNAP_NAME="${REPO_NAME}-${VERSION}"
|
||||
AUTH="${APTLY_USER}:${APTLY_PASS}"
|
||||
|
||||
echo "Deploying Wild Central ${VERSION} to APT repository..."
|
||||
echo " URL: ${APTLY_URL}"
|
||||
|
||||
# Signing config
|
||||
if [ -n "$APTLY_GPG_KEY" ]; then
|
||||
SIGNING="{\"GpgKey\":\"${APTLY_GPG_KEY}\"}"
|
||||
else
|
||||
echo "APTLY_GPG_KEY not set — publishing without GPG signature"
|
||||
SIGNING="{\"Skip\":true}"
|
||||
fi
|
||||
|
||||
api() {
|
||||
local METHOD="$1"
|
||||
local ENDPOINT="$2"
|
||||
local DATA="${3:-}"
|
||||
if [ -n "$DATA" ]; then
|
||||
curl -sf --http1.1 -u "$AUTH" -X "$METHOD" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "$DATA" \
|
||||
"${APTLY_URL}/api${ENDPOINT}"
|
||||
else
|
||||
curl -sf --http1.1 -u "$AUTH" -X "$METHOD" "${APTLY_URL}/api${ENDPOINT}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Ensure local repo exists
|
||||
echo ""
|
||||
echo "Ensuring repository '${REPO_NAME}' exists..."
|
||||
if ! api GET "/repos/${REPO_NAME}" > /dev/null 2>&1; then
|
||||
api POST "/repos" \
|
||||
"{\"Name\":\"${REPO_NAME}\",\"DefaultDistribution\":\"${DIST}\",\"DefaultComponent\":\"${COMPONENT}\"}" \
|
||||
> /dev/null
|
||||
echo " Created '${REPO_NAME}'"
|
||||
else
|
||||
echo " '${REPO_NAME}' already exists"
|
||||
fi
|
||||
|
||||
# 2. Upload .deb files
|
||||
echo ""
|
||||
echo "Uploading packages..."
|
||||
UPLOAD_DIR="${REPO_NAME}-${VERSION}"
|
||||
FOUND=0
|
||||
for DEB in "${PACKAGE_DIR}"/wild-central_"${VERSION}"_*.deb; do
|
||||
[ -f "$DEB" ] || continue
|
||||
FOUND=1
|
||||
FILENAME=$(basename "$DEB")
|
||||
echo " ${FILENAME}..."
|
||||
curl -sf --http1.1 -u "$AUTH" -X POST \
|
||||
-F "file=@${DEB}" \
|
||||
"${APTLY_URL}/api/files/${UPLOAD_DIR}" > /dev/null
|
||||
done
|
||||
if [ "$FOUND" -eq 0 ]; then
|
||||
echo "No .deb files found for version ${VERSION} in ${PACKAGE_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
echo " Uploaded"
|
||||
|
||||
# 3. Add packages to repo
|
||||
echo ""
|
||||
echo "Adding packages to '${REPO_NAME}'..."
|
||||
api POST "/repos/${REPO_NAME}/file/${UPLOAD_DIR}" > /dev/null
|
||||
echo " Added"
|
||||
|
||||
# 4. Clean up uploaded files from aptly's staging area
|
||||
api DELETE "/files/${UPLOAD_DIR}" > /dev/null 2>&1 || true
|
||||
|
||||
# 5. Create snapshot for this version
|
||||
echo ""
|
||||
echo "Snapshotting as '${SNAP_NAME}'..."
|
||||
if api GET "/snapshots/${SNAP_NAME}" > /dev/null 2>&1; then
|
||||
echo " Snapshot '${SNAP_NAME}' already exists, using as-is"
|
||||
else
|
||||
api POST "/repos/${REPO_NAME}/snapshots" \
|
||||
"{\"Name\":\"${SNAP_NAME}\",\"Description\":\"Wild Central ${VERSION}\"}" \
|
||||
> /dev/null
|
||||
echo " Snapshot '${SNAP_NAME}' created"
|
||||
fi
|
||||
|
||||
# 6. Publish or update
|
||||
echo ""
|
||||
PUBLISH_PREFIX=$(api GET "/publish" \
|
||||
| jq -r ".[] | select(.Distribution == \"${DIST}\") | .Prefix" 2>/dev/null \
|
||||
| head -1)
|
||||
|
||||
UPDATE_BODY="{\"Snapshots\":[{\"Component\":\"${COMPONENT}\",\"Name\":\"${SNAP_NAME}\"}],\"Signing\":${SIGNING},\"ForceOverwrite\":true}"
|
||||
|
||||
if [ -z "$PUBLISH_PREFIX" ]; then
|
||||
echo "Publishing '${DIST}' for the first time..."
|
||||
api POST "/publish" \
|
||||
"{\"SourceKind\":\"snapshot\",\"Sources\":[{\"Component\":\"${COMPONENT}\",\"Name\":\"${SNAP_NAME}\"}],\"Distribution\":\"${DIST}\",\"Architectures\":[\"amd64\",\"arm64\"],\"Signing\":${SIGNING}}" \
|
||||
> /dev/null
|
||||
else
|
||||
echo "Updating published '${DIST}'..."
|
||||
URL_PREFIX=$(echo "$PUBLISH_PREFIX" | sed 's/^\.$/:/')
|
||||
api PUT "/publish/${URL_PREFIX}/${DIST}" "$UPDATE_BODY" > /dev/null
|
||||
fi
|
||||
echo " Published"
|
||||
|
||||
echo ""
|
||||
echo "Wild Central ${VERSION} is live."
|
||||
echo ""
|
||||
echo "To install on a new device:"
|
||||
echo ""
|
||||
echo " curl -fsSL ${APTLY_URL}/wild-central.gpg \\"
|
||||
echo " | sudo tee /usr/share/keyrings/wild-central.gpg > /dev/null"
|
||||
echo ""
|
||||
echo " sudo tee /etc/apt/sources.list.d/wild-central.sources <<EOF"
|
||||
echo " Types: deb"
|
||||
echo " URIs: ${APTLY_URL}"
|
||||
echo " Suites: ${DIST}"
|
||||
echo " Components: ${COMPONENT}"
|
||||
echo " Signed-By: /usr/share/keyrings/wild-central.gpg"
|
||||
echo " EOF"
|
||||
echo ""
|
||||
echo " sudo apt update && sudo apt install wild-central"
|
||||
49
dist/scripts/package-deb.sh
vendored
Executable file
49
dist/scripts/package-deb.sh
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
ARCH="$1"
|
||||
BINARY_PATH="$2"
|
||||
VERSION="$3"
|
||||
|
||||
BUILD_DIR="build"
|
||||
DIST_DIR="dist"
|
||||
DEB_DIR="debian-package"
|
||||
WEB_DIST="${BUILD_DIR}/src/web/dist"
|
||||
|
||||
echo "Creating .deb package for ${ARCH}..."
|
||||
|
||||
# Create directories
|
||||
mkdir -p "${DIST_DIR}/bin" "${DIST_DIR}/packages"
|
||||
|
||||
# Copy debian package structure
|
||||
cp -r debian/ "${BUILD_DIR}/${DEB_DIR}-${ARCH}/"
|
||||
|
||||
# Copy binary to correct location
|
||||
mkdir -p "${BUILD_DIR}/${DEB_DIR}-${ARCH}/usr/bin"
|
||||
cp "${BINARY_PATH}" "${BUILD_DIR}/${DEB_DIR}-${ARCH}/usr/bin/wild-central"
|
||||
|
||||
# Copy static web files from built web app
|
||||
mkdir -p "${BUILD_DIR}/${DEB_DIR}-${ARCH}/var/www/html/wild-central"
|
||||
if [ -d "${WEB_DIST}" ]; then
|
||||
cp -r "${WEB_DIST}"/* "${BUILD_DIR}/${DEB_DIR}-${ARCH}/var/www/html/wild-central/"
|
||||
echo "Copied web app files"
|
||||
else
|
||||
echo "Warning: Web app dist not found at ${WEB_DIST}"
|
||||
fi
|
||||
|
||||
# Set script permissions
|
||||
chmod 755 "${BUILD_DIR}/${DEB_DIR}-${ARCH}/DEBIAN/postinst"
|
||||
chmod 755 "${BUILD_DIR}/${DEB_DIR}-${ARCH}/DEBIAN/prerm"
|
||||
chmod 755 "${BUILD_DIR}/${DEB_DIR}-${ARCH}/DEBIAN/postrm"
|
||||
|
||||
# Substitute placeholders in control file
|
||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" "${BUILD_DIR}/${DEB_DIR}-${ARCH}/DEBIAN/control"
|
||||
sed -i "s/ARCH_PLACEHOLDER/${ARCH}/g" "${BUILD_DIR}/${DEB_DIR}-${ARCH}/DEBIAN/control"
|
||||
|
||||
# Build package and copy to dist directories
|
||||
dpkg-deb --root-owner-group --build "${BUILD_DIR}/${DEB_DIR}-${ARCH}" "${BUILD_DIR}/wild-central_${VERSION}_${ARCH}.deb"
|
||||
cp "${BINARY_PATH}" "${DIST_DIR}/bin/wild-central-${ARCH}"
|
||||
cp "${BUILD_DIR}/wild-central_${VERSION}_${ARCH}.deb" "${DIST_DIR}/packages/"
|
||||
|
||||
echo "Package created: ${DIST_DIR}/packages/wild-central_${VERSION}_${ARCH}.deb"
|
||||
echo "Daemon binary copied: ${DIST_DIR}/bin/wild-central-${ARCH}"
|
||||
@@ -1,6 +1,6 @@
|
||||
# Building Wild App
|
||||
# Building Wild Central Web App
|
||||
|
||||
This document describes the architecture and tooling used to build the Wild App, the web-based interface for managing Wild Cloud instances, hosted on Wild Central.
|
||||
This document describes the architecture and tooling used to build the Wild Central web app, the browser-based interface for managing Wild Central and its connected services.
|
||||
|
||||
## Principles
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Wild Cloud Web App
|
||||
# Wild Central Web App
|
||||
|
||||
The Wild Cloud Web App is a web-based interface for managing Wild Cloud instances. It allows users to view and control their Wild Cloud environments, including deploying applications, monitoring resources, and configuring settings.
|
||||
The Wild Central Web App is a web-based interface for managing Wild Central and its connected services. It allows users to configure networking (DNS, gateway, VPN, firewall, TLS), manage Wild Cloud instances, deploy applications, and monitor resources.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting the web app, ensure the Wild Central API is running:
|
||||
|
||||
```bash
|
||||
cd ../api
|
||||
cd ..
|
||||
make dev
|
||||
```
|
||||
|
||||
@@ -57,11 +57,9 @@ pnpm run check # Run lint, type-check, and tests
|
||||
|
||||
### `VITE_API_BASE_URL`
|
||||
|
||||
The base URL of the Wild Central API server.
|
||||
The base URL of the Wild Central API.
|
||||
|
||||
- **Default:** `http://localhost:5055`
|
||||
- **Example:** `http://192.168.1.100:5055`
|
||||
- **Usage:** Set in `.env` file (see `.env.example` for template)
|
||||
|
||||
This variable is used by the API client to connect to the Wild Central API. If not set, it defaults to `http://localhost:5055`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user