refactor: flatten hash updates

This commit is contained in:
2025-12-22 21:18:23 +02:00
parent 2f4005a7ba
commit 9bf71b347f
9 changed files with 82 additions and 90 deletions

1
.dockerignore Symbolic link
View File

@@ -0,0 +1 @@
./.gitignore

6
.env
View File

@@ -1,6 +0,0 @@
KDOORPI_DOOR=workshop
KDOORPI_API_ALLOWED=http://127.0.0.1:3333/allowed
KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll
KDOORPI_API_SWIPE=http://127.0.0.1:3333/cardswipe
KDOORPI_API_KEY=keykey
KDOORPI_MOCK_HW=true

3
.gitignore vendored
View File

@@ -2,3 +2,6 @@
godoor godoor
godoor_server/godoor_server godoor_server/godoor_server
godoor_server/keys.json godoor_server/keys.json
__debug_bin*
.env
.vscode

View File

@@ -1,4 +1,4 @@
FROM mirror.gcr.io/library/golang:1.21-alpine as build FROM mirror.gcr.io/library/golang:1.25-alpine AS build
RUN apk add ca-certificates RUN apk add ca-certificates
WORKDIR /godoor WORKDIR /godoor

3
go.mod
View File

@@ -1,9 +1,8 @@
module godoor module godoor
go 1.21 go 1.25
require ( require (
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_golang v1.16.0
github.com/warthog618/gpiod v0.8.2 github.com/warthog618/gpiod v0.8.2
golang.org/x/crypto v0.12.0 golang.org/x/crypto v0.12.0

2
go.sum
View File

@@ -11,8 +11,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

104
godoor.go
View File

@@ -18,7 +18,6 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/joho/godotenv"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@@ -33,18 +32,12 @@ const (
solenoid_default = 21 solenoid_default = 21
) )
type card struct { type upstreamUpdate struct {
UidHash string `json:"uid_hash"` AllowedHashes []string `json:"allowed_hashes"`
}
type cardList struct {
AllowedUids []struct {
Token card `json:"token"`
} `json:"allowed_uids"`
KeepOpenUntil *time.Time `json:"keep_open_until,omitempty"` KeepOpenUntil *time.Time `json:"keep_open_until,omitempty"`
} }
type ValidUids map[string]bool // bool has no meaning type ValidHashesT map[string]bool // bool has no meaning
type Config struct { type Config struct {
door string door string
@@ -90,11 +83,11 @@ var Commit = func() string {
var Version string var Version string
var ( var (
config Config config Config
globalLock sync.Mutex VALID_HASHES_LOCK sync.Mutex
validUids ValidUids VALID_HASHES = make(ValidHashesT)
wiegand Wiegand wiegand Wiegand
keepDoorOpen KeepDoorOpen keepDoorOpen KeepDoorOpen
) )
var ( var (
@@ -130,6 +123,7 @@ func main() {
defer cancel() defer cancel()
loadConfig() loadConfig()
log.Printf("Door name: %s\n", config.door)
godoorBuildInfo.WithLabelValues(Version, Commit).Set(1) godoorBuildInfo.WithLabelValues(Version, Commit).Set(1)
@@ -146,12 +140,6 @@ func main() {
func loadConfig() { func loadConfig() {
var err error var err error
log.Println("Loading .env config")
err = godotenv.Load()
if err != nil {
log.Println("Failed to load .env config, using internal defaults")
}
config.door = os.Getenv("KDOORPI_DOOR") config.door = os.Getenv("KDOORPI_DOOR")
config.api.allowed = os.Getenv("KDOORPI_API_ALLOWED") config.api.allowed = os.Getenv("KDOORPI_API_ALLOWED")
config.api.longpoll = os.Getenv("KDOORPI_API_LONGPOLL") config.api.longpoll = os.Getenv("KDOORPI_API_LONGPOLL")
@@ -189,8 +177,6 @@ func loadConfig() {
} }
func setup(ctx context.Context) { func setup(ctx context.Context) {
log.Println("Started Setup")
if config.mock { if config.mock {
log.Println("MOCK mode enabled") log.Println("MOCK mode enabled")
if config.door == "" { if config.door == "" {
@@ -208,7 +194,7 @@ func setup(ctx context.Context) {
for { for {
log.Println("Start initial token population") log.Println("Start initial token population")
err := reloadTokens() err := reloadInfo()
if err == nil { if err == nil {
break break
} }
@@ -230,9 +216,9 @@ func setup(ctx context.Context) {
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
go func() { go func() {
err := reloadTokens() err := reloadInfo()
if err != nil { if err != nil {
log.Printf("ReloadTokens failed: %q", err) log.Printf("reloadTokens failed: %q", err)
apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc() apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc()
} }
}() }()
@@ -325,9 +311,9 @@ func cardRunner(w Wiegand) {
hashedHex := hash.HashCardUid(card) hashedHex := hash.HashCardUid(card)
log.Println(hashedHex) log.Println(hashedHex)
globalLock.Lock() VALID_HASHES_LOCK.Lock()
ok := validUids[hashedHex] ok := VALID_HASHES[hashedHex]
globalLock.Unlock() VALID_HASHES_LOCK.Unlock()
go func() { go func() {
err := sendSwipeEvent(hashedHex, ok) err := sendSwipeEvent(hashedHex, ok)
@@ -416,12 +402,13 @@ func waitEvents() error {
} }
} }
func reloadTokens() error { func reloadInfo() error {
req, err := http.NewRequest(http.MethodGet, config.api.allowed, nil) req, err := http.NewRequest(http.MethodGet, config.api.allowed, nil)
if err != nil { if err != nil {
return err return err
} }
req.Header.Add("KEY", config.api.key) req.Header.Add("KEY", config.api.key)
req.Header.Add("DOOR_NAME", config.door)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return err return err
@@ -430,32 +417,35 @@ func reloadTokens() error {
log.Printf("%v\n", resp) log.Printf("%v\n", resp)
} }
var cl cardList var info upstreamUpdate
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal(body, &cl) err = json.Unmarshal(body, &info)
if err != nil { if err != nil {
return err return err
} }
globalLock.Lock() // Update Allowed hashes
defer globalLock.Unlock() validHashesPre := make(ValidHashesT)
validUids = make(ValidUids) for _, hash := range info.AllowedHashes {
var totalCardCount int = 0 validHashesPre[hash] = true
for i, val := range cl.AllowedUids {
// log.Printf("%d: %+v\n", i, val.Token.UidHash)
validUids[val.Token.UidHash] = true
totalCardCount = i
} }
log.Printf("Got %d cards from server", totalCardCount)
nrCardsInAllowList.Set(float64(totalCardCount))
if cl.KeepOpenUntil != nil { VALID_HASHES_LOCK.Lock()
updateKeepOpenDoor(*cl.KeepOpenUntil) VALID_HASHES = validHashesPre
VALID_HASHES_LOCK.Unlock()
log.Printf("Got %d cards from server", len(info.AllowedHashes))
nrCardsInAllowList.Set(float64(len(info.AllowedHashes)))
// Update Keep open
if info.KeepOpenUntil != nil {
updateKeepOpenDoor(*info.KeepOpenUntil)
} }
lastSyncTimestamp.SetToCurrentTime() lastSyncTimestamp.SetToCurrentTime()
@@ -463,32 +453,6 @@ func reloadTokens() error {
return nil return nil
} }
func updateKeepOpenDoor(newKeepOpenTime time.Time) {
// is there one active?
if keepDoorOpen.timer != nil {
keepDoorOpen.timer.Stop()
keepDoorOpen = KeepDoorOpen{}
}
if newKeepOpenTime.After(time.Now()) {
log.Printf("Keeping door open until %v", newKeepOpenTime)
OpenDoor(wiegand)
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
keepDoorOpen = KeepDoorOpen{
timer: timer,
until: newKeepOpenTime,
}
} else {
CloseDoor(wiegand)
}
}
func handleKeepDoorOpenCloseCleanup() {
fmt.Println("Keep door open time is reached!")
CloseDoor(wiegand)
keepDoorOpen = KeepDoorOpen{}
}
func sendSwipeEvent(cardUidHash string, success bool) error { func sendSwipeEvent(cardUidHash string, success bool) error {
swipeEvent := map[string]any{ swipeEvent := map[string]any{
"uid_hash": cardUidHash, "uid_hash": cardUidHash,

33
keepopen.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"log"
"time"
)
func updateKeepOpenDoor(newKeepOpenTime time.Time) {
// is there one active?
if keepDoorOpen.timer != nil {
keepDoorOpen.timer.Stop()
keepDoorOpen = KeepDoorOpen{}
}
if newKeepOpenTime.After(time.Now()) {
log.Printf("Keeping door open until %v", newKeepOpenTime)
OpenDoor(wiegand)
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
keepDoorOpen = KeepDoorOpen{
timer: timer,
until: newKeepOpenTime,
}
} else {
CloseDoor(wiegand)
}
}
func handleKeepDoorOpenCloseCleanup() {
fmt.Println("Keep door open time is reached!")
CloseDoor(wiegand)
keepDoorOpen = KeepDoorOpen{}
}

View File

@@ -57,6 +57,7 @@ func (w *WiegandHW) IsDoorOpen() (bool, error) {
defer w.lock.RUnlock() defer w.lock.RUnlock()
return w.isDoorOpen() return w.isDoorOpen()
} }
func (w *WiegandHW) isDoorOpen() (bool, error) { func (w *WiegandHW) isDoorOpen() (bool, error) {
i, err := w.solenoidLine.Value() i, err := w.solenoidLine.Value()
if err != nil { if err != nil {
@@ -90,19 +91,18 @@ func (w *WiegandHW) GetCardUid() (uint64, error) {
func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) { func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) {
w.bitTimeoutTimer.Reset(w.bitTimeout) w.bitTimeoutTimer.Reset(w.bitTimeout)
w.bits[w.bitNr] = true w.bits[w.bitNr] = true
//fmt.Printf("1") // fmt.Printf("1")
w.bitNr += 1 w.bitNr += 1
} }
func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) { func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) {
w.bitTimeoutTimer.Reset(w.bitTimeout) w.bitTimeoutTimer.Reset(w.bitTimeout)
w.bits[w.bitNr] = false w.bits[w.bitNr] = false
//fmt.Printf("0") // fmt.Printf("0")
w.bitNr += 1 w.bitNr += 1
} }
func WiegandSetup(a int, b int, bitTimeout time.Duration, solenoid int) *WiegandHW { func WiegandSetup(a int, b int, bitTimeout time.Duration, solenoid int) *WiegandHW {
log.Printf("Wiegand GPIO-s: A:%d B:%d Solenoid:%d", a, b, solenoid) log.Printf("Wiegand GPIO-s: A:%d B:%d Solenoid:%d", a, b, solenoid)
var wiegand WiegandHW var wiegand WiegandHW
wiegand.bitTimeout = bitTimeout wiegand.bitTimeout = bitTimeout
@@ -141,9 +141,9 @@ func (w *WiegandHW) WiegandClose() {
w.solenoidLine.Close() w.solenoidLine.Close()
} }
func printCardId(card uint64) { // func printCardId(card uint64) {
for i := 0; i < 7; i++ { // for i := 0; i < 7; i++ {
fmt.Printf("%02x", (card>>(8*i))&0xff) // fmt.Printf("%02x", (card>>(8*i))&0xff)
} // }
fmt.Printf("\n") // fmt.Printf("\n")
} // }