diff --git a/data.json b/data.json index 7a28418..204a3b0 100644 --- a/data.json +++ b/data.json @@ -2,5 +2,6 @@ "job": "none", "emails": [ "zipfriis@gmail.com" - ] + ], + "newletter-sent": 0 } \ No newline at end of file diff --git a/go.mod b/go.mod index 45aa8ae..50c690c 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,13 @@ module site -go 1.23.4 +go 1.25.0 -require github.com/joho/godotenv v1.5.1 +require ( + github.com/joho/godotenv v1.5.1 + github.com/pquerna/otp v1.5.0 +) + +require ( + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + golang.org/x/crypto v0.52.0 +) diff --git a/go.sum b/go.sum index d61b19e..6717ca8 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,12 @@ +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= +github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= diff --git a/main.go b/main.go index 056bee0..422a0a0 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "crypto/rand" + "crypto/sha256" "encoding/hex" "encoding/json" "flag" @@ -9,10 +10,12 @@ import ( "log" "net/http" "os" + "strconv" "sync" "time" "github.com/joho/godotenv" + "github.com/pquerna/otp/totp" ) type Config struct { @@ -37,36 +40,47 @@ func saveConfig(c *Config) error { return os.WriteFile("data.json", data, 0644) } -// ---------- Main ---------- - -func main() { - +func validateEnv() { + // checking if env file is there in the directory: err := godotenv.Load(".env") if err != nil { log.Fatal("Error loading .env file") } + // checking secret for rooling 2fa + secret := os.Getenv("secret") + if secret == "" { + fmt.Println("set secret first") + os.Exit(1) + } +} + +func main() { + validateEnv() + port := flag.String("port", "8081", "port to listen on") flag.Parse() + mux := http.NewServeMux() + fs := http.FileServer(http.Dir("static")) - http.Handle("/static/", http.StripPrefix("/static/", fs)) + mux.Handle("/static/", http.StripPrefix("/static/", fs)) - http.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/index.html") }) - http.HandleFunc("GET /infra", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/infra.html") }) - http.HandleFunc("GET /finance", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/finance.html") }) - http.HandleFunc("GET /cinema", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cinema.html") }) - http.HandleFunc("GET /engine", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/engine.html") }) - http.HandleFunc("GET /42", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/42.html") }) - http.HandleFunc("GET /secret", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/secret.html") }) - http.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/icon.png") }) - http.HandleFunc("GET /login", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/login.html") }) + mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/index.html") }) + mux.HandleFunc("GET /infra", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/infra.html") }) + mux.HandleFunc("GET /finance", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/finance.html") }) + mux.HandleFunc("GET /cinema", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cinema.html") }) + mux.HandleFunc("GET /engine", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/engine.html") }) + mux.HandleFunc("GET /42", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/42.html") }) + mux.HandleFunc("GET /secret", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/secret.html") }) + mux.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/icon.png") }) + mux.HandleFunc("GET /login", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/login.html") }) - http.HandleFunc("GET /admin", requireAuth(func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("GET /admin", requireAuth(func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/admin.html") })) - http.HandleFunc("POST /newsletter/subscribe", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("POST /newsletter/subscribe", func(w http.ResponseWriter, r *http.Request) { var req struct { Email string `json:"email"` } @@ -91,7 +105,7 @@ func main() { saveConfig(conf) }) - http.HandleFunc("GET /status", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("GET /status", func(w http.ResponseWriter, r *http.Request) { config, err := loadConfig() if err != nil { http.Error(w, "failed to load config", http.StatusInternalServerError) @@ -109,13 +123,17 @@ func main() { } }) - http.HandleFunc("POST /login", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("POST /login", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() password := r.Form.Get("password") auth := r.Form.Get("totp") - fmt.Println(os.Getenv("password"), os.Getenv("auth")) - if password == os.Getenv("password") && auth == os.Getenv("auth") { + // cheking password + h := sha256.New() + h.Write([]byte(password)) + b := hex.EncodeToString(h.Sum(nil)) + + if string(b) == os.Getenv("password_sha") && totp.Validate(auth, os.Getenv("secret")) { fmt.Println("logged in") token := generateSession() @@ -144,12 +162,36 @@ func main() { }) //http.HandleFunc("/portfolio", Portfolio) - http.HandleFunc("/git", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/git", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "https://git.samantha42.xyz", http.StatusFound) }) + // --- /admin/ sub-mux --- + adminMux := http.NewServeMux() + adminMux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "./static/admin.html") + }) + + // strip the /admin prefix before handing off + mux.Handle("/admin/", http.StripPrefix("/admin", adminMux)) + + // --- /admin/api/ sub-mux --- + apiMux := http.NewServeMux() + apiMux.HandleFunc("GET /stats/subscribers", requireAuth(func(w http.ResponseWriter, r *http.Request) { + conf, err := loadConfig() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + + } + + fmt.Fprintf(w, "%s", strconv.Itoa(len(conf.Emails))) + })) + + // strip /admin/api before handing off + adminMux.Handle("/api/", http.StripPrefix("/api", apiMux)) + fmt.Printf("running on http://localhost:%s/\n", *port) - if err := http.ListenAndServe(":"+*port, nil); err != nil { + if err := http.ListenAndServe(":"+*port, mux); err != nil { log.Fatal(err) } } diff --git a/site b/site index 89129da..cf1d45c 100755 Binary files a/site and b/site differ diff --git a/static/index.html b/static/index.html index 5427805..fd12ed5 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@