web: add outpost list page
This commit is contained in:
		
							
								
								
									
										39
									
								
								web/src/api/Outposts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								web/src/api/Outposts.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| import { DefaultClient, PBResponse, QueryArguments } from "./Client"; | ||||
| import { Provider } from "./Providers"; | ||||
|  | ||||
| export interface OutpostHealth { | ||||
|     last_seen: number; | ||||
|     version: string; | ||||
|     version_should: string; | ||||
|     version_outdated: boolean; | ||||
| } | ||||
|  | ||||
| export class Outpost { | ||||
|  | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     providers: Provider[]; | ||||
|     service_connection?: string; | ||||
|     _config: QueryArguments; | ||||
|     token_identifier: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Outpost> { | ||||
|         return DefaultClient.fetch<Outpost>(["outposts", "outposts", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<PBResponse<Outpost>> { | ||||
|         return DefaultClient.fetch<PBResponse<Outpost>>(["outposts", "outposts"], filter); | ||||
|     } | ||||
|  | ||||
|     static health(pk: string): Promise<OutpostHealth[]> { | ||||
|         return DefaultClient.fetch<OutpostHealth[]>(["outposts", "outposts", pk, "health"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/outposts/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -27,7 +27,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|             `^/sources/(?<slug>${SLUG_REGEX})$`, | ||||
|         ), | ||||
|         new SidebarItem("Providers", "/providers"), | ||||
|         new SidebarItem("Outposts", "/administration/outposts/"), | ||||
|         new SidebarItem("Outposts", "/outposts"), | ||||
|         new SidebarItem("Outpost Service Connections", "/administration/outpost_service_connections/"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|  | ||||
							
								
								
									
										49
									
								
								web/src/pages/outposts/OutpostHealth.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								web/src/pages/outposts/OutpostHealth.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { Outpost } from "../../api/Outposts"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| @customElement("ak-outpost-health") | ||||
| export class OutpostHealth extends LitElement { | ||||
|  | ||||
|     @property() | ||||
|     outpostId?: string; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES; | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         if (!this.outpostId) { | ||||
|             return html`<ak-spinner></ak-spinner>`; | ||||
|         } | ||||
|         return html`<ul>${until(Outpost.health(this.outpostId).then((oh) => { | ||||
|             if (oh.length === 0) { | ||||
|                 return html`<li> | ||||
|                     <ul> | ||||
|                         <li role="cell"> | ||||
|                             <i class="fas fa-question-circle"></i> ${gettext("Not available")} | ||||
|                         </li> | ||||
|                     </ul> | ||||
|                 </li>`; | ||||
|             } | ||||
|             return oh.map((h) => { | ||||
|                 return html`<li> | ||||
|                     <ul> | ||||
|                         <li role="cell"> | ||||
|                             <i class="fas fa-check pf-m-success"></i> ${gettext(`Last seen: ${new Date(h.last_seen * 1000).toLocaleTimeString()}`)} | ||||
|                         </li> | ||||
|                         <li role="cell"> | ||||
|                             ${h.version_outdated ? | ||||
|                             html`<i class="fas fa-times pf-m-danger"></i>  | ||||
|                                 ${gettext(`${h.version}, should be ${h.version_should}`)}` : | ||||
|                             html`<i class="fas fa-check pf-m-success"></i> ${gettext(`Version: ${h.version}`)}`} | ||||
|                         </li> | ||||
|                     </ul> | ||||
|                 </li>`; | ||||
|             }); | ||||
|         }), html`<ak-spinner></ak-spinner>`)}</ul>`; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										121
									
								
								web/src/pages/outposts/OutpostListPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								web/src/pages/outposts/OutpostListPage.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { html, TemplateResult } from "lit-html"; | ||||
| import { PBResponse } from "../../api/Client"; | ||||
| import { Outpost } from "../../api/Outposts"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| import "./OutpostHealth"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
|  | ||||
| @customElement("ak-outpost-list") | ||||
| export class OutpostListPage extends TablePage<Outpost> { | ||||
|     pageTitle(): string { | ||||
|         return "Outposts"; | ||||
|     } | ||||
|     pageDescription(): string | undefined { | ||||
|         return "Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies."; | ||||
|     } | ||||
|     pageIcon(): string { | ||||
|         return "pf-icon pf-icon-zone"; | ||||
|     } | ||||
|     searchEnabled(): boolean { | ||||
|         return true; | ||||
|     } | ||||
|     apiEndpoint(page: number): Promise<PBResponse<Outpost>> { | ||||
|         return Outpost.list({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
|     columns(): TableColumn[] { | ||||
|         return [ | ||||
|             new TableColumn("Name", "name"), | ||||
|             new TableColumn("Providers"), | ||||
|             new TableColumn("Health and Version"), | ||||
|             new TableColumn(""), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     @property() | ||||
|     order = "name"; | ||||
|  | ||||
|     row(item: Outpost): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`<ul>${item.providers.map((p) => { | ||||
|                 return html`<li><a href="#/providers/${p.pk}">${p.name}</a></li>`; | ||||
|             })}</ul>`, | ||||
|             html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button> | ||||
|                 <button slot="trigger" class="pf-c-button pf-m-tertiary"> | ||||
|                     ${gettext('View Deployment Info')} | ||||
|                 </button> | ||||
|                 <div slot="modal"> | ||||
|                     <div class="pf-c-modal-box__header"> | ||||
|                         <h1 class="pf-c-title pf-m-2xl" id="modal-title">${gettext('Outpost Deployment Info')}</h1> | ||||
|                     </div> | ||||
|                     <div class="pf-c-modal-box__body" id="modal-description"> | ||||
|                         <p><a href="https://goauthentik.io/docs/outposts/outposts/#deploy">${gettext('View deployment documentation')}</a></p> | ||||
|                         <form class="pf-c-form"> | ||||
|                             <div class="pf-c-form__group"> | ||||
|                                 <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|                                     <span class="pf-c-form__label-text">AUTHENTIK_HOST</span> | ||||
|                                 </label> | ||||
|                                 <input class="pf-c-form-control" readonly type="text" value="${document.location.toString()}" /> | ||||
|                             </div> | ||||
|                             <div class="pf-c-form__group"> | ||||
|                                 <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|                                     <span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span> | ||||
|                                 </label> | ||||
|                                 <div> | ||||
|                                     <ak-token-copy-button identifier="${item.token_identifier}"> | ||||
|                                         ${gettext('Click to copy token')} | ||||
|                                     </ak-token-copy-button> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <h3>${gettext('If your authentik Instance is using a self-signed certificate, set this value.')}</h3> | ||||
|                             <div class="pf-c-form__group"> | ||||
|                                 <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|                                     <span class="pf-c-form__label-text">AUTHENTIK_INSECURE</span> | ||||
|                                 </label> | ||||
|                                 <input class="pf-c-form-control" readonly type="text" value="true" /> | ||||
|                             </div> | ||||
|                         </form> | ||||
|                     </div> | ||||
|                     <footer class="pf-c-modal-box__footer pf-m-align-left"> | ||||
|                         <a class="pf-c-button pf-m-primary">${gettext('Close')}</a> | ||||
|                     </footer> | ||||
|                 </div> | ||||
|             </ak-modal-button>`, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Outpost.adminUrl("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|             <div slot="modal"></div> | ||||
|         </ak-modal-button> | ||||
|         ${super.renderToolbar()} | ||||
|         `; | ||||
|     } | ||||
| } | ||||
| @ -14,6 +14,7 @@ import "./pages/events/RuleListPage"; | ||||
| import "./pages/providers/ProviderListPage"; | ||||
| import "./pages/providers/ProviderViewPage"; | ||||
| import "./pages/property-mappings/PropertyMappingListPage"; | ||||
| import "./pages/outposts/OutpostListPage"; | ||||
|  | ||||
| export const ROUTES: Route[] = [ | ||||
|     // Prevent infinite Shell loops | ||||
| @ -42,4 +43,5 @@ export const ROUTES: Route[] = [ | ||||
|     new Route(new RegExp("^/events/transports$"), html`<ak-event-transport-list></ak-event-transport-list>`), | ||||
|     new Route(new RegExp("^/events/rules$"), html`<ak-event-rule-list></ak-event-rule-list>`), | ||||
|     new Route(new RegExp("^/property-mappings$"), html`<ak-property-mapping-list></ak-property-mapping-list>`), | ||||
|     new Route(new RegExp("^/outposts$"), html`<ak-outpost-list></ak-outpost-list>`), | ||||
| ]; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer