diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 9207186..c6a57ab 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,4 +1,4 @@
# These are supported funding model platforms
-github: christianchiarulli
+github: [christianchiarulli, jchiarulli]
patreon: chrisatmachine
diff --git a/.gitignore b/.gitignore
index ac8a393..171e37b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
relaywiz
+
+# Ignore .DS_Store files
+.DS_Store
diff --git a/README.md b/README.md
index a0b121d..e77383f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
Relay Wizard 🪄
+
Relay Wizard 🧙
@@ -35,7 +35,7 @@ The program will automate the following steps:
To install a relay, spin up a new Debian server, hook up a domain name, and run the following command:
```bash
-curl -sL https://relayrunner.org/relaywizard.sh | bash
+curl -sL https://relaywizard.com/install.sh | bash
```
## Learn more
diff --git a/cmd/install.go b/cmd/install.go
index 107af41..ce09785 100644
--- a/cmd/install.go
+++ b/cmd/install.go
@@ -1,11 +1,12 @@
package cmd
import (
- "github.com/nodetec/relaywiz/pkg/manager"
- "github.com/nodetec/relaywiz/pkg/network"
- "github.com/nodetec/relaywiz/pkg/relays/khatru_pyramid"
- "github.com/nodetec/relaywiz/pkg/relays/strfry"
- "github.com/nodetec/relaywiz/pkg/ui"
+ "github.com/nodetec/rwz/pkg/manager"
+ "github.com/nodetec/rwz/pkg/network"
+ "github.com/nodetec/rwz/pkg/relays/khatru29"
+ "github.com/nodetec/rwz/pkg/relays/khatru_pyramid"
+ "github.com/nodetec/rwz/pkg/relays/strfry"
+ "github.com/nodetec/rwz/pkg/ui"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
@@ -23,10 +24,10 @@ var installCmd = &cobra.Command{
pterm.Println(pterm.Yellow("Leave email empty if you don't want to receive notifications from Let's Encrypt about your SSL cert."))
pterm.Println()
ssl_email, _ := pterm.DefaultInteractiveTextInput.Show("Email address")
-
pterm.Println()
+
// Supported relay options
- options := []string{"Khatru Pyramid", "strfry"}
+ options := []string{"Khatru Pyramid", "strfry", "khatru29"}
// Use PTerm's interactive select feature to present the options to the user and capture their selection
// The Show() method displays the options and waits for the user's input
@@ -35,10 +36,14 @@ var installCmd = &cobra.Command{
// Display the selected option to the user with a green color for emphasis
pterm.Info.Printfln("Selected option: %s", pterm.Green(selectedRelayOption))
+ var privkey string
var pubkey string
if selectedRelayOption == "Khatru Pyramid" {
pterm.Println()
pubkey, _ = pterm.DefaultInteractiveTextInput.Show("Public key (hex not npub)")
+ } else if selectedRelayOption == "khatru29" {
+ pterm.Println()
+ privkey, _ = pterm.DefaultInteractiveTextInput.Show("Private key (hex not nsec)")
}
pterm.Println()
@@ -48,53 +53,91 @@ var installCmd = &cobra.Command{
// Step 1: Install necessary packages using APT
manager.AptInstallPackages()
- if selectedRelayOption == "strfry" {
- strfry.AptInstallDependencies()
- }
-
- // Step 2: Configure the firewall
- network.ConfigureFirewall()
-
- // Step 3: Configure Nginx for HTTP
if selectedRelayOption == "Khatru Pyramid" {
- khatru_pyramid.ConfigureNginxHttp(relayDomain)
- } else if selectedRelayOption == "strfry" {
- strfry.ConfigureNginxHttp(relayDomain)
- }
+ // Step 2: Configure the firewall
+ network.ConfigureFirewall()
- // Step 4: Get SSL certificates
- var shouldContinue = network.GetCertificates(relayDomain, ssl_email)
+ // Step 3: Configure Nginx for HTTP
+ khatru_pyramid.ConfigureNginxHttp(relayDomain)
- if !shouldContinue {
- return
- }
+ // Step 4: Get SSL certificates
+ var shouldContinue = network.GetCertificates(relayDomain, ssl_email)
+ if !shouldContinue {
+ return
+ }
- // Step 5: Configure Nginx for HTTPS
- if selectedRelayOption == "Khatru Pyramid" {
+ // Step 5: Configure Nginx for HTTPS
khatru_pyramid.ConfigureNginxHttps(relayDomain)
- } else if selectedRelayOption == "strfry" {
- strfry.ConfigureNginxHttps(relayDomain)
- }
- // Step 6: Download and install the relay binary
- if selectedRelayOption == "Khatru Pyramid" {
+ // Step 6: Download and install the relay binary
khatru_pyramid.InstallRelayBinary()
- } else if selectedRelayOption == "strfry" {
- strfry.InstallRelayBinary()
- }
- // Step 7: Set up the relay service
- if selectedRelayOption == "Khatru Pyramid" {
+ // Step 7: Set up the relay service
khatru_pyramid.SetupRelayService(relayDomain, pubkey)
+
+ // Step 8: Show success messages
+ khatru_pyramid.SuccessMessages(relayDomain)
} else if selectedRelayOption == "strfry" {
+ // Step 2: Install necessary strfry package dependencies
+ strfry.AptInstallDependencies()
+
+ // Step 3: Configure the firewall
+ network.ConfigureFirewall()
+
+ // Step 4: Configure Nginx for HTTP
+ strfry.ConfigureNginxHttp(relayDomain)
+
+ // Step 5: Get SSL certificates
+ var shouldContinue = network.GetCertificates(relayDomain, ssl_email)
+ if !shouldContinue {
+ return
+ }
+
+ // Step 6: Configure Nginx for HTTPS
+ strfry.ConfigureNginxHttps(relayDomain)
+
+ // Step 7: Download and install the relay binary
+ strfry.InstallRelayBinary()
+
+ // Step 8: Set up the relay service
strfry.SetupRelayService(relayDomain)
+
+ // Step 9: Show success messages
+ strfry.SuccessMessages(relayDomain)
+ } else if selectedRelayOption == "khatru29" {
+ // Step 2: Configure the firewall
+ network.ConfigureFirewall()
+
+ // Step 3: Configure Nginx for HTTP
+ khatru29.ConfigureNginxHttp(relayDomain)
+
+ // Step 4: Get SSL certificates
+ var shouldContinue = network.GetCertificates(relayDomain, ssl_email)
+ if !shouldContinue {
+ return
+ }
+
+ // Step 5: Configure Nginx for HTTPS
+ khatru29.ConfigureNginxHttps(relayDomain)
+
+ // Step 6: Download and install the relay binary
+ khatru29.InstallRelayBinary()
+
+ // Step 7: Set up the relay service
+ khatru29.SetupRelayService(relayDomain, privkey)
+
+ // Step 8: Show success messages
+ khatru29.SuccessMessages(relayDomain)
}
pterm.Println()
- pterm.Println(pterm.Magenta("The installation is complete."))
- pterm.Println(pterm.Magenta("You can access your relay at wss://" + relayDomain))
+ pterm.Println(pterm.Magenta("Join the NODE-TEC Discord to get support:"))
+ pterm.Println(pterm.Magenta("https://discord.gg/J9gRK5pbWb"))
+ pterm.Println()
+ pterm.Println(pterm.Magenta("We plan to use relay groups for support in the future..."))
+
pterm.Println()
- pterm.Println(pterm.Magenta("You can re-run this installer with `relaywiz install`."))
+ pterm.Println(pterm.Magenta("You can re-run this installer with `rwz install`."))
},
}
diff --git a/cmd/root.go b/cmd/root.go
index e4cfdc4..e735b3d 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -6,9 +6,9 @@ import (
)
var rootCmd = &cobra.Command{
- Use: "relaywiz",
+ Use: "rwz",
Short: "A wizard for relay runners",
- Long: `relaywiz is a CLI tool for relays operators that
+ Long: `rwz is a CLI tool for relays operators that
helps install and configure your relay.`,
}
@@ -30,5 +30,5 @@ func init() {
// Cobra also supports local flags, which will only run
// when this action is called directly.
- rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+ // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
diff --git a/go.mod b/go.mod
index 52f4f1f..60f8778 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/nodetec/relaywiz
+module github.com/nodetec/rwz
go 1.22.4
diff --git a/main.go b/main.go
index e600d09..b6092fd 100644
--- a/main.go
+++ b/main.go
@@ -1,7 +1,7 @@
package main
import (
- "github.com/nodetec/relaywiz/cmd"
+ "github.com/nodetec/rwz/cmd"
)
func main() {
diff --git a/pkg/network/certbot.go b/pkg/network/certbot.go
index c3cfd8e..0cd3e65 100644
--- a/pkg/network/certbot.go
+++ b/pkg/network/certbot.go
@@ -2,7 +2,7 @@ package network
import (
"fmt"
- "github.com/nodetec/relaywiz/pkg/utils"
+ "github.com/nodetec/rwz/pkg/utils"
"github.com/pterm/pterm"
"log"
"os"
diff --git a/pkg/relays/khatru29/install.go b/pkg/relays/khatru29/install.go
new file mode 100644
index 0000000..cffab49
--- /dev/null
+++ b/pkg/relays/khatru29/install.go
@@ -0,0 +1,84 @@
+package khatru29
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+// Function to download and make the binary executable
+func InstallRelayBinary() {
+ // URL of the binary to download
+ const downloadURL = "https://github.com/nodetec/relays/releases/download/v0.1.0/khatru29-0.4.0-x86_64-linux-gnu.tar.gz"
+
+ // Name of the binary after downloading
+ const binaryName = "nostr-relay-khatru29"
+
+ // Destination directory for the binary
+ const destDir = "/usr/local/bin"
+
+ // Data directory for the relay
+ const dataDir = "/var/lib/nostr-relay-khatru29"
+
+ spinner, _ := pterm.DefaultSpinner.Start("Installing khatru29 relay...")
+
+ // Ensure the data directory exists
+ err := os.MkdirAll(dataDir, 0755)
+ if err != nil {
+ log.Fatalf("Error creating data directory: %v", err)
+ }
+
+ // Determine the file name from the URL
+ tempFileName := filepath.Base(downloadURL)
+
+ // Create the temporary file
+ out, err := os.Create(fmt.Sprintf("/tmp/%s", tempFileName))
+ if err != nil {
+ log.Fatalf("Error creating temporary file: %v", err)
+ }
+ defer out.Close()
+
+ // Download the file
+ resp, err := http.Get(downloadURL)
+ if err != nil {
+ log.Fatalf("Error downloading file: %v", err)
+ }
+ defer resp.Body.Close()
+
+ // Check server response
+ if resp.StatusCode != http.StatusOK {
+ log.Fatalf("Bad status: %s", resp.Status)
+ }
+
+ // Write the body to the temporary file
+ _, err = io.Copy(out, resp.Body)
+ if err != nil {
+ log.Fatalf("Error writing to temporary file: %v", err)
+ }
+
+ // Extract binary
+ err = exec.Command("tar", "-xf", fmt.Sprintf("/tmp/%s", tempFileName), "-C", fmt.Sprintf("%s", destDir)).Run()
+ if err != nil {
+ log.Fatalf("Error extracting binary to /usr/local/bin: %v", err)
+ }
+
+ // TODO
+ // Currently, the downloaded binary is expected to have a name that matches the binaryName variable
+ // Ideally, the extracted binary file should be renamed to match the binaryName variable
+
+ // Define the final destination path
+ destPath := filepath.Join(destDir, binaryName)
+
+ // Make the file executable
+ err = os.Chmod(destPath, 0755)
+ if err != nil {
+ log.Fatalf("Error making file executable: %v", err)
+ }
+
+ spinner.Success("khatru29 relay installed successfully.")
+}
diff --git a/pkg/relays/khatru29/nginx_http.go b/pkg/relays/khatru29/nginx_http.go
new file mode 100644
index 0000000..1afa220
--- /dev/null
+++ b/pkg/relays/khatru29/nginx_http.go
@@ -0,0 +1,71 @@
+package khatru29
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "log"
+ "os"
+ "os/exec"
+)
+
+// Function to configure nginx for HTTP
+func ConfigureNginxHttp(domainName string) {
+ spinner, _ := pterm.DefaultSpinner.Start("Configuring nginx for HTTP...")
+
+ err := os.MkdirAll(fmt.Sprintf("/var/www/%s/.well-known/acme-challenge/", domainName), 0755)
+ if err != nil {
+ log.Fatalf("Error creating directories: %v", err)
+ }
+
+ const configFile = "nostr_relay_khatru29.conf"
+
+ err = os.Remove(fmt.Sprintf("/etc/nginx/conf.d/%s", configFile))
+ if err != nil && !os.IsNotExist(err) {
+ log.Fatalf("Error removing existing nginx configuration: %v", err)
+ }
+
+ var configContent string
+
+ configContent = fmt.Sprintf(`map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
+upstream websocket_khatru29 {
+ server 0.0.0.0:5577;
+}
+
+# %s
+server {
+ listen 80;
+ listen [::]:80;
+ server_name %s;
+
+ location /.well-known/acme-challenge/ {
+ root /var/www/%s;
+ allow all;
+ }
+
+ location / {
+ proxy_pass http://websocket_khatru29;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ }
+}
+`, domainName, domainName, domainName)
+
+ err = os.WriteFile(fmt.Sprintf("/etc/nginx/conf.d/%s", configFile), []byte(configContent), 0644)
+ if err != nil {
+ log.Fatalf("Error writing nginx configuration: %v", err)
+ }
+
+ err = exec.Command("systemctl", "restart", "nginx").Run()
+ if err != nil {
+ log.Fatalf("Error reloading nginx: %v", err)
+ }
+
+ spinner.Success("Nginx configured for HTTP")
+}
diff --git a/pkg/relays/khatru29/nginx_https.go b/pkg/relays/khatru29/nginx_https.go
new file mode 100644
index 0000000..8137896
--- /dev/null
+++ b/pkg/relays/khatru29/nginx_https.go
@@ -0,0 +1,142 @@
+package khatru29
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "log"
+ "os"
+ "os/exec"
+)
+
+// Function to configure nginx for HTTPS
+func ConfigureNginxHttps(domainName string) {
+ spinner, _ := pterm.DefaultSpinner.Start("Configuring nginx for HTTPS...")
+
+ const configFile = "nostr_relay_khatru29.conf"
+
+ err := os.Remove(fmt.Sprintf("/etc/nginx/conf.d/%s", configFile))
+ if err != nil && !os.IsNotExist(err) {
+ log.Fatalf("Error removing existing nginx configuration: %v", err)
+ }
+
+ var configContent string
+
+ configContent = fmt.Sprintf(`map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
+upstream websocket_khatru29 {
+ server 0.0.0.0:5577;
+}
+
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name %s;
+
+ root /var/www/%s;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to displaying 404.
+ try_files $uri $uri/ =404;
+ proxy_pass http://websocket_khatru29;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ }
+
+ #### SSL Configuration ####
+ # Test configuration:
+ # https://www.ssllabs.com/ssltest/analyze.html
+ # https://cryptcheck.fr/
+ ssl_certificate /etc/letsencrypt/live/%s/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/%s/privkey.pem;
+ # Verify chain of trust of OCSP response using Root CA and Intermediate certs
+ ssl_trusted_certificate /etc/letsencrypt/live/%s/chain.pem;
+
+ # Only return Nginx in server header
+ server_tokens off;
+
+ # TODO
+ # Add support to generate the file in the script
+ #ssl_dhparam /etc/ssl/certs/dhparam.pem;
+
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ # For more information on the security of different cipher suites, you can refer to the following link:
+ # https://ciphersuite.info/
+ # Compilation of the top cipher suites 2024:
+ # https://ssl-config.mozilla.org/#server=nginx
+ ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305";
+
+ # Perfect Forward Secrecy (PFS) is frequently compromised without this
+ ssl_prefer_server_ciphers on;
+
+ ssl_session_tickets off;
+
+ # Enable SSL session caching for improved performance
+ # Try setting ssl_session_timeout to 1d if performance is bad
+ ssl_session_timeout 10m;
+ ssl_session_cache shared:SSL:10m;
+
+ # By default, the buffer size is 16k, which corresponds to minimal overhead when sending big responses.
+ # To minimize Time To First Byte it may be beneficial to use smaller values
+ ssl_buffer_size 8k;
+
+ # OCSP stapling
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ # Security headers
+ # Test configuration:
+ # https://securityheaders.com/
+ # https://observatory.mozilla.org/
+ add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
+
+ add_header X-Frame-Options DENY;
+
+ # Avoid MIME type sniffing
+ add_header X-Content-Type-Options nosniff always;
+
+ add_header Referrer-Policy "no-referrer" always;
+
+ add_header X-XSS-Protection 0 always;
+
+ add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()" always;
+
+ # Content-Security-Policy (CSP)
+ add_header Content-Security-Policy "base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests;" always;
+}
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name %s;
+
+ location /.well-known/acme-challenge/ {
+ root /var/www/%s;
+ allow all;
+ }
+
+ location / {
+ return 301 https://%s$request_uri;
+ }
+}
+`, domainName, domainName, domainName, domainName, domainName, domainName, domainName, domainName)
+
+ err = os.WriteFile(fmt.Sprintf("/etc/nginx/conf.d/%s", configFile), []byte(configContent), 0644)
+ if err != nil {
+ log.Fatalf("Error writing nginx configuration: %v", err)
+ }
+
+ err = exec.Command("systemctl", "reload", "nginx").Run()
+ if err != nil {
+ log.Fatalf("Error reloading nginx: %v", err)
+ }
+
+ spinner.Success("Nginx configured for HTTPS")
+}
diff --git a/pkg/relays/khatru29/service.go b/pkg/relays/khatru29/service.go
new file mode 100644
index 0000000..4fbbcd3
--- /dev/null
+++ b/pkg/relays/khatru29/service.go
@@ -0,0 +1,159 @@
+package khatru29
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "log"
+ "os"
+ "os/exec"
+ "text/template"
+)
+
+// Function to check if a user exists
+func userExists(username string) bool {
+ cmd := exec.Command("id", "-u", username)
+ err := cmd.Run()
+ return err == nil
+}
+
+// Function to set up the relay service
+func SetupRelayService(domain, privKey string) {
+ // Template for the systemd service file
+ const serviceTemplate = `[Unit]
+Description=Nostr Relay Khatru29
+After=network.target
+
+[Service]
+Type=simple
+User=nostr
+Group=nostr
+WorkingDirectory=/home/nostr
+EnvironmentFile=/etc/systemd/system/nostr-relay-khatru29.env
+ExecStart=/usr/local/bin/nostr-relay-khatru29
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+`
+
+ // Template for the environment file
+ const envTemplate = `
+PORT=5577
+DOMAIN={{.Domain}}
+RELAY_NAME=nostr-relay-khatru29
+RELAY_PRIVKEY={{.PrivKey}}
+RELAY_DESCRIPTION=Khatru29 Nostr Relay
+RELAY_CONTACT=devs@node-tec.com
+DATABASE_PATH=/var/lib/nostr-relay-khatru29/db
+`
+ // Path for the systemd service file
+ const serviceFilePath = "/etc/systemd/system/nostr-relay-khatru29.service"
+
+ // Path for the environment file
+ const envFilePath = "/etc/systemd/system/nostr-relay-khatru29.env"
+
+ // Data directory
+ const dataDir = "/var/lib/nostr-relay-khatru29"
+
+ // Relay service
+ const relayService = "nostr-relay-khatru29"
+
+ spinner, _ := pterm.DefaultSpinner.Start("Configuring relay service...")
+
+ // Check if the service file exists and remove it if it does
+ if _, err := os.Stat(serviceFilePath); err == nil {
+ err = os.Remove(serviceFilePath)
+ if err != nil {
+ log.Fatalf("Error removing service file: %v", err)
+ }
+ }
+
+ // Check if the environment file exists and remove it if it does
+ if _, err := os.Stat(envFilePath); err == nil {
+ err = os.Remove(envFilePath)
+ if err != nil {
+ log.Fatalf("Error removing environment file: %v", err)
+ }
+ }
+
+ // Ensure the user for the relay service exists
+ if !userExists("nostr") {
+ spinner.UpdateText("Creating user 'nostr'...")
+ err := exec.Command("adduser", "--disabled-login", "--gecos", "", "nostr").Run()
+ if err != nil {
+ log.Fatalf("Error creating user: %v", err)
+ }
+ } else {
+ spinner.UpdateText("User 'nostr' already exists")
+ }
+
+ // Ensure the data directory exists and set ownership
+ spinner.UpdateText("Creating data directory...")
+ err := os.MkdirAll(dataDir, 0755)
+ if err != nil {
+ log.Fatalf("Error creating data directory: %v", err)
+ }
+
+ // Use chown command to set ownership of the data directory to the nostr user
+ err = exec.Command("chown", "-R", "nostr:nostr", dataDir).Run()
+ if err != nil {
+ log.Fatalf("Error setting ownership of the data directory: %v", err)
+ }
+
+ // Create the environment file
+ spinner.UpdateText("Creating environment file...")
+ envFile, err := os.Create(envFilePath)
+ if err != nil {
+ log.Fatalf("Error creating environment file: %v", err)
+ }
+ defer envFile.Close()
+
+ envTmpl, err := template.New("env").Parse(envTemplate)
+ if err != nil {
+ log.Fatalf("Error parsing environment template: %v", err)
+ }
+
+ err = envTmpl.Execute(envFile, struct{ Domain, PrivKey string }{Domain: domain, PrivKey: privKey})
+ if err != nil {
+ log.Fatalf("Error executing environment template: %v", err)
+ }
+
+ // Create the systemd service file
+ spinner.UpdateText("Creating service file...")
+ serviceFile, err := os.Create(serviceFilePath)
+ if err != nil {
+ log.Fatalf("Error creating service file: %v", err)
+ }
+ defer serviceFile.Close()
+
+ tmpl, err := template.New("service").Parse(serviceTemplate)
+ if err != nil {
+ log.Fatalf("Error parsing service template: %v", err)
+ }
+
+ err = tmpl.Execute(serviceFile, struct{ Domain, PrivKey string }{Domain: domain, PrivKey: privKey})
+ if err != nil {
+ log.Fatalf("Error executing service template: %v", err)
+ }
+
+ // Reload systemd to apply the new service
+ spinner.UpdateText("Reloading systemd daemon...")
+ err = exec.Command("systemctl", "daemon-reload").Run()
+ if err != nil {
+ log.Fatalf("Error reloading systemd daemon: %v", err)
+ }
+
+ // Enable and start the nostr relay service
+ spinner.UpdateText("Enabling and starting service...")
+ err = exec.Command("systemctl", "enable", fmt.Sprintf("%s", relayService)).Run()
+ if err != nil {
+ log.Fatalf("Error enabling nostr relay service: %v", err)
+ }
+
+ err = exec.Command("systemctl", "start", fmt.Sprintf("%s", relayService)).Run()
+ if err != nil {
+ log.Fatalf("Error starting nostr relay service: %v", err)
+ }
+
+ spinner.Success("Nostr relay service configured")
+}
diff --git a/pkg/relays/khatru29/success-messages.go b/pkg/relays/khatru29/success-messages.go
new file mode 100644
index 0000000..0719eed
--- /dev/null
+++ b/pkg/relays/khatru29/success-messages.go
@@ -0,0 +1,43 @@
+package khatru29
+
+import (
+ "github.com/pterm/pterm"
+)
+
+func SuccessMessages(domain string) {
+ const dataDir = "/var/lib/nostr-relay-khatru29"
+ const envFile = "/etc/systemd/system/nostr-relay-khatru29.env"
+ const service = "nostr-relay-khatru29"
+ const githubLink = "https://github.com/fiatjaf/relay29/tree/master"
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("The installation is complete."))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("You can access your relay at:"))
+ pterm.Println(pterm.Magenta("wss://" + domain))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's data directory is located here:"))
+ pterm.Println(pterm.Magenta(dataDir))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's environment file is located here:"))
+ pterm.Println(pterm.Magenta(envFile))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To check the status of your relay run:"))
+ pterm.Println(pterm.Magenta("systemctl status " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To reload the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl reload " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To restart the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl restart " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Khatru29 GitHub"))
+ pterm.Println(pterm.Magenta(githubLink))
+}
diff --git a/pkg/relays/khatru_pyramid/install.go b/pkg/relays/khatru_pyramid/install.go
index 9c96ff6..a1f10f1 100644
--- a/pkg/relays/khatru_pyramid/install.go
+++ b/pkg/relays/khatru_pyramid/install.go
@@ -1,18 +1,20 @@
package khatru_pyramid
import (
+ "fmt"
"github.com/pterm/pterm"
"io"
"log"
"net/http"
"os"
+ "os/exec"
"path/filepath"
)
// Function to download and make the binary executable
func InstallRelayBinary() {
// URL of the binary to download
- const downloadURL = "https://github.com/github-tijlxyz/khatru-pyramid/releases/download/v0.0.5/khatru-pyramid-v0.0.5-linux-amd64"
+ const downloadURL = "https://github.com/nodetec/relays/releases/download/v0.1.0/khatru-pyramid-0.0.5-x86_64-linux-gnu.tar.gz"
// Name of the binary after downloading
const binaryName = "nostr-relay-khatru-pyramid"
@@ -24,6 +26,7 @@ func InstallRelayBinary() {
const dataDir = "/var/lib/nostr-relay-khatru-pyramid"
spinner, _ := pterm.DefaultSpinner.Start("Installing Khatru Pyramid relay...")
+
// Ensure the data directory exists
err := os.MkdirAll(dataDir, 0755)
if err != nil {
@@ -34,7 +37,7 @@ func InstallRelayBinary() {
tempFileName := filepath.Base(downloadURL)
// Create the temporary file
- out, err := os.Create(tempFileName)
+ out, err := os.Create(fmt.Sprintf("/tmp/%s", tempFileName))
if err != nil {
log.Fatalf("Error creating temporary file: %v", err)
}
@@ -58,15 +61,19 @@ func InstallRelayBinary() {
log.Fatalf("Error writing to temporary file: %v", err)
}
- // Define the final destination path
- destPath := filepath.Join(destDir, binaryName)
-
- // Move the file to the destination directory
- err = os.Rename(tempFileName, destPath)
+ // Extract binary
+ err = exec.Command("tar", "-xf", fmt.Sprintf("/tmp/%s", tempFileName), "-C", fmt.Sprintf("%s", destDir)).Run()
if err != nil {
- log.Fatalf("Error moving file to /usr/local/bin: %v", err)
+ log.Fatalf("Error extracting binary to /usr/local/bin: %v", err)
}
+ // TODO
+ // Currently, the downloaded binary is expected to have a name that matches the binaryName variable
+ // Ideally, the extracted binary file should be renamed to match the binaryName variable
+
+ // Define the final destination path
+ destPath := filepath.Join(destDir, binaryName)
+
// Make the file executable
err = os.Chmod(destPath, 0755)
if err != nil {
diff --git a/pkg/relays/khatru_pyramid/nginx_http.go b/pkg/relays/khatru_pyramid/nginx_http.go
index ae4191c..df5f9b3 100644
--- a/pkg/relays/khatru_pyramid/nginx_http.go
+++ b/pkg/relays/khatru_pyramid/nginx_http.go
@@ -2,7 +2,6 @@ package khatru_pyramid
import (
"fmt"
- // "github.com/nodetec/relaywiz/pkg/utils"
"github.com/pterm/pterm"
"log"
"os"
@@ -32,7 +31,7 @@ func ConfigureNginxHttp(domainName string) {
'' close;
}
-upstream websocket {
+upstream websocket_khatru_pyramid {
server 0.0.0.0:3334;
}
@@ -48,7 +47,7 @@ server {
}
location / {
- proxy_pass http://websocket;
+ proxy_pass http://websocket_khatru_pyramid;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
diff --git a/pkg/relays/khatru_pyramid/nginx_https.go b/pkg/relays/khatru_pyramid/nginx_https.go
index cd85f2a..760cbfd 100644
--- a/pkg/relays/khatru_pyramid/nginx_https.go
+++ b/pkg/relays/khatru_pyramid/nginx_https.go
@@ -2,7 +2,6 @@ package khatru_pyramid
import (
"fmt"
- // "github.com/nodetec/relaywiz/pkg/utils"
"github.com/pterm/pterm"
"log"
"os"
@@ -27,7 +26,7 @@ func ConfigureNginxHttps(domainName string) {
'' close;
}
-upstream websocket {
+upstream websocket_khatru_pyramid {
server 0.0.0.0:3334;
}
@@ -42,7 +41,7 @@ server {
# First attempt to serve request as file, then
# as directory, then fall back to displaying 404.
try_files $uri $uri/ =404;
- proxy_pass http://websocket;
+ proxy_pass http://websocket_khatru_pyramid;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
diff --git a/pkg/relays/khatru_pyramid/success-messages.go b/pkg/relays/khatru_pyramid/success-messages.go
new file mode 100644
index 0000000..1005ccf
--- /dev/null
+++ b/pkg/relays/khatru_pyramid/success-messages.go
@@ -0,0 +1,43 @@
+package khatru_pyramid
+
+import (
+ "github.com/pterm/pterm"
+)
+
+func SuccessMessages(domain string) {
+ const dataDir = "/var/lib/nostr-relay-khatru-pyramid"
+ const envFile = "/etc/systemd/system/nostr-relay-khatru-pyramid.env"
+ const service = "nostr-relay-khatru-pyramid"
+ const githubLink = "https://github.com/github-tijlxyz/khatru-pyramid"
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("The installation is complete."))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("You can access your relay at:"))
+ pterm.Println(pterm.Magenta("wss://" + domain))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's data directory is located here:"))
+ pterm.Println(pterm.Magenta(dataDir))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's environment file is located here:"))
+ pterm.Println(pterm.Magenta(envFile))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To check the status of your relay run:"))
+ pterm.Println(pterm.Magenta("systemctl status " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To reload the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl reload " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To restart the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl restart " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Khatru Pyramid GitHub"))
+ pterm.Println(pterm.Magenta(githubLink))
+}
diff --git a/pkg/relays/strfry/apt.go b/pkg/relays/strfry/apt.go
index 9985051..9929b1a 100644
--- a/pkg/relays/strfry/apt.go
+++ b/pkg/relays/strfry/apt.go
@@ -2,7 +2,7 @@ package strfry
import (
"fmt"
- "github.com/nodetec/relaywiz/pkg/manager"
+ "github.com/nodetec/rwz/pkg/manager"
"github.com/pterm/pterm"
"os/exec"
)
diff --git a/pkg/relays/strfry/install.go b/pkg/relays/strfry/install.go
index 1480c4e..d62f126 100644
--- a/pkg/relays/strfry/install.go
+++ b/pkg/relays/strfry/install.go
@@ -2,24 +2,22 @@ package strfry
import (
"fmt"
+ "github.com/pterm/pterm"
+ "io"
"log"
+ "net/http"
"os"
"os/exec"
-
- "github.com/pterm/pterm"
+ "path/filepath"
)
-// Function to download, build, and install the binary
+// Function to download and make the binary executable
func InstallRelayBinary() {
- // TODO
- // Create the binary on a different machine then download it instead of building it here
- // Use these variables and model it after Khatru Pyramid installation
-
// Temporary directory for git repository
const tempDir = "/tmp/strfry"
// URL of the binary to download
- // const downloadURL = "https://..."
+ const downloadURL = "https://github.com/nodetec/relays/releases/download/v0.1.0/strfry-0.9.7-x86_64-linux-gnu.tar.gz"
// Name of the binary after downloading
const binaryName = "nostr-relay-strfry"
@@ -27,67 +25,67 @@ func InstallRelayBinary() {
// Destination directory for the binary
const destDir = "/usr/local/bin"
- // Data directory for the relay
- // const dataDir = "/var/lib/nostr-relay-strfry"
-
spinner, _ := pterm.DefaultSpinner.Start("Installing strfry relay...")
- pterm.Println()
- pterm.Println(pterm.Magenta("Go get coffee, this may take a few minutes..."))
- pterm.Println()
-
- // Download
- // Check for and remove existing repository
+ // Check for and remove existing git repository
err := os.RemoveAll(fmt.Sprintf("%s", tempDir))
if err != nil && !os.IsNotExist(err) {
log.Fatalf("Error removing existing repository: %v", err)
}
// Download git repository
- err = exec.Command("git", "clone", "https://github.com/hoytech/strfry.git", fmt.Sprintf("%s", tempDir)).Run()
+ err = exec.Command("git", "clone", "-b", "0.9.7", "https://github.com/hoytech/strfry.git", fmt.Sprintf("%s", tempDir)).Run()
if err != nil {
log.Fatalf("Error downloading repository: %v", err)
}
- // Build
- // TODO
- // Check for development environment variable instead of commenting and uncommenting these lines
+ // Install
+ // Determine the file name from the URL
+ tempFileName := filepath.Base(downloadURL)
- // Check for and remove existing binary
- // When developing comment to prevent unecessary builds
- err = os.Remove(fmt.Sprintf("%s/%s", destDir, binaryName))
- if err != nil && !os.IsNotExist(err) {
- log.Fatalf("Error removing existing binary: %v", err)
+ // Create the temporary file
+ out, err := os.Create(fmt.Sprintf("/tmp/%s", tempFileName))
+ if err != nil {
+ log.Fatalf("Error creating temporary file: %v", err)
}
+ defer out.Close()
- // Check if binary exists
- // When developing uncomment to prevent unecessary builds
- // _, err = os.Stat(fmt.Sprintf("%s/%s", destDir, binaryName))
- // if os.IsNotExist(err) {
- // Intialize and update git submodule
- err = exec.Command("git", "-C", fmt.Sprintf("%s", tempDir), "submodule", "update", "--init").Run()
+ // Download the file
+ resp, err := http.Get(downloadURL)
if err != nil {
- log.Fatalf("Error initializing and updating git submodule: %v", err)
+ log.Fatalf("Error downloading file: %v", err)
+ }
+ defer resp.Body.Close()
+
+ // Check server response
+ if resp.StatusCode != http.StatusOK {
+ log.Fatalf("Bad status: %s", resp.Status)
}
- // Make setup-golpe
- err = exec.Command("make", "-C", fmt.Sprintf("%s", tempDir), "setup-golpe").Run()
+ // Write the body to the temporary file
+ _, err = io.Copy(out, resp.Body)
if err != nil {
- log.Fatalf("Error making setup-golpe: %v", err)
+ log.Fatalf("Error writing to temporary file: %v", err)
}
- // Make -j2
- err = exec.Command("make", "-C", fmt.Sprintf("%s", tempDir), "-j2").Run()
+ // Extract binary
+ err = exec.Command("tar", "-xf", fmt.Sprintf("/tmp/%s", tempFileName), "-C", fmt.Sprintf("%s", destDir)).Run()
if err != nil {
- log.Fatalf("Error making -j2: %v", err)
+ log.Fatalf("Error extracting binary to /usr/local/bin: %v", err)
}
- // Install
- err = exec.Command("mv", fmt.Sprintf("%s/strfry", tempDir), fmt.Sprintf("%s/%s", destDir, binaryName)).Run()
+ // TODO
+ // Currently, the downloaded binary is expected to have a name that matches the binaryName variable
+ // Ideally, the extracted binary file should be renamed to match the binaryName variable
+
+ // Define the final destination path
+ destPath := filepath.Join(destDir, binaryName)
+
+ // Make the file executable
+ err = os.Chmod(destPath, 0755)
if err != nil {
- log.Fatalf("Error installing binary: %v", err)
+ log.Fatalf("Error making file executable: %v", err)
}
- // }
spinner.Success("strfry relay installed successfully.")
}
diff --git a/pkg/relays/strfry/service.go b/pkg/relays/strfry/service.go
index 27e050c..dfeeec0 100644
--- a/pkg/relays/strfry/service.go
+++ b/pkg/relays/strfry/service.go
@@ -17,6 +17,8 @@ func userExists(username string) bool {
}
// Function to set up the relay service
+// TODO
+// Check working directory
// WorkingDirectory=/home/nostr
func SetupRelayService(domain string) {
// Template for the systemd service file
@@ -92,7 +94,7 @@ WantedBy=multi-user.target
// TODO
// Determine system hard limit
- // Set nofiles option in config file
+ // Determine preferred nofiles value
cmd = exec.Command("sed", "-i", `s|nofiles = .*|nofiles = 0|`, filePath)
// Execute the command
@@ -126,9 +128,6 @@ WantedBy=multi-user.target
}
defer serviceFile.Close()
- // TODO
- // Try setting the owner to be nostr
-
tmpl, err := template.New("service").Parse(serviceTemplate)
if err != nil {
log.Fatalf("Error parsing service template: %v", err)
diff --git a/pkg/relays/strfry/success-messages.go b/pkg/relays/strfry/success-messages.go
new file mode 100644
index 0000000..bdf42ab
--- /dev/null
+++ b/pkg/relays/strfry/success-messages.go
@@ -0,0 +1,43 @@
+package strfry
+
+import (
+ "github.com/pterm/pterm"
+)
+
+func SuccessMessages(domain string) {
+ const dataDir = "/var/lib/nostr-relay-strfry"
+ const configFile = "/etc/strfry.conf"
+ const service = "nostr-relay-strfry"
+ const githubLink = "https://github.com/hoytech/strfry"
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("The installation is complete."))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("You can access your relay at:"))
+ pterm.Println(pterm.Magenta("wss://" + domain))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's data directory is located here:"))
+ pterm.Println(pterm.Magenta(dataDir))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("Your relay's config file is located here:"))
+ pterm.Println(pterm.Magenta(configFile))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To check the status of your relay run:"))
+ pterm.Println(pterm.Magenta("systemctl status " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To reload the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl reload " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("To restart the relay service run:"))
+ pterm.Println(pterm.Magenta("systemctl restart " + service))
+
+ pterm.Println()
+ pterm.Println(pterm.Magenta("strfry GitHub"))
+ pterm.Println(pterm.Magenta(githubLink))
+}
diff --git a/pkg/ui/greeter.go b/pkg/ui/greeter.go
index 80232a2..8823bac 100644
--- a/pkg/ui/greeter.go
+++ b/pkg/ui/greeter.go
@@ -6,5 +6,5 @@ import (
func Greet() {
pterm.DefaultCenter.WithCenterEachLineSeparately().Println(
- pterm.Magenta("\nWelcome to Relay Wizard 🪄") + pterm.Gray("\nInstall and manage your relays with ease!") + pterm.Gray("\nv0.1.0"))
+ pterm.Magenta("\nWelcome to Relay Wizard 🧙") + pterm.Gray("\nInstall and manage your relays with ease!") + pterm.Gray("\nv0.2.0"))
}