Local-first runtime cleanup and recovery hardening

This commit is contained in:
Mikhail Chusavitin
2026-03-07 23:18:07 +03:00
parent 4e977737ee
commit 06397a6bd1
53 changed files with 1856 additions and 2080 deletions

View File

@@ -6,7 +6,6 @@ import (
"strings"
"time"
"git.mchus.pro/mchus/quoteforge/internal/middleware"
"git.mchus.pro/mchus/quoteforge/internal/models"
"git.mchus.pro/mchus/quoteforge/internal/services"
"github.com/gin-gonic/gin"
@@ -16,17 +15,20 @@ type ExportHandler struct {
exportService *services.ExportService
configService services.ConfigurationGetter
projectService *services.ProjectService
dbUsername string
}
func NewExportHandler(
exportService *services.ExportService,
configService services.ConfigurationGetter,
projectService *services.ProjectService,
dbUsername string,
) *ExportHandler {
return &ExportHandler{
exportService: exportService,
configService: configService,
projectService: projectService,
dbUsername: dbUsername,
}
}
@@ -71,8 +73,7 @@ func (h *ExportHandler) ExportCSV(c *gin.Context) {
// Get project code for filename
projectCode := req.ProjectName // legacy field: may contain code from frontend
if projectCode == "" && req.ProjectUUID != "" {
username := middleware.GetUsername(c)
if project, err := h.projectService.GetByUUID(req.ProjectUUID, username); err == nil && project != nil {
if project, err := h.projectService.GetByUUID(req.ProjectUUID, h.dbUsername); err == nil && project != nil {
projectCode = project.Code
}
}
@@ -144,11 +145,10 @@ func sanitizeFilenameSegment(value string) string {
}
func (h *ExportHandler) ExportConfigCSV(c *gin.Context) {
username := middleware.GetUsername(c)
uuid := c.Param("uuid")
// Get config before streaming (can return JSON error)
config, err := h.configService.GetByUUID(uuid, username)
config, err := h.configService.GetByUUID(uuid, h.dbUsername)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
@@ -165,7 +165,7 @@ func (h *ExportHandler) ExportConfigCSV(c *gin.Context) {
// Get project code for filename
projectCode := config.Name // fallback: use config name if no project
if config.ProjectUUID != nil && *config.ProjectUUID != "" {
if project, err := h.projectService.GetByUUID(*config.ProjectUUID, username); err == nil && project != nil {
if project, err := h.projectService.GetByUUID(*config.ProjectUUID, h.dbUsername); err == nil && project != nil {
projectCode = project.Code
}
}
@@ -189,16 +189,15 @@ func (h *ExportHandler) ExportConfigCSV(c *gin.Context) {
// ExportProjectCSV exports all active configurations of a project as a single CSV file.
func (h *ExportHandler) ExportProjectCSV(c *gin.Context) {
username := middleware.GetUsername(c)
projectUUID := c.Param("uuid")
project, err := h.projectService.GetByUUID(projectUUID, username)
project, err := h.projectService.GetByUUID(projectUUID, h.dbUsername)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
result, err := h.projectService.ListConfigurations(projectUUID, username, "active")
result, err := h.projectService.ListConfigurations(projectUUID, h.dbUsername, "active")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@@ -223,7 +222,6 @@ func (h *ExportHandler) ExportProjectCSV(c *gin.Context) {
}
func (h *ExportHandler) ExportProjectPricingCSV(c *gin.Context) {
username := middleware.GetUsername(c)
projectUUID := c.Param("uuid")
var req ProjectExportOptionsRequest
@@ -232,13 +230,13 @@ func (h *ExportHandler) ExportProjectPricingCSV(c *gin.Context) {
return
}
project, err := h.projectService.GetByUUID(projectUUID, username)
project, err := h.projectService.GetByUUID(projectUUID, h.dbUsername)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
result, err := h.projectService.ListConfigurations(projectUUID, username, "active")
result, err := h.projectService.ListConfigurations(projectUUID, h.dbUsername, "active")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return