From 109f4bd56fab9fdb619d89a9c0183e2fb8abab8c Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:09:37 -0400 Subject: [PATCH 01/11] migrate cli from ruby to javascript --- cli/Gemfile | 7 -- cli/Gemfile.lock | 14 ---- cli/add42_subdomain.sh | 71 ---------------- cli/add_domain.sh | 106 ------------------------ cli/add_subdomain.sh | 71 ---------------- cli/cli.rb | 129 ------------------------------ cli/{ => helpers}/create_db.sh | 0 cli/index.js | 93 +++++++++++++++++++++ cli/nest | 1 - cli/package.json | 12 +++ cli/remove42_subdomain.sh | 44 ---------- cli/remove_domain.sh | 49 ------------ cli/remove_subdomain.sh | 44 ---------- cli/root42_subdomain_template.txt | 22 ----- cli/root_domain_template.txt | 19 ----- cli/root_subdomain_template.txt | 19 ----- cli/user42_subdomain_template.txt | 6 -- cli/user_domain_template.txt | 6 -- cli/user_subdomain_template.txt | 6 -- cli/yarn.lock | 8 ++ 20 files changed, 113 insertions(+), 614 deletions(-) delete mode 100644 cli/Gemfile delete mode 100644 cli/Gemfile.lock delete mode 100755 cli/add42_subdomain.sh delete mode 100755 cli/add_domain.sh delete mode 100755 cli/add_subdomain.sh delete mode 100755 cli/cli.rb rename cli/{ => helpers}/create_db.sh (100%) create mode 100755 cli/index.js delete mode 120000 cli/nest create mode 100644 cli/package.json delete mode 100755 cli/remove42_subdomain.sh delete mode 100755 cli/remove_domain.sh delete mode 100755 cli/remove_subdomain.sh delete mode 100644 cli/root42_subdomain_template.txt delete mode 100644 cli/root_domain_template.txt delete mode 100644 cli/root_subdomain_template.txt delete mode 100644 cli/user42_subdomain_template.txt delete mode 100644 cli/user_domain_template.txt delete mode 100644 cli/user_subdomain_template.txt create mode 100644 cli/yarn.lock diff --git a/cli/Gemfile b/cli/Gemfile deleted file mode 100644 index 2d99042..0000000 --- a/cli/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -# gem "rails" - -gem "thor", "~> 1.3" diff --git a/cli/Gemfile.lock b/cli/Gemfile.lock deleted file mode 100644 index 13a3307..0000000 --- a/cli/Gemfile.lock +++ /dev/null @@ -1,14 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - thor (1.3.0) - -PLATFORMS - ruby - x86_64-linux-gnu - -DEPENDENCIES - thor (~> 1.3) - -BUNDLED WITH - 2.5.6 diff --git a/cli/add42_subdomain.sh b/cli/add42_subdomain.sh deleted file mode 100755 index cb23dfe..0000000 --- a/cli/add42_subdomain.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# Strip the subdomain of whitespace -SUBDOMAIN=$(echo "$1" | xargs) - -# Validate the subdomain -if ! grep -P '^(?![0-9]+$)(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$' <<< $SUBDOMAIN &> /dev/null; then - echo "Invalid subdomain!" - exit 1 -fi - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -FULL_SUBDOMAIN="$SUBDOMAIN.$NEST_USER.hackclub.dn42" - -# Check for existance of subdomain -if grep $FULL_SUBDOMAIN /etc/caddy/Caddyfile &> /dev/null; then - echo "You already have this subdomain ($FULL_SUBDOMAIN)!" - exit 1 -fi - -# Check for Caddyfile symlink -if readlink /home/$NEST_USER/Caddyfile &> /dev/null; then - echo "Symlinked Caddyfiles are not supported." - exit 1 -fi - -# Set temp Caddyfiles -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile -cat /home/$NEST_USER/Caddyfile > /var/nest-cli/user_caddyfile - -# Append configurations -NEW_ROOT_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/root42_subdomain_template.txt | sed "s//$SUBDOMAIN/g")" -echo "$NEW_ROOT_BLOCK" >> /var/nest-cli/root_caddyfile - -NEW_USER_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/user42_subdomain_template.txt | sed "s//$SUBDOMAIN/g")" -echo "$NEW_USER_BLOCK" >> /var/nest-cli/user_caddyfile - -# Validate Caddyfiles -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -if [ "$2" != "no_validate" ]; then - if ! caddy validate --config /var/nest-cli/user_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in user Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 - fi -fi - -# Save Caddyfiles -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -cat /var/nest-cli/user_caddyfile > /home/$NEST_USER/Caddyfile -rm /var/nest-cli/root_caddyfile /var/nest-cli/user_caddyfile - -# Format Caddyfiles -caddy fmt --overwrite /etc/caddy/Caddyfile -caddy fmt --overwrite /home/$NEST_USER/Caddyfile - -# Reload Caddy instances -systemctl reload caddy -systemctl --user -M $NEST_USER@ reload caddy - -echo "Added $FULL_SUBDOMAIN! A new block has been added to your Caddy configuration at ~/Caddyfile" \ No newline at end of file diff --git a/cli/add_domain.sh b/cli/add_domain.sh deleted file mode 100755 index 4c210ae..0000000 --- a/cli/add_domain.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/bash - -# Strip the subdomain of whitespace -DOMAIN=$(echo "$1" | xargs) - -# Validate the domain -if ! grep -P '^((?![0-9]+$)(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}\.?)+$' <<< $DOMAIN &> /dev/null; then - echo "Invalid domain!" - exit 1 -fi - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -# We should validate the domain. -FQDN_REGEX='^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' - -if [[ ! $DOMAIN =~ $FQDN_REGEX ]]; then - echo "Invalid FQDN" - exit 1 -fi - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -NEEDED_CNAME="$NEST_USER.hackclub.app." -REAL_CNAME=$(dig +short -t CNAME "$DOMAIN") - -REAL_CNAME=${REAL_CNAME:-"None"} # Weird hack to get "None" printed instead of a blank line - -if [ "$NEEDED_CNAME" != "$REAL_CNAME" ]; then - NEEDED_A="37.27.51.34" - NEEDED_AAAA="2a01:4f9:3081:399c::4" - - REAL_A=$(dig +short -t A "$DOMAIN") - REAL_AAAA=$(dig +short -t AAAA "$DOMAIN") - - if [ "$REAL_A" != "$NEEDED_A" ] || [ "$REAL_AAAA" != "$NEEDED_AAAA" ]; then - echo "The domain $DOMAIN does not have the correct CNAME or A/AAAA. Check valid records at https://guides.hackclub.app/index.php/Subdomains_and_Custom_Domains." - exit 1 - fi - - HASH=$(echo "$DOMAIN;$NEST_USER" | md5sum | awk '{print $1}') - - NEEDED_TXT="nest-verification=$HASH" - REAL_TXT=$(dig +short -t TXT "$DOMAIN") - - if ! (echo "$REAL_TXT" | grep -q "$NEEDED_TXT"); then - echo "Add the following TXT record to $DOMAIN and then retry. (You might have to wait a bit for the record to propagate!)" - echo "nest-verification=$HASH" - exit 1 - fi -fi - -# Check for existance of domain -if grep "^$DOMAIN {$" /etc/caddy/Caddyfile &> /dev/null; then - echo "You already have this custom domain ($DOMAIN)!" - exit 1 -fi - -# Check for Caddyfile symlink -if readlink /home/$NEST_USER/Caddyfile &> /dev/null; then - echo "Symlinked Caddyfiles are not supported." - exit 1 -fi - -# Set temp Caddyfiles -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile -cat /home/$NEST_USER/Caddyfile > /var/nest-cli/user_caddyfile - -# Append configurations -NEW_ROOT_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/root_domain_template.txt | sed "s//$DOMAIN/g")" -echo "$NEW_ROOT_BLOCK" >> /var/nest-cli/root_caddyfile - -NEW_USER_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/user_domain_template.txt | sed "s//$DOMAIN/g")" -echo "$NEW_USER_BLOCK" >> /var/nest-cli/user_caddyfile - -# Validate Caddyfiles -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -if [ "$2" != "no_validate" ]; then - if ! caddy validate --config /var/nest-cli/user_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in user Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 - fi -fi - -# Save Caddyfiles -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -cat /var/nest-cli/user_caddyfile > /home/$NEST_USER/Caddyfile -rm /var/nest-cli/root_caddyfile /var/nest-cli/user_caddyfile - -# Format Caddyfiles -caddy fmt --overwrite /etc/caddy/Caddyfile -caddy fmt --overwrite /home/$NEST_USER/Caddyfile - -# Reload Caddy instances -systemctl reload caddy -systemctl --user -M $NEST_USER@ reload caddy - -echo "Added $DOMAIN! A new block has been added to your Caddy configuration at ~/Caddyfile" diff --git a/cli/add_subdomain.sh b/cli/add_subdomain.sh deleted file mode 100755 index 0af806d..0000000 --- a/cli/add_subdomain.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# Strip the subdomain of whitespace -SUBDOMAIN=$(echo "$1" | xargs) - -# Validate the subdomain -if ! grep -P '^(?![0-9]+$)(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$' <<< $SUBDOMAIN &> /dev/null; then - echo "Invalid subdomain!" - exit 1 -fi - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -FULL_SUBDOMAIN="$SUBDOMAIN.$NEST_USER.hackclub.app" - -# Check for existance of subdomain -if grep $FULL_SUBDOMAIN /etc/caddy/Caddyfile &> /dev/null; then - echo "You already have this subdomain ($FULL_SUBDOMAIN)!" - exit 1 -fi - -# Check for Caddyfile symlink -if readlink /home/$NEST_USER/Caddyfile &> /dev/null; then - echo "Symlinked Caddyfiles are not supported." - exit 1 -fi - -# Set temp Caddyfiles -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile -cat /home/$NEST_USER/Caddyfile > /var/nest-cli/user_caddyfile - -# Append configurations -NEW_ROOT_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/root_subdomain_template.txt | sed "s//$SUBDOMAIN/g")" -echo "$NEW_ROOT_BLOCK" >> /var/nest-cli/root_caddyfile - -NEW_USER_BLOCK="$(sed "s//$NEST_USER/g" /usr/local/nest/cli/user_subdomain_template.txt | sed "s//$SUBDOMAIN/g")" -echo "$NEW_USER_BLOCK" >> /var/nest-cli/user_caddyfile - -# Validate Caddyfiles -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -if [ "$2" != "no_validate" ]; then - if ! caddy validate --config /var/nest-cli/user_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in user Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 - fi -fi - -# Save Caddyfiles -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -cat /var/nest-cli/user_caddyfile > /home/$NEST_USER/Caddyfile -rm /var/nest-cli/root_caddyfile /var/nest-cli/user_caddyfile - -# Format Caddyfiles -caddy fmt --overwrite /etc/caddy/Caddyfile -caddy fmt --overwrite /home/$NEST_USER/Caddyfile - -# Reload Caddy instances -systemctl reload caddy -systemctl --user -M $NEST_USER@ reload caddy - -echo "Added $FULL_SUBDOMAIN! A new block has been added to your Caddy configuration at ~/Caddyfile" \ No newline at end of file diff --git a/cli/cli.rb b/cli/cli.rb deleted file mode 100755 index e0b3189..0000000 --- a/cli/cli.rb +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/ruby - -require "thor" -require "etc" -require "socket" - -module NestCLI - class Subdomain < Thor - include Thor::Actions - desc "add ", "Add subdomain .youruser.hackclub.app to the Caddyfile" - option :no_validate, :type => :boolean, :desc => "Skip validation of your user Caddyfile. Useful if you have custom Caddy plugins not available in the system Caddy installation." - def add(name) - run "sudo /usr/local/nest/cli/add_subdomain.sh #{name} #{options[:no_validate] ? "no_validate" : ""}" - end - - desc "remove ", "Remove subdomain .youruser.hackclub.app from the Caddyfile" - def remove(name) - run "sudo /usr/local/nest/cli/remove_subdomain.sh #{name}" - end - - desc "add42 ", "Add subdomain .youruser.hackclub.dn42 to the Caddyfile" - option :no_validate, :type => :boolean, :desc => "Skip validation of your user Caddyfile. Useful if you have custom Caddy plugins not available in the system Caddy installation." - def add42(name) - run "sudo /usr/local/nest/cli/add42_subdomain.sh #{name} #{options[:no_validate] ? "no_validate" : ""}" - end - - desc "remove42 ", "Remove subdomain .youruser.hackclub.dn42 from the Caddyfile" - def remove42(name) - run "sudo /usr/local/nest/cli/remove42_subdomain.sh #{name}" - end - - def self.exit_on_failure? - return true - end - end - - class Domain < Thor - include Thor::Actions - desc "add ", "Add a custom domain to the Caddyfile" - option :no_validate, :type => :boolean, :desc => "Skip validation of your user Caddyfile. Useful if you have custom Caddy plugins not available in the system Caddy installation." - def add(name) - run "sudo /usr/local/nest/cli/add_domain.sh #{name} #{options[:no_validate] ? "no_validate" : ""}" - end - - desc "remove ", "Remove a custom domain from the Caddyfile" - def remove(name) - run "sudo /usr/local/nest/cli/remove_domain.sh #{name}" - end - - def self.exit_on_failure? - return true - end - end - - class DB < Thor - include Thor::Actions - desc "create ", "Create a new Postgres database" - def create(name) - run "sudo -u postgres /usr/local/nest/cli/create_db.sh #{name}" - end - - def self.exit_on_failure? - return true - end - end - - class Setup < Thor - include Thor::Actions - desc "docker", "Set up rootless docker, so you can run docker containers" - def docker() - run "dockerd-rootless-setuptool.sh install" - run "sed -i '/^ExecStart/ s/$/ --exec-opt native.cgroupdriver=cgroupfs /' ~/.config/systemd/user/docker.service" - run "systemctl --user daemon-reload" - run "systemctl --user enable docker" - run "systemctl --user restart docker" - run "docker context use rootless" - puts "Successfully configured docker." - end - def self.exit_on_failure? - return true - end - end - - class Nest < Thor - include Thor::Actions - - desc "subdomain SUBCOMMAND ...ARGS", "manage your nest subdomains" - subcommand "subdomain", Subdomain - - desc "domain SUBCOMMAND ...ARGS", "manage your nest custom domains" - subcommand "domain", Domain - desc "setup SUBCOMMAND ...ARGS", "setup a tool to use on nest" - subcommand "setup", Setup - - desc "db SUBCOMMAND ...ARGS", "manage your nest postgres databases" - subcommand "db", DB - - desc "get_port", "Get an open port to use for your app" - def get_port() - # Hack - binding to port 0 will force the kernel to assign an open port, which we can then read - s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM - s.bind Addrinfo.tcp("127.0.0.1", 0) - port = s.local_address.ip_port - puts "Port #{port} is free to use!" - end - - desc "resources", "See your Nest resource usage and limits" - def resources() - quota = run("quota", { capture: true, verbose: false }) - diskUsage = ((quota.split("\n")[-1].split(/\s+/)[2].to_f) / (1024 * 1024)).round(2) - diskLimit = ((quota.split("\n")[-1].split(/\s+/)[3].to_f) / (1024 * 1024)).round(2) - - puts "Disk usage: #{diskUsage} GB used out of #{diskLimit} GB limit" - - id = Process.uid - memoryUsage = (Float(File.read("/sys/fs/cgroup/user.slice/user-#{id}.slice/memory.current")) / (1024 * 1024 * 1024)).round(2) - memoryLimit = (Float(File.read("/sys/fs/cgroup/user.slice/user-#{id}.slice/memory.high")) / (1024 * 1024 * 1024)).round(2) - - puts "Memory usage: #{memoryUsage} GB used out of #{memoryLimit} GB limit" - end - - def self.exit_on_failure? - return true - end - end - -end - -NestCLI::Nest.start(ARGV) diff --git a/cli/create_db.sh b/cli/helpers/create_db.sh similarity index 100% rename from cli/create_db.sh rename to cli/helpers/create_db.sh diff --git a/cli/index.js b/cli/index.js new file mode 100755 index 0000000..e6d13b3 --- /dev/null +++ b/cli/index.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node +const { Command } = require('commander'); +const program = new Command(); +const { execSync, spawn } = require('child_process'); +const net = require('net'); +const fs = require('fs'); + +function run(command) { + try { + console.log(`> ${command}`) + return execSync(command, { stdio: 'pipe' }).toString(); + } catch (error) { + console.error(error.message); + process.exit(1); + } +} +program + .name('nest') + .description('Yet another tilde caddy manager') + .version(require("./package.json").version); +program + .command('get_port') + .description('Get an open port to use for your app') + .action(() => { + const server = net.createServer(); + server.listen(0, '127.0.0.1', () => { + const port = server.address().port; + console.log(`Port ${port} is free to use!`); + server.close(); + }); + }); +program + .command('caddy [cmd] [args...]') + .description('Manages caddy') + .action((cmd, args) => { + const yatcmArgs = cmd ? [cmd, ...args] : args; + const yatcm = spawn('yatcm', yatcmArgs); + + yatcm.stdout.on('data', (data) => { + console.log(data.toString()); + }); + + yatcm.stderr.on('data', (data) => { + console.error(data.toString()); + }); + }); +program + .command('resources') + .description('See your Nest resource usage and limits') + .action(() => { + const output = run(`quota`).toString(); + const numbers = output.split("\n").find(line => line.includes("/dev/sda")).match(/\d+/g); + const array = numbers.map(Number) + const blockSize = 1024; + + const usedGB = (array[1] * blockSize) / (1024 ** 3); + const quotaGB = (array[2] * blockSize) / (1024 ** 3); + + console.log(`Disk usage: ${usedGB.toFixed(2)} GB used out of ${quotaGB.toFixed(2)} GB limit`); + + const userId = process.getuid() + const memoryUsage = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.current`)) / (1024 * 1024 * 1024)).toFixed(2); + const memoryLimit = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.high`)) / (1024 * 1024 * 1024)).toFixed(2); + console.log(`Memory usage: ${memoryUsage} GB used out of ${memoryLimit} GB limit`); + }); +const setup = program.command('setup'); +setup + .command('docker') + .description('Set up rootless docker, so you can run docker containers') + .action(() => { + run('dockerd-rootless-setuptool.sh install'); + run(`sed -i '/^ExecStart/ s/$/ --exec-opt native.cgroupdriver=cgroupfs /' ~/.config/systemd/user/docker.service`); + run('systemctl --user daemon-reload'); + run('systemctl --user enable docker'); + run('systemctl --user restart docker'); + run('docker context use rootless'); + console.log('Successfully configured docker.'); + }); +const db = program.command("db") + +db + .command('create ') + .description('Create a new Postgres database') + .action((name) => { + if (/[^a-z0-9]/gi.test(name)) { + console.error("Your database name can only include alphanumeric characters (a-Z, 0-9)") + process.exit(1) + } + run(`sudo -u postgres /usr/local/nest/cli/helpers/acreate_db.sh ${name}`); + }); + + +program.parse(process.argv); \ No newline at end of file diff --git a/cli/nest b/cli/nest deleted file mode 120000 index 2e4d867..0000000 --- a/cli/nest +++ /dev/null @@ -1 +0,0 @@ -cli.rb \ No newline at end of file diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..1c55560 --- /dev/null +++ b/cli/package.json @@ -0,0 +1,12 @@ +{ + "name": "nestcli", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "bin": { + "nest": "./index.js" + }, + "dependencies": { + "commander": "^12.1.0" + } +} diff --git a/cli/remove42_subdomain.sh b/cli/remove42_subdomain.sh deleted file mode 100755 index cb1b69d..0000000 --- a/cli/remove42_subdomain.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# Strip the subdomain of whitespace -SUBDOMAIN=$(echo "$1" | xargs) - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -FULL_SUBDOMAIN="$SUBDOMAIN.$NEST_USER.hackclub.dn42" - -# Check for existence of subdomain -if ! grep $FULL_SUBDOMAIN /etc/caddy/Caddyfile &> /dev/null; then - echo "You don't have this subdomain ($FULL_SUBDOMAIN)!" - exit 1 -fi - -# Set temp Caddyfile -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile - -# Remove configuration -sed -i "/^$FULL_SUBDOMAIN {/,/^ }/d" /var/nest-cli/root_caddyfile - -# Validate Caddyfile -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -# Save Caddyfile -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -rm /var/nest-cli/root_caddyfile - -# Format Caddyfile -caddy fmt --overwrite /etc/caddy/Caddyfile - -# Reload Caddy instance -systemctl reload caddy - -echo "Removed $FULL_SUBDOMAIN! You may still need to remove it from your Caddy configuration at ~/Caddyfile" \ No newline at end of file diff --git a/cli/remove_domain.sh b/cli/remove_domain.sh deleted file mode 100755 index 20fdc8f..0000000 --- a/cli/remove_domain.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -# Strip the domain of whitespace -FULL_DOMAIN=$(echo "$1" | xargs) - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -# Check for existence of domain -if ! grep $FULL_DOMAIN /etc/caddy/Caddyfile &> /dev/null; then - echo "This domain ($FULL_DOMAIN) isn't in the Caddyfile!" - exit 1 -fi - -# Check for user ownership of domain (JANK ALERT) -DOMAIN_BLOCK="$(grep -Pzo "(?s)$FULL_DOMAIN {.*?}\n" /etc/caddy/Caddyfile)" -if ! grep "/home/$NEST_USER/" <<< $DOMAIN_BLOCK &> /dev/null; then - echo "You don't own this domain ($FULL_DOMAIN)!" - exit 1 -fi - -# Set temp Caddyfile -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile - -# Remove configuration -sed -i "/^$FULL_DOMAIN {/,/^ }/d" /var/nest-cli/root_caddyfile - -# Validate Caddyfile -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -# Save Caddyfile -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -rm /var/nest-cli/root_caddyfile - -# Format Caddyfile -caddy fmt --overwrite /etc/caddy/Caddyfile - -# Reload Caddy instance -systemctl reload caddy - -echo "Removed $FULL_DOMAIN! You may still need to remove it from your Caddy configuration at ~/Caddyfile" \ No newline at end of file diff --git a/cli/remove_subdomain.sh b/cli/remove_subdomain.sh deleted file mode 100755 index 13eb28b..0000000 --- a/cli/remove_subdomain.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# Strip the subdomain of whitespace -SUBDOMAIN=$(echo "$1" | xargs) - -# "who am i" responds correctly even with sudo -NEST_USER=$(who am i | awk '{print $1}') - -if [ "$NEST_USER" = "root" ]; then - echo "Command cannot be run as root!" - exit 1 -fi - -FULL_SUBDOMAIN="$SUBDOMAIN.$NEST_USER.hackclub.app" - -# Check for existence of subdomain -if ! grep $FULL_SUBDOMAIN /etc/caddy/Caddyfile &> /dev/null; then - echo "You don't have this subdomain ($FULL_SUBDOMAIN)!" - exit 1 -fi - -# Set temp Caddyfile -cat /etc/caddy/Caddyfile > /var/nest-cli/root_caddyfile - -# Remove configuration -sed -i "/^$FULL_SUBDOMAIN {/,/^ }/d" /var/nest-cli/root_caddyfile - -# Validate Caddyfile -if ! caddy validate --config /var/nest-cli/root_caddyfile --adapter caddyfile &> /dev/null; then - echo "Error in root Caddyfile! Please contact the Nest admins (@nestadmins) in #nest" - exit 1 -fi - -# Save Caddyfile -cat /var/nest-cli/root_caddyfile > /etc/caddy/Caddyfile -rm /var/nest-cli/root_caddyfile - -# Format Caddyfile -caddy fmt --overwrite /etc/caddy/Caddyfile - -# Reload Caddy instance -systemctl reload caddy - -echo "Removed $FULL_SUBDOMAIN! You may still need to remove it from your Caddy configuration at ~/Caddyfile" \ No newline at end of file diff --git a/cli/root42_subdomain_template.txt b/cli/root42_subdomain_template.txt deleted file mode 100644 index 02c4142..0000000 --- a/cli/root42_subdomain_template.txt +++ /dev/null @@ -1,22 +0,0 @@ -..hackclub.dn42 { - tls { - ca https://acme.burble.dn42/v1/dn42/acme/directory - } - reverse_proxy unix//home//..dn42.webserver.sock { - health_status 2xx - health_interval 5s - health_timeout 60s - } - handle_errors { - @502 expression {err.status_code} == 502 - handle @502 { - respond * 502 { - body "This site is either down or does not exist. \nIf this site really does exist, please make sure your Caddy is running. Try systemctl --user start caddy. It is also possible you have a config issue, if so, please reach out to us on the slack #nest." - } - } - respond "{err.status_code} | {err.status_text} (on {re.host.1})" { - close - } - } -} - diff --git a/cli/root_domain_template.txt b/cli/root_domain_template.txt deleted file mode 100644 index 1e179de..0000000 --- a/cli/root_domain_template.txt +++ /dev/null @@ -1,19 +0,0 @@ - { - reverse_proxy unix//home//..webserver.sock { - health_status 2xx - health_interval 5s - health_timeout 60s - } - handle_errors { - @502 expression {err.status_code} == 502 - handle @502 { - respond * 502 { - body "This site is either down or does not exist. \nIf this site really does exist, please make sure your Caddy is running. Try systemctl --user start caddy. It is also possible you have a config issue, if so, please reach out to us on the slack #nest." - } - } - respond "{err.status_code} | {err.status_text} (on {re.host.1})" { - close - } - } -} - diff --git a/cli/root_subdomain_template.txt b/cli/root_subdomain_template.txt deleted file mode 100644 index 8866956..0000000 --- a/cli/root_subdomain_template.txt +++ /dev/null @@ -1,19 +0,0 @@ -..hackclub.app { - reverse_proxy unix//home//..webserver.sock { - health_status 2xx - health_interval 5s - health_timeout 60s - } - handle_errors { - @502 expression {err.status_code} == 502 - handle @502 { - respond * 502 { - body "This site is either down or does not exist. \nIf this site really does exist, please make sure your Caddy is running. Try systemctl --user start caddy. It is also possible you have a config issue, if so, please reach out to us on the slack #nest." - } - } - respond "{err.status_code} | {err.status_text} (on {re.host.1})" { - close - } - } -} - diff --git a/cli/user42_subdomain_template.txt b/cli/user42_subdomain_template.txt deleted file mode 100644 index d5c8ef4..0000000 --- a/cli/user42_subdomain_template.txt +++ /dev/null @@ -1,6 +0,0 @@ -http://..hackclub.dn42 { - bind unix/..dn42.webserver.sock|777 - - # Add your Caddy directives here! -} - diff --git a/cli/user_domain_template.txt b/cli/user_domain_template.txt deleted file mode 100644 index da95790..0000000 --- a/cli/user_domain_template.txt +++ /dev/null @@ -1,6 +0,0 @@ -http:// { - bind unix/..webserver.sock|777 - - # Add your Caddy directives here! -} - diff --git a/cli/user_subdomain_template.txt b/cli/user_subdomain_template.txt deleted file mode 100644 index 0505d42..0000000 --- a/cli/user_subdomain_template.txt +++ /dev/null @@ -1,6 +0,0 @@ -http://..hackclub.app { - bind unix/..webserver.sock|777 - - # Add your Caddy directives here! -} - diff --git a/cli/yarn.lock b/cli/yarn.lock new file mode 100644 index 0000000..d082ea0 --- /dev/null +++ b/cli/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== From 1677ce48c93761273b42ed84f5d62c0e7cc08595 Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:31:05 -0400 Subject: [PATCH 02/11] Change description for nest Co-authored-by: Samuel Fernandez <79737178+polypixeldev@users.noreply.github.com> --- cli/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/index.js b/cli/index.js index e6d13b3..93d5d97 100755 --- a/cli/index.js +++ b/cli/index.js @@ -16,7 +16,7 @@ function run(command) { } program .name('nest') - .description('Yet another tilde caddy manager') + .description('A command-line interface for Nest tools and services') .version(require("./package.json").version); program .command('get_port') From 5dbe7e7df65d15c4ae41d2cf8fbb83a85bc2ea1f Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:31:28 -0400 Subject: [PATCH 03/11] Fix create_db.sh naming Co-authored-by: Samuel Fernandez <79737178+polypixeldev@users.noreply.github.com> --- cli/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/index.js b/cli/index.js index 93d5d97..4abb362 100755 --- a/cli/index.js +++ b/cli/index.js @@ -86,7 +86,7 @@ db console.error("Your database name can only include alphanumeric characters (a-Z, 0-9)") process.exit(1) } - run(`sudo -u postgres /usr/local/nest/cli/helpers/acreate_db.sh ${name}`); + run(`sudo -u postgres /usr/local/nest/cli/helpers/create_db.sh ${name}`); }); From 0e9e2ac22ac9f4882773d2eafa9536ba0d946b44 Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:33:22 -0400 Subject: [PATCH 04/11] Add nest symlink --- cli/nest | 1 + 1 file changed, 1 insertion(+) create mode 120000 cli/nest diff --git a/cli/nest b/cli/nest new file mode 120000 index 0000000..945ce43 --- /dev/null +++ b/cli/nest @@ -0,0 +1 @@ +index.js \ No newline at end of file From 85458fdf1c4a523f703e61b60c2de356ea1bca53 Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:33:22 -0400 Subject: [PATCH 05/11] Migrate nest bot to new caddy manager --- nest-bot/src/os/os_functions.ts | 10 ++------- nest-bot/src/os/scripts/setup.sh | 1 - .../os/templates/root_caddyfile_config.txt | 21 ------------------- .../os/templates/user_caddyfile_config.txt | 2 +- 4 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 nest-bot/src/os/templates/root_caddyfile_config.txt diff --git a/nest-bot/src/os/os_functions.ts b/nest-bot/src/os/os_functions.ts index c06f25e..af2df2d 100644 --- a/nest-bot/src/os/os_functions.ts +++ b/nest-bot/src/os/os_functions.ts @@ -1,14 +1,8 @@ import { readFile, writeFile } from "fs/promises"; -import { exec, ExecException } from "child_process"; +import { exec, ExecException, execSync } from "child_process"; export async function add_root_caddyfile_config(username: string) { - const templateConfig = ( - await readFile("./src/os/templates/root_caddyfile_config.txt") - ).toString("utf8"); - - const newConfig = templateConfig.replace(//g, username); - - await writeFile("/etc/caddy/Caddyfile", newConfig, { flag: "a" }); + execSync(`nest caddy add ${username}.hackclub.app --user ${username}`) } function log_output(err: ExecException | null, stdout: string, stderr: string) { diff --git a/nest-bot/src/os/scripts/setup.sh b/nest-bot/src/os/scripts/setup.sh index 20a56e2..8df73a9 100755 --- a/nest-bot/src/os/scripts/setup.sh +++ b/nest-bot/src/os/scripts/setup.sh @@ -12,7 +12,6 @@ touch /var/lib/systemd/linger/$1 export XDG_RUNTIME_DIR=/run/user/$(id -u $1) systemctl --user -M $1@ daemon-reload systemctl --user -M $1@ enable caddy -systemctl reload caddy # Limits setquota -u $1 15G 15G 0 0 / diff --git a/nest-bot/src/os/templates/root_caddyfile_config.txt b/nest-bot/src/os/templates/root_caddyfile_config.txt deleted file mode 100644 index ffe2af1..0000000 --- a/nest-bot/src/os/templates/root_caddyfile_config.txt +++ /dev/null @@ -1,21 +0,0 @@ -.hackclub.app { - reverse_proxy unix//home//.webserver.sock { - health_status 2xx - health_interval 5s - health_timeout 60s - } - handle_errors { - @502 expression {err.status_code} == 502 - handle @502 { - respond * 502 { - body "This site is either down or does not exist. -If this site really does exist, please make sure your Caddy is running. Try systemctl --user start caddy. It is also possible you have a config issue, if so, please reach out to us on the slack #nest. -" - } - } - respond "{err.status_code} | {err.status_text} (on {re.host.1})" { - close - } - } -} - diff --git a/nest-bot/src/os/templates/user_caddyfile_config.txt b/nest-bot/src/os/templates/user_caddyfile_config.txt index 7363ecf..f03c958 100644 --- a/nest-bot/src/os/templates/user_caddyfile_config.txt +++ b/nest-bot/src/os/templates/user_caddyfile_config.txt @@ -2,7 +2,7 @@ admin unix//home//caddy-admin.sock } http://.hackclub.app { - bind unix/.webserver.sock|777 + bind unix/..hackclub.app.webserver.sock|777 root * /home//pub file_server { hide .git .env From 5b551f8a69e8e9fd904f1bf57f37638a793cba14 Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:21:23 -0400 Subject: [PATCH 06/11] merge yatcm & nest cli --- .gitignore | 3 + cli/.whitelist | 2 + cli/index.js | 95 +++++++++++++++--- cli/package.json | 7 +- cli/prisma/schema.prisma | 16 +++ cli/utils.js | 206 +++++++++++++++++++++++++++++++++++++++ cli/yarn.lock | 79 ++++++++++++++- 7 files changed, 392 insertions(+), 16 deletions(-) create mode 100644 .gitignore create mode 100644 cli/.whitelist create mode 100644 cli/prisma/schema.prisma create mode 100644 cli/utils.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8f054 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +*.sqlite \ No newline at end of file diff --git a/cli/.whitelist b/cli/.whitelist new file mode 100644 index 0000000..d64e3fa --- /dev/null +++ b/cli/.whitelist @@ -0,0 +1,2 @@ +*.$USERNAME.hackclub.app +$USERNAME.hackclub.app \ No newline at end of file diff --git a/cli/index.js b/cli/index.js index 4abb362..0c695e3 100755 --- a/cli/index.js +++ b/cli/index.js @@ -4,6 +4,13 @@ const program = new Command(); const { execSync, spawn } = require('child_process'); const net = require('net'); const fs = require('fs'); +const utils = require("./utils") +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); +const os = require("os") +var username = os.userInfo().username +const isAdmin = !process.getuid() || os.userInfo().username == "nest-internal" +const validator = require('validator'); function run(command) { try { @@ -29,21 +36,7 @@ program server.close(); }); }); -program - .command('caddy [cmd] [args...]') - .description('Manages caddy') - .action((cmd, args) => { - const yatcmArgs = cmd ? [cmd, ...args] : args; - const yatcm = spawn('yatcm', yatcmArgs); - - yatcm.stdout.on('data', (data) => { - console.log(data.toString()); - }); - yatcm.stderr.on('data', (data) => { - console.error(data.toString()); - }); - }); program .command('resources') .description('See your Nest resource usage and limits') @@ -89,5 +82,79 @@ db run(`sudo -u postgres /usr/local/nest/cli/helpers/create_db.sh ${name}`); }); +const caddy = program.command("caddy") +caddy +.command('list') + .description('lists all domains you have configured in caddy') + .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .action(async (options) => { + if (options?.user && isAdmin) username = options.user + var domains = await utils.getDomains(username) + domains = domains.map(domain => `- ${domain.domain} (${domain.proxy})`).join("\n") + console.log(domains) + }); +caddy +.command('add ') +.description('adds a domain to caddy') +.option('--proxy', 'changes where the domain should be proxied to (advanced)') +.option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') +.action(async (domain, options) => { + if (options?.user && isAdmin) username = options.user + if (!validator.isFQDN(domain)) { + console.error("This domain is not a valid domain name. Please choose a valid domain name.") + process.exit(1) + } + if (await utils.domainExists(domain)) { + console.error("This domain already has already been taken by you or someone else. Pick another one!") + process.exit(1) + } + if (utils.checkWhitelist(domain, username)) { + await prisma.domain.create({ + data: { + domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` + } + }) + await utils.reloadCaddy() + return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) + } + // Proceed as a regular domain + if (!await utils.checkVerification(domain, username)) { + console.error(`Please set the TXT record for domain-verification to your username (${username}). You can remove it after it is added.`) + process.exit(1) + } + await prisma.domain.create({ + data: { + domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` + } + }) + await utils.reloadCaddy() + return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) +}); +caddy +.command('rm ') + .description('removes a domain from caddy') + .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .action(async (domain, options) => { + if (options?.user && isAdmin) username = options.user + if (!validator.isFQDN(domain)) { + console.error("This domain is not a valid domain name. Please choose a valid domain name.") + process.exit(1) + } + if (!await utils.domainExists(domain)) { + console.error("This domain is not in Caddy.") + process.exit(1) + } + if (!await utils.domainOwnership(domain, username)) { + console.error("You do not own the domain, so you cannot remove it.") + process.exit(1) + } + await prisma.domain.delete({ + where: { + domain, username + } + }) + await utils.reloadCaddy() + console.log(`${domain} removed.`) + }); program.parse(process.argv); \ No newline at end of file diff --git a/cli/package.json b/cli/package.json index 1c55560..e2ea802 100644 --- a/cli/package.json +++ b/cli/package.json @@ -7,6 +7,11 @@ "nest": "./index.js" }, "dependencies": { - "commander": "^12.1.0" + "@prisma/client": "5.18.0", + "commander": "12.1.0", + "dotenv": "16.4.5", + "minimatch": "10.0.1", + "prisma": "5.18.0", + "validator": "13.12.0" } } diff --git a/cli/prisma/schema.prisma b/cli/prisma/schema.prisma new file mode 100644 index 0000000..2f81120 --- /dev/null +++ b/cli/prisma/schema.prisma @@ -0,0 +1,16 @@ +generator client { + provider = "prisma-client-js" + binaryTargets = ["native"] +} + +datasource db { + provider = "sqlite" + url = "file:./database.sqlite" +} + +model Domain { + domain String @id @unique + createdAt DateTime @default(now()) + username String + proxy String +} diff --git a/cli/utils.js b/cli/utils.js new file mode 100644 index 0000000..fecbce8 --- /dev/null +++ b/cli/utils.js @@ -0,0 +1,206 @@ +const fs = require("node:fs") +const { minimatch } = require('minimatch') +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); +const dns = require('node:dns').promises; +const os = require("os") +const isAdmin = !process.getuid() || os.userInfo().username == "nest-internal" + +module.exports = { + checkWhitelist: function (domain, username) { + if (!fs.existsSync(".whitelist")) return + const whitelist = fs.readFileSync(".whitelist", "utf8").split("\n") + + whitelist + var i = 0 + while (i < whitelist.length) { + if (minimatch(domain, whitelist[i].replace("$USERNAME", username))) return true + i++ + } + return false + }, + async getDomains(username) { + const domains = await prisma.domain.findMany({ + where: { + username + } + }) + return domains + }, + async domainExists(domain) { + const d = await prisma.domain.findFirst({ + where: { + domain + } + }) + if (!d) return false + else return true + }, + async domainOwnership(domain, username) { + if (isAdmin) return true // If sudo, skip. + const d = await prisma.domain.findFirst({ + where: { + domain, + username + } + }) + if (!d) return false + else return true + }, + async checkVerification(domain, username) { + if (isAdmin) return true // If sudo, skip. + try { + const records = await dns.resolveTxt(domain); + + for (const record of records) { + for (const entry of record) { + if (entry.includes('domain-verification=' + username)) { + return true; + } + } + } + + return false; + } catch (err) { + console.error('Error:', err); + return false; + } + }, + async reloadCaddy() { + const domains = await prisma.domain.findMany({ + where: { + + } + }) + var caddy = { + apps: { + http: { + servers: { + srv0: { + listen: [":443", ":80"], + routes: [], // normal routes + errors: { + routes: [] // error routing + } + } + } + }, + tls: { + automation: { + policies: [] + } + } + } + } + // dn42 certification support + + const dn42Domains = domains.filter(domain => domain.domain.endsWith(".dn42")).map(domain => domain.domain) + if (dn42Domains.length > 1) { + caddy.apps.tls.automation.policies.push({ + "subjects": dn42Domains, + "issuers": [ + { + "ca": "https://acme.burble.dn42/v1/dn42/acme/directory", + "module": "acme" + } + ] + }) + } + domains.forEach(domain => { + caddy.apps.http.servers.srv0.routes.push({ + "match": [ + { + "host": [ + domain.domain + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "reverse_proxy", + "health_checks": { + "active": { + "expect_status": 2, + "interval": 5000000000, + "timeout": 60000000000 + } + }, + "upstreams": [ + { + "dial": domain.proxy + } + ] + } + ] + } + ] + } + ], + "terminal": true + }) + + caddy.apps.http.servers.srv0.errors.routes.push({ + "match": [ + { + "host": [ + domain.domain + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "This site is either down or does not exist.\nIf this site really does exist, please make sure your Caddy is running. Try systemctl --user start caddy. It is also possible you have a >\n", + "handler": "static_response", + "status_code": 502 + } + ] + } + ] + } + ], + "match": [ + { + "expression": "{err.status_code} == 502" + } + ] + }, + { + "handle": [ + { + "body": "{err.status_code} | {err.status_text} (on {http.regexp.host.1})", + "close": true, + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + }) + }) + await fetch(process.env.CADDY_ADMIN_PATH || 'http://localhost:2019/load', { + unix: process.env.CADDY_SOCKET_PATH, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(caddy) + }); + } +} \ No newline at end of file diff --git a/cli/yarn.lock b/cli/yarn.lock index d082ea0..9137b04 100644 --- a/cli/yarn.lock +++ b/cli/yarn.lock @@ -2,7 +2,84 @@ # yarn lockfile v1 -commander@^12.1.0: +"@prisma/client@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.18.0.tgz#526e4281a448f214c0ff81d65c39243608c98294" + integrity sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw== + +"@prisma/debug@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.18.0.tgz#527799e044d2903a35945e61ac2d8916e4b61ead" + integrity sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw== + +"@prisma/engines-version@5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169": + version "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz#203426ebf4ec4e1acce7da4a59ec8f0df92b29e7" + integrity sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg== + +"@prisma/engines@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.18.0.tgz#26ea46e26498be622407cf95663d7fb4c39c895b" + integrity sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg== + dependencies: + "@prisma/debug" "5.18.0" + "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + "@prisma/fetch-engine" "5.18.0" + "@prisma/get-platform" "5.18.0" + +"@prisma/fetch-engine@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz#5b343e2b36b27e2713901ddd032ddd6932b3d55f" + integrity sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg== + dependencies: + "@prisma/debug" "5.18.0" + "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + "@prisma/get-platform" "5.18.0" + +"@prisma/get-platform@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.18.0.tgz#0dc4c82fe9a4971f4519a57cb2dd69d8e0df4b71" + integrity sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA== + dependencies: + "@prisma/debug" "5.18.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +commander@12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +dotenv@16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +minimatch@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + +prisma@5.18.0: + version "5.18.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.18.0.tgz#5ef69c802a075b7596231ea57003496873610b9e" + integrity sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g== + dependencies: + "@prisma/engines" "5.18.0" + +validator@13.12.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== From f89b049e91813ab30c9f71488094359e58ef911b Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:22:04 -0400 Subject: [PATCH 07/11] add caddy migrator to cli --- cli/caddy_migrate.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 cli/caddy_migrate.js diff --git a/cli/caddy_migrate.js b/cli/caddy_migrate.js new file mode 100644 index 0000000..b982c60 --- /dev/null +++ b/cli/caddy_migrate.js @@ -0,0 +1,31 @@ +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); +const data = JSON.parse("./caddyfile.json") + +const set = []; +const routes = data.apps.http.servers.srv0.routes; + +routes.forEach(route => { + const match = route.match && route.match[0]; + const handle = route.handle && route.handle[0]; + + if (match && match.host && handle && handle.routes) { + const proxyRoute = handle.routes[0].handle[0]; + if (proxyRoute.handler === 'reverse_proxy') { + const proxyDial = proxyRoute.upstreams[0].dial; + match.host.forEach(hostname => { + if (!hostname || !proxyDial) return + const match = proxyDial.match(/\/home\/([^\/]+)\//); + const username = (match && match[1]) ? match[1] : "root" + + set.push({ domain: hostname, proxy: proxyDial, username: username }); + }); + } + } +}); +async function main() { + await prisma.domain.create({ + data: set + }) +} +main() \ No newline at end of file From 39dc2aa68779752f05aab2f33987cf4f6d0161d9 Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Sat, 7 Sep 2024 20:46:07 -0400 Subject: [PATCH 08/11] finish migration to seperate files --- cli/commands/caddy.js | 84 ++++++++++++++++++++++ cli/commands/db.js | 14 ++++ cli/commands/get_port.js | 15 ++++ cli/commands/resources.js | 23 ++++++ cli/commands/setup.js | 15 ++++ cli/index.js | 145 ++------------------------------------ 6 files changed, 157 insertions(+), 139 deletions(-) create mode 100644 cli/commands/caddy.js create mode 100644 cli/commands/db.js create mode 100644 cli/commands/get_port.js create mode 100644 cli/commands/resources.js create mode 100644 cli/commands/setup.js diff --git a/cli/commands/caddy.js b/cli/commands/caddy.js new file mode 100644 index 0000000..91bd253 --- /dev/null +++ b/cli/commands/caddy.js @@ -0,0 +1,84 @@ +const utils = require("../utils") +const os = require("node:os") +const { PrismaClient } = require("@prisma/client"); +const prisma = new PrismaClient(); +const validator = require('validator'); + +module.exports = function ({ program, run }) { + const caddy = program.command("caddy") + var username = os.userInfo().username + caddy + .command('list') + .description('lists all domains you have configured in caddy') + .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .action(async (options) => { + if (options?.user) username = options.user + var domains = await utils.getDomains(username) + domains = domains.map(domain => `- ${domain.domain} (${domain.proxy})`).join("\n") + console.log(domains) + }); + caddy + .command('add ') + .description('adds a domain to caddy') + .option('--proxy', 'changes where the domain should be proxied to (advanced)') + .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .action(async (domain, options) => { + if (options?.user) username = options.user + if (!validator.isFQDN(domain)) { + console.error("This domain is not a valid domain name. Please choose a valid domain name.") + process.exit(1) + } + if (await utils.domainExists(domain)) { + console.error("This domain already has already been taken by you or someone else. Pick another one!") + process.exit(1) + } + if (utils.checkWhitelist(domain, username)) { + await prisma.domain.create({ + data: { + domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` + } + }) + await utils.reloadCaddy() + return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) + + } + // Proceed as a regular domain + if (!await utils.checkVerification(domain, username)) { + console.error(`Please set the TXT record for domain-verification to your username (${username}). You can remove it after it is added.`) + process.exit(1) + } + await prisma.domain.create({ + data: { + domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` + } + }) + await utils.reloadCaddy() + return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) + }); + caddy + .command('rm ') + .description('removes a domain from caddy') + .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .action(async (domain, options) => { + if (options?.user) username = options.user + if (!validator.isFQDN(domain)) { + console.error("This domain is not a valid domain name. Please choose a valid domain name.") + process.exit(1) + } + if (!await utils.domainExists(domain)) { + console.error("This domain is not in Caddy.") + process.exit(1) + } + if (!await utils.domainOwnership(domain, username)) { + console.error("You do not own the domain, so you cannot remove it.") + process.exit(1) + } + await prisma.domain.delete({ + where: { + domain, username + } + }) + await utils.reloadCaddy() + console.log(`${domain} removed.`) + }); +} \ No newline at end of file diff --git a/cli/commands/db.js b/cli/commands/db.js new file mode 100644 index 0000000..20cd232 --- /dev/null +++ b/cli/commands/db.js @@ -0,0 +1,14 @@ +module.exports = function ({ program, run }) { + const db = program.command("db") + + db + .command('create ') + .description('Create a new Postgres database') + .action((name) => { + if (/[^a-z0-9]/gi.test(name)) { + console.error("Your database name can only include alphanumeric characters (a-Z, 0-9)") + process.exit(1) + } + run(`sudo -u postgres /usr/local/nest/cli/helpers/create_db.sh ${name}`); + }); +} \ No newline at end of file diff --git a/cli/commands/get_port.js b/cli/commands/get_port.js new file mode 100644 index 0000000..05c665d --- /dev/null +++ b/cli/commands/get_port.js @@ -0,0 +1,15 @@ +const net = require('node:net'); + +module.exports = function ({ program, run }) { + program + .command('get_port') + .description('Get an open port to use for your app') + .action(() => { + const server = net.createServer(); + server.listen(0, '127.0.0.1', () => { + const port = server.address().port; + console.log(`Port ${port} is free to use!`); + server.close(); + }); + }); +} \ No newline at end of file diff --git a/cli/commands/resources.js b/cli/commands/resources.js new file mode 100644 index 0000000..6a39ea6 --- /dev/null +++ b/cli/commands/resources.js @@ -0,0 +1,23 @@ +const fs = require('node:fs'); + +module.exports = function ({ program, run }) { + program + .command('resources') + .description('See your Nest resource usage and limits') + .action(() => { + const output = run(`quota`).toString(); + const numbers = output.split("\n").find(line => line.includes("/dev/sda")).match(/\d+/g); + const array = numbers.map(Number) + const blockSize = 1024; + + const usedGB = (array[1] * blockSize) / (1024 ** 3); + const quotaGB = (array[2] * blockSize) / (1024 ** 3); + + console.log(`Disk usage: ${usedGB.toFixed(2)} GB used out of ${quotaGB.toFixed(2)} GB limit`); + + const userId = process.getuid() + const memoryUsage = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.current`)) / (1024 * 1024 * 1024)).toFixed(2); + const memoryLimit = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.high`)) / (1024 * 1024 * 1024)).toFixed(2); + console.log(`Memory usage: ${memoryUsage} GB used out of ${memoryLimit} GB limit`); + }); +} \ No newline at end of file diff --git a/cli/commands/setup.js b/cli/commands/setup.js new file mode 100644 index 0000000..52f7fd1 --- /dev/null +++ b/cli/commands/setup.js @@ -0,0 +1,15 @@ +module.exports = function ({ program, run }) { + const setup = program.command('setup'); + setup + .command('docker') + .description('Set up rootless docker, so you can run docker containers') + .action(() => { + run('dockerd-rootless-setuptool.sh install'); + run(`sed -i '/^ExecStart/ s/$/ --exec-opt native.cgroupdriver=cgroupfs /' ~/.config/systemd/user/docker.service`); + run('systemctl --user daemon-reload'); + run('systemctl --user enable docker'); + run('systemctl --user restart docker'); + run('docker context use rootless'); + console.log('Successfully configured docker.'); + }); +} \ No newline at end of file diff --git a/cli/index.js b/cli/index.js index 0c695e3..4b84519 100755 --- a/cli/index.js +++ b/cli/index.js @@ -1,16 +1,7 @@ #!/usr/bin/env node const { Command } = require('commander'); const program = new Command(); -const { execSync, spawn } = require('child_process'); -const net = require('net'); -const fs = require('fs'); -const utils = require("./utils") -const { PrismaClient } = require("@prisma/client"); -const prisma = new PrismaClient(); -const os = require("os") -var username = os.userInfo().username -const isAdmin = !process.getuid() || os.userInfo().username == "nest-internal" -const validator = require('validator'); +const { execSync } = require('child_process'); function run(command) { try { @@ -25,136 +16,12 @@ program .name('nest') .description('A command-line interface for Nest tools and services') .version(require("./package.json").version); -program - .command('get_port') - .description('Get an open port to use for your app') - .action(() => { - const server = net.createServer(); - server.listen(0, '127.0.0.1', () => { - const port = server.address().port; - console.log(`Port ${port} is free to use!`); - server.close(); - }); - }); - -program - .command('resources') - .description('See your Nest resource usage and limits') - .action(() => { - const output = run(`quota`).toString(); - const numbers = output.split("\n").find(line => line.includes("/dev/sda")).match(/\d+/g); - const array = numbers.map(Number) - const blockSize = 1024; - - const usedGB = (array[1] * blockSize) / (1024 ** 3); - const quotaGB = (array[2] * blockSize) / (1024 ** 3); - - console.log(`Disk usage: ${usedGB.toFixed(2)} GB used out of ${quotaGB.toFixed(2)} GB limit`); - const userId = process.getuid() - const memoryUsage = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.current`)) / (1024 * 1024 * 1024)).toFixed(2); - const memoryLimit = (parseFloat(fs.readFileSync(`/sys/fs/cgroup/user.slice/user-${userId}.slice/memory.high`)) / (1024 * 1024 * 1024)).toFixed(2); - console.log(`Memory usage: ${memoryUsage} GB used out of ${memoryLimit} GB limit`); - }); -const setup = program.command('setup'); -setup - .command('docker') - .description('Set up rootless docker, so you can run docker containers') - .action(() => { - run('dockerd-rootless-setuptool.sh install'); - run(`sed -i '/^ExecStart/ s/$/ --exec-opt native.cgroupdriver=cgroupfs /' ~/.config/systemd/user/docker.service`); - run('systemctl --user daemon-reload'); - run('systemctl --user enable docker'); - run('systemctl --user restart docker'); - run('docker context use rootless'); - console.log('Successfully configured docker.'); - }); -const db = program.command("db") -db - .command('create ') - .description('Create a new Postgres database') - .action((name) => { - if (/[^a-z0-9]/gi.test(name)) { - console.error("Your database name can only include alphanumeric characters (a-Z, 0-9)") - process.exit(1) - } - run(`sudo -u postgres /usr/local/nest/cli/helpers/create_db.sh ${name}`); - }); +require("./commands/setup")({ program, run }) +require("./commands/db")({ program, run }) +require("./commands/caddy")({ program, run }) +require("./commands/resources")({ program, run }) +require("./commands/get_port")({ program, run }) -const caddy = program.command("caddy") -caddy -.command('list') - .description('lists all domains you have configured in caddy') - .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') - .action(async (options) => { - if (options?.user && isAdmin) username = options.user - var domains = await utils.getDomains(username) - domains = domains.map(domain => `- ${domain.domain} (${domain.proxy})`).join("\n") - console.log(domains) - }); -caddy -.command('add ') -.description('adds a domain to caddy') -.option('--proxy', 'changes where the domain should be proxied to (advanced)') -.option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') -.action(async (domain, options) => { - if (options?.user && isAdmin) username = options.user - if (!validator.isFQDN(domain)) { - console.error("This domain is not a valid domain name. Please choose a valid domain name.") - process.exit(1) - } - if (await utils.domainExists(domain)) { - console.error("This domain already has already been taken by you or someone else. Pick another one!") - process.exit(1) - } - if (utils.checkWhitelist(domain, username)) { - await prisma.domain.create({ - data: { - domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` - } - }) - await utils.reloadCaddy() - return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) - - } - // Proceed as a regular domain - if (!await utils.checkVerification(domain, username)) { - console.error(`Please set the TXT record for domain-verification to your username (${username}). You can remove it after it is added.`) - process.exit(1) - } - await prisma.domain.create({ - data: { - domain, username, proxy: options?.proxy || `unix//home/${username}/.${domain}.webserver.sock` - } - }) - await utils.reloadCaddy() - return console.log(`${domain} added. (${options?.proxy || `unix//home/${username}/.${domain}.webserver.sock`})`) -}); -caddy -.command('rm ') - .description('removes a domain from caddy') - .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') - .action(async (domain, options) => { - if (options?.user && isAdmin) username = options.user - if (!validator.isFQDN(domain)) { - console.error("This domain is not a valid domain name. Please choose a valid domain name.") - process.exit(1) - } - if (!await utils.domainExists(domain)) { - console.error("This domain is not in Caddy.") - process.exit(1) - } - if (!await utils.domainOwnership(domain, username)) { - console.error("You do not own the domain, so you cannot remove it.") - process.exit(1) - } - await prisma.domain.delete({ - where: { - domain, username - } - }) - await utils.reloadCaddy() - console.log(`${domain} removed.`) - }); program.parse(process.argv); \ No newline at end of file From 54a066fdfe2202092bfed8351da1f2a7ed80fffb Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Sat, 7 Sep 2024 23:11:08 -0400 Subject: [PATCH 09/11] added original stuff + root checks --- cli/commands/caddy.js | 16 +++++++++++----- cli/utils.js | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/cli/commands/caddy.js b/cli/commands/caddy.js index 91bd253..d214380 100644 --- a/cli/commands/caddy.js +++ b/cli/commands/caddy.js @@ -3,7 +3,7 @@ const os = require("node:os") const { PrismaClient } = require("@prisma/client"); const prisma = new PrismaClient(); const validator = require('validator'); - +const isAdmin = !process.getuid() || os.userInfo().username == "nest-internal" module.exports = function ({ program, run }) { const caddy = program.command("caddy") var username = os.userInfo().username @@ -12,7 +12,9 @@ module.exports = function ({ program, run }) { .description('lists all domains you have configured in caddy') .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') .action(async (options) => { - if (options?.user) username = options.user + username = options?.user && !isAdmin + ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) + : options?.user; var domains = await utils.getDomains(username) domains = domains.map(domain => `- ${domain.domain} (${domain.proxy})`).join("\n") console.log(domains) @@ -21,9 +23,11 @@ module.exports = function ({ program, run }) { .command('add ') .description('adds a domain to caddy') .option('--proxy', 'changes where the domain should be proxied to (advanced)') - .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') + .option('--user', "allows you to list a different user's domains (requires sudo)") .action(async (domain, options) => { - if (options?.user) username = options.user + username = options?.user && !isAdmin + ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) + : options?.user; if (!validator.isFQDN(domain)) { console.error("This domain is not a valid domain name. Please choose a valid domain name.") process.exit(1) @@ -60,7 +64,9 @@ module.exports = function ({ program, run }) { .description('removes a domain from caddy') .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') .action(async (domain, options) => { - if (options?.user) username = options.user + username = options?.user && !isAdmin + ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) + : options?.user; if (!validator.isFQDN(domain)) { console.error("This domain is not a valid domain name. Please choose a valid domain name.") process.exit(1) diff --git a/cli/utils.js b/cli/utils.js index fecbce8..b5f9382 100644 --- a/cli/utils.js +++ b/cli/utils.js @@ -73,6 +73,18 @@ module.exports = { } }) var caddy = { + admin: { + listen: "unix//run/caddy/caddy-admin.sock" + }, + logging: { + logs: { + default: { + encoder: { + format: "console" + } + } + } + }, apps: { http: { servers: { @@ -88,6 +100,12 @@ module.exports = { tls: { automation: { policies: [] + }, + on_demand: { + permission: { + endpoint: "https://my.hackclub.app/ok", + module: "http" + } } } } From ccc2229ab51de6891150d251374a8913e4e9795c Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Sat, 7 Sep 2024 23:12:26 -0400 Subject: [PATCH 10/11] use string values for better reading --- cli/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/utils.js b/cli/utils.js index b5f9382..c3c31c3 100644 --- a/cli/utils.js +++ b/cli/utils.js @@ -144,8 +144,8 @@ module.exports = { "health_checks": { "active": { "expect_status": 2, - "interval": 5000000000, - "timeout": 60000000000 + "interval": "60s", + "timeout": "5s" } }, "upstreams": [ From dc901ac2272d0233dc25215b4ceb8b6c76c264cd Mon Sep 17 00:00:00 2001 From: David M <62346025+aboutdavid@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:21:47 -0400 Subject: [PATCH 11/11] change to if/else statements --- cli/commands/caddy.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/cli/commands/caddy.js b/cli/commands/caddy.js index d214380..d4afc80 100644 --- a/cli/commands/caddy.js +++ b/cli/commands/caddy.js @@ -12,9 +12,12 @@ module.exports = function ({ program, run }) { .description('lists all domains you have configured in caddy') .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') .action(async (options) => { - username = options?.user && !isAdmin - ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) - : options?.user; + if (options?.user && !isAdmin) { + console.error("To change/see another user's domains, you'll need to use sudo or be root.") + process.exit(1) + } else if (options?.user) { + username = options.user + } var domains = await utils.getDomains(username) domains = domains.map(domain => `- ${domain.domain} (${domain.proxy})`).join("\n") console.log(domains) @@ -25,9 +28,12 @@ module.exports = function ({ program, run }) { .option('--proxy', 'changes where the domain should be proxied to (advanced)') .option('--user', "allows you to list a different user's domains (requires sudo)") .action(async (domain, options) => { - username = options?.user && !isAdmin - ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) - : options?.user; + if (options?.user && !isAdmin) { + console.error("To change/see another user's domains, you'll need to use sudo or be root.") + process.exit(1) + } else if (options?.user) { + username = options.user + } if (!validator.isFQDN(domain)) { console.error("This domain is not a valid domain name. Please choose a valid domain name.") process.exit(1) @@ -64,9 +70,12 @@ module.exports = function ({ program, run }) { .description('removes a domain from caddy') .option('--user', 'allows you to add a domain on behalf of a user (requires sudo)') .action(async (domain, options) => { - username = options?.user && !isAdmin - ? (console.error("To change/see another user's domains, you'll need to use sudo or be root."), process.exit(1)) - : options?.user; + if (options?.user && !isAdmin) { + console.error("To change/see another user's domains, you'll need to use sudo or be root.") + process.exit(1) + } else if (options?.user) { + username = options.user + } if (!validator.isFQDN(domain)) { console.error("This domain is not a valid domain name. Please choose a valid domain name.") process.exit(1)