This commit is contained in:
Magnus Åhall 2023-06-16 07:11:27 +02:00
parent b8a673fb0a
commit a76c093d44
7 changed files with 192 additions and 36 deletions

52
main.go
View File

@ -51,6 +51,8 @@ func main() {// {{{
static = http.FileServer(http.Dir("./static")) static = http.FileServer(http.Dir("./static"))
http.HandleFunc("/css_updated", cssUpdateHandler) http.HandleFunc("/css_updated", cssUpdateHandler)
http.HandleFunc("/session/create", sessionCreate) http.HandleFunc("/session/create", sessionCreate)
http.HandleFunc("/session/retrieve", sessionRetrieve)
http.HandleFunc("/session/authenticate", sessionAuthenticate)
http.HandleFunc("/ws", websocketHandler) http.HandleFunc("/ws", websocketHandler)
http.HandleFunc("/", staticHandler) http.HandleFunc("/", staticHandler)
@ -68,12 +70,62 @@ func sessionCreate(w http.ResponseWriter, r *http.Request) {// {{{
session, err := NewSession() session, err := NewSession()
if err != nil { if err != nil {
responseError(w, err) responseError(w, err)
return
} }
responseData(w, map[string]interface{}{ responseData(w, map[string]interface{}{
"OK": true, "OK": true,
"Session": session, "Session": session,
}) })
}// }}} }// }}}
func sessionRetrieve(w http.ResponseWriter, r *http.Request) {// {{{
var err error
var uuid string
if uuid, err = sessionUUID(r); err != nil {
responseError(w, err)
return
}
session := Session{ UUID: uuid }
if err = session.Retrieve(); err != nil {
responseError(w, err)
return
}
responseData(w, map[string]interface{}{
"OK": true,
"Session": session,
})
}// }}}
func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{
var err error
var uuid string
if uuid, err = sessionUUID(r); err != nil {
responseError(w, err)
return
}
session := Session{
UUID: uuid,
}
req := struct{ Username string; Password string }{}
if err = request(r, &req); err != nil {
responseError(w, err)
return
}
if err = session.Authenticate(req.Username, req.Password); err != nil {
responseError(w, err)
return
}
responseData(w, map[string]interface{}{
"OK": true,
"Session": session,
})
}// }}}
func websocketHandler(w http.ResponseWriter, r *http.Request) {// {{{ func websocketHandler(w http.ResponseWriter, r *http.Request) {// {{{
var err error var err error

View File

@ -3,6 +3,8 @@ package main
import ( import (
// Standard // Standard
"encoding/json" "encoding/json"
"errors"
"io"
"net/http" "net/http"
) )
@ -26,3 +28,18 @@ func responseData(w http.ResponseWriter, data interface{}) {
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
w.Write(resJSON) w.Write(resJSON)
} }
func request(r *http.Request, data interface{}) (err error) {
body, _ := io.ReadAll(r.Body)
defer r.Body.Close()
err = json.Unmarshal(body, data)
return
}
func sessionUUID(r *http.Request) (string, error) {
headers := r.Header["X-Session-Id"]
if len(headers) > 0 {
return headers[0], nil
}
return "", errors.New("Invalid session")
}

View File

@ -3,6 +3,7 @@ package main
import ( import (
// Standard // Standard
"database/sql" "database/sql"
"errors"
"time" "time"
) )
@ -29,3 +30,60 @@ func NewSession() (session Session, err error) {
return return
} }
func (session *Session) Retrieve() (err error) {
var rows *sql.Rows
if rows, err = db.Query(`
SELECT
uuid, user_id, created
FROM public.session
WHERE
uuid = $1
`,
session.UUID,
); err != nil {
return
}
defer rows.Close()
if rows.Next() {
rows.Scan(&session.UUID, &session.UserID, &session.Created)
}
return
}
func (session *Session) Authenticate(username, password string) (err error) {
authenticated := false
var rows *sql.Rows
if rows, err = db.Query(`
SELECT id
FROM public.user
WHERE
username=$1 AND
password=$2
`,
username,
password,
); err != nil {
return
}
defer rows.Close()
if rows.Next() {
rows.Scan(&session.UserID)
authenticated = session.UserID > 0
}
if authenticated {
_, err = db.Exec("UPDATE public.session SET user_id=$1 WHERE uuid=$2", session.UserID, session.UUID)
if err != nil {
return
}
} else {
err = errors.New("Invalid authentication")
}
return
}

View File

