From 8b2c9709fc0488f8d441d9eccaa6ac3d5d923a66 Mon Sep 17 00:00:00 2001 From: jdl Date: Thu, 4 Jun 2026 16:00:21 +0200 Subject: [PATCH] WIP --- hub/api/api.go | 69 ++++----- hub/api/db/generated.go | 175 +++++----------------- hub/api/db/sanitize-validate.go | 42 +++--- hub/api/db/tables.defs | 17 +-- hub/api/db/written.go | 26 ---- hub/api/migrations/2024-11-30-init.sql | 29 ++-- hub/api/types.go | 8 +- hub/app.go | 18 ++- hub/handler.go | 5 - hub/handlers.go | 50 ++++--- hub/main.go | 2 +- hub/templates/admin-network-create.html | 3 +- hub/templates/admin-password-edit.html | 3 +- hub/templates/admin-sign-out.html | 3 +- hub/templates/network/network-delete.html | 3 +- hub/templates/network/network-view.html | 12 +- hub/templates/network/peer-create.html | 17 ++- hub/templates/network/peer-delete.html | 3 +- hub/templates/network/peer-edit.html | 17 ++- hub/templates/network/peer-view.html | 9 +- hub/templates/sign-in.html | 3 +- m/models.go | 20 +-- 22 files changed, 217 insertions(+), 317 deletions(-) diff --git a/hub/api/api.go b/hub/api/api.go index 7a534ca..2c678ef 100644 --- a/hub/api/api.go +++ b/hub/api/api.go @@ -19,8 +19,10 @@ import ( var migrations embed.FS type API struct { - db *sql.DB - lock sync.Mutex + db *sql.DB + lock sync.Mutex + sessionsMu sync.Mutex + sessions map[string]*Session } func New(dbPath string) (*API, error) { @@ -34,7 +36,8 @@ func New(dbPath string) (*API, error) { } a := &API{ - db: sqlDB, + db: sqlDB, + sessions: make(map[string]*Session), } return a, a.ensurePassword() @@ -75,47 +78,41 @@ func (a *API) Config_Update(conf *Config) error { } func (a *API) Session_Delete(sessionID string) error { - return db.Session_Delete(a.db, sessionID) + a.sessionsMu.Lock() + defer a.sessionsMu.Unlock() + delete(a.sessions, sessionID) + return nil } func (a *API) Session_Get(sessionID string) (*Session, error) { - if sessionID == "" { - return a.session_CreatePub() - } + a.sessionsMu.Lock() + defer a.sessionsMu.Unlock() - session, err := db.Session_Get(a.db, sessionID) - if err != nil { - return a.session_CreatePub() - } - - if timeSince(session.LastSeenAt) > 86400*21 { - return a.session_CreatePub() - } - - if timeSince(session.LastSeenAt) > 86400*7 { - session.LastSeenAt = time.Now().Unix() - if err := db.Session_UpdateLastSeenAt(a.db, session.SessionID); err != nil { - log.Printf("Failed to update session: %v", err) + if sessionID != "" { + s, ok := a.sessions[sessionID] + if ok { + if timeSince(s.LastSeenAt) <= 86400*21 { + if timeSince(s.LastSeenAt) > 86400*7 { + s.LastSeenAt = time.Now().Unix() + } + return s, nil + } + delete(a.sessions, sessionID) } } - return session, nil + return a.session_Create(), nil } -func (a *API) session_CreatePub() (*Session, error) { +// caller must hold sessionsMu +func (a *API) session_Create() *Session { s := &Session{ SessionID: idgen.NewToken(), - CSRF: idgen.NewToken(), - SignedIn: false, CreatedAt: time.Now().Unix(), LastSeenAt: time.Now().Unix(), } - err := db.Session_Insert(a.db, s) - return s, err -} - -func (a *API) Session_DeleteBefore(timestamp int64) error { - return db.Session_DeleteBefore(a.db, timestamp) + a.sessions[s.SessionID] = s + return s } func (a *API) Session_SignIn(s *Session, pwd string) error { @@ -123,8 +120,10 @@ func (a *API) Session_SignIn(s *Session, pwd string) error { if err := bcrypt.CompareHashAndPassword(conf.Password, []byte(pwd)); err != nil { return ErrNotAuthorized } - - return db.Session_SetSignedIn(a.db, s.SessionID) + a.sessionsMu.Lock() + s.SignedIn = true + a.sessionsMu.Unlock() + return nil } func (a *API) Network_Create(n *Network) error { @@ -147,8 +146,7 @@ func (a *API) Network_List() ([]*Network, error) { func (a *API) Peer_CreateNew(p *Peer) error { p.Version = idgen.NextID(0) - p.PubKey = []byte{} - p.PubSignKey = []byte{} + p.WGPubKey = []byte{} p.APIKey = idgen.NewToken() return db.Peer_Insert(a.db, p) @@ -159,8 +157,7 @@ func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) error { defer a.lock.Unlock() peer.Version = idgen.NextID(0) - peer.PubKey = args.EncPubKey - peer.PubSignKey = args.PubSignKey + peer.WGPubKey = args.WGPubKey return db.Peer_UpdateFull(a.db, peer) } diff --git a/hub/api/db/generated.go b/hub/api/db/generated.go index 88aec6c..01ad967 100644 --- a/hub/api/db/generated.go +++ b/hub/api/db/generated.go @@ -123,7 +123,9 @@ func Config_Get( ) { row = &Config{} r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID) - err = r.Scan(&row.ConfigID, &row.Password) + if err = r.Scan(&row.ConfigID, &row.Password); err != nil { + row = nil + } return } @@ -137,7 +139,9 @@ func Config_GetWhere( ) { row = &Config{} r := tx.QueryRow(query, args...) - err = r.Scan(&row.ConfigID, &row.Password) + if err = r.Scan(&row.ConfigID, &row.Password); err != nil { + row = nil + } return } @@ -182,124 +186,6 @@ func Config_List( return l, nil } -// ---------------------------------------------------------------------------- -// Table: sessions -// ---------------------------------------------------------------------------- - -type Session struct { - SessionID string - CSRF string - SignedIn bool - CreatedAt int64 - LastSeenAt int64 -} - -const Session_SelectQuery = "SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions" - -func Session_Insert( - tx TX, - row *Session, -) (err error) { - Session_Sanitize(row) - if err = Session_Validate(row); err != nil { - return err - } - - _, err = tx.Exec("INSERT INTO sessions(SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt) VALUES(?,?,?,?,?)", row.SessionID, row.CSRF, row.SignedIn, row.CreatedAt, row.LastSeenAt) - return err -} - -func Session_Delete( - tx TX, - SessionID string, -) (err error) { - result, err := tx.Exec("DELETE FROM sessions WHERE SessionID=?", SessionID) - if err != nil { - return err - } - - n, err := result.RowsAffected() - if err != nil { - panic(err) - } - switch n { - case 0: - return sql.ErrNoRows - case 1: - return nil - default: - panic("multiple rows deleted") - } -} - -func Session_Get( - tx TX, - SessionID string, -) ( - row *Session, - err error, -) { - row = &Session{} - r := tx.QueryRow("SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions WHERE SessionID=?", SessionID) - err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt) - return -} - -func Session_GetWhere( - tx TX, - query string, - args ...any, -) ( - row *Session, - err error, -) { - row = &Session{} - r := tx.QueryRow(query, args...) - err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt) - return -} - -func Session_Iterate( - tx TX, - query string, - args ...any, -) iter.Seq2[*Session, error] { - rows, err := tx.Query(query, args...) - if err != nil { - return func(yield func(*Session, error) bool) { - yield(nil, err) - } - } - - return func(yield func(*Session, error) bool) { - defer rows.Close() - for rows.Next() { - row := &Session{} - err := rows.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt) - if !yield(row, err) { - return - } - } - } -} - -func Session_List( - tx TX, - query string, - args ...any, -) ( - l []*Session, - err error, -) { - for row, err := range Session_Iterate(tx, query, args...) { - if err != nil { - return nil, err - } - l = append(l, row) - } - return l, nil -} - // ---------------------------------------------------------------------------- // Table: networks // ---------------------------------------------------------------------------- @@ -385,7 +271,9 @@ func Network_Get( ) { row = &Network{} r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID) - err = r.Scan(&row.NetworkID, &row.Name, &row.Network) + if err = r.Scan(&row.NetworkID, &row.Name, &row.Network); err != nil { + row = nil + } return } @@ -399,7 +287,9 @@ func Network_GetWhere( ) { row = &Network{} r := tx.QueryRow(query, args...) - err = r.Scan(&row.NetworkID, &row.Name, &row.Network) + if err = r.Scan(&row.NetworkID, &row.Name, &row.Network); err != nil { + row = nil + } return } @@ -449,19 +339,20 @@ func Network_List( // ---------------------------------------------------------------------------- type Peer struct { - NetworkID int64 - PeerIP byte - Version int64 - APIKey string - Name string - PublicIP []byte - Port uint16 - Relay bool - PubKey []byte - PubSignKey []byte + NetworkID int64 + PeerIP byte + Version int64 + APIKey string + Name string + PublicIP1 []byte + Port1 uint16 + PublicIP2 []byte + Port2 uint16 + Relay bool + WGPubKey []byte } -const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers" +const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey FROM peers" func Peer_Insert( tx TX, @@ -472,7 +363,7 @@ func Peer_Insert( return err } - _, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey) + _, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey) VALUES(?,?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.WGPubKey) return err } @@ -485,7 +376,7 @@ func Peer_Update( return err } - result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.NetworkID, row.PeerIP) + result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP1=?,Port1=?,PublicIP2=?,Port2=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.NetworkID, row.PeerIP) if err != nil { return err } @@ -513,7 +404,7 @@ func Peer_UpdateFull( return err } - result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.NetworkID, row.PeerIP) + result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP1=?,Port1=?,PublicIP2=?,Port2=?,Relay=?,WGPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.WGPubKey, row.NetworkID, row.PeerIP) if err != nil { return err } @@ -565,8 +456,10 @@ func Peer_Get( err error, ) { row = &Peer{} - r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) - err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) + r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) + if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey); err != nil { + row = nil + } return } @@ -580,7 +473,9 @@ func Peer_GetWhere( ) { row = &Peer{} r := tx.QueryRow(query, args...) - err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) + if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey); err != nil { + row = nil + } return } @@ -600,7 +495,7 @@ func Peer_Iterate( defer rows.Close() for rows.Next() { row := &Peer{} - err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) + err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey) if !yield(row, err) { return } diff --git a/hub/api/db/sanitize-validate.go b/hub/api/db/sanitize-validate.go index 71785e9..869633d 100644 --- a/hub/api/db/sanitize-validate.go +++ b/hub/api/db/sanitize-validate.go @@ -21,13 +21,6 @@ func Config_Validate(c *Config) error { return nil } -func Session_Sanitize(s *Session) { -} - -func Session_Validate(s *Session) error { - return nil -} - func Network_Sanitize(n *Network) { n.Name = strings.TrimSpace(n.Name) @@ -66,25 +59,39 @@ func Network_Validate(c *Network) error { func Peer_Sanitize(p *Peer) { p.Name = strings.TrimSpace(p.Name) - if len(p.PublicIP) != 0 { - addr, ok := netip.AddrFromSlice(p.PublicIP) - if ok && addr.Is4() { - p.PublicIP = addr.AsSlice() + if len(p.PublicIP1) != 0 { + if addr, ok := netip.AddrFromSlice(p.PublicIP1); ok { + p.PublicIP1 = addr.AsSlice() } } - if p.Port == 0 { - p.Port = 456 + if len(p.PublicIP2) != 0 { + if addr, ok := netip.AddrFromSlice(p.PublicIP2); ok { + p.PublicIP2 = addr.AsSlice() + } + } + if p.Port1 == 0 { + p.Port1 = 456 + } + if len(p.PublicIP2) != 0 && p.Port2 == 0 { + p.Port2 = 456 } } func Peer_Validate(p *Peer) error { - if len(p.PublicIP) > 0 { - _, ok := netip.AddrFromSlice(p.PublicIP) - if !ok { + if len(p.PublicIP1) > 0 { + if _, ok := netip.AddrFromSlice(p.PublicIP1); !ok { return ErrInvalidIP } } - if p.Port == 0 { + if len(p.PublicIP2) > 0 { + if _, ok := netip.AddrFromSlice(p.PublicIP2); !ok { + return ErrInvalidIP + } + if p.Port2 == 0 { + return ErrInvalidPort + } + } + if p.Port1 == 0 { return ErrInvalidPort } @@ -98,7 +105,6 @@ func Peer_Validate(p *Peer) error { if c == '.' || c == '-' || c == '_' { continue } - return ErrInvalidPeerName } diff --git a/hub/api/db/tables.defs b/hub/api/db/tables.defs index d6dc338..7e22294 100644 --- a/hub/api/db/tables.defs +++ b/hub/api/db/tables.defs @@ -3,14 +3,6 @@ TABLE config OF Config ( Password []byte ); -TABLE sessions OF Session NoUpdate ( - SessionID string PK, - CSRF string, - SignedIn bool, - CreatedAt int64, - LastSeenAt int64 -); - TABLE networks OF Network ( NetworkID int64 PK, Name string NoUpdate, @@ -23,9 +15,10 @@ TABLE peers OF Peer ( Version int64, APIKey string NoUpdate, Name string, - PublicIP []byte, - Port uint16, + PublicIP1 []byte, + Port1 uint16, + PublicIP2 []byte, + Port2 uint16, Relay bool, - PubKey []byte NoUpdate, - PubSignKey []byte NoUpdate + WGPubKey []byte NoUpdate ); diff --git a/hub/api/db/written.go b/hub/api/db/written.go index 6d61bb5..20496ef 100644 --- a/hub/api/db/written.go +++ b/hub/api/db/written.go @@ -1,31 +1,5 @@ package db -import "time" - -func Session_UpdateLastSeenAt( - tx TX, - id string, -) (err error) { - _, err = tx.Exec("UPDATE sessions SET LastSeenAt=? WHERE SessionID=?", time.Now().Unix(), id) - return err -} - -func Session_SetSignedIn( - tx TX, - id string, -) (err error) { - _, err = tx.Exec("UPDATE sessions SET SignedIn=1 WHERE SessionID=?", id) - return err -} - -func Session_DeleteBefore( - tx TX, - timestamp int64, -) (err error) { - _, err = tx.Exec("DELETE FROM sessions WHERE LastSeenAtCreate Network
- -

+


diff --git a/hub/templates/admin-password-edit.html b/hub/templates/admin-password-edit.html index 32c8d43..e65d2d4 100644 --- a/hub/templates/admin-password-edit.html +++ b/hub/templates/admin-password-edit.html @@ -2,8 +2,7 @@

Change Password

- -

+


diff --git a/hub/templates/admin-sign-out.html b/hub/templates/admin-sign-out.html index 7141fb8..fd5aa19 100644 --- a/hub/templates/admin-sign-out.html +++ b/hub/templates/admin-sign-out.html @@ -2,8 +2,7 @@

Sign Out

- -

+

Cancel

diff --git a/hub/templates/network/network-delete.html b/hub/templates/network/network-delete.html index 8b61116..f80bc79 100644 --- a/hub/templates/network/network-delete.html +++ b/hub/templates/network/network-delete.html @@ -5,8 +5,7 @@

You must first delete all peers.

{{- else -}} - - +

Cancel diff --git a/hub/templates/network/network-view.html b/hub/templates/network/network-view.html index 860814e..1dc453d 100644 --- a/hub/templates/network/network-view.html +++ b/hub/templates/network/network-view.html @@ -22,8 +22,10 @@ PeerIP Name - Public IP - Port + Public IP 1 + Port 1 + Public IP 2 + Port 2 Relay @@ -36,8 +38,10 @@ {{.Name}} - {{ipToString .PublicIP}} - {{.Port}} + {{ipToString .PublicIP1}} + {{.Port1}} + {{ipToString .PublicIP2}} + {{.Port2}} {{if .Relay}}T{{else}}F{{end}} diff --git a/hub/templates/network/peer-create.html b/hub/templates/network/peer-create.html index cc6b92f..ed6dd1b 100644 --- a/hub/templates/network/peer-create.html +++ b/hub/templates/network/peer-create.html @@ -2,7 +2,6 @@

New Peer

-


@@ -13,12 +12,20 @@

-
- +
+

-
- +
+ +

+

+
+ +

+

+
+

diff --git a/hub/templates/network/peer-edit.html b/hub/templates/network/peer-edit.html index a27f674..5953790 100644 --- a/hub/templates/network/peer-edit.html +++ b/hub/templates/network/peer-edit.html @@ -3,7 +3,6 @@ {{with .Peer -}} -


@@ -13,12 +12,20 @@

-
- +
+

-
- +
+ +

+

+
+ +

+

+
+

@@ -19,7 +21,6 @@

{{.APIKey}}

- {{- end}} {{- end}} diff --git a/hub/templates/sign-in.html b/hub/templates/sign-in.html index 07e9d87..6d885ad 100644 --- a/hub/templates/sign-in.html +++ b/hub/templates/sign-in.html @@ -2,8 +2,7 @@

Sign In

- -

+


diff --git a/m/models.go b/m/models.go index 0bac684..eabfce2 100644 --- a/m/models.go +++ b/m/models.go @@ -2,8 +2,7 @@ package m type PeerInitArgs struct { - EncPubKey []byte - PubSignKey []byte + WGPubKey []byte } type PeerInitResp struct { @@ -13,14 +12,15 @@ type PeerInitResp struct { } type Peer struct { - PeerIP byte - Version int64 - Name string - PublicIP []byte - Port uint16 - Relay bool - PubKey []byte - PubSignKey []byte + PeerIP byte + Version int64 + Name string + PublicIP1 []byte + Port1 uint16 + PublicIP2 []byte + Port2 uint16 + Relay bool + WGPubKey []byte } type NetworkState struct {