security: Add proper authentication, RBAC, and tenant isolation
- Add password hashing with bcrypt - Add AuthService with proper login - Add password strength validation - Add RBAC middleware (AdminOnly, ManagerOrAdmin) - Add tenant isolation middleware - Update CRM handler with tenant filtering - Add JWT fallback for development mode - Add user context helpers - Build successful
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
// LandvexRealHandler hanterar riktig integration mot Landvex API
|
||||
type LandvexRealHandler struct {
|
||||
baseURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewLandvexRealHandler skapar en ny handler
|
||||
func NewLandvexRealHandler() *LandvexRealHandler {
|
||||
return &LandvexRealHandler{
|
||||
baseURL: getEnv("LANDVEX_API_URL", "http://172.17.0.1:8081"),
|
||||
client: &http.Client{Timeout: 10 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// LandvexObject representerar ett Landvex-objekt
|
||||
type LandvexObject struct {
|
||||
LvxID string `json:"lvx_id"`
|
||||
Slug string `json:"slug"`
|
||||
Namn map[string]string `json:"namn"`
|
||||
Beskrivning map[string]string `json:"beskrivning"`
|
||||
Kategorier []string `json:"kategorier"`
|
||||
Standarder []string `json:"standarder"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
|
||||
// LandvexSearchResult representerar sökresultat
|
||||
type LandvexSearchResult struct {
|
||||
LvxID string `json:"lvx_id"`
|
||||
Namn map[string]string `json:"namn"`
|
||||
Relevans float64 `json:"relevans"`
|
||||
Kategorier []string `json:"kategorier"`
|
||||
}
|
||||
|
||||
// fetchFromLandvex hämtar data från Landvex API
|
||||
func (h *LandvexRealHandler) fetchFromLandvex(endpoint string) (map[string]interface{}, error) {
|
||||
resp, err := h.client.Get(h.baseURL + endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetHealth hämtar health från Landvex
|
||||
func (h *LandvexRealHandler) GetHealth(w http.ResponseWriter, r *http.Request) {
|
||||
health, err := h.fetchFromLandvex("/health")
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"landvex": health,
|
||||
})
|
||||
}
|
||||
|
||||
// GetObjects hämtar alla objekt
|
||||
func (h *LandvexRealHandler) GetObjects(w http.ResponseWriter, r *http.Request) {
|
||||
// Sök efter alla objekt (tom sökning)
|
||||
results, err := h.fetchFromLandvex("/v0/search?q=")
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"results": results,
|
||||
})
|
||||
}
|
||||
|
||||
// GetObject hämtar ett specifikt objekt
|
||||
func (h *LandvexRealHandler) GetObject(w http.ResponseWriter, r *http.Request) {
|
||||
lvxID := chi.URLParam(r, "id")
|
||||
|
||||
obj, err := h.fetchFromLandvex("/v0/objects/" + lvxID)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"object": obj,
|
||||
})
|
||||
}
|
||||
|
||||
// Search söker i Landvex
|
||||
func (h *LandvexRealHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query().Get("q")
|
||||
if query == "" {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": "missing query parameter 'q'",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.client.Get(h.baseURL + "/v0/search?q=" + url.QueryEscape(query))
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Landvex returnerar inte JSON med ok/error, utan direkt resultat
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"query": query,
|
||||
"raw": string(body),
|
||||
"parse_error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"query": query,
|
||||
"results": result,
|
||||
})
|
||||
}
|
||||
|
||||
// Identify identifierar ett objekt från bild/text
|
||||
func (h *LandvexRealHandler) Identify(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
ImageURL string `json:"image_url,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Vidarebefordra till Landvex identify
|
||||
landvexReq := map[string]interface{}{
|
||||
"image_url": req.ImageURL,
|
||||
"description": req.Description,
|
||||
}
|
||||
|
||||
reqBody, _ := json.Marshal(landvexReq)
|
||||
resp, err := h.client.Post(h.baseURL+"/v0/identify", "application/json", strings.NewReader(string(reqBody)))
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"ok": true,
|
||||
"result": result,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user