From 06a0a3008b75f79cb6c2b047deba88c4b6535cd2 Mon Sep 17 00:00:00 2001 From: Shahrad Elahi Date: Wed, 29 May 2024 18:59:49 +0330 Subject: [PATCH] feat: dns server --- .changeset/thick-moles-kick.md | 5 + Dockerfile | 32 +++---- Dockerfile-Dev | 28 +++--- docker-entrypoint.sh | 77 ++++----------- .../etc/tor/torrc.d/bridges.conf | 0 {config => rootfs/etc/tor}/torrc.template | 0 rootfs/etc/wireadmin/internal/dns.sh | 54 +++++++++++ rootfs/etc/wireadmin/internal/logrotate.sh | 15 +++ rootfs/etc/wireadmin/internal/tor.sh | 95 +++++++++++++++++++ rootfs/etc/wireadmin/xscript.sh | 22 +++++ .../components/iconset/dnsmasq-icon.svelte | 14 +++ web/src/lib/components/iconset/index.ts | 3 + .../lib/components/iconset/onion-icon.svelte | 19 ++-- web/src/lib/services.ts | 30 ++++++ web/src/lib/wireguard/schema.ts | 1 + web/src/routes/+page.svelte | 10 +- web/src/routes/Server.svelte | 4 +- web/src/routes/Service.svelte | 4 +- web/src/routes/api/health/+server.ts | 9 +- .../api/health/[serviceName]/+server.ts | 13 --- .../routes/api/health/[service]/+server.ts | 19 ++++ web/src/routes/service/[slug]/+page.server.ts | 53 +++++++++++ .../{tor => service/[slug]}/+page.svelte | 8 +- web/src/routes/tor/+page.server.ts | 36 ------- 24 files changed, 386 insertions(+), 165 deletions(-) create mode 100644 .changeset/thick-moles-kick.md rename config/obfs4-bridges.conf => rootfs/etc/tor/torrc.d/bridges.conf (100%) rename {config => rootfs/etc/tor}/torrc.template (100%) create mode 100644 rootfs/etc/wireadmin/internal/dns.sh create mode 100755 rootfs/etc/wireadmin/internal/logrotate.sh create mode 100755 rootfs/etc/wireadmin/internal/tor.sh create mode 100755 rootfs/etc/wireadmin/xscript.sh create mode 100644 web/src/lib/components/iconset/dnsmasq-icon.svelte delete mode 100644 web/src/routes/api/health/[serviceName]/+server.ts create mode 100644 web/src/routes/api/health/[service]/+server.ts create mode 100644 web/src/routes/service/[slug]/+page.server.ts rename web/src/routes/{tor => service/[slug]}/+page.svelte (93%) delete mode 100644 web/src/routes/tor/+page.server.ts diff --git a/.changeset/thick-moles-kick.md b/.changeset/thick-moles-kick.md new file mode 100644 index 0000000..d067406 --- /dev/null +++ b/.changeset/thick-moles-kick.md @@ -0,0 +1,5 @@ +--- +"wireadmin": major +--- + +feat: Creates a Dnsmasq server on port 53 and forwards DNS queries through the Tor network. diff --git a/Dockerfile b/Dockerfile index 1c02964..f48565c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Dockerfile-Dev b/Dockerfile-Dev index 4e88ffa..d2f7738 100644 --- a/Dockerfile-Dev +++ b/Dockerfile-Dev @@ -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 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 25c7b3d..4fc4da4 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -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 "$@" diff --git a/config/obfs4-bridges.conf b/rootfs/etc/tor/torrc.d/bridges.conf similarity index 100% rename from config/obfs4-bridges.conf rename to rootfs/etc/tor/torrc.d/bridges.conf diff --git a/config/torrc.template b/rootfs/etc/tor/torrc.template similarity index 100% rename from config/torrc.template rename to rootfs/etc/tor/torrc.template diff --git a/rootfs/etc/wireadmin/internal/dns.sh b/rootfs/etc/wireadmin/internal/dns.sh new file mode 100644 index 0000000..1552fdb --- /dev/null +++ b/rootfs/etc/wireadmin/internal/dns.sh @@ -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 +} diff --git a/rootfs/etc/wireadmin/internal/logrotate.sh b/rootfs/etc/wireadmin/internal/logrotate.sh new file mode 100755 index 0000000..2a0d13e --- /dev/null +++ b/rootfs/etc/wireadmin/internal/logrotate.sh @@ -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 +} diff --git a/rootfs/etc/wireadmin/internal/tor.sh b/rootfs/etc/wireadmin/internal/tor.sh new file mode 100755 index 0000000..02fd49e --- /dev/null +++ b/rootfs/etc/wireadmin/internal/tor.sh @@ -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}' +} diff --git a/rootfs/etc/wireadmin/xscript.sh b/rootfs/etc/wireadmin/xscript.sh new file mode 100755 index 0000000..23c5b2f --- /dev/null +++ b/rootfs/etc/wireadmin/xscript.sh @@ -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" +} diff --git a/web/src/lib/components/iconset/dnsmasq-icon.svelte b/web/src/lib/components/iconset/dnsmasq-icon.svelte new file mode 100644 index 0000000..535ae01 --- /dev/null +++ b/web/src/lib/components/iconset/dnsmasq-icon.svelte @@ -0,0 +1,14 @@ + + + + + diff --git a/web/src/lib/components/iconset/index.ts b/web/src/lib/components/iconset/index.ts index 267f78c..7ac57c3 100644 --- a/web/src/lib/components/iconset/index.ts +++ b/web/src/lib/components/iconset/index.ts @@ -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 { export { Root, + Dnsmasq, Onion, type Props, // Root as Icon, + Dnsmasq as DnsmasqIcon, Onion as OnionIcon, }; diff --git a/web/src/lib/components/iconset/onion-icon.svelte b/web/src/lib/components/iconset/onion-icon.svelte index e8d3452..bed2028 100644 --- a/web/src/lib/components/iconset/onion-icon.svelte +++ b/web/src/lib/components/iconset/onion-icon.svelte @@ -5,11 +5,16 @@ type $$Props = Props; - - - - + + + diff --git a/web/src/lib/services.ts b/web/src/lib/services.ts index e513549..3911cb1 100644 --- a/web/src/lib/services.ts +++ b/web/src/lib/services.ts @@ -6,14 +6,44 @@ import { fsTouch } from '@lib/utils/fs-extra'; export const SERVICES = { 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]; diff --git a/web/src/lib/wireguard/schema.ts b/web/src/lib/wireguard/schema.ts index 4cc7a32..3cf0f9e 100644 --- a/web/src/lib/wireguard/schema.ts +++ b/web/src/lib/wireguard/schema.ts @@ -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 diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 6bf7f20..f8ef2aa 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -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 @@ {/if} + Services @@ -69,7 +70,12 @@ - + + + + + + diff --git a/web/src/routes/Server.svelte b/web/src/routes/Server.svelte index aeeb534..d0b7a82 100644 --- a/web/src/routes/Server.svelte +++ b/web/src/routes/Server.svelte @@ -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 @@ > {#if server.tor} - + {/if} diff --git a/web/src/routes/Service.svelte b/web/src/routes/Service.svelte index 0c53903..a78a58e 100644 --- a/web/src/routes/Service.svelte +++ b/web/src/routes/Service.svelte @@ -30,7 +30,7 @@ - + {name} @@ -45,7 +45,7 @@ - diff --git a/web/src/routes/api/health/+server.ts b/web/src/routes/api/health/+server.ts index e67f1c1..0a4d185 100644 --- a/web/src/routes/api/health/+server.ts +++ b/web/src/routes/api/health/+server.ts @@ -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' } }); } diff --git a/web/src/routes/api/health/[serviceName]/+server.ts b/web/src/routes/api/health/[serviceName]/+server.ts deleted file mode 100644 index c7ab254..0000000 --- a/web/src/routes/api/health/[serviceName]/+server.ts +++ /dev/null @@ -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 }); - } -}; diff --git a/web/src/routes/api/health/[service]/+server.ts b/web/src/routes/api/health/[service]/+server.ts new file mode 100644 index 0000000..de6449f --- /dev/null +++ b/web/src/routes/api/health/[service]/+server.ts @@ -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 }); + } +}; diff --git a/web/src/routes/service/[slug]/+page.server.ts b/web/src/routes/service/[slug]/+page.server.ts new file mode 100644 index 0000000..9adb91d --- /dev/null +++ b/web/src/routes/service/[slug]/+page.server.ts @@ -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'); + } + }, +}; diff --git a/web/src/routes/tor/+page.svelte b/web/src/routes/service/[slug]/+page.svelte similarity index 93% rename from web/src/routes/tor/+page.svelte rename to web/src/routes/service/[slug]/+page.svelte index c07d8bb..9df4ad6 100644 --- a/web/src/routes/tor/+page.svelte +++ b/web/src/routes/service/[slug]/+page.svelte @@ -1,10 +1,9 @@