commit 0deed94a83ef642232cb32cd1f1fafddfe0ee085 Author: Sabudaki Date: Tue Sep 3 22:34:04 2024 +0300 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26db00f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*_templ.go \ No newline at end of file diff --git a/db.go b/db.go new file mode 100644 index 0000000..aec1a6a --- /dev/null +++ b/db.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +type DBLoginResult struct { + Idx int +} + +func DBLogin(username string, password string) (DBLoginResult, error) { + row := db.QueryRow("SELECT idx FROM Users WHERE username = $1 AND password = $2", username, password) + + login := DBLoginResult{} + err := row.Scan(&login.Idx) + + if err != nil { + fmt.Println(err) + } + + return login, err +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f9d463d --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module git.synapse.enterprises/synent/rpg + +go 1.22.3 + +require github.com/a-h/templ v0.2.771 + +require github.com/lib/pq v1.10.9 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..df22ed5 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/a-h/templ v0.2.771 h1:4KH5ykNigYGGpCe0fRJ7/hzwz72k3qFqIiiLLJskbSo= +github.com/a-h/templ v0.2.771/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/initdb.sql b/initdb.sql new file mode 100644 index 0000000..d46c74e --- /dev/null +++ b/initdb.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS Users; + +CREATE TABLE Users ( + idx SERIAL PRIMARY KEY, + username VARCHAR(255) UNIQUE NOT NULL, + password VARCHAR(255) UNIQUE NOT NULL +); \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..cac5f2f --- /dev/null +++ b/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "crypto/rand" + "database/sql" + "encoding/base64" + "sync" + + _ "github.com/lib/pq" + + "fmt" + "net/http" +) + +type UserSession struct { + AccountIDX int + Username string +} + +type PageData struct { + session *UserSession +} + +var sessionsMutex sync.Mutex +var sessions = map[string]*UserSession{} + +var db *sql.DB + +func SendError(w http.ResponseWriter, r *http.Request, code int) { + w.WriteHeader(code) + errorString := fmt.Sprintf("Error %d", code) + + page := tPage(tError(errorString)) + page.Render(r.Context(), w) +} + +func AttemptCookieAuth(req *http.Request) *UserSession { + cookie, err := req.Cookie("session") + if err != nil { + return nil + } + + sessionsMutex.Lock() + session := sessions[cookie.Value] + sessionsMutex.Unlock() + + return session +} + +func RootHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + SendError(w, r, http.StatusBadRequest) + return + } + + session := AttemptCookieAuth(r) + if session != nil { + page := tPage(tHello(session)) + page.Render(r.Context(), w) + return + } else { + page := tPage(tLoginForm("")) + page.Render(r.Context(), w) + return + } +} + +func LoginHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + SendError(w, r, http.StatusBadRequest) + return + } + + err := r.ParseForm() + if err != nil { + SendError(w, r, http.StatusBadRequest) + return + } + + session := AttemptCookieAuth(r) + if session != nil { + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + username := r.FormValue("username") + password := r.FormValue("password") + + loginResult, err := DBLogin(username, password) + if err != nil { + loginForm := tLoginForm("Invalid username or password") + page := tPage(loginForm) + page.Render(r.Context(), w) + return + } + + var cookieBytes [24]byte + rand.Read(cookieBytes[:]) + cookie := base64.URLEncoding.EncodeToString(cookieBytes[:]) + + session = &UserSession{ + AccountIDX: loginResult.Idx, + Username: username, + } + + sessionsMutex.Lock() + sessions[cookie] = session + sessionsMutex.Unlock() + + http.SetCookie(w, &http.Cookie{ + Name: "session", + Value: cookie, + HttpOnly: true, + Path: "/", + }) + + pageContents := tHello(session) + page := tPage(pageContents) + page.Render(r.Context(), w) +} + +func main() { + var err error + + conninfo := "host=localhost user=postgres password=work dbname=rpg sslmode=disable" + listenEndpoint := "localhost:8000" + + db, err = sql.Open("postgres", conninfo) + if err != nil { + panic(err) + } + + defer db.Close() + + err = db.Ping() + if err != nil { + panic(err) + } + fmt.Println("Connected to database") + + http.HandleFunc("/login", LoginHandler) + http.HandleFunc("/", RootHandler) + + fmt.Printf("Listening on %s\n", listenEndpoint) + http.ListenAndServe(listenEndpoint, nil) +} diff --git a/templates.templ b/templates.templ new file mode 100644 index 0000000..c93e28a --- /dev/null +++ b/templates.templ @@ -0,0 +1,39 @@ +package main + + +templ tLoginForm(err string) { +
+

Login

+ Username:
+ Password:
+ + if err != "" { +
{err}
+ } + +
+} + +templ tHello(session *UserSession) { +
Hello {session.Username}
+} + +templ tError(err string) { +
err
+} + +templ tPage(contents templ.Component) { + + + The Game + + + + +

THE GAME

+ @contents + + + + +} \ No newline at end of file