init
This commit is contained in:
174
back/api/api.go
Normal file
174
back/api/api.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anotherhadi/eleakxir/backend/search"
|
||||
"github.com/anotherhadi/eleakxir/backend/server"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TODO: We need to know when we hit the LIMIT
|
||||
|
||||
func routes(s *server.Server, cache *map[string]*search.Result) {
|
||||
s.Router.Use(
|
||||
func(c *gin.Context) {
|
||||
if s.Settings.Password != "" {
|
||||
password := c.GetHeader("X-Password")
|
||||
if password != s.Settings.Password {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
},
|
||||
)
|
||||
|
||||
s.Router.GET("/", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"Settings": s.Settings,
|
||||
"Dataleaks": s.Dataleaks,
|
||||
"TotalDataleaks": s.TotalDataleaks,
|
||||
"TotalRows": s.TotalRows,
|
||||
"TotalSize": s.TotalSize,
|
||||
})
|
||||
})
|
||||
|
||||
s.Router.GET("/history", func(c *gin.Context) {
|
||||
type historyItem struct {
|
||||
Id string
|
||||
Status string
|
||||
Date time.Time
|
||||
Query search.Query
|
||||
Results int
|
||||
}
|
||||
var history []historyItem
|
||||
s.Mu.RLock()
|
||||
for _, r := range *cache {
|
||||
history = append(history, historyItem{
|
||||
Id: r.Id,
|
||||
Status: r.Status,
|
||||
Date: r.Date,
|
||||
Query: r.Query,
|
||||
Results: len(r.LeakResult.Rows),
|
||||
})
|
||||
}
|
||||
s.Mu.RUnlock()
|
||||
for i := 0; i < len(history)-1; i++ {
|
||||
for j := 0; j < len(history)-i-1; j++ {
|
||||
if history[j].Date.Before(history[j+1].Date) {
|
||||
history[j], history[j+1] = history[j+1], history[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"History": history,
|
||||
})
|
||||
})
|
||||
|
||||
s.Router.POST("/search", func(c *gin.Context) {
|
||||
var query search.Query
|
||||
if err := c.BindJSON(&query); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"Error": "invalid JSON"})
|
||||
return
|
||||
}
|
||||
query = cleanQuery(query)
|
||||
if len(query.Text) <= s.Settings.MinimumQueryLength {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"Error": "query too short"})
|
||||
return
|
||||
}
|
||||
id := search.EncodeQueryID(query, *s.TotalDataleaks)
|
||||
s.Mu.RLock()
|
||||
_, exists := (*cache)[id]
|
||||
s.Mu.RUnlock()
|
||||
if exists {
|
||||
c.JSON(http.StatusOK, gin.H{"Id": id})
|
||||
return
|
||||
}
|
||||
r := search.Result{
|
||||
Id: id,
|
||||
}
|
||||
go search.Search(s, query, &r, s.Mu)
|
||||
s.Mu.Lock()
|
||||
(*cache)[id] = &r
|
||||
s.Mu.Unlock()
|
||||
c.JSON(http.StatusOK, gin.H{"Id": id})
|
||||
})
|
||||
|
||||
s.Router.GET("/search/:id", func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
s.Mu.RLock()
|
||||
r, exists := (*cache)[id]
|
||||
s.Mu.RUnlock()
|
||||
if !exists {
|
||||
c.JSON(http.StatusNotFound, gin.H{"Error": "not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, r)
|
||||
})
|
||||
}
|
||||
|
||||
func Init(s *server.Server) {
|
||||
if !s.Settings.Debug {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
s.Router = gin.Default()
|
||||
s.Router.Use(CORSMiddleware())
|
||||
|
||||
cache := make(map[string]*search.Result)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Minute)
|
||||
deleteOldCache(s, &cache)
|
||||
}
|
||||
}()
|
||||
|
||||
routes(s, &cache)
|
||||
}
|
||||
|
||||
func deleteOldCache(s *server.Server, cache *map[string]*search.Result) {
|
||||
s.Mu.Lock()
|
||||
defer s.Mu.Unlock()
|
||||
now := time.Now()
|
||||
for id, r := range *cache {
|
||||
if now.Sub(r.Date) > s.Settings.MaxCacheDuration {
|
||||
delete(*cache, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cleanQuery(q search.Query) search.Query {
|
||||
q.Column = strings.ToLower(strings.TrimSpace(q.Column))
|
||||
q.Column = strings.Join(strings.Fields(q.Column), " ")
|
||||
q.Column = strings.ReplaceAll(q.Column, "`", "")
|
||||
q.Column = strings.ReplaceAll(q.Column, "'", "")
|
||||
q.Column = strings.ReplaceAll(q.Column, "-", "_")
|
||||
q.Column = strings.ReplaceAll(q.Column, " ", "_")
|
||||
q.Column = strings.ReplaceAll(q.Column, "\"", "")
|
||||
|
||||
q.Text = strings.TrimSpace(q.Text)
|
||||
q.Text = strings.Join(strings.Fields(q.Text), " ")
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().
|
||||
Set("Access-Control-Allow-Headers", "X-Password, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user