Fix queue & warn about queued or cancelled search

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2025-10-04 19:31:25 +02:00
parent ad789c73bd
commit 215d98af37
2 changed files with 202 additions and 190 deletions

View File

@@ -11,6 +11,8 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
var cancelled = make(map[string]struct{})
func searchWorker(s *server.Server, cache *map[string]*search.Result, searchQueue chan string, limiter chan struct{}) { func searchWorker(s *server.Server, cache *map[string]*search.Result, searchQueue chan string, limiter chan struct{}) {
for id := range searchQueue { for id := range searchQueue {
s.Mu.RLock() s.Mu.RLock()
@@ -21,6 +23,16 @@ func searchWorker(s *server.Server, cache *map[string]*search.Result, searchQueu
continue continue
} }
s.Mu.RLock()
_, isCancelled := cancelled[id]
s.Mu.RUnlock()
if isCancelled {
s.Mu.Lock()
r.Status = "cancelled"
s.Mu.Unlock()
continue
}
limiter <- struct{}{} limiter <- struct{}{}
search.Search(s, r.Query, r, s.Mu) search.Search(s, r.Query, r, s.Mu)
<-limiter <-limiter
@@ -130,30 +142,14 @@ func routes(s *server.Server, cache *map[string]*search.Result, searchQueue chan
return return
} }
s.Mu.Lock()
cancelled[id] = struct{}{}
if r.Status == "queued" { if r.Status == "queued" {
newQueue := make(chan string, cap(searchQueue))
for {
select {
case queuedId := <-searchQueue:
if queuedId != id {
newQueue <- queuedId
}
default:
goto Done
}
}
Done:
searchQueue = newQueue
s.Mu.Lock()
r.Status = "cancelled" r.Status = "cancelled"
s.Mu.Unlock()
c.JSON(http.StatusOK, gin.H{"Status": "Cancelled"})
return
} }
s.Mu.Unlock()
c.JSON(http.StatusBadRequest, gin.H{"Error": "Cannot cancel running or finished search"}) c.JSON(http.StatusOK, gin.H{"Status": "Cancelled"})
}) })
s.Router.GET("/dataleak/sample", func(c *gin.Context) { s.Router.GET("/dataleak/sample", func(c *gin.Context) {

View File

@@ -43,7 +43,11 @@
.then((r) => { .then((r) => {
result = r.data; result = r.data;
console.log(result); console.log(result);
if (result && result.Status !== "pending") { if (
result &&
result.Status !== "pending" &&
result.Status !== "queued"
) {
clearInterval(intervalId); clearInterval(intervalId);
} }
}) })
@@ -71,7 +75,7 @@
elapsedTime += pollingInterval; elapsedTime += pollingInterval;
// Check for status change inside the interval // Check for status change inside the interval
if (result && result.Status !== "pending") { if (result && result.Status !== "pending" && result.Status !== "queued") {
clearInterval(intervalId); clearInterval(intervalId);
return; return;
} }
@@ -102,7 +106,7 @@
{#if result} {#if result}
<header class="flex gap-5 flex-col"> <header class="flex gap-5 flex-col">
<a href="/search" class="w-fit"> <a href="/search" class="w-fit">
<h1 class="h1 "><span class="text-2xl align-middle">🔍</span> Search</h1> <h1 class="h1"><span class="text-2xl align-middle">🔍</span> Search</h1>
</a> </a>
<Searchbar <Searchbar
@@ -122,182 +126,194 @@
<Stats {result} /> <Stats {result} />
</div> </div>
{#if !result.LeakResult.Inactive} {#if result.Status === "queued"}
<div class="collapse collapse-arrow bg-base-100 border"> <div class="my-10 m-auto flex gap-5 flex-col">
<input type="radio" name="my-accordion-2" checked={true} /> <h2 class="text-center font-bold text-8xl">(o^^)o</h2>
<div <h2 class="text-center font-semibold text-4xl">Search queued...</h2>
class="collapse-title font-semibold text-xl flex justify-between items-center" </div>
> {:else if result.Status === "cancelled"}
<div class="flex items-center gap-2"> <div class="my-10 m-auto flex gap-5 flex-col">
<Database size={18} class="text-base-content/60" /> <h2 class="text-center font-bold text-8xl">(^-^*)</h2>
Data wells lookup <h2 class="text-center font-semibold text-4xl">Search cancelled</h2>
</div> </div>
{#if result.LeakResult.Error !== ""} {:else}
<CircleX size={16} class="text-error" /> {#if !result.LeakResult.Inactive}
{:else if result.LeakResult.Duration === 0} <div class="collapse collapse-arrow bg-base-100 border">
<span class="loading loading-dots loading-xs"></span> <input type="radio" name="my-accordion-2" checked={true} />
{:else if result.LeakResult.Rows.length > 0} <div
<CircleCheck size={16} class="text-success" /> class="collapse-title font-semibold text-xl flex justify-between items-center"
{:else} >
<CircleMinus size={16} class="text-base-content/60" /> <div class="flex items-center gap-2">
{/if} <Database size={18} class="text-base-content/60" />
</div> Data wells lookup
<div class="collapse-content">
{#if result.LeakResult.Error !== ""}
<div role="alert" class="alert alert-soft alert-error">
<CircleAlert size={20} />
<span>Error! {result.LeakResult.Error}</span>
</div> </div>
{:else if result.LeakResult.Duration === 0} {#if result.LeakResult.Error !== ""}
<ul class="list rounded-box"> <CircleX size={16} class="text-error" />
{#each Array(5) as _} {:else if result.LeakResult.Duration === 0}
<div class="list-row text-left"> <span class="loading loading-dots loading-xs"></span>
<div> {:else if result.LeakResult.Rows.length > 0}
<div <CircleCheck size={16} class="text-success" />
class="skeleton size-10 rounded-box items-center justify-center flex" {:else}
></div> <CircleMinus size={16} class="text-base-content/60" />
</div> {/if}
<div> </div>
<div class="skeleton h-5 mb-1 w-52"></div> <div class="collapse-content">
<div {#if result.LeakResult.Error !== ""}
class="text-xs skeleton h-4 w-34 uppercase font-semibold opacity-60" <div role="alert" class="alert alert-soft alert-error">
></div> <CircleAlert size={20} />
</div> <span>Error! {result.LeakResult.Error}</span>
<div class="btn btn-square btn-ghost"> </div>
<ChevronDown size={12} /> {:else if result.LeakResult.Duration === 0}
</div> <ul class="list rounded-box">
</div> {#each Array(5) as _}
{/each} <div class="list-row text-left">
</ul> <div>
{:else if !result.LeakResult.Rows || result.LeakResult.Rows.length == 0} <div
<div role="alert" class="alert alert-soft"> class="skeleton size-10 rounded-box items-center justify-center flex"
<CircleMinus size={20} /> ></div>
<span>No result</span> </div>
</div> <div>
{:else} <div class="skeleton h-5 mb-1 w-52"></div>
<p class="text-base-content/60 mb-2"> <div
{result.LeakResult.Rows.length} results in {convertNanoSeconds( class="text-xs skeleton h-4 w-34 uppercase font-semibold opacity-60"
result.LeakResult.Duration, ></div>
)} </div>
</p> <div class="btn btn-square btn-ghost">
{#if result.LeakResult.LimitHit} <ChevronDown size={12} />
<div role="alert" class="alert alert-soft my-4"> </div>
<CircleAlert size={20} /> </div>
<div> {/each}
<span class="font-semibold">Limit hit!</span> Consider refining </ul>
your search query for more specific results. {:else if !result.LeakResult.Rows || result.LeakResult.Rows.length == 0}
</div> <div role="alert" class="alert alert-soft">
</div> <CircleMinus size={20} />
<span>No result</span>
</div>
{:else}
<p class="text-base-content/60 mb-2">
{result.LeakResult.Rows.length} results in {convertNanoSeconds(
result.LeakResult.Duration,
)}
</p>
{#if result.LeakResult.LimitHit}
<div role="alert" class="alert alert-soft my-4">
<CircleAlert size={20} />
<div>
<span class="font-semibold">Limit hit!</span> Consider refining
your search query for more specific results.
</div>
</div>
{/if}
<Rows {result} />
{/if} {/if}
<Rows {result} />
{/if}
</div>
</div>
{/if}
{#if !result.GithubResult.Inactive}
<div class="collapse collapse-arrow bg-base-100 border">
<input type="radio" name="my-accordion-2" />
<div
class="collapse-title font-semibold text-xl flex justify-between items-center"
>
<div class="flex items-center gap-2">
<Github size={18} class="text-base-content/60" />
Github Recon
</div> </div>
{#if result.GithubResult.Error !== ""}
<CircleX size={16} class="text-error" />
{:else if result.GithubResult.Duration === 0}
<span class="loading loading-dots loading-xs"></span>
{:else if !result.GithubResult.EmailResult?.Commits && !result.GithubResult.EmailResult?.Spoofing && !result.GithubResult.UsernameResult?.User}
<CircleMinus size={16} class="text-base-content/60" />
{:else if result.GithubResult.UsernameResult || result.GithubResult.EmailResult}
<CircleCheck size={16} class="text-success" />
{/if}
</div> </div>
<div class="collapse-content"> {/if}
{#if result.GithubResult.Error !== ""} {#if !result.GithubResult.Inactive}
<div role="alert" class="alert alert-soft alert-error"> <div class="collapse collapse-arrow bg-base-100 border">
<CircleAlert size={20} /> <input type="radio" name="my-accordion-2" />
<span>Error! {result.GithubResult.Error}</span> <div
class="collapse-title font-semibold text-xl flex justify-between items-center"
>
<div class="flex items-center gap-2">
<Github size={18} class="text-base-content/60" />
Github Recon
</div> </div>
{:else if result.GithubResult.Duration === 0} {#if result.GithubResult.Error !== ""}
<div role="alert" class="alert alert-soft"> <CircleX size={16} class="text-error" />
<span class="loading loading-dots loading-sm"></span> {:else if result.GithubResult.Duration === 0}
<span>Loading...</span> <span class="loading loading-dots loading-xs"></span>
</div> {:else if !result.GithubResult.EmailResult?.Commits && !result.GithubResult.EmailResult?.Spoofing && !result.GithubResult.UsernameResult?.User}
{:else if !result.GithubResult.EmailResult?.Commits && !result.GithubResult.EmailResult?.Spoofing && !result.GithubResult.UsernameResult?.User} <CircleMinus size={16} class="text-base-content/60" />
<div role="alert" class="alert alert-soft"> {:else if result.GithubResult.UsernameResult || result.GithubResult.EmailResult}
<CircleMinus size={20} /> <CircleCheck size={16} class="text-success" />
<span>No result</span> {/if}
</div> </div>
{:else} <div class="collapse-content">
<p class="text-base-content/60 mb-4"> {#if result.GithubResult.Error !== ""}
Found a result in {convertNanoSeconds( <div role="alert" class="alert alert-soft alert-error">
result.GithubResult.Duration, <CircleAlert size={20} />
)} <span>Error! {result.GithubResult.Error}</span>
</p> </div>
<GithubResult githubResult={result.GithubResult} /> {:else if result.GithubResult.Duration === 0}
{/if} <div role="alert" class="alert alert-soft">
<span class="loading loading-dots loading-sm"></span>
<span>Loading...</span>
</div>
{:else if !result.GithubResult.EmailResult?.Commits && !result.GithubResult.EmailResult?.Spoofing && !result.GithubResult.UsernameResult?.User}
<div role="alert" class="alert alert-soft">
<CircleMinus size={20} />
<span>No result</span>
</div>
{:else}
<p class="text-base-content/60 mb-4">
Found a result in {convertNanoSeconds(
result.GithubResult.Duration,
)}
</p>
<GithubResult githubResult={result.GithubResult} />
{/if}
</div>
</div> </div>
</div> {/if}
{/if} {#if !result.GravatarResult.Inactive}
{#if !result.GravatarResult.Inactive} <div class="collapse collapse-arrow bg-base-100 border">
<div class="collapse collapse-arrow bg-base-100 border"> <input type="radio" name="my-accordion-2" />
<input type="radio" name="my-accordion-2" /> <div
<div class="collapse-title font-semibold text-xl flex justify-between items-center"
class="collapse-title font-semibold text-xl flex justify-between items-center" >
> <div class="flex items-center gap-2">
<div class="flex items-center gap-2"> <svg
<svg width="18"
width="18" height="18"
height="18" viewBox="0 0 18 18"
viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" >
> <path
<path d="M7.20008 1.79933V8.09932C7.20008 8.57653 7.38965 9.0342 7.72709 9.37164C8.06453 9.70908 8.5222 9.89865 8.99941 9.89865C9.47662 9.89865 9.93429 9.70908 10.2717 9.37164C10.6092 9.0342 10.7987 8.57653 10.7987 8.09932V3.90799C11.9031 4.29735 12.851 5.03509 13.4996 6.01006C14.1482 6.98502 14.4623 8.14438 14.3947 9.31342C14.327 10.4825 13.8812 11.5978 13.1245 12.4915C12.3678 13.3851 11.3411 14.0086 10.1992 14.2679C9.05725 14.5273 7.86198 14.4084 6.79347 13.9294C5.72497 13.4503 4.84112 12.6369 4.27513 11.6117C3.70914 10.5866 3.49168 9.40529 3.6555 8.24581C3.81933 7.08634 4.35557 6.01152 5.18342 5.18333C5.51545 4.84434 5.70032 4.38803 5.69786 3.91353C5.69541 3.43902 5.50582 2.98465 5.17029 2.64912C4.83476 2.31359 4.38039 2.12401 3.90589 2.12155C3.43138 2.11909 2.97508 2.30396 2.63609 2.636C1.16373 4.10834 0.247437 6.04566 0.043349 8.11786C-0.160739 10.1901 0.360003 12.2689 1.51684 14.0002C2.67368 15.7315 4.39505 17.0081 6.38762 17.6125C8.38019 18.2169 10.5207 18.1117 12.4444 17.3148C14.3681 16.5179 15.956 15.0786 16.9374 13.2422C17.9189 11.4059 18.2333 9.28595 17.827 7.24376C17.4207 5.20156 16.3188 3.36344 14.7091 2.04258C13.0995 0.721724 11.0816 -0.000136192 8.99941 1.92733e-08C8.5222 1.92733e-08 8.06453 0.189572 7.72709 0.527012C7.38965 0.864452 7.20008 1.32212 7.20008 1.79933Z"
d="M7.20008 1.79933V8.09932C7.20008 8.57653 7.38965 9.0342 7.72709 9.37164C8.06453 9.70908 8.5222 9.89865 8.99941 9.89865C9.47662 9.89865 9.93429 9.70908 10.2717 9.37164C10.6092 9.0342 10.7987 8.57653 10.7987 8.09932V3.90799C11.9031 4.29735 12.851 5.03509 13.4996 6.01006C14.1482 6.98502 14.4623 8.14438 14.3947 9.31342C14.327 10.4825 13.8812 11.5978 13.1245 12.4915C12.3678 13.3851 11.3411 14.0086 10.1992 14.2679C9.05725 14.5273 7.86198 14.4084 6.79347 13.9294C5.72497 13.4503 4.84112 12.6369 4.27513 11.6117C3.70914 10.5866 3.49168 9.40529 3.6555 8.24581C3.81933 7.08634 4.35557 6.01152 5.18342 5.18333C5.51545 4.84434 5.70032 4.38803 5.69786 3.91353C5.69541 3.43902 5.50582 2.98465 5.17029 2.64912C4.83476 2.31359 4.38039 2.12401 3.90589 2.12155C3.43138 2.11909 2.97508 2.30396 2.63609 2.636C1.16373 4.10834 0.247437 6.04566 0.043349 8.11786C-0.160739 10.1901 0.360003 12.2689 1.51684 14.0002C2.67368 15.7315 4.39505 17.0081 6.38762 17.6125C8.38019 18.2169 10.5207 18.1117 12.4444 17.3148C14.3681 16.5179 15.956 15.0786 16.9374 13.2422C17.9189 11.4059 18.2333 9.28595 17.827 7.24376C17.4207 5.20156 16.3188 3.36344 14.7091 2.04258C13.0995 0.721724 11.0816 -0.000136192 8.99941 1.92733e-08C8.5222 1.92733e-08 8.06453 0.189572 7.72709 0.527012C7.38965 0.864452 7.20008 1.32212 7.20008 1.79933Z" class="fill-base-content/60"
class="fill-base-content/60" />
/> </svg>
</svg>
Gravatar Recon Gravatar Recon
</div>
{#if result.GravatarResult.Error !== ""}
<CircleX size={16} class="text-error" />
{:else if result.GravatarResult.Duration === 0}
<span class="loading loading-dots loading-xs"></span>
{:else if !result.GravatarResult.Results || result.GravatarResult.Results.length == 0}
<CircleMinus size={16} class="text-base-content/60" />
{:else if result.GravatarResult.Results}
<CircleCheck size={16} class="text-success" />
{/if}
</div>
<div class="collapse-content">
{#if result.GravatarResult.Error !== ""}
<div role="alert" class="alert alert-soft alert-error">
<CircleAlert size={20} />
<span>Error! {result.GravatarResult.Error}</span>
</div>
{:else if result.GravatarResult.Duration === 0}
<div role="alert" class="alert alert-soft">
<span class="loading loading-dots loading-sm"></span>
<span>Loading...</span>
</div>
{:else if !result.GravatarResult.Results || result.GravatarResult.Results.length == 0}
<div role="alert" class="alert alert-soft">
<CircleMinus size={20} />
<span>No result</span>
</div>
{:else}
<p class="text-base-content/60 mb-4">
Found a result in {convertNanoSeconds(
result.GravatarResult.Duration,
)}
</p>
<GravatarResult result={result.GravatarResult} />
{/if}
</div> </div>
{#if result.GravatarResult.Error !== ""}
<CircleX size={16} class="text-error" />
{:else if result.GravatarResult.Duration === 0}
<span class="loading loading-dots loading-xs"></span>
{:else if !result.GravatarResult.Results || result.GravatarResult.Results.length == 0}
<CircleMinus size={16} class="text-base-content/60" />
{:else if result.GravatarResult.Results}
<CircleCheck size={16} class="text-success" />
{/if}
</div> </div>
<div class="collapse-content"> {/if}
{#if result.GravatarResult.Error !== ""}
<div role="alert" class="alert alert-soft alert-error">
<CircleAlert size={20} />
<span>Error! {result.GravatarResult.Error}</span>
</div>
{:else if result.GravatarResult.Duration === 0}
<div role="alert" class="alert alert-soft">
<span class="loading loading-dots loading-sm"></span>
<span>Loading...</span>
</div>
{:else if !result.GravatarResult.Results || result.GravatarResult.Results.length == 0}
<div role="alert" class="alert alert-soft">
<CircleMinus size={20} />
<span>No result</span>
</div>
{:else}
<p class="text-base-content/60 mb-4">
Found a result in {convertNanoSeconds(
result.GravatarResult.Duration,
)}
</p>
<GravatarResult result={result.GravatarResult} />
{/if}
</div>
</div>
{/if} {/if}
</div> </div>
{/if} {/if}