godoor/godoor_server/godoor_server.go
rasmus 8c14e3bc41
Some checks failed
continuous-integration/drone/pr Build is failing
use Duration for doorOpenTime + lint
2023-08-17 20:45:11 +03:00

279 lines
6.8 KiB
Go

// really stupid test door server
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"time"
)
type DoorBoyServer struct {
longPollers map[string]chan string
}
type card struct {
UidHash string `json:"uid_hash"`
}
type cardToken struct {
Token card `json:"token"`
}
type cardList struct {
AllowedUids []cardToken `json:"allowed_uids"`
KeepOpenUntil *time.Time `json:"keep_open_until,omitempty"`
}
var keys []cardToken = []cardToken{
//{card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},
{card{UidHash: "873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},
{card{UidHash: "a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},
{card{UidHash: "d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},
}
var keyLock sync.RWMutex
var keepOpenUntil *time.Time
func getRoot(w http.ResponseWriter, r *http.Request) {
fmt.Printf("got / request\n")
io.WriteString(w, "This is my website!\n")
}
func getAllowed(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
keyLock.RLock()
keys := cardList{
AllowedUids: keys,
KeepOpenUntil: keepOpenUntil,
}
respJson, _ := json.Marshal(keys)
keyLock.RUnlock()
_, err := w.Write(respJson)
if err != nil {
return
}
}
func writeAndFlush(writer http.ResponseWriter, buffer []byte) error {
_, err := writer.Write(buffer)
if err != nil {
return err
}
if f, ok := writer.(http.Flusher); ok {
f.Flush()
}
return nil
}
func (doorboyserver *DoorBoyServer) getLongPoll(w http.ResponseWriter, r *http.Request) {
err := writeAndFlush(w, []byte("data: response-generator-started\n\n"))
if err != nil {
return
}
events := make(chan string)
doorboyserver.longPollers[r.RemoteAddr] = events
defer delete(doorboyserver.longPollers, r.RemoteAddr)
err = writeAndFlush(w, []byte("data: watch-stream-opened\n\n"))
if err != nil {
return
}
d := <-events // get door open event
_, err = w.Write([]byte("data: "))
if err != nil {
return
}
_, err = w.Write([]byte(d))
if err != nil {
return
}
err = writeAndFlush(w, []byte("\n\n"))
if err != nil {
return
}
}
func (doorboyserver *DoorBoyServer) getLongPollJson(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
fmt.Println("Failed to get flusher for http response")
}
w.WriteHeader(http.StatusOK)
flusher.Flush()
respJson, _ := json.Marshal([]string{"123456789", "aabbccddeeff", "aabbcc"})
for i := 1; i <= 10; i++ {
w.Write(respJson)
w.Write([]byte("\n"))
fmt.Println("send")
flusher.Flush() // Trigger "chunked" encoding and send a chunk...
time.Sleep(500 * time.Millisecond)
}
}
func (doobserver *DoorBoyServer) postOpenDoor(w http.ResponseWriter, r *http.Request) {
fmt.Println("longPollers:")
for key, value := range doobserver.longPollers {
fmt.Println(" Key:", key, "Value:", value)
value <- "workshop"
}
w.Write([]byte("OK"))
}
func postCardSwipe(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
log.Println("Swipe Request with key:", string(data))
w.Write([]byte("OK"))
}
func (doobserver *DoorBoyServer) postAddCard(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
cardHex := string(data)
if len(cardHex) != 128 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Key invalid"))
return
}
keyLock.RLock()
found := false
for _, key := range keys {
if key.Token.UidHash == cardHex {
// key is already in there!
found = true
}
}
keyLock.RUnlock()
if found {
return
}
keyLock.Lock()
keys = append(keys, cardToken{Token: card{UidHash: cardHex}})
keyLock.Unlock()
for _, datastream := range doobserver.longPollers {
datastream <- "update"
}
log.Println("Added new key:", string(data))
}
func (doobserver *DoorBoyServer) postRemoveCard(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
cardHex := string(data)
if len(cardHex) != 128 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Key invalid"))
return
}
keyLock.Lock()
foundIndex := -1
for i, key := range keys {
if key.Token.UidHash == cardHex {
// key is already in there!
foundIndex = i
}
}
if foundIndex == -1 {
keyLock.Unlock()
return
}
if foundIndex == len(keys)-1 {
keys = keys[:foundIndex]
} else {
keys = append(keys[:foundIndex], keys[foundIndex+1:]...)
}
keyLock.Unlock()
for _, datastream := range doobserver.longPollers {
datastream <- "update"
}
log.Println("Remove key:", string(data))
}
func (doobserver *DoorBoyServer) postKeepDoorOpen(w http.ResponseWriter, r *http.Request) {
rs, _ := io.ReadAll(r.Body)
parsedTime, err := time.Parse(time.RFC3339, strings.TrimSpace(string(rs)))
if err != nil {
fmt.Printf("Error with parsing time: %v", err)
w.WriteHeader(http.StatusBadRequest)
}
keepOpenUntil = &parsedTime
doobserver.postOpenDoor(w, r)
}
func main() {
fmt.Println("Running...")
var dumpKeys []cardToken
savedKeys, err := os.ReadFile("keys.json")
if err != nil {
log.Fatalf("reading saved keys from keys.json: %v", err)
}
err = json.Unmarshal(savedKeys, &dumpKeys)
if err == nil {
for _, k := range dumpKeys {
keys = append(keys, k)
}
log.Println("Loaded keys successfully!")
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
doorboyserver := DoorBoyServer{longPollers: map[string]chan string{}}
http.HandleFunc("/", getRoot)
http.HandleFunc("/allowed", getAllowed)
http.HandleFunc("/longpoll", doorboyserver.getLongPoll)
http.HandleFunc("/jspoll", doorboyserver.getLongPollJson)
http.HandleFunc("/open", doorboyserver.postOpenDoor)
http.HandleFunc("/cardswipe", postCardSwipe)
http.HandleFunc("/keepdooropen", doorboyserver.postKeepDoorOpen)
http.HandleFunc("/addcard", doorboyserver.postAddCard)
http.HandleFunc("/removecard", doorboyserver.postRemoveCard)
go func() {
err := http.ListenAndServe(":3333", nil)
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error starting server: %s\n", err)
os.Exit(1)
}
}()
<-ctx.Done()
log.Println("Shutting down!")
keyLock.Lock()
data, err := json.Marshal(keys)
if err == nil {
os.WriteFile("keys.json", data, 0o600)
log.Println("Saved keys successfully!")
}
keyLock.Unlock()
}