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/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/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 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/commands/caddy.js b/cli/commands/caddy.js new file mode 100644 index 0000000..d4afc80 --- /dev/null +++ b/cli/commands/caddy.js @@ -0,0 +1,99 @@ +const utils = require("../utils") +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 + 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) { + 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) + }); + 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 list a different user's domains (requires sudo)") + .action(async (domain, options) => { + 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) + } + 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) { + 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) + } + 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/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..4b84519 --- /dev/null +++ b/cli/index.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node +const { Command } = require('commander'); +const program = new Command(); +const { execSync } = require('child_process'); + +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('A command-line interface for Nest tools and services') + .version(require("./package.json").version); + + +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 }) + +program.parse(process.argv); \ No newline at end of file diff --git a/cli/nest b/cli/nest index 2e4d867..945ce43 120000 --- a/cli/nest +++ b/cli/nest @@ -1 +1 @@ -cli.rb \ No newline at end of file +index.js \ No newline at end of file diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..e2ea802 --- /dev/null +++ b/cli/package.json @@ -0,0 +1,17 @@ +{ + "name": "nestcli", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "bin": { + "nest": "./index.js" + }, + "dependencies": { + "@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/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/utils.js b/cli/utils.js new file mode 100644 index 0000000..c3c31c3 --- /dev/null +++ b/cli/utils.js @@ -0,0 +1,224 @@ +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 = { + admin: { + listen: "unix//run/caddy/caddy-admin.sock" + }, + logging: { + logs: { + default: { + encoder: { + format: "console" + } + } + } + }, + apps: { + http: { + servers: { + srv0: { + listen: [":443", ":80"], + routes: [], // normal routes + errors: { + routes: [] // error routing + } + } + } + }, + tls: { + automation: { + policies: [] + }, + on_demand: { + permission: { + endpoint: "https://my.hackclub.app/ok", + module: "http" + } + } + } + } + } + // 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": "60s", + "timeout": "5s" + } + }, + "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 new file mode 100644 index 0000000..9137b04 --- /dev/null +++ b/cli/yarn.lock @@ -0,0 +1,85 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@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== 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