feat: dns server

This commit is contained in:
Shahrad Elahi 2024-05-29 18:59:49 +03:30
parent ae787625b9
commit 06a0a3008b
No known key found for this signature in database
24 changed files with 386 additions and 165 deletions

View File

@ -0,0 +1,5 @@
---
"wireadmin": major
---
feat: Creates a Dnsmasq server on port 53 and forwards DNS queries through the Tor network.

View File

@ -7,17 +7,15 @@ ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apk update \
&& apk upgrade \
&& apk add -U --no-cache \
iptables net-tools \
screen logrotate bash \
wireguard-tools \
dnsmasq \
tor \
&& rm -rf /var/cache/apk/*
FROM node as base
RUN apk add -U --no-cache \
iptables net-tools \
screen bash \
wireguard-tools \
tor \
&& rm -rf /var/cache/apk/*
FROM base AS build
FROM node AS build
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
@ -29,28 +27,28 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
&& mv build /tmp/build \
&& rm -rf ./*
FROM base
FROM node
WORKDIR /app
COPY --from=tor /usr/local/bin/lyrebird /usr/local/bin/lyrebird
COPY rootfs /
COPY /config/torrc.template /etc/tor/torrc.template
# Base env
ENV PROTOCOL_HEADER=x-forwarded-proto
ENV HOST_HEADER=x-forwarded-host
ENV NODE_ENV=production
ENV LOG_LEVEL=error
# Copy the goods from the build stage
# Copy the goodies from the build stage
COPY --from=build /tmp/package.json package.json
COPY --from=build /tmp/node_modules node_modules
COPY --from=build /tmp/build build
# Fix permissions
RUN mkdir -p /data && chmod 700 /data
RUN mkdir -p /etc/tor/torrc.d && chmod -R 400 /etc/tor/torrc.d
RUN mkdir -p /var/log/wireadmin && touch /var/log/wireadmin/web.log
RUN mkdir -p /data/ && chmod 700 /data/
RUN mkdir -p /etc/tor/torrc.d/ && chmod -R 400 /etc/tor/
RUN mkdir -p /var/log/wireadmin/ && touch /var/log/wireadmin/web.log
RUN echo '* * * * * /usr/bin/env logrotate /etc/logrotate.d/rotator' > /etc/crontabs/root
# Setup entrypoint
COPY docker-entrypoint.sh /entrypoint.sh

View File

@ -7,18 +7,15 @@ ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apk update \
&& apk upgrade \
&& apk add -U --no-cache \
iptables net-tools \
screen logrotate bash \
wireguard-tools \
dnsmasq \
tor \
&& rm -rf /var/cache/apk/*
FROM node as base
RUN apk add -U --no-cache \
iptables net-tools \
screen logrotate vim bash \
wireguard-tools \
tor &&\
# Clear APK cache
rm -rf /var/cache/apk/*
FROM base
FROM node
WORKDIR /app
# Setup Pnpm
@ -32,15 +29,12 @@ ENV PROTOCOL_HEADER=x-forwarded-proto
ENV HOST_HEADER=x-forwarded-host
COPY --from=tor /usr/local/bin/lyrebird /usr/local/bin/lyrebird
# Copy Tor Configs
COPY /config/torrc.template /etc/tor/torrc.template
COPY /config/obfs4-bridges.conf /etc/tor/torrc.d/obfs4-bridges.conf
COPY rootfs /
# Fix permissions
RUN mkdir -p /data && chmod 700 /data
RUN mkdir -p /etc/tor/torrc.d && chmod -R 400 /etc/tor/torrc.d
RUN mkdir -p /var/log/wireadmin && touch /var/log/wireadmin/web.log
RUN mkdir -p /data/ && chmod 700 /data/
RUN mkdir -p /etc/tor/torrc.d/ && chmod -R 400 /etc/tor/
RUN mkdir -p /var/log/wireadmin/ && touch /var/log/wireadmin/web.log
RUN echo '* * * * * /usr/bin/env logrotate /etc/logrotate.d/rotator' > /etc/crontabs/root

View File

@ -1,51 +1,10 @@
#!/usr/bin/env bash
set -e
source /etc/wireadmin/xscript.sh
ENV_FILE="/app/.env"
TOR_CONFIG="/etc/tor/torrc"
TOR_CONFIG_TEMPLATE="${TOR_CONFIG}.template"
log() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")] [$1] $2"
}
to_camel_case() {
echo "${1}" | awk -F_ '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2));}1' OFS=""
}
generate_tor_config() {
# Copying the torrc template to the torrc file
cp "${TOR_CONFIG_TEMPLATE}" "${TOR_CONFIG}"
# IP address of the container
local inet_address="$(hostname -i | awk '{print $1}')"
sed -i "s/{{INET_ADDRESS}}/$inet_address/g" "${TOR_CONFIG}"
# any other environment variables that start with TOR_ are added to the torrc
# file
env | grep ^TOR_ | sed -e 's/TOR_//' -e 's/=/ /' | while read -r line; do
key=$(echo "$line" | awk '{print $1}')
value=$(echo "$line" | awk '{print $2}')
key=$(to_camel_case "$key")
echo "$key $value" >> "${TOR_CONFIG}"
done
# Removing duplicated tor options
awk -F= '!a[tolower($1)]++' "${TOR_CONFIG}" > "/tmp/$(basename "${TOR_CONFIG}")" \
&& mv "/tmp/$(basename "${TOR_CONFIG}")" "${TOR_CONFIG}"
# Remove comment line with single Hash
sed -i '/^#\([^#]\)/d' "${TOR_CONFIG}"
# Remove options with no value. (KEY[:space:]{...VALUE})
sed -i '/^[^ ]* $/d' "${TOR_CONFIG}"
# Remove double empty lines
sed -i '/^$/N;/^\n$/D' "${TOR_CONFIG}"
log "notice" "Tor configuration file has been generated"
}
echo " "
echo " _ ___ ___ __ _ "
echo "| | / (_)_______ / | ____/ /___ ___ (_)___ "
@ -72,33 +31,29 @@ fi
# Generate Tor configuration
generate_tor_config
setup_logrotate
setup_dns
tee "/etc/logrotate.d/rotator" &> /dev/null << EOF
/var/log/wireadmin/*.log {
size 512K
rotate 3
missingok
notifempty
create 0640 root adm
copytruncate
}
EOF
# Start Tor on the background
# Background services
crond
_TOR_UID=$(id -u tor)
mkdir -p /var/lib/tor && chown -R "$_TOR_UID:$_TOR_UID" /var/lib/tor
screen -L -Logfile /var/log/wireadmin/tor.log -dmS tor bash -c "screen -S tor -X wrap off; tor -f $TOR_CONFIG"
dnsmasq
# Start Tor
screen -L -Logfile /var/log/wireadmin/tor.log -dmS tor \
bash -c "screen -S tor -X wrap off; tor -f $TOR_CONFIG"
sleep 1
echo -e "\n======================== Versions ========================"
echo -e "Alpine: \c" && cat /etc/alpine-release
echo -e "WireGuard: \c" && wg -v | head -n 1 | awk '{print $2}'
echo -e "Tor: \c" && tor --version | head -n 1 | cut -d ' ' -f3
echo -e "Tor: \c" && tor --version | head -n 1 | awk '{print $3}' | sed 's/.$//'
echo -e "Dnsmasq: \c" && dnsmasq -v | head -n 1 | cut -d ' ' -f3
echo -e "Lyrebird: \c" && lyrebird -version
echo -e "\n========================= Torrc ========================="
echo -e "\n======================= Tor Config ======================="
grep -v "^#" "$TOR_CONFIG"
echo -e "========================================================\n"
echo -e "====================== Dnsmasq Config ======================"
grep -v "^#" "$DNSMASQ_CONFIG"
echo -e "==========================================================\n"
sleep 1
exec "$@"

View File

@ -0,0 +1,54 @@
#!/bin/bash
DNSMASQ_CONFIG=/etc/dnsmasq.d/tor-dns.conf
setup_dns() {
local _TOR_DNS_PORT="$(get_torrc_option "DNSPort")"
local _TOR_DNS_HOST="127.0.0.1"
if [ -z "$_TOR_DNS_PORT" ]; then
log ERROR "DNSPort is not set in $TOR_CONFIG"
exit 1
fi
if echo "$_TOR_DNS_PORT" | grep -q ":"; then
_TOR_DNS_HOST="$(awk -F: '{print $1}' <<< "$_TOR_DNS_PORT")"
_TOR_DNS_PORT="$(awk -F: '{print $2}' <<< "$_TOR_DNS_PORT")"
fi
# DNS must be a number
if ! [[ "$_TOR_DNS_PORT" =~ ^[0-9]+$ ]]; then
log ERROR "DNSPort options is malformed."
exit 1
fi
log NOTICE "Setting up Dnsmasq to use Tor DNS on $_TOR_DNS_HOST:$_TOR_DNS_PORT"
_IFACE="$(ip route show default | awk '/default/ {print $5}')"
tee /etc/resolv.conf &> /dev/null << EOF
# Generated by WireAdmin; DO NOT EDIT
nameserver 127.0.0.1
option allow-domains *.onion
search .
EOF
tee "$DNSMASQ_CONFIG" &> /dev/null << EOF
pid-file=/var/run/dnsmasq.pid
interface=$_IFACE
user=dnsmasq
group=dnsmasq
bind-dynamic
no-resolv
no-poll
no-negcache
bogus-priv
log-queries
domain-needed
cache-size=1500
min-port=4096
server=$_TOR_DNS_HOST#$_TOR_DNS_PORT
log-facility=/var/log/dnsmasq/dnsmasq.log
EOF
mkdir -p /var/log/dnsmasq
uown dnsmasq /var/log/dnsmasq
}

View File

@ -0,0 +1,15 @@
#!/bin/bash
setup_logrotate() {
tee "/etc/logrotate.d/rotator" &> /dev/null << EOF
/var/log/dnsmasq/dnsmasq.log
/var/log/wireadmin/*.log {
size 512K
rotate 3
missingok
notifempty
create 0640 root adm
copytruncate
}
EOF
}

View File

@ -0,0 +1,95 @@
#!/bin/bash
TOR_CONFIG="/etc/tor/torrc"
TOR_CONFIG_TEMPLATE="$TOR_CONFIG.template"
_cleanse_config() {
# Remove comment line with single Hash
sed -i '/^#\([^#]\)/d' "$TOR_CONFIG"
# Remove options with no value. (KEY[:space:]{...VALUE})
sed -i '/^[^ ]* $/d' "$TOR_CONFIG"
# Remove duplicate lines
sed -i '/^$/N;/\n.*\n/d' "$TOR_CONFIG"
# Remove double empty lines
sed -i '/^$/N;/^\n$/D' "$TOR_CONFIG"
}
_fix_permissions() {
mkdir -p /var/lib/tor
uown tor /var/lib/tor
chmod +x /var/lib/tor
}
_load_from_env() {
local added_count=0
local updated_count=0
for _env_name in $(env | grep -o "^TOR_[^=]*"); do
# skip custom options
if [[ " ${CUSTOM_TOR_OPTIONS[*]} " == *" ${_env_name} "* ]]; then
continue
fi
local env_value="${!_env_name}"
# remove prefix and convert to camel case
local option=$(to_camel_case "${_env_name#TOR_}")
if [ -n "$env_value" ]; then
# Check if there is a corresponding option in the torrc file, and update it
if grep -i -q "^$option" "$TOR_CONFIG"; then
sed -i "s/^$option.*/$option $env_value/" "$TOR_CONFIG"
updated_count=$((updated_count + 1))
else
echo "$option $env_value" >> "$TOR_CONFIG"
added_count=$((added_count + 1))
fi
fi
done
# Add a blank line at the end of the file
echo "" >> "$TOR_CONFIG"
if [ "$added_count" -gt 0 ] || [ "$updated_count" -gt 0 ]; then
echo ""
log NOTICE "Added $added_count and updated $updated_count options from environment variables."
fi
}
generate_tor_config() {
# Copying the torrc template to the torrc file
cp "${TOR_CONFIG_TEMPLATE}" "$TOR_CONFIG"
# IP address of the container
local inet_address="$(hostname -i | awk '{print $1}')"
sed -i "s/{{INET_ADDRESS}}/$inet_address/g" "$TOR_CONFIG"
# any other environment variables that start with TOR_ are added to the torrc
# file
env | grep ^TOR_ | sed -e 's/TOR_//' -e 's/=/ /' | while read -r line; do
key=$(echo "$line" | awk '{print $1}')
value=$(echo "$line" | awk '{print $2}')
key=$(to_camel_case "$key")
echo "$key $value" >> "$TOR_CONFIG"
done
# Removing duplicated tor options
awk -F= '!a[tolower($1)]++' "$TOR_CONFIG" > "/tmp/$(basename "$TOR_CONFIG")" \
&& mv "/tmp/$(basename "$TOR_CONFIG")" "$TOR_CONFIG"
_load_from_env
_cleanse_config
_fix_permissions
log "notice" "Tor configuration file has been generated"
}
get_torrc_option() {
grep -i "^$1" "$TOR_CONFIG" | awk '{print $2}'
}

