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