feat: data store
This commit is contained in:
parent
4f8ee6cf9c
commit
19bdd27de1
8 changed files with 123 additions and 53 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +1,2 @@
|
|||
.env
|
||||
data/
|
||||
data.json
|
||||
|
|
@ -2,12 +2,16 @@ package handler
|
|||
|
||||
import (
|
||||
"api/internal/service"
|
||||
"api/internal/storage"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HandleGetHitCounter(w http.ResponseWriter, r *http.Request) {
|
||||
data := service.GetHitCounter()
|
||||
data := storage.GlobalDataStore.Get("hits")
|
||||
if data == nil {
|
||||
data = 0
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(data)
|
||||
|
|
|
|||
|
|
@ -1,63 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"api/internal/model"
|
||||
"api/internal/storage"
|
||||
)
|
||||
|
||||
const path = "./data/hit.json"
|
||||
|
||||
func GetHitCounter() model.HitCounter {
|
||||
var data model.HitCounter
|
||||
|
||||
jsonFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
slog.Warn("File not found or unable to open", slog.Any("error", err), slog.Any("path", path))
|
||||
return data
|
||||
}
|
||||
defer jsonFile.Close()
|
||||
|
||||
bytes, err := io.ReadAll(jsonFile)
|
||||
if err != nil {
|
||||
slog.Error("Error reading file", slog.Any("error", err), slog.Any("path", path))
|
||||
return data
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytes, &data)
|
||||
if err != nil {
|
||||
slog.Error("Error unmarshalling JSON", slog.Any("error", err), slog.Any("path", path))
|
||||
return data
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func IncrementHitCounter() model.Success {
|
||||
data := GetHitCounter()
|
||||
data.Counter++
|
||||
hitsData := storage.GlobalDataStore.Get("hits")
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(path), 0755)
|
||||
if err != nil {
|
||||
slog.Error("Unable to create directory", slog.Any("error", err), slog.Any("path", filepath.Dir(path)))
|
||||
return model.Success{}
|
||||
var hits uint32
|
||||
if hitsData != nil {
|
||||
hits = hitsData.(uint32)
|
||||
}
|
||||
|
||||
jsonString, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
slog.Error("Error marshalling JSON", slog.Any("error", err), slog.Any("path", path))
|
||||
return model.Success{}
|
||||
}
|
||||
|
||||
err = os.WriteFile(path, jsonString, 0644)
|
||||
if err != nil {
|
||||
slog.Error("Error writing to file", slog.Any("error", err), slog.Any("path", path))
|
||||
return model.Success{}
|
||||
}
|
||||
storage.GlobalDataStore.Set("hits", hits+1)
|
||||
|
||||
return model.Success{
|
||||
Success: true,
|
||||
|
|
|
|||
72
internal/storage/datastore.go
Normal file
72
internal/storage/datastore.go
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type DataStore struct {
|
||||
Data map[string]any
|
||||
Mutex sync.Mutex
|
||||
}
|
||||
|
||||
var GlobalDataStore *DataStore
|
||||
|
||||
func InitDataStore() *DataStore {
|
||||
GlobalDataStore = &DataStore{
|
||||
Data: make(map[string]any),
|
||||
}
|
||||
|
||||
file, err := os.Open("./data.json")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
slog.Warn("Data store file not found; creating new store")
|
||||
return GlobalDataStore
|
||||
}
|
||||
slog.Error("Could not load data store file!", slog.Any("error", err))
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
decoder := json.NewDecoder(file)
|
||||
if err := decoder.Decode(&GlobalDataStore.Data); err != nil {
|
||||
slog.Error("Failed to decode data store file", slog.Any("error", err))
|
||||
return nil
|
||||
}
|
||||
|
||||
return GlobalDataStore
|
||||
}
|
||||
|
||||
func (store *DataStore) Get(key string) any {
|
||||
store.Mutex.Lock()
|
||||
defer store.Mutex.Unlock()
|
||||
return store.Data[key]
|
||||
}
|
||||
|
||||
func (store *DataStore) Set(key string, value any) {
|
||||
store.Mutex.Lock()
|
||||
defer store.Mutex.Unlock()
|
||||
store.Data[key] = value
|
||||
}
|
||||
|
||||
func (store *DataStore) Save() error {
|
||||
store.Mutex.Lock()
|
||||
defer store.Mutex.Unlock()
|
||||
|
||||
file, err := os.Create("./data.json")
|
||||
if err != nil {
|
||||
slog.Error("Could not create data store file", slog.Any("error", err))
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
encoder := json.NewEncoder(file)
|
||||
if err := encoder.Encode(store.Data); err != nil {
|
||||
slog.Error("Failed to encode data store", slog.Any("error", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
17
internal/worker/datastore.go
Normal file
17
internal/worker/datastore.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package worker
|
||||
|
||||
import (
|
||||
"api/internal/storage"
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
func StartDataStoreWorker() {
|
||||
slog.Info("Starting data store worker...")
|
||||
|
||||
for range time.Tick(1 * time.Minute) {
|
||||
if err := storage.GlobalDataStore.Save(); err != nil {
|
||||
slog.Error("Error saving data store", slog.Any("error", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ func StartLastFMWorker() {
|
|||
LastFMData = service.GetLastFMData()
|
||||
|
||||
for range time.Tick(30 * time.Second) {
|
||||
slog.Info("Requesting last.fm...")
|
||||
LastFMData = service.GetLastFMData()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package worker
|
||||
|
||||
func StartWorkers() {
|
||||
go StartDataStoreWorker()
|
||||
go StartLastFMWorker()
|
||||
go StartComputerWorker()
|
||||
}
|
||||
|
|
|
|||
21
main.go
21
main.go
|
|
@ -3,11 +3,14 @@ package main
|
|||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/lmittmann/tint"
|
||||
|
||||
"api/internal/server"
|
||||
"api/internal/storage"
|
||||
"api/internal/worker"
|
||||
)
|
||||
|
||||
|
|
@ -20,6 +23,24 @@ func main() {
|
|||
slog.Warn("No .env file was found; using environment variables.", slog.Any("error", err))
|
||||
}
|
||||
|
||||
storage.InitDataStore()
|
||||
go worker.StartWorkers()
|
||||
|
||||
// Shutdown-chan~~
|
||||
shutdownChan := make(chan os.Signal, 1)
|
||||
signal.Notify(shutdownChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
<-shutdownChan
|
||||
|
||||
if err := storage.GlobalDataStore.Save(); err != nil {
|
||||
slog.Error("Error saving data store on shutdown", slog.Any("error", err))
|
||||
} else {
|
||||
slog.Info("Data store saved successfully on shutdown")
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
server.NewRouter()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue