From 5017ea4c5ff6833be2a10ae857a2ba2161e70076 Mon Sep 17 00:00:00 2001 From: Tiago Batista Cardoso Date: Tue, 4 Nov 2025 23:32:35 +0100 Subject: [PATCH] initial commit --- enxos-server.go | 300 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 5 + go.sum | 2 + users.db | Bin 0 -> 16384 bytes 4 files changed, 307 insertions(+) create mode 100644 enxos-server.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 users.db diff --git a/enxos-server.go b/enxos-server.go new file mode 100644 index 0000000..ed6226a --- /dev/null +++ b/enxos-server.go @@ -0,0 +1,300 @@ +package main + +import ( + "crypto/rand" + "database/sql" + "encoding/hex" + "encoding/json" + "fmt" + "log" + "math/big" + "net/http" + "strings" + "time" + + _ "github.com/mattn/go-sqlite3" +) + +type User struct { + UserID string `json:"user_id"` + Username string `json:"username"` +} + +type RegistrationResponse struct { + UserID string `json:"user_id"` +} + +type ProfileRequest struct { + UserID string `json:"user_id"` +} + +type ProfileResponse struct { + Username string `json:"username"` +} + +var db *sql.DB + +func generateUsername() (string, error) { + adjectives := []string{ + "freaky", + "suspicious", + "malicious", + "supersticious", + "handsome", + "ugly", + "crazy", + "demonic", + "peeping", + "slippery", + "mysterious", + "spooky", + "creepy", + "sinister", + "ghoulish", + "macabre", + "eerie", + "pessimistic", + "frightening", + "grotesque", + "haunted", + "infernal", + "menacing", + "morose", + "nefarious", + "nightmarish", + "obscure", + "ominous", + "phantasmagoric", + "pallid", + "perilous", + "phantom", + "aberrant", + "bizarre", + "chilling", + "cryptic", + "dark", + "dreadful", + "obsolete", + "greasy", + "bald", + "putrid", + "old", + } + words := []string{ + "magician", + "welder", + "wizard", + "criminal", + "rat", + "castle", + "droid", + "robot", + "cyborg", + "joe", + "bob", + "tom", + "builder", + "architect", + "troll", + "ogre", + "nomad", + "bohemian", + "skeleton", + "goblin", + "monster", + "engineer", + "dude", + "guy", + "mob", + "creep", + "juice", + "smoke", + "potion", + "fart", + "mage", + "vermin", + "crook", + "flunkey", + "insect", + "bug", + "handyman", + } + + index1Big, err := rand.Int(rand.Reader, big.NewInt(int64(len(adjectives)))) + if err != nil { + return "", fmt.Errorf("failed to generate random index for array1: %w", err) + } + index1 := int(index1Big.Int64()) + + index2Big, err := rand.Int(rand.Reader, big.NewInt(int64(len(words)))) + if err != nil { + return "", fmt.Errorf("failed to generate random index for array2: %w", err) + } + index2 := int(index2Big.Int64()) + + randomNumberBig, err := rand.Int(rand.Reader, big.NewInt(101)) + if err != nil { + return "", fmt.Errorf("failed to generate random number: %w", err) + } + randomNumber := randomNumberBig.Int64() + + username := fmt.Sprintf("%s_%s%d", adjectives[index1], words[index2], randomNumber) + + return username, nil +} + +func generateSecureUserID() (string, error) { + b := make([]byte, 8) + _, err := rand.Read(b) + if err != nil { + return "", err + } + return hex.EncodeToString(b), nil +} + +//func validateUsername(username string) error { +// if len(username) < 3 || len(username) > 32 { +// return fmt.Errorf("[-] username must be between 3 and 32 characters") +// } +// +// pattern := `^[a-zA-Z0-9_]+$` +// match, err := regexp.MatchString(pattern, username) +// if err != nil { +// return fmt.Errorf("[-] error validating username") +// } +// if !match { +// return fmt.Errorf("[-] username can only contain letters, numbers, and underscores") +// } +// +// return nil +//} + +func InitDB(dbPath string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return nil, fmt.Errorf("failed to open db: %v", err) + } + _, err = db.Exec(` + CREATE TABLE IF NOT EXISTS users ( + user_id TEXT PRIMARY KEY CHECK(length(user_id) = 16), + username TEXT NOT NULL UNIQUE CHECK(length(username) >= 3 AND length(username) <= 32), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `) + if err != nil { + db.Close() + return nil, fmt.Errorf("failed to create users table: %v", err) + } + + return db, nil +} + +func greet(w http.ResponseWriter, r *http.Request) { + println("[+] ping") + fmt.Fprintf(w, "[+] enxos server %s", time.Now()) +} + +func u_profile(w http.ResponseWriter, r *http.Request) { + println("[+] u_profile") + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var profRequest ProfileRequest + err := json.NewDecoder(r.Body).Decode(&profRequest) + if err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + profRequest.UserID = strings.TrimSpace(profRequest.UserID) + + var username string + err = db.QueryRow("SELECT username FROM users WHERE user_id = ?", profRequest.UserID).Scan(&username) + if err != nil { + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + + if strings.Compare(strings.TrimSpace(username), "") == 0 { + http.Error(w, "UserID not found", http.StatusBadRequest) + return + } + + response := ProfileResponse{ + Username: username, + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + json.NewEncoder(w).Encode(response) +} + +func u_register(w http.ResponseWriter, r *http.Request) { + println("[+] u_register") + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + username, err := generateUsername() + if err != nil { + http.Error(w, "Failed to generate username", http.StatusInternalServerError) + return + } + + userid, err := generateSecureUserID() + if err != nil { + http.Error(w, "Failed to generate user ID", http.StatusInternalServerError) + return + } + _, err = db.Exec("INSERT INTO users (user_id, username) VALUES (?, ?)", userid, username) + if err != nil { + http.Error(w, "Failed to create user", http.StatusInternalServerError) + return + } + + response := RegistrationResponse{ + UserID: userid, + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + json.NewEncoder(w).Encode(response) +} + +func main() { + const dbPath = "./users.db" + + var err error + + db, err = InitDB(dbPath) + if err != nil { + log.Fatalf("DB Initialization failed: %v", err) + } + + err = db.Ping() + if err != nil { + log.Fatal(err) + } + + http.HandleFunc("/", greet) + http.HandleFunc("/u/profile", u_profile) + http.HandleFunc("/u/register", u_register) + //http.ListenAndServe(":8801", nil) + + server := &http.Server{ + Addr: ":8801", + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + log.Printf("[+] enxos server starting on %s", server.Addr) + + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("[-] server failed to start: %v", err) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..318882c --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module enxos-server + +go 1.25.3 + +require github.com/mattn/go-sqlite3 v1.14.32 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..66f7516 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= diff --git a/users.db b/users.db new file mode 100644 index 0000000000000000000000000000000000000000..269061e9e4a29ca84a9c5669f00bc097f8224ed6 GIT binary patch literal 16384 zcmeI&O>fgM7zc2>sbHD31;M0TpJSs@lo!#miB1cx5!KSLWr@U2QQEptt?NXSNpM8m z_!yk`LAdZu*qtM8LM2o*aYfVLvg5>lY}vn?t(=|&(?ZhZa&eIrtxs&3W(9qN>!niAbus&1*mjc-h4BnUtN0uX=z1Rwwb z2tWV=5P$##{&RsAuQzwSq5G^g_UvJMuPyD7GnuH%hVzfHS hTYvxrAOHafKmY;|fB*y_009U