security measures

This commit is contained in:
Fran Jurmanović
2025-06-25 22:37:38 +02:00
parent 1ecd558e18
commit 69733e4940
16 changed files with 614 additions and 506 deletions

102
local/utl/cache/cache.go vendored Normal file
View File

@@ -0,0 +1,102 @@
package cache
import (
"sync"
"time"
"acc-server-manager/local/utl/logging"
"go.uber.org/dig"
)
// CacheItem represents an item in the cache
type CacheItem struct {
Value interface{}
Expiration int64
}
// InMemoryCache is a thread-safe in-memory cache
type InMemoryCache struct {
items map[string]CacheItem
mu sync.RWMutex
}
// NewInMemoryCache creates and returns a new InMemoryCache instance
func NewInMemoryCache() *InMemoryCache {
return &InMemoryCache{
items: make(map[string]CacheItem),
}
}
// Set adds an item to the cache with an expiration duration (in seconds)
func (c *InMemoryCache) Set(key string, value interface{}, duration time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
var expiration int64
if duration > 0 {
expiration = time.Now().Add(duration).UnixNano()
}
c.items[key] = CacheItem{
Value: value,
Expiration: expiration,
}
}
// Get retrieves an item from the cache
func (c *InMemoryCache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
item, found := c.items[key]
if !found {
return nil, false
}
if item.Expiration > 0 && time.Now().UnixNano() > item.Expiration {
// Item has expired, but don't delete here to avoid lock upgrade.
// It will be overwritten on the next Set.
return nil, false
}
return item.Value, true
}
// Delete removes an item from the cache
func (c *InMemoryCache) Delete(key string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.items, key)
}
// GetOrSet retrieves an item from the cache. If the item is not found, it
// calls the provided function to get the value, sets it in the cache, and
// returns it.
func GetOrSet[T any](c *InMemoryCache, key string, duration time.Duration, fetcher func() (T, error)) (T, error) {
if cached, found := c.Get(key); found {
if value, ok := cached.(T); ok {
return value, nil
}
}
value, err := fetcher()
if err != nil {
var zero T
return zero, err
}
c.Set(key, value, duration)
return value, nil
}
// Start initializes the cache and provides it to the DI container.
func Start(di *dig.Container) {
cache := NewInMemoryCache()
err := di.Provide(func() *InMemoryCache {
return cache
})
if err != nil {
logging.Panic("failed to provide cache")
}
}

View File

@@ -1,8 +1,33 @@
package configs
const (
Version = "0.0.1"
Prefix = "v1"
Secret = "Donde4sta"
SecretCode = "brasno"
import (
"log"
"os"
)
var (
Version = "0.0.1"
Prefix = "v1"
Secret string
SecretCode string
EncryptionKey string
)
func init() {
Secret = getEnv("APP_SECRET", "default-secret-for-dev-use-only")
SecretCode = getEnv("APP_SECRET_CODE", "another-secret-for-dev-use-only")
EncryptionKey = getEnv("ENCRYPTION_KEY", "a-secure-32-byte-long-key-!!!!!!") // Fallback MUST be 32 bytes for AES-256
if len(EncryptionKey) != 32 {
log.Fatal("ENCRYPTION_KEY must be 32 bytes long")
}
}
// getEnv retrieves an environment variable or returns a fallback value.
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
log.Printf("Environment variable %s not set, using fallback.", key)
return fallback
}

View File

