mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
146
back/internal/api/handler/search.go
Normal file
146
back/internal/api/handler/search.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/anotherhadi/iknowyou/internal/respond"
|
||||
"github.com/anotherhadi/iknowyou/internal/search"
|
||||
"github.com/anotherhadi/iknowyou/internal/tools"
|
||||
)
|
||||
|
||||
type SearchHandler struct {
|
||||
manager *search.Manager
|
||||
demo bool
|
||||
}
|
||||
|
||||
func NewSearchHandler(manager *search.Manager, demo bool) *SearchHandler {
|
||||
return &SearchHandler{manager: manager, demo: demo}
|
||||
}
|
||||
|
||||
type postSearchRequest struct {
|
||||
Target string `json:"target"`
|
||||
InputType tools.InputType `json:"input_type"`
|
||||
Profile string `json:"profile,omitempty"`
|
||||
}
|
||||
|
||||
type searchSummary struct {
|
||||
ID string `json:"id"`
|
||||
Target string `json:"target"`
|
||||
InputType tools.InputType `json:"input_type"`
|
||||
Profile string `json:"profile,omitempty"`
|
||||
Status search.Status `json:"status"`
|
||||
StartedAt string `json:"started_at"`
|
||||
PlannedTools []search.ToolStatus `json:"planned_tools"`
|
||||
}
|
||||
|
||||
type searchDetail struct {
|
||||
searchSummary
|
||||
Events []tools.Event `json:"events"`
|
||||
}
|
||||
|
||||
func toSummary(s *search.Search) searchSummary {
|
||||
planned := s.PlannedTools
|
||||
if planned == nil {
|
||||
planned = []search.ToolStatus{}
|
||||
}
|
||||
return searchSummary{
|
||||
ID: s.ID,
|
||||
Target: s.Target,
|
||||
InputType: s.InputType,
|
||||
Profile: s.Profile,
|
||||
Status: s.Status(),
|
||||
StartedAt: s.StartedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
PlannedTools: planned,
|
||||
}
|
||||
}
|
||||
|
||||
var validInputTypes = map[tools.InputType]struct{}{
|
||||
tools.InputTypeEmail: {},
|
||||
tools.InputTypeUsername: {},
|
||||
tools.InputTypePhone: {},
|
||||
tools.InputTypeIP: {},
|
||||
tools.InputTypeDomain: {},
|
||||
tools.InputTypePassword: {},
|
||||
tools.InputTypeName: {},
|
||||
}
|
||||
|
||||
// POST /searches
|
||||
func (h *SearchHandler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
if h.demo {
|
||||
respond.Error(w, http.StatusForbidden, "demo mode: searches are disabled")
|
||||
return
|
||||
}
|
||||
var req postSearchRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid JSON body")
|
||||
return
|
||||
}
|
||||
if req.Target == "" {
|
||||
respond.Error(w, http.StatusBadRequest, "target is required")
|
||||
return
|
||||
}
|
||||
if len(req.Target) > 500 {
|
||||
respond.Error(w, http.StatusBadRequest, "target is too long (max 500 characters)")
|
||||
return
|
||||
}
|
||||
if req.Target[0] == '-' || req.Target[0] == '@' {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid target")
|
||||
return
|
||||
}
|
||||
if req.InputType == "" {
|
||||
respond.Error(w, http.StatusBadRequest, "input_type is required")
|
||||
return
|
||||
}
|
||||
if _, ok := validInputTypes[req.InputType]; !ok {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid input_type")
|
||||
return
|
||||
}
|
||||
|
||||
s, err := h.manager.Start(context.WithoutCancel(r.Context()), req.Target, req.InputType, req.Profile)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respond.JSON(w, http.StatusCreated, toSummary(s))
|
||||
}
|
||||
|
||||
// GET /searches
|
||||
func (h *SearchHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
all := h.manager.All()
|
||||
summaries := make([]searchSummary, len(all))
|
||||
for i, s := range all {
|
||||
summaries[i] = toSummary(s)
|
||||
}
|
||||
respond.JSON(w, http.StatusOK, summaries)
|
||||
}
|
||||
|
||||
// GET /searches/{id}
|
||||
func (h *SearchHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
s, err := h.manager.Get(id)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
detail := searchDetail{
|
||||
searchSummary: toSummary(s),
|
||||
Events: s.Events(),
|
||||
}
|
||||
respond.JSON(w, http.StatusOK, detail)
|
||||
}
|
||||
|
||||
// DELETE /searches/{id}
|
||||
func (h *SearchHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
if err := h.manager.Delete(id); err != nil {
|
||||
respond.Error(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
Reference in New Issue
Block a user