@ -16,8 +16,7 @@
"preact/devtools": "/js/{{ .VERSION }}/lib/preact/devtools.mjs", "preact/devtools": "/js/{{ .VERSION }}/lib/preact/devtools.mjs",
"@preact/signals-core": "/js/{{ .VERSION }}/lib/signals/signals-core.mjs", "@preact/signals-core": "/js/{{ .VERSION }}/lib/signals/signals-core.mjs",
"preact/signals": "/js/{{ .VERSION }}/lib/signals/signals.mjs", "preact/signals": "/js/{{ .VERSION }}/lib/signals/signals.mjs",
"htm": "/js/{{ .VERSION }}/lib/htm/htm.mjs" "htm": "/js/{{ .VERSION }}/lib/htm/htm.mjs",
"session": "/js/{{ .VERSION }}/session.mjs" "session": "/js/{{ .VERSION }}/session.mjs"
} }
} }

View File

@ -3,7 +3,7 @@ import 'preact/devtools'
//import { signal } from 'preact/signals' //import { signal } from 'preact/signals'
import { h, Component, render, createRef } from 'preact' import { h, Component, render, createRef } from 'preact'
import htm from 'htm' import htm from 'htm'
import Session from 'session' import { Session } from 'session'
const html = htm.bind(h) const html = htm.bind(h)
class App extends Component { class App extends Component {
@ -15,10 +15,16 @@ class App extends Component {
this.wsConnect() this.wsConnect()
this.wsLoop() this.wsLoop()
this.session = new Session(this)
this.session.initialize()
}//}}} }//}}}
render() {//{{{ render() {//{{{
if(this.sessionID === null) { if(!this.session.initialized) {
return false;
//return html`<div>Validating session</div>`
}
if(!this.session.authenticated()) {
return html`<${Login} />` return html`<${Login} />`
} }
@ -85,8 +91,9 @@ class App extends Component {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
if(this.sessionID !== null) console.log(this.session)
headers['X-SESSION-ID'] = this.sessionID if(this.session.UUID !== '')
headers['X-Session-Id'] = this.session.UUID
fetch(url, { fetch(url, {
method: 'POST', method: 'POST',
@ -116,22 +123,6 @@ class App extends Component {
break; break;
} }
}//}}} }//}}}
login(username, password) {//{{{
this.request('/session/create', {})
.then(res=>{
this.setSessionID(res.Session.UUID)
})
.catch(this.responseError)
}//}}}
getSessionID() {
return window.localStorage.getItem("sessionID")
}
setSessionID(uuid) {
console.log('wut')
this.sessionID = uuid
window.localStorage.setItem('sessionID', uuid)
}
} }
class Login extends Component { class Login extends Component {
@ -152,7 +143,7 @@ class Login extends Component {
login() {//{{{ login() {//{{{
let username = document.getElementById('username').value let username = document.getElementById('username').value
let password = document.getElementById('password').value let password = document.getElementById('password').value
window._app.current.login(username, password) window._app.current.session.authenticate(username, password)
}//}}} }//}}}
} }

51
static/js/session.mjs Normal file
View File

@ -0,0 +1,51 @@
export class Session {
constructor(app) {
this.app = app
this.UUID = ''
this.initialized = false
this.UserID = 0
}
initialize() {//{{{
// Retrieving the stored session UUID, if any.
// If one found, validate with server.
let uuid = window.localStorage.getItem("session.UUID")
if(uuid === null) {
this.create()
} else {
this.UUID = uuid
this.app.request('/session/retrieve', {})
.then(res=>{
this.UserID = res.Session.UserID
this.initialized = true
this.app.forceUpdate()
})
.catch(this.app.responseError)
}
}//}}}
create() {//{{{
this.app.request('/session/create', {})
.then(res=>{
this.UUID = res.Session.UUID
window.localStorage.setItem('session.UUID', this.UUID)
this.app.forceUpdate()
})
.catch(this.responseError)
}//}}}
authenticate(username, password) {//{{{
this.app.request('/session/authenticate', {
username,
password,
})
.then(res=>{
this.UserID = res.Session.UserID
this.app.forceUpdate()
})
.catch(this.app.responseError)
}//}}}
authenticated() {//{{{
return this.UserID != 0
}//}}}
}
// vim: foldmethod=marker

View File

@ -1,12 +0,0 @@
export class Session {
static createFromStorage() {
let session = new Session()
session.UUID
}
constructor() {
this.UUID = ''
this.authenticated = false
this.UserID = 0
}
}