add bug report creation handler and update routing structure
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,3 +34,5 @@ go.work.sum
|
|||||||
|
|
||||||
# Claude
|
# Claude
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
|
tmp/
|
||||||
@@ -7,7 +7,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
@@ -15,6 +18,125 @@ import (
|
|||||||
"emly-api-go/internal/models"
|
"emly-api-go/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var fileRoles = []struct {
|
||||||
|
field string
|
||||||
|
role models.FileRole
|
||||||
|
defaultMime string
|
||||||
|
}{
|
||||||
|
{"attachment", models.FileRoleAttachment, "application/octet-stream"},
|
||||||
|
{"screenshot", models.FileRoleScreenshot, "image/png"},
|
||||||
|
{"log", models.FileRoleLog, "text/plain"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateBugReport(db *sqlx.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "invalid multipart form: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name := r.FormValue("name")
|
||||||
|
email := r.FormValue("email")
|
||||||
|
description := r.FormValue("description")
|
||||||
|
hwid := r.FormValue("hwid")
|
||||||
|
hostname := r.FormValue("hostname")
|
||||||
|
osUser := r.FormValue("os_user")
|
||||||
|
systemInfoStr := r.FormValue("system_info")
|
||||||
|
|
||||||
|
if name == "" || email == "" || description == "" {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "name, email and description are required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitterIP := strings.TrimSpace(strings.SplitN(r.Header.Get("X-Forwarded-For"), ",", 2)[0])
|
||||||
|
if submitterIP == "" {
|
||||||
|
submitterIP = r.Header.Get("X-Real-IP")
|
||||||
|
}
|
||||||
|
if submitterIP == "" {
|
||||||
|
submitterIP = "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemInfo json.RawMessage
|
||||||
|
if systemInfoStr != "" && json.Valid([]byte(systemInfoStr)) {
|
||||||
|
systemInfo = json.RawMessage(systemInfoStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[BUGREPORT] Received from name=%s hwid=%s ip=%s", name, hwid, submitterIP)
|
||||||
|
|
||||||
|
result, err := db.ExecContext(r.Context(),
|
||||||
|
"INSERT INTO emly_bugreports.bug_reports (name, email, description, hwid, hostname, os_user, submitter_ip, system_info, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
name, email, description, hwid, hostname, osUser, submitterIP, systemInfo, models.BugReportStatusNew,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reportID, err := result.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fr := range fileRoles {
|
||||||
|
file, header, err := r.FormFile(fr.field)
|
||||||
|
if err != nil {
|
||||||
|
// no file uploaded for this role — skip
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
data, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "reading file " + fr.field + ": " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeType := header.Header.Get("Content-Type")
|
||||||
|
if mimeType == "" {
|
||||||
|
mimeType = fr.defaultMime
|
||||||
|
}
|
||||||
|
filename := header.Filename
|
||||||
|
if filename == "" {
|
||||||
|
filename = fr.field + ".bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[BUGREPORT] File uploaded: role=%s size=%d bytes", fr.role, len(data))
|
||||||
|
|
||||||
|
_, err = db.ExecContext(r.Context(),
|
||||||
|
"INSERT INTO emly_bugreports.bug_report_files (report_id, file_role, filename, mime_type, file_size, data) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
|
reportID, fr.role, filename, mimeType, len(data), data,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[BUGREPORT] Created successfully with id=%d", reportID)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"success": true,
|
||||||
|
"report_id": reportID,
|
||||||
|
"message": "Bug report submitted successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetAllBugReports(db *sqlx.DB) http.HandlerFunc {
|
func GetAllBugReports(db *sqlx.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var reports []models.BugReport
|
var reports []models.BugReport
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
apimw "emly-api-go/internal/middleware"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -15,7 +16,6 @@ import (
|
|||||||
"emly-api-go/internal/config"
|
"emly-api-go/internal/config"
|
||||||
"emly-api-go/internal/database"
|
"emly-api-go/internal/database"
|
||||||
"emly-api-go/internal/handlers"
|
"emly-api-go/internal/handlers"
|
||||||
apimw "emly-api-go/internal/middleware"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -52,7 +52,7 @@ func main() {
|
|||||||
w.Write([]byte("emly-api-go"))
|
w.Write([]byte("emly-api-go"))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Route("/api/v1", func(r chi.Router) {
|
r.Route("/v1", func(r chi.Router) {
|
||||||
r.Use(func(next http.Handler) http.Handler {
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("X-Server", "emly-api-go")
|
w.Header().Set("X-Server", "emly-api-go")
|
||||||
@@ -63,6 +63,7 @@ func main() {
|
|||||||
// Health – public, no API key required
|
// Health – public, no API key required
|
||||||
r.Get("/health", handlers.Health(db))
|
r.Get("/health", handlers.Health(db))
|
||||||
|
|
||||||
|
r.Route("/api", func(r chi.Router) {
|
||||||
// ROUTE: Bug Reports - Protected via API Key
|
// ROUTE: Bug Reports - Protected via API Key
|
||||||
r.Route("/bug-reports", func(r chi.Router) {
|
r.Route("/bug-reports", func(r chi.Router) {
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
@@ -72,6 +73,7 @@ func main() {
|
|||||||
r.Use(httprate.LimitByIP(30, time.Minute))
|
r.Use(httprate.LimitByIP(30, time.Minute))
|
||||||
|
|
||||||
r.Get("/count", handlers.GetReportsCount(db))
|
r.Get("/count", handlers.GetReportsCount(db))
|
||||||
|
r.Post("/", handlers.CreateBugReport(db))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
@@ -89,6 +91,7 @@ func main() {
|
|||||||
r.Get("/{id}/zip", handlers.GetBugReportZipById(db))
|
r.Get("/{id}/zip", handlers.GetBugReportZipById(db))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user