restart front
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
@ -1,3 +1,6 @@
|
||||
from django_filters.filters import BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from rest_framework.fields import ReadOnlyField
|
||||
from rest_framework.mixins import (
|
||||
ListModelMixin,
|
||||
RetrieveModelMixin,
|
||||
@ -11,6 +14,9 @@ from authentik.tenants.utils import get_current_tenant
|
||||
|
||||
|
||||
class TaskSerializer(ModelSerializer):
|
||||
rel_obj_app_label = ReadOnlyField(source="rel_obj_content_type.app_label")
|
||||
rel_obj_model = ReadOnlyField(source="rel_obj_content_type.model")
|
||||
|
||||
messages = LogEventSerializer(many=True, source="_messages")
|
||||
|
||||
class Meta:
|
||||
@ -21,7 +27,8 @@ class TaskSerializer(ModelSerializer):
|
||||
"actor_name",
|
||||
"state",
|
||||
"mtime",
|
||||
"rel_obj_content_type",
|
||||
"rel_obj_app_label",
|
||||
"rel_obj_model",
|
||||
"rel_obj_id",
|
||||
"uid",
|
||||
"messages",
|
||||
@ -29,6 +36,23 @@ class TaskSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class TaskFilter(FilterSet):
|
||||
rel_obj_id__isnull = BooleanFilter("rel_obj_id", "isnull")
|
||||
|
||||
class Meta:
|
||||
model = Task
|
||||
fields = (
|
||||
"queue_name",
|
||||
"actor_name",
|
||||
"state",
|
||||
"rel_obj_content_type__app_label",
|
||||
"rel_obj_content_type__model",
|
||||
"rel_obj_id",
|
||||
"rel_obj_id__isnull",
|
||||
"aggregated_status",
|
||||
)
|
||||
|
||||
|
||||
class TaskViewSet(
|
||||
RetrieveModelMixin,
|
||||
ListModelMixin,
|
||||
@ -41,16 +65,16 @@ class TaskViewSet(
|
||||
"queue_name",
|
||||
"actor_name",
|
||||
"state",
|
||||
"rel_obj_app_label",
|
||||
"rel_obj_model",
|
||||
"rel_obj_id",
|
||||
"_uid",
|
||||
"aggregated_status",
|
||||
)
|
||||
filterset_fields = (
|
||||
"queue_name",
|
||||
"actor_name",
|
||||
"state",
|
||||
"aggregated_status",
|
||||
)
|
||||
filterset_class = TaskFilter
|
||||
ordering = ("-mtime",)
|
||||
|
||||
def get_queryset(self):
|
||||
return Task.objects.filter(tenant=get_current_tenant())
|
||||
return Task.objects.select_related("rel_obj_content_type").filter(
|
||||
tenant=get_current_tenant()
|
||||
)
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
from django_filters.filters import BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from dramatiq.actor import Actor
|
||||
from dramatiq.broker import get_broker
|
||||
from dramatiq.errors import ActorNotFound
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import ReadOnlyField
|
||||
from rest_framework.mixins import (
|
||||
ListModelMixin,
|
||||
RetrieveModelMixin,
|
||||
@ -20,19 +23,25 @@ from authentik.tasks.schedules.models import Schedule
|
||||
|
||||
|
||||
class ScheduleSerializer(ModelSerializer):
|
||||
rel_obj_app_label = ReadOnlyField(source="rel_obj_content_type.app_label")
|
||||
rel_obj_model = ReadOnlyField(source="rel_obj_content_type.model")
|
||||
|
||||
description = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = [
|
||||
fields = (
|
||||
"id",
|
||||
"uid",
|
||||
"actor_name",
|
||||
"rel_obj_app_label",
|
||||
"rel_obj_model",
|
||||
"rel_obj_id",
|
||||
"crontab",
|
||||
"paused",
|
||||
"next_run",
|
||||
"description",
|
||||
]
|
||||
)
|
||||
|
||||
def get_description(self, instance: Schedule) -> str | None:
|
||||
if instance.rel_obj:
|
||||
@ -43,22 +52,48 @@ class ScheduleSerializer(ModelSerializer):
|
||||
actor: Actor = get_broker().get_actor(instance.actor_name)
|
||||
except ActorNotFound:
|
||||
return "FIXME this shouldn't happen"
|
||||
if not actor.fn.__doc__:
|
||||
return "no doc"
|
||||
return actor.fn.__doc__.strip()
|
||||
|
||||
|
||||
class ScheduleFilter(FilterSet):
|
||||
rel_obj_id__isnull = BooleanFilter("rel_obj_id", "isnull")
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = (
|
||||
"actor_name",
|
||||
"rel_obj_content_type__app_label",
|
||||
"rel_obj_content_type__model",
|
||||
"rel_obj_id",
|
||||
"rel_obj_id__isnull",
|
||||
"paused",
|
||||
)
|
||||
|
||||
|
||||
class ScheduleViewSet(
|
||||
RetrieveModelMixin,
|
||||
UpdateModelMixin,
|
||||
ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
queryset = Schedule.objects.all()
|
||||
queryset = Schedule.objects.select_related("rel_obj_content_type").all()
|
||||
serializer_class = ScheduleSerializer
|
||||
search_fields = (
|
||||
"id",
|
||||
"uid",
|
||||
"actor_name",
|
||||
"rel_obj_content_type__app_label",
|
||||
"rel_obj_content_type__model",
|
||||
"rel_obj_id",
|
||||
"description",
|
||||
)
|
||||
filterset_class = ScheduleFilter
|
||||
ordering = (
|
||||
"next_run",
|
||||
"uid",
|
||||
)
|
||||
ordering = ("next_run", "uid")
|
||||
|
||||
@permission_required("authentik_tasks_schedules.send_schedule")
|
||||
@extend_schema(
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.1.10 on 2025-06-11 12:57
|
||||
|
||||
import pgtrigger.compiler
|
||||
import pgtrigger.migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_tasks_schedules", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="schedule",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="set_next_run_on_paused",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
condition='WHEN (NEW."paused" AND NOT OLD."paused")',
|
||||
func="\n NEW.next_run = to_timestamp(0);\n RETURN NEW;\n ",
|
||||
hash="7fe580a86de70723522cfcbac712785984000f92",
|
||||
operation="UPDATE",
|
||||
pgid="pgtrigger_set_next_run_on_paused_95c6d",
|
||||
table="authentik_tasks_schedules_schedule",
|
||||
when="BEFORE",
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -1,6 +1,7 @@
|
||||
import pickle # nosec
|
||||
from uuid import uuid4
|
||||
|
||||
import pgtrigger
|
||||
from cron_converter import Cron
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@ -55,6 +56,18 @@ class Schedule(SerializerModel):
|
||||
("send_schedule", _("Manually trigger a schedule")),
|
||||
]
|
||||
indexes = (models.Index(fields=("rel_obj_content_type", "rel_obj_id")),)
|
||||
triggers = (
|
||||
pgtrigger.Trigger(
|
||||
name="set_next_run_on_paused",
|
||||
operation=pgtrigger.Update,
|
||||
when=pgtrigger.Before,
|
||||
condition=pgtrigger.Q(new__paused=True) & pgtrigger.Q(old__paused=False),
|
||||
func="""
|
||||
NEW.next_run = to_timestamp(0);
|
||||
RETURN NEW;
|
||||
""",
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.uid
|
||||
|
||||
@ -15878,6 +15878,14 @@
|
||||
"model_authentik_tasks_schedules.schedule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rel_obj_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"title": "Rel obj id"
|
||||
},
|
||||
"crontab": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
|
||||
72
schema.yml
72
schema.yml
@ -40441,6 +40441,10 @@ paths:
|
||||
get:
|
||||
operationId: tasks_schedules_list
|
||||
parameters:
|
||||
- in: query
|
||||
name: actor_name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
@ -40459,6 +40463,26 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: paused
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: rel_obj_content_type__app_label
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_content_type__model
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_id
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_id__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -40668,6 +40692,22 @@ paths:
|
||||
name: queue_name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_content_type__app_label
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_content_type__model
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_id
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: rel_obj_id__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -54942,6 +54982,10 @@ components:
|
||||
PatchedScheduleRequest:
|
||||
type: object
|
||||
properties:
|
||||
rel_obj_id:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
crontab:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -58883,6 +58927,16 @@ components:
|
||||
type: string
|
||||
readOnly: true
|
||||
description: Dramatiq actor to call
|
||||
rel_obj_app_label:
|
||||
type: string
|
||||
readOnly: true
|
||||
rel_obj_model:
|
||||
type: string
|
||||
title: Python model class name
|
||||
readOnly: true
|
||||
rel_obj_id:
|
||||
type: string
|
||||
nullable: true
|
||||
crontab:
|
||||
type: string
|
||||
description: When to schedule tasks
|
||||
@ -58903,10 +58957,16 @@ components:
|
||||
- description
|
||||
- id
|
||||
- next_run
|
||||
- rel_obj_app_label
|
||||
- rel_obj_model
|
||||
- uid
|
||||
ScheduleRequest:
|
||||
type: object
|
||||
properties:
|
||||
rel_obj_id:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
crontab:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -59849,9 +59909,13 @@ components:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Task last modified time
|
||||
rel_obj_content_type:
|
||||
type: integer
|
||||
nullable: true
|
||||
rel_obj_app_label:
|
||||
type: string
|
||||
readOnly: true
|
||||
rel_obj_model:
|
||||
type: string
|
||||
title: Python model class name
|
||||
readOnly: true
|
||||
rel_obj_id:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -59868,6 +59932,8 @@ components:
|
||||
- actor_name
|
||||
- aggregated_status
|
||||
- messages
|
||||
- rel_obj_app_label
|
||||
- rel_obj_model
|
||||
- uid
|
||||
Tenant:
|
||||
type: object
|
||||
|
||||
@ -16,10 +16,10 @@ export const ROUTES: Route[] = [
|
||||
await import("@goauthentik/admin/admin-overview/DashboardUserPage");
|
||||
return html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`;
|
||||
}),
|
||||
// new Route(new RegExp("^/administration/system-tasks$"), async () => {
|
||||
// await import("@goauthentik/admin/system-tasks/SystemTaskListPage");
|
||||
// return html`<ak-system-task-list></ak-system-task-list>`;
|
||||
// }),
|
||||
new Route(new RegExp("^/administration/system-tasks$"), async () => {
|
||||
await import("@goauthentik/admin/system-tasks/SystemTasksPage");
|
||||
return html`<ak-system-tasks></ak-system-tasks>`;
|
||||
}),
|
||||
new Route(new RegExp("^/core/providers$"), async () => {
|
||||
await import("@goauthentik/admin/providers/ProviderListPage");
|
||||
return html`<ak-provider-list></ak-provider-list>`;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { formatElapsedTime } from "#common/temporal";
|
||||
import "@goauthentik/admin/system-tasks/ScheduleForm";
|
||||
import "@goauthentik/admin/system-tasks/TaskList";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
@ -31,16 +31,42 @@ export class ScheduleList extends Table<Schedule> {
|
||||
@property()
|
||||
order = "next_run";
|
||||
|
||||
@property()
|
||||
relObjAppLabel?: string;
|
||||
@property()
|
||||
relObjModel?: string;
|
||||
@property()
|
||||
relObjId?: string;
|
||||
|
||||
@property()
|
||||
showOnlyStandalone: boolean = true;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDescriptionList);
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Schedule>> {
|
||||
const relObjIdIsnull =
|
||||
typeof this.relObjId !== "undefined"
|
||||
? undefined
|
||||
: this.showOnlyStandalone
|
||||
? true
|
||||
: undefined;
|
||||
return new TasksApi(DEFAULT_CONFIG).tasksSchedulesList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
relObjContentTypeAppLabel: this.relObjAppLabel,
|
||||
relObjContentTypeModel: this.relObjModel,
|
||||
relObjId: this.relObjId,
|
||||
relObjIdIsnull,
|
||||
});
|
||||
}
|
||||
|
||||
#toggleShowOnlyStandalone = () => {
|
||||
this.showOnlyStandalone = !this.showOnlyStandalone;
|
||||
this.page = 1;
|
||||
return this.fetch();
|
||||
};
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Schedule"), "actor_name"),
|
||||
@ -50,6 +76,35 @@ export class ScheduleList extends Table<Schedule> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
if (this.relObjId !== undefined) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria - hidden="true"> </i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone schedules")}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: Schedule): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.description}</div>
|
||||
@ -59,7 +114,7 @@ export class ScheduleList extends Table<Schedule> {
|
||||
${item.paused
|
||||
? html`Paused`
|
||||
: html`
|
||||
<div>${getRelativeTime(item.nextRun)}</div>
|
||||
<div>${formatElapsedTime(item.nextRun)}</div>
|
||||
<small>${item.nextRun.toLocaleString()}</small>
|
||||
`}
|
||||
`,
|
||||
@ -101,7 +156,11 @@ export class ScheduleList extends Table<Schedule> {
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<div class="pf-c-content">
|
||||
<ak-task-list .schedule=${item}></ak-task-list>
|
||||
<ak-task-list
|
||||
.relObjAppLabel="authentik_tasks_schedules"
|
||||
.relObjModel="schedule"
|
||||
.relObjId="${item.id}"
|
||||
></ak-task-list>
|
||||
</div>
|
||||
</div>
|
||||
</td>`;
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { formatElapsedTime } from "@goauthentik/common/temporal";
|
||||
import { PFColor } from "@goauthentik/elements/Label";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/events/LogViewer";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import { EventsApi, SystemTask, SystemTaskStatusEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-system-task-list")
|
||||
export class SystemTaskListPage extends TablePage<SystemTask> {
|
||||
pageTitle(): string {
|
||||
return msg("System Tasks");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return msg("Long-running operations which authentik executes in the background.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return "pf-icon pf-icon-automation";
|
||||
}
|
||||
|
||||
expandable = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDescriptionList);
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<SystemTask>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsSystemTasksList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Identifier"), "name"),
|
||||
new TableColumn(msg("Description")),
|
||||
new TableColumn(msg("Last run")),
|
||||
new TableColumn(msg("Status"), "status"),
|
||||
new TableColumn(msg("Actions")),
|
||||
];
|
||||
}
|
||||
|
||||
taskStatus(task: SystemTask): TemplateResult {
|
||||
switch (task.status) {
|
||||
case SystemTaskStatusEnum.Successful:
|
||||
return html`<ak-label color=${PFColor.Green}>${msg("Successful")}</ak-label>`;
|
||||
case SystemTaskStatusEnum.Warning:
|
||||
return html`<ak-label color=${PFColor.Orange}>${msg("Warning")}</ak-label>`;
|
||||
case SystemTaskStatusEnum.Error:
|
||||
return html`<ak-label color=${PFColor.Red}>${msg("Error")}</ak-label>`;
|
||||
default:
|
||||
return html`<ak-label color=${PFColor.Grey}>${msg("Unknown")}</ak-label>`;
|
||||
}
|
||||
}
|
||||
|
||||
renderExpanded(item: SystemTask): TemplateResult {
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Duration")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${msg(str`${item.duration.toFixed(2)} seconds`)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Expiry")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.expiring
|
||||
? html`
|
||||
<pf-tooltip
|
||||
position="top"
|
||||
content=${(
|
||||
item.expires || new Date()
|
||||
).toLocaleString()}
|
||||
>
|
||||
${formatElapsedTime(item.expires || new Date())}
|
||||
</pf-tooltip>
|
||||
`
|
||||
: msg("-")}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Messages")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-log-viewer .logs=${item?.messages}></ak-log-viewer>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
row(item: SystemTask): TemplateResult[] {
|
||||
return [
|
||||
html`<pre>${item.name}${item.uid ? `:${item.uid}` : ""}</pre>`,
|
||||
html`${item.description}`,
|
||||
html`<div>${formatElapsedTime(item.finishTimestamp)}</div>
|
||||
<small>${item.finishTimestamp.toLocaleString()}</small>`,
|
||||
this.taskStatus(item),
|
||||
html`<ak-action-button
|
||||
class="pf-m-plain"
|
||||
.apiRequest=${() => {
|
||||
return new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsSystemTasksRunCreate({
|
||||
uuid: item.uuid,
|
||||
})
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Restart task")}>
|
||||
<i class="fas fa-redo" aria-hidden="true"></i>
|
||||
</pf-tooltip>
|
||||
</ak-action-button>`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-system-task-list": SystemTaskListPage;
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ export class SystemTasksPage extends AKElement {
|
||||
</section>
|
||||
<section
|
||||
slot="page-tasks"
|
||||
data-tab-title="${msg("One-off tasks")}"
|
||||
data-tab-title="${msg("Tasks")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { formatElapsedTime } from "#common/temporal";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionModal";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import { PFColor } from "@goauthentik/elements/Label";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/events/LogViewer";
|
||||
@ -16,7 +16,7 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import { Schedule, Task, TasksApi, TasksTasksListStateEnum } from "@goauthentik/api";
|
||||
import { Task, TasksApi, TasksTasksListStateEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-task-list")
|
||||
export class TaskList extends Table<Task> {
|
||||
@ -24,7 +24,14 @@ export class TaskList extends Table<Task> {
|
||||
clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
schedule: Schedule | undefined;
|
||||
relObjAppLabel?: string;
|
||||
@property()
|
||||
relObjModel?: string;
|
||||
@property()
|
||||
relObjId?: string;
|
||||
|
||||
@property()
|
||||
showOnlyStandalone: boolean = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
@ -38,14 +45,27 @@ export class TaskList extends Table<Task> {
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Task>> {
|
||||
const excludeScheduled = this.schedule === undefined;
|
||||
const relObjIdIsnull =
|
||||
typeof this.relObjId !== "undefined"
|
||||
? undefined
|
||||
: this.showOnlyStandalone
|
||||
? true
|
||||
: undefined;
|
||||
return new TasksApi(DEFAULT_CONFIG).tasksTasksList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
excludeScheduled: excludeScheduled,
|
||||
scheduleUid: this.schedule?.uid,
|
||||
relObjContentTypeAppLabel: this.relObjAppLabel,
|
||||
relObjContentTypeModel: this.relObjModel,
|
||||
relObjId: this.relObjId,
|
||||
relObjIdIsnull,
|
||||
});
|
||||
}
|
||||
|
||||
#toggleShowOnlyStandalone = () => {
|
||||
this.showOnlyStandalone = !this.showOnlyStandalone;
|
||||
this.page = 1;
|
||||
return this.fetch();
|
||||
};
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Task"), "actor_name"),
|
||||
@ -56,6 +76,37 @@ export class TaskList extends Table<Task> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
console.log("task show standalone");
|
||||
console.log(this.showOnlyStandalone);
|
||||
if (this.relObjId !== undefined) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria - hidden="true"> </i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone tasks")}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
taskState(task: Task): TemplateResult {
|
||||
switch (task.state) {
|
||||
case TasksTasksListStateEnum.Queued:
|
||||
@ -74,9 +125,9 @@ export class TaskList extends Table<Task> {
|
||||
row(item: Task): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.actorName}</div>
|
||||
<small>${item.uid}</small>`,
|
||||
<small>${item.uid.replace(new RegExp("^authentik."), "")}</small>`,
|
||||
html`${item.queueName}`,
|
||||
html`<div>${getRelativeTime(item.mtime)}</div>
|
||||
html`<div>${formatElapsedTime(item.mtime || new Date())}</div>
|
||||
<small>${item.mtime.toLocaleString()}</small>`,
|
||||
this.taskState(item),
|
||||
html``,
|
||||
|
||||
Reference in New Issue
Block a user