22
rootfs/etc/wireadmin/xscript.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
source /etc/wireadmin/internal/dns.sh
source /etc/wireadmin/internal/logrotate.sh
source /etc/wireadmin/internal/tor.sh
uppercase() {
echo "$1" | tr '[:lower:]' '[:upper:]'
}
log() {
echo -e "$(date +"%b %d %H:%M:%S %Z") [$(uppercase "$1")] $2"
}
to_camel_case() {
echo "$1" | awk -F_ '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2));}1' OFS=""
}
uown() {
_UID="$(id -u "$1")"
chown -R "$_UID":"$_UID" "$2"
}

View File

@ -0,0 +1,14 @@
<script lang="ts">
import Icon from '@lib/components/iconset/icon.svelte';
import type { Props } from '@lib/components/iconset';
type $$Props = Props;
</script>
<Icon {...$$restProps} viewBox="0 0 56 30" fill="currentColor">
<path
d="M47.084 5.32128C42.17 2.30228 35.377 0.436279 27.878 0.436279C20.378 0.436279 13.588 2.30228 8.67402 5.32128C3.75902 8.33728 0.718018 12.5053 0.718018 17.1103C0.718018 21.7143 3.75902 25.8823 8.67402 28.8983C13.588 31.9183 20.378 24.6273 27.878 24.6273C35.377 24.6273 42.17 31.9183 47.084 28.8983C51.998 25.8823 55.039 21.7133 55.039 17.1103C55.039 12.5043 51.998 8.33728 47.084 5.32128ZM17.083 17.4883C14.385 17.4883 12.198 16.5663 12.198 15.4273C12.198 14.2873 14.385 13.3643 17.083 13.3643C19.78 13.3643 21.968 14.2883 21.968 15.4273C21.968 16.5663 19.78 17.4883 17.083 17.4883ZM38.584 17.6793C35.898 17.6793 33.723 16.8233 33.723 15.7673C33.723 14.7133 35.899 13.8563 38.584 13.8563C41.269 13.8563 43.447 14.7133 43.447 15.7673C43.447 16.8233 41.269 17.6793 38.584 17.6793Z"
fill="#6700AD"
aria-label="Dnsmasq"
/>
</Icon>

