first
This commit is contained in:
commit
0deed94a83
7 changed files with 224 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*_templ.go
|
20
db.go
Normal file
20
db.go
Normal file
|
@ -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
|
||||
}
|
7
go.mod
Normal file
7
go.mod
Normal file
|
@ -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
|
4
go.sum
Normal file
4
go.sum
Normal file
|
@ -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=
|
7
initdb.sql
Normal file
7
initdb.sql
Normal file
|
@ -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
|
||||
);
|
146
main.go
Normal file
146
main.go
Normal file
|
@ -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)
|
||||
}
|
39
templates.templ
Normal file
39
templates.templ
Normal file
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
|
||||
templ tLoginForm(err string) {
|
||||
<form action="/login" method="POST">
|
||||
<h2>Login</h2>
|
||||
Username: <input name="username"><br>
|
||||
Password: <input name="password" type="password"><br>
|
||||
|
||||
if err != "" {
|
||||
<div style="color:red">{err}</div>
|
||||
}
|
||||
<input type="submit">
|
||||
</form>
|
||||
}
|
||||
|
||||
templ tHello(session *UserSession) {
|
||||
<div>Hello {session.Username}</div>
|
||||
}
|
||||
|
||||
templ tError(err string) {
|
||||
<div style="color:red;">err</div>
|
||||
}
|
||||
|
||||
templ tPage(contents templ.Component) {
|
||||
<html>
|
||||
<head>
|
||||
<title>The Game</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>THE GAME</h1>
|
||||
@contents
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
}
|
Loading…
Reference in a new issue