@@ -3,6 +3,7 @@ package db
import (
"acc-server-manager/local/model"
"acc-server-manager/local/utl/logging"
"os"
"time"
"go.uber.org/dig"
@@ -11,7 +12,12 @@ import (
)
func Start(di *dig.Container) {
db, err := gorm.Open(sqlite.Open("acc.db"), &gorm.Config{})
dbName := os.Getenv("DB_NAME")
if dbName == "" {
dbName = "acc.db"
}
db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{})
if err != nil {
logging.Panic("failed to connect database")
}
@@ -25,50 +31,25 @@ func Start(di *dig.Container) {
}
func Migrate(db *gorm.DB) {
err := db.AutoMigrate(&model.ApiModel{})
logging.Info("Migrating database")
err := db.AutoMigrate(
&model.ApiModel{},
&model.Config{},
&model.Track{},
&model.CarModel{},
&model.CupCategory{},
&model.DriverCategory{},
&model.SessionType{},
&model.StateHistory{},
&model.SteamCredentials{},
&model.SystemConfig{},
)
if err != nil {
logging.Panic("failed to migrate model.ApiModel")
}
err = db.AutoMigrate(&model.Server{})
if err != nil {
logging.Panic("failed to migrate model.Server")
}
err = db.AutoMigrate(&model.Config{})
if err != nil {
logging.Panic("failed to migrate model.Config")
}
err = db.AutoMigrate(&model.Track{})
if err != nil {
logging.Panic("failed to migrate model.Track")
}
err = db.AutoMigrate(&model.CarModel{})
if err != nil {
logging.Panic("failed to migrate model.CarModel")
}
err = db.AutoMigrate(&model.CupCategory{})
if err != nil {
logging.Panic("failed to migrate model.CupCategory")
}
err = db.AutoMigrate(&model.DriverCategory{})
if err != nil {
logging.Panic("failed to migrate model.DriverCategory")
}
err = db.AutoMigrate(&model.SessionType{})
if err != nil {
logging.Panic("failed to migrate model.SessionType")
}
err = db.AutoMigrate(&model.StateHistory{})
if err != nil {
logging.Panic("failed to migrate model.StateHistory")
}
err = db.AutoMigrate(&model.SteamCredentials{})
if err != nil {
logging.Panic("failed to migrate model.SteamCredentials")
}
err = db.AutoMigrate(&model.SystemConfig{})
if err != nil {
logging.Panic("failed to migrate model.SystemConfig")
logging.Panic("failed to migrate database models")
}
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
Seed(db)
@@ -90,50 +71,13 @@ func Seed(db *gorm.DB) error {
if err := seedSessionTypes(db); err != nil {
return err
}
if err := seedServers(db); err != nil {
return err
}
if err := seedSteamCredentials(db); err != nil {
return err
}
if err := seedSystemConfigs(db); err != nil {
return err
}
return nil
}
func seedSteamCredentials(db *gorm.DB) error {
credentials := []model.SteamCredentials{
{
ID: 1,
Username: "test",
Password: "test",
DateCreated: time.Now().UTC(),
},
}
for _, credential := range credentials {
if err := db.FirstOrCreate(&credential).Error; err != nil {
return err
}
}
return nil
}
func seedServers(db *gorm.DB) error {
servers := []model.Server{
{ID: 1, Name: "ACC Server - Barcelona", ServiceName: "ACC-Barcelona", Path: "C:\\steamcmd\\acc", FromSteamCMD: true},
{ID: 2, Name: "ACC Server - Monza", ServiceName: "ACC-Monza", Path: "C:\\steamcmd\\acc2", FromSteamCMD: true},
{ID: 3, Name: "ACC Server - Spa", ServiceName: "ACC-Spa", Path: "C:\\steamcmd\\acc3", FromSteamCMD: true},
{ID: 4, Name: "ACC Server - League", ServiceName: "ACC-League", Path: "C:\\steamcmd\\acc-league", FromSteamCMD: true},
}
for _, track := range servers {
if err := db.FirstOrCreate(&track).Error; err != nil {
return err
}
}
return nil
}
func seedTracks(db *gorm.DB) error {
tracks := []model.Track{

View File

@@ -3,32 +3,31 @@ package server
import (
"acc-server-manager/local/api"
"acc-server-manager/local/utl/logging"
"fmt"
"os"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/swagger"
"go.uber.org/dig"
)
func Start(di *dig.Container) *fiber.App {
// Initialize logger
logger, err := logging.Initialize()
if err != nil {
fmt.Printf("Failed to initialize logger: %v\n", err)
os.Exit(1)
}
defer logger.Close()
// Set up panic recovery
defer logging.RecoverAndLog()
app := fiber.New(fiber.Config{
EnablePrintRoutes: true,
})
app.Use(cors.New())
app.Use(helmet.New())
allowedOrigin := os.Getenv("CORS_ALLOWED_ORIGIN")
if allowedOrigin == "" {
allowedOrigin = "http://localhost:5173"
}
app.Use(cors.New(cors.Config{
AllowOrigins: allowedOrigin,
AllowHeaders: "Origin, Content-Type, Accept",
}))
app.Get("/swagger/*", swagger.HandlerDefault)