refactored for ordered consistent logging
This commit is contained in:
parent
d0eb8f733c
commit
cb202de53f
147
admin.go
147
admin.go
|
@ -13,66 +13,113 @@ import (
|
|||
|
||||
// GetUsersHandler handles GET requests to /admin/user
|
||||
func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
username := r.Context().Value(userKey).(string)
|
||||
|
||||
log.Info("Admin request to get all users by user: %s", username)
|
||||
|
||||
var users []User
|
||||
db.Find(&users)
|
||||
if err := db.Find(&users).Error; err != nil {
|
||||
log.Error("Failed to fetch users: %v", err)
|
||||
http.Error(w, "Failed to fetch users", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Successfully retrieved %d users", len(users))
|
||||
json.NewEncoder(w).Encode(users)
|
||||
}
|
||||
|
||||
// CreateUserHandler handles POST requests to /admin/user
|
||||
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
adminUser := r.Context().Value(userKey).(string)
|
||||
|
||||
var user User
|
||||
err := json.NewDecoder(r.Body).Decode(&user)
|
||||
if err != nil {
|
||||
log.Error("Failed to decode user creation request: %v", err)
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Admin user %s attempting to create new user: %s", adminUser, user.Username)
|
||||
|
||||
// Hash the password before storing
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), 12)
|
||||
if err != nil {
|
||||
log.Error("Failed to hash password for new user %s: %v", user.Username, err)
|
||||
http.Error(w, "Failed to hash password", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
user.Password = string(hashedPassword)
|
||||
|
||||
db.Create(&user)
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
log.Error("Failed to create user %s: %v", user.Username, err)
|
||||
http.Error(w, "Failed to create user", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.DatabaseAction("create", fmt.Sprintf("User %s created by admin %s", user.Username, adminUser))
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
// GetUserHandler handles GET requests to /admin/user/{id}
|
||||
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
adminUser := r.Context().Value(userKey).(string)
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
log.Info("Admin user %s requesting details for user ID: %s", adminUser, id)
|
||||
|
||||
var user User
|
||||
db.First(&user, id)
|
||||
if user.ID == 0 {
|
||||
if err := db.First(&user, id).Error; err != nil {
|
||||
log.Warn("User not found with ID %s, requested by admin %s", id, adminUser)
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Successfully retrieved user %s details", user.Username)
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
// DeleteUserHandler handles DELETE requests to /admin/user/{id}
|
||||
func DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
adminUser := r.Context().Value(userKey).(string)
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
log.Info("Admin user %s attempting to delete user ID: %s", adminUser, id)
|
||||
|
||||
var user User
|
||||
db.First(&user, id)
|
||||
if user.ID == 0 {
|
||||
if err := db.First(&user, id).Error; err != nil {
|
||||
log.Warn("Attempt to delete non-existent user ID %s by admin %s", id, adminUser)
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
db.Delete(&user)
|
||||
|
||||
if err := db.Delete(&user).Error; err != nil {
|
||||
log.Error("Failed to delete user %s: %v", user.Username, err)
|
||||
http.Error(w, "Failed to delete user", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.DatabaseAction("delete", fmt.Sprintf("User %s deleted by admin %s", user.Username, adminUser))
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// BackupDatabaseHandler handles GET requests to /admin/db
|
||||
func BackupDatabaseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
fmt.Println("BackupDatabaseHandler called")
|
||||
log := GetLogger()
|
||||
adminUser := r.Context().Value(userKey).(string)
|
||||
|
||||
log.Info("Database backup requested by admin user: %s", adminUser)
|
||||
|
||||
// Open the database file using the path from the config
|
||||
file, err := os.Open(*DatabasePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to open database file for backup: %v", err)
|
||||
http.Error(w, "Failed to open database file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -81,85 +128,129 @@ func BackupDatabaseHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// Copy the file to the response writer
|
||||
_, err = io.Copy(w, file)
|
||||
if err != nil {
|
||||
log.Error("Failed to send database backup: %v", err)
|
||||
http.Error(w, "Failed to send database file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.DatabaseAction("backup", fmt.Sprintf("Database backup created by admin %s", adminUser))
|
||||
log.Info("Database backup successfully completed")
|
||||
}
|
||||
|
||||
// RestoreDatabaseHandler handles POST requests to /admin/db
|
||||
func RestoreDatabaseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
adminUser := r.Context().Value(userKey).(string)
|
||||
|
||||
log.Info("Database restore requested by admin user: %s", adminUser)
|
||||
|
||||
// Create a backup of the existing database
|
||||
err := createDatabaseBackup()
|
||||
if err != nil {
|
||||
log.Error("Failed to create backup before restore: %v", err)
|
||||
http.Error(w, "Failed to create database backup", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Info("Created backup of existing database")
|
||||
|
||||
// Save the new database
|
||||
err = saveNewDatabase(r)
|
||||
if err != nil {
|
||||
log.Error("Failed to save new database during restore: %v", err)
|
||||
http.Error(w, "Failed to save new database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Info("Saved new database file")
|
||||
|
||||
// Validate the new database is properly initialized
|
||||
err = validateNewDatabase()
|
||||
if err != nil {
|
||||
log.Error("New database validation failed: %v", err)
|
||||
http.Error(w, "New database is not properly initialized", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Info("New database validation successful")
|
||||
|
||||
// Switch to the new database app-wide
|
||||
err = switchToNewDatabase()
|
||||
if err != nil {
|
||||
log.Error("Failed to switch to new database: %v", err)
|
||||
http.Error(w, "Failed to switch to new database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println("Database restored successfully")
|
||||
|
||||
log.DatabaseAction("restore", fmt.Sprintf("Database restored by admin %s", adminUser))
|
||||
log.Info("Database restore completed successfully")
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{"message": "Database restored successfully"})
|
||||
}
|
||||
|
||||
func createDatabaseBackup() error {
|
||||
// Create a backup of the existing database
|
||||
log := GetLogger()
|
||||
backupPath := *DatabasePath + ".bak"
|
||||
|
||||
log.Info("Creating database backup at: %s", backupPath)
|
||||
|
||||
src, err := os.Open(*DatabasePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to open source database for backup: %v", err)
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.Create(*DatabasePath + ".bak")
|
||||
dst, err := os.Create(backupPath)
|
||||
if err != nil {
|
||||
log.Error("Failed to create backup file: %v", err)
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, src)
|
||||
return err
|
||||
if err != nil {
|
||||
log.Error("Failed to copy database to backup: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Database backup created successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveNewDatabase(r *http.Request) error {
|
||||
// Save the new database
|
||||
log := GetLogger()
|
||||
|
||||
file, _, err := r.FormFile("database")
|
||||
if err != nil {
|
||||
log.Error("Failed to get database file from request: %v", err)
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
dst, err := os.Create(*DatabasePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to create new database file: %v", err)
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, file)
|
||||
return err
|
||||
if err != nil {
|
||||
log.Error("Failed to save new database file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("New database file saved successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNewDatabase() error {
|
||||
// Validate the new database is properly initialized
|
||||
log := GetLogger()
|
||||
|
||||
log.Info("Validating new database")
|
||||
|
||||
db, err := ConnectDB(*DatabasePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to connect to new database: %v", err)
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
@ -168,22 +259,36 @@ func validateNewDatabase() error {
|
|||
tables := []string{"users", "boxes", "items"}
|
||||
for _, table := range tables {
|
||||
var count int
|
||||
db.Debug().Raw("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?;", table).Row().Scan(&count)
|
||||
err := db.Debug().Raw("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?;", table).Row().Scan(&count)
|
||||
if err != nil {
|
||||
log.Error("Error checking for table %s: %v", table, err)
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
log.Error("Required table %s does not exist in new database", table)
|
||||
return fmt.Errorf("table %s does not exist", table)
|
||||
}
|
||||
log.Info("Validated table exists: %s", table)
|
||||
}
|
||||
|
||||
log.Info("Database validation completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func switchToNewDatabase() error {
|
||||
// Switch to the new database app-wide
|
||||
db, err := ConnectDB(*DatabasePath)
|
||||
log := GetLogger()
|
||||
|
||||
log.Info("Switching to new database")
|
||||
|
||||
newDB, err := ConnectDB(*DatabasePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to connect to new database during switch: %v", err)
|
||||
return err
|
||||
}
|
||||
// Update the db variable with the new database connection
|
||||
db = db
|
||||
|
||||
// Update the global db variable
|
||||
db = newDB
|
||||
|
||||
log.Info("Successfully switched to new database")
|
||||
return nil
|
||||
}
|
||||
|
|
75
auth.go
75
auth.go
|
@ -30,42 +30,46 @@ type LoginResponse struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
fmt.Printf("handlers.go init: config = %+v", config)
|
||||
log := GetLogger()
|
||||
if log != nil {
|
||||
log.Info("Initializing authentication module")
|
||||
log.Debug("Current config: %+v", config)
|
||||
}
|
||||
}
|
||||
|
||||
// loginHandler handles the /login endpoint.
|
||||
// LoginHandler handles the /login endpoint.
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
var req LoginRequest
|
||||
//fmt.Println("db is ", db)
|
||||
//fmt.Println("config is ", config)
|
||||
|
||||
if db == nil {
|
||||
fmt.Println("DB is nil")
|
||||
if log != nil {
|
||||
log.Error("Database connection not initialized in LoginHandler")
|
||||
}
|
||||
http.Error(w, "Database not initialized", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println("DB is not nil")
|
||||
|
||||
//if config == nil {
|
||||
//fmt.Println("Config is nil in LoginHandler")
|
||||
//h//ttp.Error(w, "Configuration not loaded", http.StatusInternalServerError)
|
||||
//return
|
||||
//}
|
||||
//fmt.Println("Config is not nil")
|
||||
if log != nil {
|
||||
log.Info("Processing login request")
|
||||
}
|
||||
|
||||
fmt.Printf("DB: %+v\n", db)
|
||||
//fmt.Printf("Config: %+v\n", config)
|
||||
fmt.Println("LoginHandler called")
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
if log != nil {
|
||||
log.Error("Failed to decode login request: %v", err)
|
||||
}
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists and the password matches
|
||||
var user User
|
||||
db.Where("username = ?", req.Username).First(&user)
|
||||
if user.ID == 0 {
|
||||
result := db.Where("username = ?", req.Username).First(&user)
|
||||
if result.Error != nil || user.ID == 0 {
|
||||
if log != nil {
|
||||
log.Warn("Login attempt failed for username: %s - user not found", req.Username)
|
||||
}
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
@ -73,6 +77,9 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// Compare the provided password with the stored hashed password
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
||||
if err != nil {
|
||||
if log != nil {
|
||||
log.Warn("Login attempt failed for username: %s - invalid password", req.Username)
|
||||
}
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
@ -85,19 +92,29 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
tokenString, err := token.SignedString(*JWTSecret)
|
||||
if err != nil {
|
||||
if log != nil {
|
||||
log.Error("Failed to generate JWT token for user %s: %v", user.Username, err)
|
||||
}
|
||||
http.Error(w, "Failed to generate token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if log != nil {
|
||||
log.Info("Successful login for user: %s", user.Username)
|
||||
log.UserAction(user.Username, "login")
|
||||
}
|
||||
|
||||
// Return the token in the response
|
||||
json.NewEncoder(w).Encode(LoginResponse{Token: tokenString})
|
||||
}
|
||||
|
||||
// authMiddleware is a middleware function that checks for a valid JWT token in the request header and enables CORS.
|
||||
// AuthMiddleware is a middleware function that checks for a valid JWT token in the request header and enables CORS.
|
||||
func AuthMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
|
||||
// Set CORS headers
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // Replace "*" with your allowed frontend origin if needed
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
|
@ -110,6 +127,9 @@ func AuthMiddleware(next http.Handler) http.Handler {
|
|||
// Get the token from the request header
|
||||
tokenString := r.Header.Get("Authorization")
|
||||
if tokenString == "" {
|
||||
if log != nil {
|
||||
log.Warn("Request rejected: missing Authorization header")
|
||||
}
|
||||
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
@ -121,21 +141,36 @@ func AuthMiddleware(next http.Handler) http.Handler {
|
|||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Make sure that the signing method is HMAC
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
if log != nil {
|
||||
log.Warn("Invalid signing method in token: %v", token.Header["alg"])
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return *JWTSecret, nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
if log != nil {
|
||||
log.Warn("Invalid token: %v", err)
|
||||
}
|
||||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the user claims from the token
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
username := claims["username"].(string)
|
||||
// Add the "user" claim to the request context
|
||||
newCtx := context.WithValue(r.Context(), userKey, claims["username"])
|
||||
newCtx := context.WithValue(r.Context(), userKey, username)
|
||||
r = r.WithContext(newCtx)
|
||||
|
||||
if log != nil {
|
||||
log.Debug("Authenticated request for user: %s", username)
|
||||
}
|
||||
} else {
|
||||
if log != nil {
|
||||
log.Warn("Invalid token claims structure")
|
||||
}
|
||||
http.Error(w, "Invalid token claims", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -10,18 +10,22 @@ import (
|
|||
|
||||
// getBoxesHandler handles the GET /boxes endpoint.
|
||||
func GetBoxesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("Received %s request to %s\n", r.Method, r.URL)
|
||||
log := GetLogger()
|
||||
log.Info("Received %s request to %s", r.Method, r.URL)
|
||||
|
||||
var boxes []Box
|
||||
db.Find(&boxes)
|
||||
json.NewEncoder(w).Encode(boxes)
|
||||
}
|
||||
|
||||
func GetBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
log.Warn("Box not found with ID: %s", id)
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
@ -31,9 +35,11 @@ func GetBoxHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// createBoxHandler handles the POST /boxes endpoint.
|
||||
func CreateBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
var box Box
|
||||
err := json.NewDecoder(r.Body).Decode(&box)
|
||||
if err != nil {
|
||||
log.Error("Failed to decode box creation request: %v", err)
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -51,26 +57,26 @@ func CreateBoxHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Name: box.Name,
|
||||
}
|
||||
|
||||
log.DatabaseAction("create", fmt.Sprintf("Created box with ID %d", box.ID))
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// deleteBoxHandler handles the DELETE /boxes/{id} endpoint.
|
||||
func DeleteBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
// Retrieve the box from the database
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
log.Warn("Attempt to delete non-existent box with ID: %s", id)
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Optionally, delete associated items (if you want cascading delete)
|
||||
// db.Where("box_id = ?", id).Delete(&Item{})
|
||||
|
||||
// Delete the box
|
||||
db.Delete(&box)
|
||||
|
||||
log.DatabaseAction("delete", fmt.Sprintf("Deleted box with ID %d", box.ID))
|
||||
w.WriteHeader(http.StatusNoContent) // 204 No Content
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ type Config struct {
|
|||
ImageStorageDir string `yaml:"image_storage_dir"`
|
||||
ListeningPort int `yaml:"listening_port"`
|
||||
LogFile string `yaml:"log_file"`
|
||||
LogLevel string `yaml:"log_level"`
|
||||
StaticFilesDir string `yaml:"static_files_dir"`
|
||||
AllowedOrigins string `yaml:"allowed_origins"`
|
||||
}
|
||||
|
|
|
@ -4,5 +4,6 @@ jwt_secret: "super_secret_key"
|
|||
image_storage_dir: "/app/images"
|
||||
listening_port: 8080
|
||||
log_file: "/app/data/boxes.log"
|
||||
log_level: "INFO"
|
||||
static_files_dir: "/app/build/"
|
||||
allowed_origins: "*"
|
||||
|
|
73
db.go
73
db.go
|
@ -30,17 +30,82 @@ type User struct {
|
|||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// ConnectDB establishes a connection to the database and sets up the schema
|
||||
func ConnectDB(dbPath string) (*gorm.DB, error) {
|
||||
log := GetLogger()
|
||||
if log != nil {
|
||||
log.Info("Attempting to connect to database at: %s", dbPath)
|
||||
}
|
||||
|
||||
db, err := gorm.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
if log != nil {
|
||||
log.Error("Failed to connect to database: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to connect to database: %v", err)
|
||||
}
|
||||
|
||||
// set auto_vacuum mode to ON
|
||||
// this automagically removes old rows from the database when idle
|
||||
db.Exec("PRAGMA auto_vacuum = ON;")
|
||||
// Enable detailed SQL logging if we're in DEBUG mode
|
||||
if log != nil && log.GetLogLevel() == "DEBUG" {
|
||||
db.LogMode(true)
|
||||
}
|
||||
|
||||
if log != nil {
|
||||
log.Info("Successfully connected to database")
|
||||
}
|
||||
|
||||
// Set auto_vacuum mode to ON
|
||||
if err := db.Exec("PRAGMA auto_vacuum = ON;").Error; err != nil {
|
||||
if log != nil {
|
||||
log.Error("Failed to set auto_vacuum pragma: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to set auto_vacuum pragma: %v", err)
|
||||
}
|
||||
|
||||
if log != nil {
|
||||
log.Info("Auto-vacuum mode enabled")
|
||||
}
|
||||
|
||||
// AutoMigrate will create the tables if they don't exist
|
||||
db.AutoMigrate(&Box{}, &Item{}, &User{})
|
||||
if err := autoMigrateSchema(db); err != nil {
|
||||
if log != nil {
|
||||
log.Error("Schema migration failed: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if log != nil {
|
||||
log.Info("Database schema migration completed successfully")
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// autoMigrateSchema handles the database schema migration
|
||||
func autoMigrateSchema(db *gorm.DB) error {
|
||||
log := GetLogger()
|
||||
if log != nil {
|
||||
log.Info("Starting schema migration")
|
||||
}
|
||||
|
||||
// List of models to migrate
|
||||
models := []interface{}{
|
||||
&Box{},
|
||||
&Item{},
|
||||
&User{},
|
||||
}
|
||||
|
||||
for _, model := range models {
|
||||
if err := db.AutoMigrate(model).Error; err != nil {
|
||||
if log != nil {
|
||||
log.Error("Failed to migrate model %T: %v", model, err)
|
||||
}
|
||||
return fmt.Errorf("failed to migrate model %T: %v", model, err)
|
||||
}
|
||||
if log != nil {
|
||||
log.Debug("Successfully migrated model: %T", model)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -11,38 +11,29 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Move all item-related handlers here:
|
||||
// - GetItemsHandler
|
||||
// - CreateItemHandler
|
||||
// - GetItemHandler
|
||||
// - UpdateItemHandler
|
||||
// - DeleteItemHandler
|
||||
// - UploadItemImageHandler
|
||||
// - GetItemImageHandler
|
||||
// - SearchItemsHandler
|
||||
// - GetItemsInBoxHandler
|
||||
|
||||
// getItemsHandler handles the GET /items endpoint.
|
||||
func GetItemsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
log.Info("Received %s request to %s", r.Method, r.URL)
|
||||
|
||||
var items []Item
|
||||
db.Find(&items)
|
||||
json.NewEncoder(w).Encode(items)
|
||||
}
|
||||
|
||||
// createItemHandler handles the POST /items endpoint.
|
||||
func CreateItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
var item Item
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&item)
|
||||
if err != nil {
|
||||
log.Error("Failed to decode item creation request: %v", err)
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Println(item)
|
||||
log.Info("Creating new item: %s", item.Name)
|
||||
|
||||
db.Create(&item)
|
||||
|
||||
// Create a response struct to include the ID
|
||||
type createItemResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -53,47 +44,46 @@ func CreateItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Name: item.Name,
|
||||
}
|
||||
|
||||
log.DatabaseAction("create", fmt.Sprintf("Created item with ID %d", item.ID))
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// UploadItemImageHandler handles the image upload for an item
|
||||
func UploadItemImageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract the authenticated user from context (assuming this is how AuthMiddleware works)
|
||||
log := GetLogger()
|
||||
user, ok := r.Context().Value(userKey).(string)
|
||||
if !ok || user == "" {
|
||||
log.Warn("Unauthorized image upload attempt")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the form data, 10MB limit for file uploads
|
||||
err := r.ParseMultipartForm(10 << 20)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse multipart form: %v", err)
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the file from the form data
|
||||
file, handler, err := r.FormFile("image")
|
||||
if err != nil {
|
||||
log.Error("Error retrieving file from form: %v", err)
|
||||
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Get item ID from the URL
|
||||
vars := mux.Vars(r)
|
||||
itemID := vars["id"]
|
||||
|
||||
// Validate that the item exists (fetch from DB using itemID)
|
||||
var item Item
|
||||
if err := db.First(&item, itemID).Error; err != nil {
|
||||
log.Warn("Attempt to upload image for non-existent item with ID: %s", itemID)
|
||||
http.Error(w, "Item not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Save the uploaded file locally or to a storage service
|
||||
// Ensure the directory exists
|
||||
if err := os.MkdirAll(*ImageStorage, 0755); err != nil {
|
||||
log.Error("Failed to create image storage directory: %v", err)
|
||||
http.Error(w, "Unable to create image storage directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -101,88 +91,87 @@ func UploadItemImageHandler(w http.ResponseWriter, r *http.Request) {
|
|||
filePath := fmt.Sprintf("%s/%s", *ImageStorage, handler.Filename)
|
||||
outFile, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
log.Error("Failed to create file: %v", err)
|
||||
http.Error(w, "Unable to save the file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Copy the uploaded file to the destination
|
||||
_, err = io.Copy(outFile, file)
|
||||
if err != nil {
|
||||
log.Error("Failed to save file: %v", err)
|
||||
http.Error(w, "Unable to save the file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the item record in the database with the image path
|
||||
item.ImagePath = filePath
|
||||
if err := db.Save(&item).Error; err != nil {
|
||||
log.Error("Failed to update item with image path: %v", err)
|
||||
http.Error(w, "Unable to save image path in database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println("Image upload called")
|
||||
// Return the image path in the response
|
||||
|
||||
log.Info("Image uploaded successfully for item %s by user %s", itemID, user)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{"imagePath": filePath})
|
||||
}
|
||||
|
||||
// GetItemImageHandler retrieves an item's image by item ID.
|
||||
func GetItemImageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
itemID := vars["id"]
|
||||
// fmt.Println("Getting image")
|
||||
// Retrieve the item from the database
|
||||
|
||||
var item Item
|
||||
if err := db.First(&item, itemID).Error; err != nil {
|
||||
log.Info("Item not found, using default image for ID: %s", itemID)
|
||||
item.ImagePath = "images/default.jpg"
|
||||
} else if item.ImagePath == "" {
|
||||
item.ImagePath = "images/default.jpg"
|
||||
}
|
||||
|
||||
// Open the image file
|
||||
imageFile, err := os.Open(item.ImagePath)
|
||||
if err != nil {
|
||||
// Log the error for debugging, but don't return an HTTP error
|
||||
fmt.Println("Error opening image.", err)
|
||||
log.Error("Error opening image file: %v", err)
|
||||
item.ImagePath = "images/default.jpg"
|
||||
return
|
||||
}
|
||||
defer imageFile.Close()
|
||||
|
||||
// Determine the content type of the image
|
||||
imageData, err := io.ReadAll(imageFile)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading image")
|
||||
log.Error("Error reading image file: %v", err)
|
||||
item.ImagePath = "images/default.jpg"
|
||||
return
|
||||
}
|
||||
contentType := http.DetectContentType(imageData)
|
||||
|
||||
// Set the content type header and write the image data to the response
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.Write(imageData)
|
||||
}
|
||||
|
||||
// searchItemsHandler handles the GET /items/search endpoint.
|
||||
func SearchItemsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
query := r.URL.Query().Get("q")
|
||||
if query == "" {
|
||||
log.Warn("Search attempt with empty query")
|
||||
http.Error(w, "Search query is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Println(query)
|
||||
|
||||
log.Info("Searching for items with query: %s", query)
|
||||
var items []Item
|
||||
db.Where("name GLOB ? OR description GLOB ?", "*"+query+"*", "*"+query+"*").Find(&items)
|
||||
json.NewEncoder(w).Encode(items)
|
||||
|
||||
}
|
||||
|
||||
// getItemHandler handles the GET /items/{id} endpoint.
|
||||
func GetItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var item Item
|
||||
if err := db.First(&item, id).Error; err != nil {
|
||||
log.Warn("Item not found with ID: %s", id)
|
||||
http.Error(w, "Item not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
@ -190,52 +179,59 @@ func GetItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||
json.NewEncoder(w).Encode(item)
|
||||
}
|
||||
|
||||
// getItemsInBoxHandler handles the GET /boxes/{id}/items endpoint.
|
||||
func GetItemsInBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var items []Item
|
||||
if err := db.Where("box_id = ?", id).Find(&items).Error; err != nil {
|
||||
log.Warn("Failed to fetch items for box ID: %s", id)
|
||||
http.Error(w, "Items not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Retrieved %d items from box %s", len(items), id)
|
||||
json.NewEncoder(w).Encode(items)
|
||||
}
|
||||
|
||||
// updateItemHandler handles the PUT /items/{id} endpoint.
|
||||
func UpdateItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var item Item
|
||||
if err := db.First(&item, id).Error; err != nil {
|
||||
log.Warn("Attempt to update non-existent item with ID: %s", id)
|
||||
http.Error(w, "Item not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&item)
|
||||
if err != nil {
|
||||
log.Error("Failed to decode item update request: %v", err)
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Println(item)
|
||||
|
||||
log.DatabaseAction("update", fmt.Sprintf("Updated item with ID %s", id))
|
||||
db.Save(&item)
|
||||
json.NewEncoder(w).Encode(item)
|
||||
}
|
||||
|
||||
// deleteItemHandler handles the DELETE /items/{id} endpoint.
|
||||
func DeleteItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log := GetLogger()
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var item Item
|
||||
if err := db.First(&item, id).Error; err != nil {
|
||||
log.Warn("Attempt to delete non-existent item with ID: %s", id)
|
||||
http.Error(w, "Item not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
log.DatabaseAction("delete", fmt.Sprintf("Deleted item with ID %s", id))
|
||||
db.Delete(&item)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
// logger.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
*log.Logger
|
||||
file *os.File
|
||||
level LogLevel
|
||||
}
|
||||
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
DEBUG LogLevel = iota
|
||||
INFO
|
||||
WARN
|
||||
ERROR
|
||||
)
|
||||
|
||||
var (
|
||||
instance *Logger
|
||||
// Map for converting string to LogLevel
|
||||
logLevelMap = map[string]LogLevel{
|
||||
"DEBUG": DEBUG,
|
||||
"INFO": INFO,
|
||||
"WARN": WARN,
|
||||
"ERROR": ERROR,
|
||||
}
|
||||
)
|
||||
|
||||
// Initialize creates a new logger instance with specified level
|
||||
func Initialize(logPath string) (*Logger, error) {
|
||||
if instance != nil {
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %v", err)
|
||||
}
|
||||
|
||||
// Default to INFO if no level specified
|
||||
logger := &Logger{
|
||||
Logger: log.New(file, "", 0),
|
||||
file: file,
|
||||
level: INFO,
|
||||
}
|
||||
instance = logger
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// SetLogLevel sets the logging level from a string
|
||||
func (l *Logger) SetLogLevel(levelStr string) error {
|
||||
level, ok := logLevelMap[levelStr]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid log level: %s. Must be one of: DEBUG, INFO, WARN, ERROR", levelStr)
|
||||
}
|
||||
l.level = level
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLogLevel returns the current logging level as a string
|
||||
func (l *Logger) GetLogLevel() string {
|
||||
for str, level := range logLevelMap {
|
||||
if level == l.level {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
// GetLogger returns the singleton logger instance
|
||||
func GetLogger() *Logger {
|
||||
return instance
|
||||
}
|
||||
|
||||
// Close closes the log file
|
||||
func (l *Logger) Close() error {
|
||||
if l.file != nil {
|
||||
return l.file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if this message should be logged based on current level
|
||||
if level < l.level {
|
||||
return
|
||||
}
|
||||
|
||||
// Get caller information
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
|
||||
// Create timestamp
|
||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||
|
||||
// Create level string
|
||||
levelStr := "INFO"
|
||||
switch level {
|
||||
case DEBUG:
|
||||
levelStr = "DEBUG"
|
||||
case WARN:
|
||||
levelStr = "WARN"
|
||||
case ERROR:
|
||||
levelStr = "ERROR"
|
||||
}
|
||||
|
||||
// Format message
|
||||
message := fmt.Sprintf(format, v...)
|
||||
|
||||
// Final log format
|
||||
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s\n",
|
||||
timestamp,
|
||||
levelStr,
|
||||
file,
|
||||
line,
|
||||
message,
|
||||
)
|
||||
|
||||
l.Logger.Print(logLine)
|
||||
}
|
||||
|
||||
// Debug logs a debug message
|
||||
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||
l.log(DEBUG, format, v...)
|
||||
}
|
||||
|
||||
// Info logs an info message
|
||||
func (l *Logger) Info(format string, v ...interface{}) {
|
||||
l.log(INFO, format, v...)
|
||||
}
|
||||
|
||||
// Warn logs a warning message
|
||||
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||
l.log(WARN, format, v...)
|
||||
}
|
||||
|
||||
// Error logs an error message
|
||||
func (l *Logger) Error(format string, v ...interface{}) {
|
||||
l.log(ERROR, format, v...)
|
||||
}
|
||||
|
||||
// HTTPRequest logs an HTTP request
|
||||
func (l *Logger) HTTPRequest(method, path, username string, statusCode int) {
|
||||
l.log(INFO, "HTTP %s %s - User: %s - Status: %d",
|
||||
method,
|
||||
path,
|
||||
username,
|
||||
statusCode,
|
||||
)
|
||||
}
|
||||
|
||||
// UserAction logs user actions like login, logout, etc.
|
||||
func (l *Logger) UserAction(username, action string) {
|
||||
l.log(INFO, "User Action - Username: %s - Action: %s",
|
||||
username,
|
||||
action,
|
||||
)
|
||||
}
|
||||
|
||||
// DatabaseAction logs database operations
|
||||
func (l *Logger) DatabaseAction(operation, details string) {
|
||||
l.log(INFO, "Database Action - Operation: %s - Details: %s",
|
||||
operation,
|
||||
details,
|
||||
)
|
||||
}
|
104
main.go
104
main.go
|
@ -23,17 +23,21 @@ var (
|
|||
|
||||
func main() {
|
||||
// Load configuration
|
||||
config, err := loadAndValidateConfig()
|
||||
var err error
|
||||
config, err = loadAndValidateConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
fmt.Printf("Config loaded successfully in main(), DB path %s", config.DatabasePath)
|
||||
fmt.Printf("Config loaded successfully in main(), DB path %s\n", config.DatabasePath)
|
||||
|
||||
// Set up logging
|
||||
// Set up logging BEFORE logging config details
|
||||
if err := setupLogging(config.LogFile); err != nil {
|
||||
log.Fatalf("Failed to set up logging: %v", err)
|
||||
}
|
||||
|
||||
// Now that logging is set up, log the config details
|
||||
logConfigDetails(config)
|
||||
|
||||
// Connect to the database
|
||||
db, err = connectToDatabase(config)
|
||||
if err != nil {
|
||||
|
@ -43,6 +47,7 @@ func main() {
|
|||
|
||||
// Create routers
|
||||
baseRouter := mux.NewRouter()
|
||||
baseRouter.Use(loggingMiddleware)
|
||||
apiRouter := createAPIRouter(baseRouter)
|
||||
staticRouter := createStaticRouter(baseRouter, config.StaticFilesDir)
|
||||
|
||||
|
@ -76,24 +81,49 @@ func loadAndValidateConfig() (*Config, error) {
|
|||
|
||||
ImageStorage = &config.ImageStorageDir
|
||||
|
||||
// Log config details
|
||||
logConfigDetails(config)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func setupLogging(logFile string) error {
|
||||
fmt.Printf("Attempting to set up logging to file: %s\n", logFile)
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
// Initialize returns a *Logger, but we don't need to store it
|
||||
_, err := Initialize(logFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open log file: %v\n", err)
|
||||
return fmt.Errorf("failed to open log file: %v", err)
|
||||
return fmt.Errorf("failed to initialize logger: %v", err)
|
||||
}
|
||||
log.SetOutput(file)
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
fmt.Println("Logging initialized")
|
||||
log.Println("Logging initialized")
|
||||
|
||||
// Get the logger instance
|
||||
log := GetLogger()
|
||||
if log == nil {
|
||||
return fmt.Errorf("logger not initialized")
|
||||
}
|
||||
|
||||
// Set log level from config if specified
|
||||
if config.LogLevel != "" {
|
||||
if err := log.SetLogLevel(config.LogLevel); err != nil {
|
||||
return fmt.Errorf("failed to set log level: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func logConfigDetails(config *Config) {
|
||||
log := GetLogger()
|
||||
if log == nil {
|
||||
fmt.Println("Warning: Logger not initialized when attempting to log config details")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Configuration loaded:")
|
||||
log.Info("Database Path: %s", config.DatabasePath)
|
||||
log.Info("Image Storage Dir: %s", config.ImageStorageDir)
|
||||
log.Info("Log File: %s", config.LogFile)
|
||||
log.Info("Log Level: %s", config.LogLevel)
|
||||
log.Info("Listening Port: %d", config.ListeningPort)
|
||||
log.Info("Allowed Origins: %s", config.AllowedOrigins)
|
||||
|
||||
// Don't log the JWT secret for security reasons
|
||||
log.Info("JWT Secret: [REDACTED]")
|
||||
}
|
||||
|
||||
func connectToDatabase(config *Config) (*gorm.DB, error) {
|
||||
if config == nil {
|
||||
|
@ -158,10 +188,16 @@ func setupStaticRoutes(router *mux.Router, staticPath string) {
|
|||
|
||||
func createCustomStaticHandler(staticPath string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Attempting to serve: %s from directory: %s", r.URL.Path, staticPath)
|
||||
log := GetLogger()
|
||||
if log != nil {
|
||||
log.Info("Attempting to serve: %s from directory: %s", r.URL.Path, staticPath)
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(staticPath, r.URL.Path)
|
||||
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
|
||||
log.Printf("File not found: %s", fullPath)
|
||||
if log != nil {
|
||||
log.Warn("File not found: %s", fullPath)
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -185,6 +221,10 @@ func createCORSHandler(allowedOrigins string) func(http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
func startServer(port int, handler http.Handler) {
|
||||
log := GetLogger()
|
||||
if log != nil {
|
||||
log.Info("Server starting on port %d", port)
|
||||
}
|
||||
fmt.Printf("Server listening on port %d\n", port)
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), handler))
|
||||
}
|
||||
|
@ -196,12 +236,30 @@ func validateStaticDirectory(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func logConfigDetails(config *Config) {
|
||||
fmt.Println("Config details:")
|
||||
fmt.Printf("Database Path: %s\n", config.DatabasePath)
|
||||
fmt.Printf("Image Storage Dir: %s\n", config.ImageStorageDir)
|
||||
fmt.Printf("JWT Secret: %s\n", config.JWTSecret)
|
||||
fmt.Printf("Log File: %s\n", config.LogFile)
|
||||
fmt.Printf("Listening Port: %d\n", config.ListeningPort)
|
||||
fmt.Printf("Allowed Origins: %s\n", config.AllowedOrigins)
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
username := "anonymous"
|
||||
if user, ok := r.Context().Value(userKey).(string); ok {
|
||||
username = user
|
||||
}
|
||||
|
||||
// Create a response wrapper to capture the status code
|
||||
rw := &responseWriter{w, http.StatusOK}
|
||||
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
if log := GetLogger(); log != nil {
|
||||
log.HTTPRequest(r.Method, r.URL.Path, username, rw.statusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.statusCode = code
|
||||
rw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue