security measures
This commit is contained in:
102
local/utl/cache/cache.go
vendored
Normal file
102
local/utl/cache/cache.go
vendored
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user