Kieran's opinionated (and probably slightly dumb) nix config

feat: add more stats and saving and loading configs for bore

dunkirk.sh 1a03933b 9ac7236a

verified
Changed files
+231 -20
modules
home
apps
nixos
+168 -15
modules/home/apps/frpc.nix
···
cfg = config.atelier.bore;
bore = pkgs.writeShellScriptBin "bore" ''
+
CONFIG_FILE="bore.toml"
+
# Check for flags
if [ "$1" = "--list" ] || [ "$1" = "-l" ]; then
${pkgs.gum}/bin/gum style --bold --foreground 212 "Active tunnels"
···
exit 0
fi
+
if [ "$1" = "--saved" ] || [ "$1" = "-s" ]; then
+
if [ ! -f "$CONFIG_FILE" ]; then
+
${pkgs.gum}/bin/gum style --foreground 117 "No bore.toml found in current directory"
+
exit 0
+
fi
+
+
${pkgs.gum}/bin/gum style --bold --foreground 212 "Saved tunnels in bore.toml"
+
echo
+
+
# Parse TOML and show tunnels
+
while IFS= read -r line; do
+
if [[ "$line" =~ ^\[([^]]+)\] ]]; then
+
current_tunnel="''${BASH_REMATCH[1]}"
+
elif [[ "$line" =~ ^port[[:space:]]*=[[:space:]]*([0-9]+) ]]; then
+
port="''${BASH_REMATCH[1]}"
+
elif [[ "$line" =~ ^label[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then
+
label="''${BASH_REMATCH[1]}"
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ $current_tunnel → localhost:$port [$label]"
+
label=""
+
elif [[ -z "$line" ]] && [[ -n "$current_tunnel" ]] && [[ -n "$port" ]]; then
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ $current_tunnel → localhost:$port"
+
current_tunnel=""
+
port=""
+
fi
+
done < "$CONFIG_FILE"
+
+
# Handle last entry if file doesn't end with blank line
+
if [[ -n "$current_tunnel" ]] && [[ -n "$port" ]]; then
+
if [[ -n "$label" ]]; then
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ $current_tunnel → localhost:$port [$label]"
+
else
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ $current_tunnel → localhost:$port"
+
fi
+
fi
+
exit 0
+
fi
+
# Get subdomain
if [ -n "$1" ]; then
subdomain="$1"
else
-
${pkgs.gum}/bin/gum style --bold --foreground 212 "Creating bore tunnel"
-
echo
-
subdomain=$(${pkgs.gum}/bin/gum input --placeholder "myapp" --prompt "Subdomain: ")
-
if [ -z "$subdomain" ]; then
-
${pkgs.gum}/bin/gum style --foreground 196 "No subdomain provided"
-
exit 1
+
# Check if we have a bore.toml in current directory
+
if [ -f "$CONFIG_FILE" ]; then
+
# Count tunnels in TOML
+
tunnel_count=$(${pkgs.gnugrep}/bin/grep -c '^\[' "$CONFIG_FILE" 2>/dev/null || echo "0")
+
+
if [ "$tunnel_count" -gt 0 ]; then
+
${pkgs.gum}/bin/gum style --bold --foreground 212 "Creating bore tunnel"
+
echo
+
+
# Show choice between new or saved
+
choice=$(${pkgs.gum}/bin/gum choose "New tunnel" "Use saved tunnel")
+
+
if [ "$choice" = "Use saved tunnel" ]; then
+
# Extract tunnel names from TOML
+
saved_names=$(${pkgs.gnugrep}/bin/grep '^\[' "$CONFIG_FILE" | ${pkgs.gnused}/bin/sed 's/^\[\(.*\)\]$/\1/')
+
subdomain=$(echo "$saved_names" | ${pkgs.gum}/bin/gum choose)
+
+
if [ -z "$subdomain" ]; then
+
${pkgs.gum}/bin/gum style --foreground 196 "No tunnel selected"
+
exit 1
+
fi
+
+
# Parse TOML for this tunnel's config
+
in_section=false
+
while IFS= read -r line; do
+
if [[ "$line" =~ ^\[([^]]+)\] ]]; then
+
if [[ "''${BASH_REMATCH[1]}" = "$subdomain" ]]; then
+
in_section=true
+
else
+
in_section=false
+
fi
+
elif [[ "$in_section" = true ]]; then
+
if [[ "$line" =~ ^port[[:space:]]*=[[:space:]]*([0-9]+) ]]; then
+
port="''${BASH_REMATCH[1]}"
+
elif [[ "$line" =~ ^label[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then
+
label="''${BASH_REMATCH[1]}"
+
fi
+
fi
+
done < "$CONFIG_FILE"
+
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ Loaded from bore.toml: $subdomain → localhost:$port''${label:+ [$label]}"
+
else
+
# New tunnel
+
subdomain=$(${pkgs.gum}/bin/gum input --placeholder "myapp" --prompt "Subdomain: ")
+
if [ -z "$subdomain" ]; then
+
${pkgs.gum}/bin/gum style --foreground 196 "No subdomain provided"
+
exit 1
+
fi
+
fi
+
else
+
${pkgs.gum}/bin/gum style --bold --foreground 212 "Creating bore tunnel"
+
echo
+
subdomain=$(${pkgs.gum}/bin/gum input --placeholder "myapp" --prompt "Subdomain: ")
+
if [ -z "$subdomain" ]; then
+
${pkgs.gum}/bin/gum style --foreground 196 "No subdomain provided"
+
exit 1
+
fi
+
fi
+
else
+
${pkgs.gum}/bin/gum style --bold --foreground 212 "Creating bore tunnel"
+
echo
+
subdomain=$(${pkgs.gum}/bin/gum input --placeholder "myapp" --prompt "Subdomain: ")
+
if [ -z "$subdomain" ]; then
+
${pkgs.gum}/bin/gum style --foreground 196 "No subdomain provided"
+
exit 1
+
fi
fi
fi
···
exit 1
fi
-
# Get port
-
if [ -n "$2" ]; then
-
port="$2"
-
else
-
port=$(${pkgs.gum}/bin/gum input --placeholder "8000" --prompt "Local port: ")
-
if [ -z "$port" ]; then
-
${pkgs.gum}/bin/gum style --foreground 196 "No port provided"
-
exit 1
+
# Get port (skip if loaded from saved config)
+
if [ -z "$port" ]; then
+
if [ -n "$2" ]; then
+
port="$2"
+
else
+
port=$(${pkgs.gum}/bin/gum input --placeholder "8000" --prompt "Local port: ")
+
if [ -z "$port" ]; then
+
${pkgs.gum}/bin/gum style --foreground 196 "No port provided"
+
exit 1
+
fi
fi
fi
···
exit 1
fi
+
# Get optional label and save flag (skip if loaded from saved config)
+
save_config=false
+
if [ -z "$label" ]; then
+
shift 2 2>/dev/null || true
+
while [[ $# -gt 0 ]]; do
+
case "$1" in
+
--label|-l)
+
label="$2"
+
shift 2
+
;;
+
--save)
+
save_config=true
+
shift
+
;;
+
*)
+
shift
+
;;
+
esac
+
done
+
fi
+
# Check if local port is accessible
if ! ${pkgs.netcat}/bin/nc -z 127.0.0.1 "$port" 2>/dev/null; then
${pkgs.gum}/bin/gum style --foreground 214 "! Warning: Nothing listening on localhost:$port"
fi
+
# Save configuration if requested
+
if [ "$save_config" = true ]; then
+
# Check if tunnel already exists in TOML
+
if [ -f "$CONFIG_FILE" ] && ${pkgs.gnugrep}/bin/grep -q "^\[$subdomain\]" "$CONFIG_FILE"; then
+
# Update existing entry
+
${pkgs.gnused}/bin/sed -i "/^\[$subdomain\]/,/^\[/{
+
s/^port[[:space:]]*=.*/port = $port/
+
''${label:+s/^label[[:space:]]*=.*/label = \"$label\"/}
+
}" "$CONFIG_FILE"
+
else
+
# Append new entry
+
{
+
echo ""
+
echo "[$subdomain]"
+
echo "port = $port"
+
if [ -n "$label" ]; then
+
echo "label = \"$label\""
+
fi
+
} >> "$CONFIG_FILE"
+
fi
+
+
${pkgs.gum}/bin/gum style --foreground 35 "✓ Configuration saved to bore.toml"
+
echo
+
fi
+
# Create config file
config_file=$(${pkgs.coreutils}/bin/mktemp)
trap "${pkgs.coreutils}/bin/rm -f $config_file" EXIT
+
# Encode label into proxy name if provided (format: subdomain[label])
+
proxy_name="$subdomain"
+
if [ -n "$label" ]; then
+
proxy_name="''${subdomain}[''${label}]"
+
fi
+
${pkgs.coreutils}/bin/cat > $config_file <<EOF
serverAddr = "${cfg.serverAddr}"
serverPort = ${toString cfg.serverPort}
···
auth.tokenSource.file.path = "${cfg.authTokenFile}"
[[proxies]]
-
name = "$subdomain"
+
name = "$proxy_name"
type = "http"
localIP = "127.0.0.1"
localPort = $port
+13
modules/nixos/services/bore/bore.toml.example
···
+
# bore tunnel configuration
+
# Save this file as "bore.toml" in your project directory
+
+
[myapp]
+
port = 8000
+
+
[api]
+
port = 3000
+
label = "dev"
+
+
[frontend]
+
port = 5173
+
label = "local"
+50 -5
modules/nixos/services/bore/dashboard.html
···
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bore</title>
+
<meta name="description" content="bore - secure tunneling service for exposing local services to the internet">
+
<meta property="og:title" content="bore - tunnel dashboard">
+
<meta property="og:description" content="secure tunneling service powered by frp on bore.dunkirk.sh">
+
<meta property="og:type" content="website">
+
<meta property="og:url" content="https://bore.dunkirk.sh">
+
<meta name="twitter:card" content="summary">
+
<meta name="twitter:title" content="bore - tunnel dashboard">
+
<meta name="twitter:description" content="secure tunneling service powered by frp on bore.dunkirk.sh">
<link rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🚇</text></svg>">
<style>
···
color: #e6edf3;
font-weight: 600;
margin-bottom: 0.25rem;
+
display: flex;
+
align-items: center;
+
gap: 0.5rem;
+
}
+
+
.tunnel-label {
+
display: inline-block;
+
padding: 0.125rem 0.5rem;
+
background: rgba(251, 146, 60, 0.2);
+
color: #fb923c;
+
border: 1px solid #fb923c;
+
font-size: 0.7rem;
+
font-weight: 500;
+
border-radius: 0;
}
.tunnel-url {
···
<div class="stat-value" id="activeTunnels">—</div>
</div>
<div class="stat-card">
+
<div class="stat-label">active connections</div>
+
<div class="stat-value" id="totalConnections">—</div>
+
</div>
+
<div class="stat-card">
<div class="stat-label">server status</div>
<div class="stat-value orange" id="serverStatus">—</div>
</div>
<div class="stat-card">
-
<div class="stat-label">active connections</div>
-
<div class="stat-value" id="totalConnections">—</div>
+
<div class="stat-label">total upload</div>
+
<div class="stat-value" id="totalUpload">—</div>
+
</div>
+
<div class="stat-card">
+
<div class="stat-label">total download</div>
+
<div class="stat-value" id="totalDownload">—</div>
</div>
</div>
···
document.getElementById('activeTunnels').textContent = serverData.clientCounts || 0;
document.getElementById('serverStatus').textContent = 'online';
document.getElementById('totalConnections').textContent = serverData.curConns || 0;
+
document.getElementById('totalUpload').textContent = formatBytes(serverData.totalTrafficOut || 0);
+
document.getElementById('totalDownload').textContent = formatBytes(serverData.totalTrafficIn || 0);
// Update page title
const tunnelCount = serverData.clientCounts || 0;
···
html += onlineTunnels.map(proxy => {
const subdomain = proxy.conf?.subdomain || 'unknown';
const url = `https://${subdomain}.bore.dunkirk.sh`;
+
+
// Parse label from proxy name (format: subdomain[label])
+
const labelMatch = proxy.name.match(/\[([^\]]+)\]$/);
+
const label = labelMatch ? labelMatch[1] : null;
+
const displayName = label ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
return `
<div class="tunnel" data-tunnel="${proxy.name}">
<div class="tunnel-info">
-
<div class="tunnel-name">${proxy.name || 'unnamed'}</div>
+
<div class="tunnel-name">
+
${displayName || 'unnamed'}
+
${label ? `<span class="tunnel-label">${label}</span>` : ''}
+
</div>
<div class="tunnel-url">
<a href="${url}" target="_blank">${url}</a>
</div>
···
html += '<div class="offline-tunnels">';
html += '<div style="color: #8b949e; font-size: 0.85rem; margin-bottom: 0.75rem;">recently disconnected</div>';
html += offlineTunnels.map(proxy => {
+
// Parse label from proxy name (format: subdomain[label])
+
const labelMatch = proxy.name.match(/\[([^\]]+)\]$/);
+
const label = labelMatch ? labelMatch[1] : null;
+
const displayName = label ? proxy.name.replace(/\[[^\]]+\]$/, '') : proxy.name;
+
if (!proxy.conf) {
return `
<div class="offline-tunnel" data-tunnel="${proxy.name}">
-
<span class="offline-tunnel-name">${proxy.name || 'unnamed'}</span>
+
<span class="offline-tunnel-name">${displayName || 'unnamed'}${label ? ` [${label}]` : ''}</span>
<span class="offline-tunnel-stats">in: <span data-traffic-in="${proxy.name}">0 B</span> • out: <span data-traffic-out="${proxy.name}">0 B</span></span>
</div>
`;
···
const url = `https://${subdomain}.bore.dunkirk.sh`;
return `
<div class="offline-tunnel" data-tunnel="${proxy.name}">
-
<span class="offline-tunnel-name">${proxy.name || 'unnamed'} → ${url}</span>
+
<span class="offline-tunnel-name">${displayName || 'unnamed'}${label ? ` [${label}]` : ''} → ${url}</span>
<span class="offline-tunnel-stats">in: <span data-traffic-in="${proxy.name}">0 B</span> • out: <span data-traffic-out="${proxy.name}">0 B</span></span>
</div>
`;