- crawl queue and watch page are now part of single view - exclusions can be edited via 'Edit Exclusions' popup
263 lines
6.4 KiB
TypeScript
263 lines
6.4 KiB
TypeScript
import { property, state } from "lit/decorators.js";
|
|
import { msg, localized, str } from "@lit/localize";
|
|
|
|
import type { ExclusionRemoveEvent } from "./queue-exclusion-table";
|
|
import type {
|
|
ExclusionAddEvent,
|
|
ExclusionChangeEvent,
|
|
} from "./queue-exclusion-form";
|
|
import type { SeedConfig } from "../pages/org/types";
|
|
import LiteElement, { html } from "../utils/LiteElement";
|
|
import type { AuthState } from "../utils/AuthService";
|
|
|
|
type URLs = string[];
|
|
type ResponseData = {
|
|
total: number;
|
|
matched: URLs;
|
|
};
|
|
|
|
/**
|
|
* Crawl queue exclusion editor
|
|
*
|
|
* Usage example:
|
|
* ```ts
|
|
* <btrix-exclusion-editor
|
|
* orgId=${this.crawl.oid}
|
|
* crawlId=${this.crawl.id}
|
|
* .config=${this.workflow.config}
|
|
* .authState=${this.authState}
|
|
* ?isActiveCrawl=${isActive}
|
|
* >
|
|
* </btrix-exclusion-editor>
|
|
* ```
|
|
*
|
|
* @event on-success On successful edit
|
|
*/
|
|
@localized()
|
|
export class ExclusionEditor extends LiteElement {
|
|
@property({ type: Object })
|
|
authState?: AuthState;
|
|
|
|
@property({ type: String })
|
|
orgId?: string;
|
|
|
|
@property({ type: String })
|
|
crawlId?: string;
|
|
|
|
@property({ type: Array })
|
|
config?: SeedConfig;
|
|
|
|
@property({ type: Boolean })
|
|
isActiveCrawl = false;
|
|
|
|
@state()
|
|
private isSubmitting = false;
|
|
|
|
@state()
|
|
private exclusionFieldErrorMessage = "";
|
|
|
|
@state()
|
|
/** `new RegExp` constructor string */
|
|
private regex: string = "";
|
|
|
|
@state()
|
|
matchedURLs: URLs | null = null;
|
|
|
|
@state()
|
|
private isLoading = false;
|
|
|
|
willUpdate(changedProperties: Map<string, any>) {
|
|
if (
|
|
changedProperties.has("authState") ||
|
|
changedProperties.has("orgId") ||
|
|
changedProperties.has("crawlId") ||
|
|
changedProperties.has("regex")
|
|
) {
|
|
this.fetchQueueMatches();
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return html`
|
|
<div class="grid gap-6 grid-cols-1 lg:grid-cols-2">
|
|
<div class="col-span-1">${this.renderTable()}</div>
|
|
<div class="col-span-1">
|
|
${this.isActiveCrawl && this.regex
|
|
? html` <section class="mt-5">${this.renderPending()}</section> `
|
|
: ""}
|
|
${this.isActiveCrawl
|
|
? html` <section class="mt-5">${this.renderQueue()}</section> `
|
|
: ""}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private renderTable() {
|
|
return html`
|
|
${this.config
|
|
? html`<btrix-queue-exclusion-table
|
|
?removable=${this.isActiveCrawl}
|
|
.exclusions=${this.config.exclude || []}
|
|
@on-remove=${this.deleteExclusion}
|
|
>
|
|
</btrix-queue-exclusion-table>`
|
|
: html`
|
|
<div class="flex items-center justify-center my-9 text-xl">
|
|
<sl-spinner></sl-spinner>
|
|
</div>
|
|
`}
|
|
${this.isActiveCrawl
|
|
? html`<div class="mt-2">
|
|
<btrix-queue-exclusion-form
|
|
?isSubmitting=${this.isSubmitting}
|
|
fieldErrorMessage=${this.exclusionFieldErrorMessage}
|
|
@on-change=${this.handleRegexChange}
|
|
@on-add=${this.handleAddRegex}
|
|
>
|
|
</btrix-queue-exclusion-form>
|
|
</div>`
|
|
: ""}
|
|
`;
|
|
}
|
|
|
|
private renderPending() {
|
|
return html`
|
|
<btrix-crawl-pending-exclusions
|
|
.matchedURLs=${this.matchedURLs}
|
|
></btrix-crawl-pending-exclusions>
|
|
`;
|
|
}
|
|
|
|
private renderQueue() {
|
|
return html`<btrix-crawl-queue
|
|
orgId=${this.orgId!}
|
|
crawlId=${this.crawlId!}
|
|
.authState=${this.authState}
|
|
regex=${this.regex}
|
|
matchedTotal=${this.matchedURLs?.length || 0}
|
|
></btrix-crawl-queue>`;
|
|
}
|
|
|
|
private handleRegexChange(e: ExclusionChangeEvent) {
|
|
const { value, valid } = e.detail;
|
|
|
|
if (valid) {
|
|
this.regex = value;
|
|
} else {
|
|
this.regex = "";
|
|
}
|
|
}
|
|
|
|
private async deleteExclusion(e: ExclusionRemoveEvent) {
|
|
const { regex } = e.detail;
|
|
|
|
try {
|
|
const data = await this.apiFetch(
|
|
`/orgs/${this.orgId}/crawls/${this.crawlId}/exclusions?regex=${regex}`,
|
|
this.authState!,
|
|
{
|
|
method: "DELETE",
|
|
}
|
|
);
|
|
|
|
if (data.success) {
|
|
this.notify({
|
|
message: msg(html`Removed exclusion: <code>${regex}</code>`),
|
|
variant: "success",
|
|
icon: "check2-circle",
|
|
});
|
|
|
|
this.dispatchEvent(new CustomEvent("on-success"));
|
|
} else {
|
|
throw data;
|
|
}
|
|
} catch (e: any) {
|
|
this.notify({
|
|
message:
|
|
e.message === "crawl_running_cant_deactivate"
|
|
? msg("Cannot remove exclusion when crawl is no longer running.")
|
|
: msg("Sorry, couldn't remove exclusion at this time."),
|
|
variant: "danger",
|
|
icon: "exclamation-octagon",
|
|
});
|
|
}
|
|
}
|
|
|
|
private async fetchQueueMatches() {
|
|
if (!this.regex) {
|
|
this.matchedURLs = null;
|
|
return;
|
|
}
|
|
|
|
this.isLoading = true;
|
|
|
|
try {
|
|
const { matched } = await this.getQueueMatches();
|
|
this.matchedURLs = matched;
|
|
} catch (e) {
|
|
this.notify({
|
|
message: msg("Sorry, couldn't fetch pending exclusions at this time."),
|
|
variant: "danger",
|
|
icon: "exclamation-octagon",
|
|
});
|
|
}
|
|
|
|
this.isLoading = false;
|
|
}
|
|
|
|
private async getQueueMatches(): Promise<ResponseData> {
|
|
const data: ResponseData = await this.apiFetch(
|
|
`/orgs/${this.orgId}/crawls/${this.crawlId}/queueMatchAll?regex=${this.regex}`,
|
|
this.authState!
|
|
);
|
|
|
|
return data;
|
|
}
|
|
|
|
private async handleAddRegex(e: ExclusionAddEvent) {
|
|
this.isSubmitting = true;
|
|
|
|
const { regex, onSuccess } = e.detail;
|
|
|
|
try {
|
|
const data = await this.apiFetch(
|
|
`/orgs/${this.orgId}/crawls/${this.crawlId}/exclusions?regex=${regex}`,
|
|
this.authState!,
|
|
{
|
|
method: "POST",
|
|
}
|
|
);
|
|
|
|
if (data.success) {
|
|
this.notify({
|
|
message: msg("Exclusion added."),
|
|
variant: "success",
|
|
icon: "check2-circle",
|
|
});
|
|
|
|
this.regex = "";
|
|
this.matchedURLs = null;
|
|
await this.updateComplete;
|
|
|
|
onSuccess();
|
|
this.dispatchEvent(new CustomEvent("on-success"));
|
|
} else {
|
|
throw data;
|
|
}
|
|
} catch (e: any) {
|
|
if (e.message === "exclusion_already_exists") {
|
|
this.exclusionFieldErrorMessage = msg("Exclusion already exists");
|
|
} else {
|
|
this.notify({
|
|
message: msg("Sorry, couldn't add exclusion at this time."),
|
|
variant: "danger",
|
|
icon: "exclamation-octagon",
|
|
});
|
|
}
|
|
}
|
|
|
|
this.isSubmitting = false;
|
|
}
|
|
}
|