Configurable Max Scale for frontend (#1557)

Allow maximum scale option to be fully configurable via
`max_crawl_scale`. Already configurable on the backend, and now exposed
to the frontend via API `/api/settings` `maxCrawlScale` value.

The workflow editor and workflow details are updated to allow selecting
the scale up to the maxCrawlScale setting (which defaults to 3 if not
set).
This commit is contained in:
Ilya Kreymer 2024-03-11 16:21:20 -07:00 committed by GitHub
parent 8462c08206
commit 08f6847194
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 52 additions and 20 deletions

View File

@ -67,6 +67,7 @@ def main():
os.environ.get("DEFAULT_PAGE_LOAD_TIME_SECONDS", 120) os.environ.get("DEFAULT_PAGE_LOAD_TIME_SECONDS", 120)
), ),
"maxPagesPerCrawl": int(os.environ.get("MAX_PAGES_PER_CRAWL", 0)), "maxPagesPerCrawl": int(os.environ.get("MAX_PAGES_PER_CRAWL", 0)),
"maxScale": int(os.environ.get("MAX_CRAWL_SCALE", 3)),
} }
invites = init_invites(mdb, email) invites = init_invites(mdb, email)

View File

@ -14,5 +14,6 @@ def test_settings():
"jwtTokenLifetime": 86400, "jwtTokenLifetime": 86400,
"defaultBehaviorTimeSeconds": 300, "defaultBehaviorTimeSeconds": 300,
"maxPagesPerCrawl": 4, "maxPagesPerCrawl": 4,
"maxScale": 3,
"defaultPageLoadTimeSeconds": 120, "defaultPageLoadTimeSeconds": 120,
} }

View File