View File

@ -1,5 +1,6 @@
import type { SVGAttributes } from 'svelte/elements';
import Dnsmasq from './dnsmasq-icon.svelte';
import Root from './icon.svelte';
import Onion from './onion-icon.svelte';
@ -13,9 +14,11 @@ interface Props extends SVGAttributes<SVGSVGElement> {
export {
Root,
Dnsmasq,
Onion,
type Props,
//
Root as Icon,
Dnsmasq as DnsmasqIcon,
Onion as OnionIcon,
};

View File

@ -5,11 +5,16 @@
type $$Props = Props;
</script>
<Icon {...$$restProps} viewBox="0 0 179.51151 205.15602" fill="currentColor">
<g transform="translate(-17.934981,-39.932967)">
<path
d="m 104.88587,47.145484 -1.60279,-2.804867 q -2.00347,-4.407649 -6.811816,-4.407649 -7.212516,0.801391 -8.414602,8.013907 -2.804867,14.024336 -11.620164,24.041719 -8.815297,9.616688 -20.435462,18.431985 0,0 0,0 0,0 0,0 -14.425031,10.818771 -26.045196,25.644501 -11.219469,14.42503 -12.02086,39.26814 0.400696,24.04172 15.226423,40.47023 14.825727,16.02781 37.66536,23.64102 l -4.407648,12.82225 q -2.003477,8.8153 6.010429,12.42156 8.815298,2.00348 12.421555,-6.01043 l 4.808344,-14.42503 q 4.006954,0.80139 8.414602,1.20208 v 10.01739 q 0.801391,8.81529 9.616685,9.61668 8.8153,-0.80139 9.61669,-9.61668 V 225.4549 q 4.40765,-0.40069 8.4146,-1.20208 l 4.80835,14.42503 q 3.60625,8.01391 12.42155,6.01043 8.01391,-3.60626 6.01043,-12.42156 l -4.40765,-12.82225 q 22.83964,-7.61321 37.66536,-23.64102 14.82573,-16.42851 15.22643,-40.47023 -0.8014,-24.84311 -12.02086,-39.26814 -11.62017,-14.82573 -26.0452,-25.644501 0,0 0,0 0,0 0,0 -11.62016,-8.815297 -20.43546,-18.431985 -8.8153,-10.017383 -11.21947,-24.041719 -1.60278,-7.212516 -8.8153,-8.013907 -4.80834,0 -6.81182,4.407649 l -1.60278,2.804867 q -2.80487,3.606258 -5.60973,0 z M 56.802427,114.4623 v -0.4007 q 0.400695,-0.40069 0.80139,-0.80139 0,0 0,0 1.602782,-2.40417 4.407649,-2.80486 1.202086,0 2.404172,0.40069 0.801391,0 2.003477,0.80139 2.404172,1.60278 2.804867,4.40765 0,1.20209 -0.400695,2.40417 0,0.80139 -0.400696,1.20209 v 0.40069 q -0.400695,0.4007 -0.80139,1.20209 -0.400696,1.60278 -2.003477,4.40765 -2.404172,6.01043 -5.209039,15.22642 -2.804868,9.61669 -3.606258,19.63407 -0.400696,10.01738 2.404172,18.03129 1.602781,6.01043 -4.006953,8.4146 -5.609735,1.60278 -8.013907,-3.60626 -4.006953,-11.62016 -3.205563,-23.64102 1.202086,-12.02086 4.006954,-22.03824 3.205562,-10.41808 6.01043,-16.82921 1.202086,-3.20556 2.003476,-4.80834 0.400695,-0.4007 0.400695,-0.80139 0,-0.4007 0.400696,-0.80139 z M 101.6803,89.218493 q 0.4007,-0.801391 0.4007,-1.202086 0.80139,-1.202086 2.00348,-2.003477 2.00347,-1.602781 4.80834,-1.202086 1.20208,0.400696 2.00348,0.801391 1.20208,0.801391 2.00347,2.003477 0.80139,1.202086 1.20209,2.804867 0,0.801391 0,2.003477 -0.4007,0.80139 -0.4007,1.202086 v 0 l -0.40069,0.80139 q 0,0 0,0.400696 -0.4007,0.80139 -0.80139,1.602781 0,0 0,0.400695 -1.20209,2.804867 -2.40418,7.212516 -3.60625,10.01738 -7.21251,25.6445 -4.006954,15.62712 -5.20904,32.05563 -0.801391,16.8292 3.20556,30.05215 1.60278,5.60973 -4.006951,8.0139 -5.609734,1.20209 -8.013906,-4.00695 -4.808344,-16.02781 -3.606258,-34.4598 1.202086,-18.43198 5.209039,-34.86049 3.606258,-16.02781 7.212516,-26.846589 2.003476,-4.808343 2.80487,-8.013906 0.80139,-1.202086 1.20208,-2.003477 v -0.400695 -0.400695 z m 31.65493,14.825727 q 0.4007,-6.01043 6.41113,-6.411125 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0.40069,0 3.60626,0 5.60974,3.205565 l -0.4007,0.40069 v 0 q 0.4007,-0.40069 0.4007,-0.40069 v 0 0 0 0.40069 l 0.40069,0.4007 q 0.4007,0.40069 0.80139,1.60278 1.20209,2.00348 2.80487,5.60973 3.60626,7.61322 8.01391,19.63407 4.40765,12.02086 6.41112,26.0452 2.00348,14.42503 -1.60278,28.04867 -2.40417,5.60974 -8.0139,4.40765 -5.60974,-2.00347 -4.40765,-7.61321 2.80486,-10.81877 1.20208,-23.24033 -1.60278,-12.02086 -5.60973,-23.64102 -4.00695,-11.21947 -7.61321,-18.03129 -1.60278,-3.60626 -2.40417,-5.60974 v 0 q -2.40418,-1.60278 -2.40418,-4.80834 z"
aria-label="Onion"
/>
</g>
<Icon {...$$restProps} viewBox="0 0 512 512" fill="currentColor">
<path
d="M256 502C391.862 502 502 391.862 502 256C502 120.138 391.862 10 256 10C120.138 10 10 120.138 10 256C10 391.862 120.138 502 256 502Z"
fill="#F2E4FF"
fill-opacity="0.5"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M256.525 465.44V434.407C354.826 434.123 434.421 354.365 434.421 255.993C434.421 157.628 354.826 77.8702 256.525 77.5863V46.5532C371.964 46.8442 465.447 140.49 465.447 255.993C465.447 371.503 371.964 465.156 256.525 465.44ZM256.525 356.82C311.97 356.529 356.849 311.516 356.849 255.993C356.849 200.477 311.97 155.464 256.525 155.173V124.147C329.115 124.43 387.882 183.339 387.882 255.993C387.882 328.654 329.115 387.562 256.525 387.846V356.82ZM256.525 201.719C286.267 202.003 310.303 226.18 310.303 255.993C310.303 285.812 286.267 309.99 256.525 310.274V201.719ZM0 255.993C0 397.384 114.609 512 256 512C397.384 512 512 397.384 512 255.993C512 114.609 397.384 0 256 0C114.609 0 0 114.609 0 255.993Z"
fill="#7D4698"
/>
</Icon>

View File

@ -6,14 +6,44 @@ import { fsTouch } from '@lib/utils/fs-extra';
export const SERVICES = <const>{
tor: {
name: 'Tor',
start:
'screen -L -Logfile /var/log/wireadmin/tor.log -dmS tor bash -c "screen -S tor -X wrap off; tor -f /etc/tor/torrc"',
stop: 'pkill tor',
logfile: '/var/log/wireadmin/tor.log',
health: async () => {
try {
const { stdout } = await execa('screen', ['-ls'], { shell: true });
return stdout.includes('tor');
} catch (_) {
return false;
}
},
},
dnsmasq: {
name: 'Dnsmasq',
start: 'dnsmasq',
stop: 'pkill dnsmasq',
logfile: '/var/log/dnsmasq/dnsmasq.log',
health: async () => {
try {
const { stdout } = await execa('ps cax | grep dnsmasq', { shell: true });
return stdout.search(/dnsmasq .* dnsmasq$/gm) !== -1;
} catch (_) {
return false;
}
},
},
};
export type ServiceName = keyof typeof SERVICES;
export type Service = (typeof SERVICES)[ServiceName];
export function getService(service: ServiceName | string | undefined): Service | undefined {
if (!!service && service in SERVICES) {
return SERVICES[service as ServiceName];
}
}
export function restart(serviceName: ServiceName) {
const service = SERVICES[serviceName];

View File

@ -40,6 +40,7 @@ export const DnsSchema = z
.regex(IPV4_REGEX, {
message: 'DNS must be a valid IPv4 address',
})
.default('9.9.9.11')
.optional();
export const MtuSchema = z

View File

@ -9,7 +9,7 @@
import CreateServerDialog from './CreateServerDialog.svelte';
import { Button } from '@lib/components/ui/button';
import { PlusIcon } from 'lucide-svelte';
import { OnionIcon } from '@lib/components/iconset';
import { DnsmasqIcon, OnionIcon } from '@lib/components/iconset';
export let data: PageData;
@ -62,6 +62,7 @@
</CardContent>
{/if}
</Card>
<Card>
<CardHeader>
<CardTitle>Services</CardTitle>
@ -69,7 +70,12 @@
<CardContent>
<Service name="Tor" slug="tor">
<svelte:fragment slot="icon">
<OnionIcon class="text-purple-700 text-xl" />
<OnionIcon class="h-6 w-6" />
</svelte:fragment>
</Service>
<Service name="Dnsmasq" slug="dnsmasq">
<svelte:fragment slot="icon">
<DnsmasqIcon class="h-6 w-6" />
</svelte:fragment>
</Service>
</CardContent>

View File

@ -7,7 +7,7 @@
import { Badge } from '@lib/components/ui/badge';
import { createEventDispatcher } from 'svelte';
import { cn } from '@lib/utils';
import { Layers, LayersIcon } from 'lucide-svelte';
import { LayersIcon } from 'lucide-svelte';
import { OnionIcon } from '@lib/components/iconset';
export let server: WgServer;
@ -29,7 +29,7 @@
>
<LayersIcon class="text-gray-400 text-xl" />
{#if server.tor}
<OnionIcon class="absolute bottom-2 right-2 w-4 h-4 text-purple-700" />
<OnionIcon class="absolute bottom-2 right-2 w-4 h-4" />
{/if}
</div>

View File

@ -30,7 +30,7 @@
<slot name="icon" />
</div>
<a href={`/${slug}`} title="Manage" class="my-auto">
<a href={`/service/${slug}`} title="Manage" class="my-auto">
<span class="text-lg font-medium md:text-base hover:text-primary hover:font-medium">
{name}
</span>
@ -45,7 +45,7 @@
</div>
</div>
<a href={`/${slug}`} title="Manage the Server" class="hidden md:block">
<a href={`/service/${slug}`} title="Manage the Server" class="hidden md:block">
<Button variant="outline" size="sm">Manage</Button>
</a>
</div>

View File

@ -1,11 +1,12 @@
import type { RequestHandler } from '@sveltejs/kit';
import logger from '@lib/logger';
import { getServers, WGServer } from '@lib/wireguard';
import { errorBox } from '@lib/logger';
import { WG_STORE } from '@lib/storage';
import { WGServer } from '@lib/wireguard';
export const GET: RequestHandler = async () => {
try {
for (const { id } of await getServers()) {
for (const { id } of await WG_STORE.listServers()) {
const wg = new WGServer(id);
const server = await wg.get();
const hasInterface = await wg.isUp();
@ -20,7 +21,7 @@ export const GET: RequestHandler = async () => {
}
}
} catch (e) {
logger.error('APIFailed: HealthCheck:', e);
errorBox(e);
return new Response('FAILED', { status: 500, headers: { 'Content-Type': 'text/plain' } });
}

View File

@ -1,13 +0,0 @@
import { json, type RequestHandler } from '@sveltejs/kit';
import { execa } from 'execa';
export const GET: RequestHandler = async ({ params }) => {
try {
const { stdout } = await execa('screen', ['-ls'], { shell: true });
const isRunning = stdout.includes(params.serviceName!);
return json({ healthy: isRunning });
} catch (e) {
return json({ healthy: false });
}
};

View File

@ -0,0 +1,19 @@
import { json, type RequestHandler } from '@sveltejs/kit';
import { errorBox } from '@lib/logger';
import { getService } from '@lib/services';
export const GET: RequestHandler = async ({ params }) => {
try {
const service = getService(params.service);
if (!service) {
return json({ healthy: false });
}
const healthy = await service.health();
return json({ healthy });
} catch (e) {
errorBox(e);
return json({ healthy: false });
}
};

View File

@ -0,0 +1,53 @@
import { error, type Actions } from '@sveltejs/kit';
import logger, { errorBox } from '@lib/logger';
import { clearLogs, getService, logs, restart, type ServiceName } from '@lib/services';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
const service = getService(params.slug);
if (!service) {
logger.error(`Service not found. Service: ${params.slug}`);
throw error(404, { message: 'Not found' });
}
return {
slug: params.slug,
title: service.name,
};
};
export const actions: Actions = {
clearLogs: async ({ params }) => {
const { slug } = params as { slug: ServiceName };
try {
await clearLogs(slug);
return {};
} catch (e) {
errorBox(e);
throw error(500, 'Unhandled Exception');
}
},
logs: async ({ params }) => {
const { slug } = params as { slug: ServiceName };
try {
return { logs: await logs(slug) };
} catch (e) {
errorBox(e);
throw error(500, 'Unhandled Exception');
}
},
restart: async ({ params }) => {
const { slug } = params as { slug: ServiceName };
try {
const success = restart(slug);
return { success };
} catch (e) {
errorBox(e);
throw error(500, 'Unhandled Exception');
}
},
};

View File

@ -1,10 +1,9 @@
<script lang="ts">
import { Card, CardContent, CardHeader, CardTitle } from '@lib/components/ui/card';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@lib/components/ui/card';
import { onMount } from 'svelte';
import type { PageData } from './$types';
import fetchAction from '@lib/utils/fetch-action';
import { Button } from '@lib/components/ui/button';
import { CardFooter } from '@lib/components/ui/card';
import toast from 'svelte-french-toast';
import { LoaderCircle } from 'lucide-svelte';
import {
@ -18,6 +17,8 @@
import BasePage from '@lib/components/page/BasePage.svelte';
import { Checkbox } from '@lib/components/ui/checkbox';
export let data: PageData;
let logElement: HTMLTextAreaElement;
let logs: string | undefined;
let autoScroll = true;
@ -83,7 +84,6 @@
}, 1000);
return () => {
console.log('clearing interval');
clearInterval(interval);
};
});
@ -98,7 +98,7 @@
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Tor</BreadcrumbPage>
<BreadcrumbPage>{data.title}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>

View File

@ -1,36 +0,0 @@
import { promises } from 'node:fs';
import { error, type Actions } from '@sveltejs/kit';
import { execa } from 'execa';
import logger from '@lib/logger';
import { clearLogs, logs, restart, SERVICES, type ServiceName } from '@lib/services';
export const actions: Actions = {
clearLogs: async ({ params }) => {
try {
await clearLogs('tor');
return {};
} catch (e) {
logger.error(e);
throw error(500, 'Unhandled Exception');
}
},
logs: async ({ params }) => {
try {
return { logs: await logs('tor') };
} catch (e) {
logger.error(e);
throw error(500, 'Unhandled Exception');
}
},
restart: async ({ params }) => {
try {
const success = restart('tor');
return { success };
} catch (e) {
logger.error(e);
throw error(500, 'Unhandled Exception');
}
},
};