Cancel queued search
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -118,6 +118,44 @@ func routes(s *server.Server, cache *map[string]*search.Result, searchQueue chan
|
|||||||
c.JSON(http.StatusOK, r)
|
c.JSON(http.StatusOK, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.Router.POST("/search/cancel/:id", func(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
s.Mu.Lock()
|
||||||
|
r, exists := (*cache)[id]
|
||||||
|
s.Mu.Unlock()
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"Error": "Search not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
s.Mu.Unlock()
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"Status": "Cancelled"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Cannot cancel running or finished search"})
|
||||||
|
})
|
||||||
|
|
||||||
s.Router.GET("/dataleak/sample", func(c *gin.Context) {
|
s.Router.GET("/dataleak/sample", func(c *gin.Context) {
|
||||||
path := c.Query("path")
|
path := c.Query("path")
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type Query struct {
|
|||||||
type Result struct {
|
type Result struct {
|
||||||
Id string
|
Id string
|
||||||
Date time.Time
|
Date time.Time
|
||||||
Status string // "queued", "pending", "completed", "error"
|
Status string // "queued", "pending", "completed", "error", "cancelled"
|
||||||
Query Query
|
Query Query
|
||||||
ResultsCount int // Total number of results found across all services
|
ResultsCount int // Total number of results found across all services
|
||||||
|
|
||||||
@@ -44,6 +44,8 @@ func Search(s *server.Server, q Query, r *Result, mu *sync.RWMutex) {
|
|||||||
r.ResultsCount = 0
|
r.ResultsCount = 0
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
|
time.Sleep(20 * time.Second) // To ensure the status update is sent to the client before starting the search
|
||||||
|
|
||||||
wg.Add(3)
|
wg.Add(3)
|
||||||
go func() {
|
go func() {
|
||||||
if !q.Datawells {
|
if !q.Datawells {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { serverUrl, serverPassword } from "$src/lib/stores/server";
|
||||||
import type { History } from "$src/lib/types";
|
import type { History } from "$src/lib/types";
|
||||||
import { formatDate } from "$src/lib/utils";
|
import { formatDate } from "$src/lib/utils";
|
||||||
import { Search } from "@lucide/svelte";
|
import { Search } from "@lucide/svelte";
|
||||||
import { navigate, p } from "sv-router/generated";
|
import axios from "axios";
|
||||||
|
import { navigate } from "sv-router/generated";
|
||||||
|
import { toast } from "svelte-sonner";
|
||||||
|
|
||||||
let { history, perPage = 5 }: { history: History; perPage?: number } =
|
let { history, perPage = 5 }: { history: History; perPage?: number } =
|
||||||
$props();
|
$props();
|
||||||
@@ -48,6 +51,26 @@
|
|||||||
page++;
|
page++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cancel(id: string) {
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
`${$serverUrl}/search/cancel/${id}`,
|
||||||
|
{},
|
||||||
|
{ headers: { "X-Password": $serverPassword } },
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Search cancelled");
|
||||||
|
history = history.map((item) =>
|
||||||
|
item.Id === id ? { ...item, Status: "cancelled" } : item,
|
||||||
|
);
|
||||||
|
filter = filter;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
toast.error("Failed to cancel search");
|
||||||
|
console.log("Failed to cancel search", e);
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="my-4 flex flex-col gap-2">
|
<div class="my-4 flex flex-col gap-2">
|
||||||
@@ -88,12 +111,20 @@
|
|||||||
class="badge badge-xs"
|
class="badge badge-xs"
|
||||||
class:badge-success={item.Status === "completed"}
|
class:badge-success={item.Status === "completed"}
|
||||||
class:badge-warning={item.Status === "pending"}
|
class:badge-warning={item.Status === "pending"}
|
||||||
class:badge-neutral={item.Status === "queued"}
|
class:badge-neutral={item.Status === "queued" ||
|
||||||
|
item.Status === "cancelled"}
|
||||||
class:badge-error={item.Status === "error"}
|
class:badge-error={item.Status === "error"}
|
||||||
>
|
>
|
||||||
{item.Status}
|
{item.Status}
|
||||||
</div></td
|
</div>
|
||||||
>
|
|
||||||
|
{#if item.Status === "queued"}
|
||||||
|
<button
|
||||||
|
class="btn btn-xs size-4 btn-square btn-soft"
|
||||||
|
onclick={() => cancel(item.Id)}>x</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
<td>{formatDate(item.Date)}</td>
|
<td>{formatDate(item.Date)}</td>
|
||||||
<td
|
<td
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export type GravatarResult = {
|
|||||||
|
|
||||||
export type Result = {
|
export type Result = {
|
||||||
Id: string;
|
Id: string;
|
||||||
Status: "queued" | "pending" | "completed" | "error";
|
Status: "queued" | "pending" | "completed" | "error" | "cancelled";
|
||||||
Date: string;
|
Date: string;
|
||||||
Query: Query;
|
Query: Query;
|
||||||
ResultsCount: number;
|
ResultsCount: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user