feat: data store

This commit is contained in:
trafficlunar 2025-03-22 12:05:19 +00:00
parent 4f8ee6cf9c
commit 19bdd27de1
8 changed files with 123 additions and 53 deletions

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
.env
data/
data.json

View file

@ -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)

View file

@ -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,

View 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
}

View 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))
}
}
}

View file

@ -14,7 +14,6 @@ func StartLastFMWorker() {
LastFMData = service.GetLastFMData()
for range time.Tick(30 * time.Second) {
slog.Info("Requesting last.fm...")
LastFMData = service.GetLastFMData()
}
}

View file

@ -1,6 +1,7 @@
package worker
func StartWorkers() {
go StartDataStoreWorker()
go StartLastFMWorker()
go StartComputerWorker()
}

21
main.go
View file

@ -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()
}