Files
enxos-server/enxos-server.go
Tiago Batista Cardoso 5017ea4c5f initial commit
2025-11-04 23:32:35 +01:00

301 lines
6.0 KiB
Go

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)
}
}