@ -33,6 +33,7 @@ import "./assets/fonts/Inter/inter.css";
import "./assets/fonts/Recursive/recursive.css"; import "./assets/fonts/Recursive/recursive.css";
import "./styles.css"; import "./styles.css";
import { theme } from "@/theme"; import { theme } from "@/theme";
import { DEFAULT_MAX_SCALE } from "./utils/crawler";
// Make theme CSS available in document // Make theme CSS available in document
document.adoptedStyleSheets = [theme]; document.adoptedStyleSheets = [theme];
@ -79,6 +80,8 @@ export class App extends LiteElement {
@state() @state()
private isRegistrationEnabled?: boolean; private isRegistrationEnabled?: boolean;
private maxScale = DEFAULT_MAX_SCALE;
async connectedCallback() { async connectedCallback() {
let authState: AuthState = null; let authState: AuthState = null;
try { try {
@ -136,6 +139,7 @@ export class App extends LiteElement {
if (settings) { if (settings) {
this.isRegistrationEnabled = settings.registrationEnabled; this.isRegistrationEnabled = settings.registrationEnabled;
this.maxScale = settings.maxScale;
} }
this.isAppSettingsLoaded = true; this.isAppSettingsLoaded = true;
@ -173,13 +177,19 @@ export class App extends LiteElement {
} }
} }
async getAppSettings(): Promise<{ registrationEnabled: boolean } | void> { async getAppSettings(): Promise<{
registrationEnabled: boolean;
maxScale: number;
} | void> {
const resp = await fetch("/api/settings", { const resp = await fetch("/api/settings", {
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
}); });
if (resp.status === 200) { if (resp.status === 200) {
const body = (await resp.json()) as { registrationEnabled: boolean }; const body = (await resp.json()) as {
registrationEnabled: boolean;
maxScale: number;
};
return body; return body;
} else { } else {
@ -600,6 +610,7 @@ export class App extends LiteElement {
.userInfo=${this.appState.userInfo ?? undefined} .userInfo=${this.appState.userInfo ?? undefined}
.viewStateData=${this.viewState.data} .viewStateData=${this.viewState.data}
.params=${this.viewState.params} .params=${this.viewState.params}
.maxScale=${this.maxScale}
slug=${slug} slug=${slug}
orgPath=${orgPath.split(slug)[1]} orgPath=${orgPath.split(slug)[1]}
orgTab=${orgTab as OrgTab} orgTab=${orgTab as OrgTab}

View File

@ -11,6 +11,7 @@ import type { OrgData } from "@/utils/orgs";
import { isAdmin, isCrawler } from "@/utils/orgs"; import { isAdmin, isCrawler } from "@/utils/orgs";
import LiteElement, { html } from "@/utils/LiteElement"; import LiteElement, { html } from "@/utils/LiteElement";
import { needLogin } from "@/utils/auth"; import { needLogin } from "@/utils/auth";
import { DEFAULT_MAX_SCALE } from "@/utils/crawler";
import "./workflow-detail"; import "./workflow-detail";
import "./workflows-list"; import "./workflows-list";
import "./workflows-new"; import "./workflows-new";
@ -104,6 +105,9 @@ export class Org extends LiteElement {
@property({ type: String }) @property({ type: String })
orgTab: OrgTab = defaultTab; orgTab: OrgTab = defaultTab;
@property({ type: Number })
maxScale: number = DEFAULT_MAX_SCALE;
@state() @state()
private orgStorageQuotaReached = false; private orgStorageQuotaReached = false;
@ -572,6 +576,7 @@ export class Org extends LiteElement {
openDialogName=${this.viewStateData?.dialog} openDialogName=${this.viewStateData?.dialog}
?isEditing=${isEditing} ?isEditing=${isEditing}
?isCrawler=${this.isCrawler} ?isCrawler=${this.isCrawler}
.maxScale=${this.maxScale}
></btrix-workflow-detail> ></btrix-workflow-detail>
`; `;
} }

View File

@ -19,7 +19,11 @@ import type {
} from "./types"; } from "./types";
import { humanizeSchedule } from "@/utils/cron"; import { humanizeSchedule } from "@/utils/cron";
import type { APIPaginatedList } from "@/types/api"; import type { APIPaginatedList } from "@/types/api";
import { inactiveCrawlStates, isActive } from "@/utils/crawler"; import {
DEFAULT_MAX_SCALE,
inactiveCrawlStates,
isActive,
} from "@/utils/crawler";
import type { SlSelect } from "@shoelace-style/shoelace"; import type { SlSelect } from "@shoelace-style/shoelace";
import type { PageChangeEvent } from "@/components/ui/pagination"; import type { PageChangeEvent } from "@/components/ui/pagination";
import { ExclusionEditor } from "@/features/crawl-workflows/exclusion-editor"; import { ExclusionEditor } from "@/features/crawl-workflows/exclusion-editor";
@ -70,6 +74,9 @@ export class WorkflowDetail extends LiteElement {
@property({ type: String }) @property({ type: String })
initialActivePanel?: Tab; initialActivePanel?: Tab;
@property({ type: Number })
maxScale = DEFAULT_MAX_SCALE;
@state() @state()
private workflow?: Workflow; private workflow?: Workflow;
@ -1261,20 +1268,13 @@ export class WorkflowDetail extends LiteElement {
private renderEditScale() { private renderEditScale() {
if (!this.workflow) return; if (!this.workflow) return;
const scaleOptions = [ const scaleOptions = [];
{ for (let value = 1; value <= this.maxScale; value++) {
value: 1, scaleOptions.push({
label: "1×", value,
}, label: `${value}×`,
{ });
value: 2, }
label: "2×",
},
{
value: 3,
label: "3×",
},
];
return html` return html`
<div> <div>

View File

@ -17,6 +17,8 @@ import {
customElement, customElement,
} from "lit/decorators.js"; } from "lit/decorators.js";
import { when } from "lit/directives/when.js"; import { when } from "lit/directives/when.js";
import { map } from "lit/directives/map.js";
import { range } from "lit/directives/range.js";
import { msg, localized, str } from "@lit/localize"; import { msg, localized, str } from "@lit/localize";
import { ifDefined } from "lit/directives/if-defined.js"; import { ifDefined } from "lit/directives/if-defined.js";
import { choose } from "lit/directives/choose.js"; import { choose } from "lit/directives/choose.js";
@ -37,6 +39,7 @@ import {
getNextDate, getNextDate,
} from "@/utils/cron"; } from "@/utils/cron";
import { maxLengthValidator } from "@/utils/form"; import { maxLengthValidator } from "@/utils/form";
import { DEFAULT_MAX_SCALE } from "@/utils/crawler";
import type { Tab } from "@/components/ui/tab-list"; import type { Tab } from "@/components/ui/tab-list";
import type { import type {
ExclusionRemoveEvent, ExclusionRemoveEvent,
@ -298,6 +301,8 @@ export class CrawlConfigEditor extends LiteElement {
@state() @state()
private serverError?: TemplateResult | string; private serverError?: TemplateResult | string;
private maxScale = DEFAULT_MAX_SCALE;
// For fuzzy search: // For fuzzy search:
private readonly fuse = new Fuse<string>([], { private readonly fuse = new Fuse<string>([], {
shouldSort: false, shouldSort: false,
@ -1542,9 +1547,13 @@ https://archiveweb.page/images/${"logo.svg"}`}
scale: +(e.target as SlCheckbox).value, scale: +(e.target as SlCheckbox).value,
})} })}
> >
<sl-radio-button value="1" size="small">1×</sl-radio-button> ${map(
<sl-radio-button value="2" size="small">2×</sl-radio-button> range(this.maxScale),
<sl-radio-button value="3" size="small">3×</sl-radio-button> (i: number) =>
html` <sl-radio-button value="${i + 1}" size="small"
>${i + 1}×</sl-radio-button
>`,
)}
</sl-radio-group> </sl-radio-group>
`)} `)}
${this.renderHelpTextCol( ${this.renderHelpTextCol(
@ -2562,6 +2571,9 @@ https://archiveweb.page/images/${"logo.svg"}`}
if (data.maxPagesPerCrawl > 0) { if (data.maxPagesPerCrawl > 0) {
orgDefaults.maxPagesPerCrawl = data.maxPagesPerCrawl; orgDefaults.maxPagesPerCrawl = data.maxPagesPerCrawl;
} }
if (data.maxScale) {
this.maxScale = data.maxScale;
}
this.orgDefaults = orgDefaults; this.orgDefaults = orgDefaults;
} catch (e) { } catch (e) {
console.debug(e); console.debug(e);

View File

@ -27,6 +27,8 @@ export const inactiveCrawlStates: CrawlState[] = [
"failed", "failed",
]; ];
export const DEFAULT_MAX_SCALE = 3;
export function isActive(state: CrawlState | null) { export function isActive(state: CrawlState | null) {
return state && activeCrawlStates.includes(state); return state && activeCrawlStates.includes(state);
} }