788b6cf430
- Add FindByUserID method to HabitEntryRepository (JOIN with habits) - Implement ExportUserDataHandler to export all user data - Add GET /api/v1/export endpoint with gzip compression - Apply strict rate limiting (1 export per hour per user) - Export includes all habits (active + archived) and entries - Add i18n translations for export errors (en/es) - Update all test mocks to implement new repository method - Export format: JSON with gzip (~10x compression ratio)
66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package http
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"apocapoc-api/internal/application/queries"
|
|
"apocapoc-api/internal/i18n"
|
|
)
|
|
|
|
type ExportHandlers struct {
|
|
exportHandler *queries.ExportUserDataHandler
|
|
translator *i18n.Translator
|
|
}
|
|
|
|
func NewExportHandlers(
|
|
exportHandler *queries.ExportUserDataHandler,
|
|
translator *i18n.Translator,
|
|
) *ExportHandlers {
|
|
return &ExportHandlers{
|
|
exportHandler: exportHandler,
|
|
translator: translator,
|
|
}
|
|
}
|
|
|
|
// ExportData godoc
|
|
// @Summary Export user data
|
|
// @Description Export all user habits and entries in JSON format with gzip compression. Limited to 1 export per hour.
|
|
// @Tags export
|
|
// @Security BearerAuth
|
|
// @Produce json
|
|
// @Success 200 {object} queries.ExportUserDataResult "Compressed JSON export"
|
|
// @Failure 401 {object} ErrorResponse "Unauthorized"
|
|
// @Failure 429 {object} ErrorResponse "Rate limit exceeded"
|
|
// @Failure 500 {object} ErrorResponse "Internal server error"
|
|
// @Router /export [get]
|
|
func (h *ExportHandlers) ExportData(w http.ResponseWriter, r *http.Request) {
|
|
userID := r.Context().Value("user_id").(string)
|
|
|
|
query := queries.ExportUserDataQuery{
|
|
UserID: userID,
|
|
}
|
|
|
|
result, err := h.exportHandler.Handle(r.Context(), query)
|
|
if err != nil {
|
|
respondErrorI18n(w, r, h.translator, http.StatusInternalServerError, "export_failed")
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Content-Encoding", "gzip")
|
|
w.Header().Set("Content-Disposition", "attachment; filename=\"apocapoc-export.json.gz\"")
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
gzipWriter := gzip.NewWriter(w)
|
|
defer gzipWriter.Close()
|
|
|
|
encoder := json.NewEncoder(gzipWriter)
|
|
encoder.SetIndent("", " ")
|
|
|
|
if err := encoder.Encode(result); err != nil {
|
|
return
|
|
}
|
|
}
|