Add nasctl: Go NAS control plane with React frontend
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/darroyo/nasctl/internal/db"
|
||||
"github.com/darroyo/nasctl/internal/engine"
|
||||
"github.com/darroyo/nasctl/internal/modules/nfs"
|
||||
"github.com/darroyo/nasctl/internal/modules/samba"
|
||||
"github.com/darroyo/nasctl/internal/modules/users"
|
||||
"github.com/darroyo/nasctl/internal/web"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", envOrDefault("NASCTL_ADDR", ":8080"), "HTTP listen address")
|
||||
dbPath := flag.String("db", envOrDefault("NASCTL_DB", "/var/lib/nasctl/nasctl.db"), "SQLite database path")
|
||||
smbConfPath := flag.String("smb-conf", envOrDefault("NASCTL_SMB_CONF", "/etc/samba/smb.conf"), "Samba config output path")
|
||||
exportsPath := flag.String("exports", envOrDefault("NASCTL_EXPORTS", "/etc/exports"), "NFS exports output path")
|
||||
execSystem := flag.Bool("exec-system", envOrDefault("NASCTL_EXEC_SYSTEM", "true") == "true", "Execute real system commands (reload services, useradd, etc.)")
|
||||
allowedRoots := flag.String("allowed-roots", envOrDefault("NASCTL_ALLOWED_ROOTS", ""), "Comma-separated allowed root dirs for shares/exports (empty = any absolute path)")
|
||||
adminUser := flag.String("admin-user", envOrDefault("NASCTL_ADMIN_USER", "admin"), "Initial admin username (only used if no admin exists)")
|
||||
adminPass := flag.String("admin-pass", envOrDefault("NASCTL_ADMIN_PASSWORD", "admin"), "Initial admin password (only used if no admin exists)")
|
||||
flag.Parse()
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(*dbPath), 0o755); err != nil {
|
||||
log.Fatalf("create db dir: %v", err)
|
||||
}
|
||||
|
||||
database, err := db.Open(*dbPath)
|
||||
if err != nil {
|
||||
log.Fatalf("open db: %v", err)
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
if err := database.Migrate(); err != nil {
|
||||
log.Fatalf("migrate db: %v", err)
|
||||
}
|
||||
|
||||
auth, err := web.NewAuthService(database)
|
||||
if err != nil {
|
||||
log.Fatalf("init auth: %v", err)
|
||||
}
|
||||
created, err := auth.EnsureAdmin(*adminUser, *adminPass)
|
||||
if err != nil {
|
||||
log.Fatalf("ensure admin: %v", err)
|
||||
}
|
||||
if created {
|
||||
log.Printf("created initial admin user %q — change the password after first login", *adminUser)
|
||||
}
|
||||
|
||||
sambaModule := samba.New(samba.Config{
|
||||
SMBConfPath: *smbConfPath,
|
||||
Reload: *execSystem,
|
||||
})
|
||||
nfsModule := nfs.New(nfs.Config{
|
||||
ExportsPath: *exportsPath,
|
||||
Reload: *execSystem,
|
||||
})
|
||||
usersModule := users.New(users.Config{
|
||||
Execute: *execSystem,
|
||||
CreateHome: true,
|
||||
})
|
||||
|
||||
eng := engine.New(sambaModule, nfsModule, usersModule)
|
||||
srv := web.NewServer(database, eng, web.Options{
|
||||
AllowedRoots: parseRoots(*allowedRoots),
|
||||
Auth: auth,
|
||||
})
|
||||
|
||||
log.Printf("nasctl listening on %s (db=%s exec-system=%v)", *addr, *dbPath, *execSystem)
|
||||
if err := http.ListenAndServe(*addr, srv.Handler()); err != nil {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseRoots(raw string) []string {
|
||||
if strings.TrimSpace(raw) == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(raw, ",")
|
||||
roots := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
roots = append(roots, p)
|
||||
}
|
||||
}
|
||||
return roots
|
||||
}
|
||||
|
||||
func envOrDefault(key, fallback string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
Reference in New Issue
Block a user