rework ca-wiz and add icons to menu for warnings (#2486)

* rework ca-wiz and add icons to menu for warnings

* remove root CA button from home page

* load fonts before calling complete in setup wiz
This commit is contained in:
Matt Hill
2023-11-01 13:36:56 -06:00
committed by GitHub
parent c14ca1d7fd
commit 1dad7965d2
10 changed files with 172 additions and 205 deletions

View File

@@ -45,18 +45,7 @@ export class SuccessPage {
async ngAfterViewInit() {
this.ngZone.runOutsideAngular(() => this.initMatrix())
try {
const ret = await this.api.complete()
if (!this.isKiosk) {
this.torAddress = ret['tor-address']
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
this.cert = ret['root-ca']
await this.api.exit()
}
} catch (e: any) {
await this.errCtrl.present(e)
}
setTimeout(() => this.complete(), 1000)
}
download() {
@@ -83,6 +72,21 @@ export class SuccessPage {
this.api.exit()
}
private async complete() {
try {
const ret = await this.api.complete()
if (!this.isKiosk) {
this.torAddress = ret['tor-address']
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
this.cert = ret['root-ca']
await this.api.exit()
}
} catch (e: any) {
await this.errCtrl.present(e)
}
}
private initMatrix() {
this.ctx = this.canvas.nativeElement.getContext('2d')!
this.canvas.nativeElement.width = window.innerWidth

View File

@@ -22,11 +22,17 @@
<ion-label class="label montserrat" routerLinkActive="label_selected">
{{ page.title }}
</ion-label>
<ion-icon
*ngIf="page.url === '/system' && (warning$ | async)"
color="warning"
size="small"
name="warning"
></ion-icon>
<ion-icon
*ngIf="page.url === '/system' && (showEOSUpdate$ | async)"
color="success"
size="small"
name="rocket-outline"
name="rocket"
></ion-icon>
<ion-badge
*ngIf="page.url === '/updates' && (updateCount$ | async) as updateCount"

View File

@@ -11,7 +11,9 @@ import {
filter,
first,
map,
merge,
Observable,
of,
pairwise,
startWith,
switchMap,
@@ -22,6 +24,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
import { Emver, THEME } from '@start9labs/shared'
import { ConnectionService } from 'src/app/services/connection.service'
import { ConfigService } from 'src/app/services/config.service'
@Component({
selector: 'app-menu',
@@ -111,6 +114,11 @@ export class MenuComponent {
readonly theme$ = inject(THEME)
readonly warning$ = merge(
of(this.config.isTorHttp()),
this.patch.watch$('server-info', 'ntp-synced').pipe(map(synced => !synced)),
)
constructor(
private readonly patch: PatchDB<DataModel>,
private readonly eosService: EOSService,
@@ -119,5 +127,6 @@ export class MenuComponent {
private readonly splitPane: SplitPaneTracker,
private readonly emver: Emver,
private readonly connectionService: ConnectionService,
private readonly config: ConfigService,
) {}
}

View File

@@ -19,33 +19,35 @@ ion-card {
font-family: 'Open Sans';
padding: 0.6rem;
font-weight: 600;
font-size: calc(12px + 0.5vw);
height: 3rem;
height: 2.4rem;
}
ion-card-content {
min-height: 9rem;
min-height: 8rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
ion-icon {
font-size: calc(90px + 0.5vw);
font-size: calc(90px + 0.4vw);
--ionicon-stroke-width: 1rem;
}
}
ion-footer {
padding: 1rem;
padding: 0 1rem;
font-family: 'Open Sans';
font-size: clamp(1rem, calc(12px + 0.5vw), 1.3rem);
height: 9rem;
height: 4.5rem;
width: clamp(13rem, 80%, 18rem);
margin: 0 auto;
* {
max-width: 100%;
}
p {
margin-top: 0;
}
}
.footer-md::before {
@@ -54,9 +56,6 @@ ion-card {
}
@media (max-width: 900px) {
ion-card-title, ion-footer {
height: auto !important;
}
ion-footer {
width: 10rem;
}

View File

@@ -1,14 +1,7 @@
<div #gridContent>
<ion-grid>
<ion-row class="ion-justify-content-center ion-align-items-center">
<ion-col
*ngFor="let card of cards"
responsiveCol
sizeLg="4"
sizeSm="6"
sizeXs="12"
class="ion-align-self-center"
>
<ion-col *ngFor="let card of cards" sizeXs="12">
<widget-card
[cardDetails]="card"
[containerDimensions]="containerDimensions"

View File

@@ -3,11 +3,17 @@ ion-col {
--ion-grid-column-padding: 1rem;
}
@media (min-width: 1800px) {
@media (min-width: 1700px) {
div {
padding: 0 20%;
padding: 0 7%;
}
ion-col {
max-width: 24rem !important;
}
}
@media (min-width: 2000px) {
div {
padding: 0 12%;
}
}

View File

@@ -38,33 +38,33 @@ export class WidgetListComponent {
cards: Card[] = [
{
title: 'Visit the Marketplace',
title: 'Server Info',
icon: 'information-circle-outline',
color: 'var(--alt-green)',
description: 'View information about your server',
link: '/system/specs',
},
{
title: 'Browse',
icon: 'storefront-outline',
color: 'var(--alt-blue)',
description: 'Shop for your favorite open source services',
color: 'var(--alt-purple)',
description: 'Browse for services to install',
link: '/marketplace',
qp: { back: 'true' },
},
{
title: 'Root CA',
icon: 'ribbon-outline',
color: 'var(--alt-orange)',
description: `Download and trust your server's root certificate authority`,
link: '/system/root-ca',
},
{
title: 'Create Backup',
icon: 'duplicate-outline',
color: 'var(--alt-purple)',
color: 'var(--alt-blue)',
description: 'Back up StartOS and service data',
link: '/system/backup',
},
{
title: 'Server Info',
icon: 'information-circle-outline',
color: 'var(--alt-green)',
description: 'View basic information about your server',
link: '/system/specs',
title: 'Monitor',
icon: 'pulse-outline',
color: 'var(--alt-orange)',
description: `View your system resource usage`,
link: '/system/metrics',
},
{
title: 'User Manual',
@@ -77,7 +77,7 @@ export class WidgetListComponent {
title: 'Contact Support',
icon: 'chatbubbles-outline',
color: 'var(--alt-red)',
description: 'Get help from the Start9 team and community',
description: 'Get help from the Start9 community',
link: 'https://start9.com/contact',
},
]

View File

@@ -1,106 +1,78 @@
<ion-grid class="grid-wiz">
<img width="60px" height="60px" src="/assets/img/icon.png" alt="StartOS" />
<ion-row>
<ion-col class="ion-text-center">
<ion-content>
<ng-container *ngIf="!caTrusted; else trusted">
<ion-icon name="lock-closed-outline" class="wiz-icon"></ion-icon>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="ion-text-center">
<h2><b>Trust your Root Certificate Authority (CA)</b></h2>
<p>
Download and trust your server's Root CA to establish secure, encrypted
(
<h1 class="title">Trust Your Root CA</h1>
<p class="subtitle">
Download and trust your server's Root Certificate Authority to establish a
secure (
<b>HTTPS</b>
) connections with your server
) connection. You will need to repeat this on every device you use to
connect to your server.
</p>
</ion-col>
</ion-row>
<ion-row>
<ion-col sizeXs="12" sizeLg="4">
<div class="wiz-card">
<ion-row class="ion-justify-content-between">
<b class="wiz-step">1</b>
<tui-tooltip
content="Your server uses its Root CA to generate SSL/TLS certificates for itself and its installed services. These certificates are used to encrypt network traffic with your client devices."
direction="right"
></tui-tooltip>
</ion-row>
<div class="ion-text-center">
<h2>Download Root CA</h2>
<p>Download your server's Root CA</p>
</div>
<ion-button class="wiz-card-button" shape="round" (click)="download()">
<ion-icon slot="start" name="download-outline"></ion-icon>
<ol>
<li>
<b>Download your server's Root CA</b>
. Your server uses its Root CA to generate SSL/TLS certificates for
itself and installed services. These certificates are then used to
encrypt network traffic with your client devices.
<br />
<ion-button strong size="small" (click)="download()">
Download
<ion-icon slot="end" name="download-outline"></ion-icon>
</ion-button>
</div>
</ion-col>
<ion-col sizeXs="12" sizeLg="4">
<div class="wiz-card" [class.disabled]="!downloadClicked">
<ion-row class="ion-justify-content-between">
<b class="wiz-step">2</b>
<tui-tooltip
content="By trusting your server's Root CA, your device can verify the authenticity of its encrypted communications with your server and installed services. You will need to trust the Root CA on every device used to connect to your server."
direction="right"
></tui-tooltip>
</ion-row>
<div class="ion-text-center">
<h2>Trust Root CA</h2>
<p>Follow instructions for your OS</p>
</div>
<ion-button
class="wiz-card-button"
shape="round"
(click)="instructions()"
[disabled]="!downloadClicked"
>
View Docs
</li>
<li>
<b>Trust your server's Root CA</b>
. Follow instructions for your OS. By trusting your server's Root CA,
your device can verify the authenticity of encrypted communications with
your server.
<br />
<ion-button strong size="small" (click)="instructions()">
View Instructions
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-button>
</div>
</ion-col>
<ion-col sizeXs="12" sizeLg="4">
<div class="wiz-card" [class.disabled]="!polling && !caTrusted">
<b class="wiz-step">3</b>
<div class="ion-text-center">
<h2>Go To Login</h2>
<p *ngIf="instructionsClicked; else space" class="inline-center">
<ion-spinner
class="wiz-spinner"
*ngIf="!caTrusted; else trusted"
></ion-spinner>
<ng-template #trusted>
<ion-icon name="ribbon-outline" color="success"></ion-icon>
</ng-template>
&nbsp;{{ caTrusted ? 'Root CA trusted!' : 'Waiting for trust...' }}
</p>
<ng-template #space>
<!-- to keep alignment -->
<p><br /></p>
</ng-template>
</div>
</li>
<li>
<b>Test</b>
. If refreshing the page does not work, you may need to quit the browser
and re-open.
<i>Tip: before quitting, bookmark this page or copy the URL.</i>
<br />
<ion-button strong size="small" (click)="refresh()">
Refresh
<ion-icon slot="end" name="refresh"></ion-icon>
</ion-button>
</li>
</ol>
<ion-button
class="wiz-card-button"
shape="round"
style="--padding-start: 0"
fill="clear"
(click)="launchHttps()"
[disabled]="!caTrusted"
[disabled]="caTrusted"
>
Open
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-button>
</div>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="ion-text-center">
<ion-button fill="clear" (click)="launchHttps()" [disabled]="caTrusted">
Skip
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
<ng-template #trusted>
<ion-icon
name="shield-checkmark-outline"
class="wiz-icon"
color="success"
></ion-icon>
<h1 class="title">Root CA Trusted!</h1>
<p class="subtitle">
You have successfully trusted your server's Root CA and may now log in
securely.
</p>
<ion-button strong (click)="launchHttps()">
Go to login
<ion-icon slot="end" name="open-outline"></ion-icon>
</ion-button>
</ng-template>
</ion-content>
<a
id="install-cert"
href="/eos/local.crt"

View File

@@ -1,44 +1,41 @@
.grid-wiz {
--ion-grid-padding: 36px;
height: 100%
ion-content {
--padding-top: 2.2rem;
--padding-bottom: 2.2rem;
--padding-start: 2.2rem;
--padding-end: 2.2rem;
}
.title {
font-size: 28px;
font-weight: 600;
}
.subtitle {
font-size: 21px;
line-height: 26px;
margin-bottom: 30px;
margin-top: 0;
}
ol {
font-size: 17px;
line-height: 25px;
li {
padding-bottom: 24px;
}
ion-button {
margin-top: 8px;
}
}
a {
cursor: pointer;
color: aquamarine;
text-decoration: underline;
}
.wiz-icon {
font-size: 84px;
}
.wiz-card {
background: #414141;
margin: 24px;
padding: 16px;
height: 280px;
border-radius: 16px;
display: grid;
& h2 {
font-weight: 600;
}
}
.wiz-card-button {
justify-self: center;
white-space: normal;
}
.wiz-spinner {
width: 14px;
height: 14px;
}
.disabled {
filter: saturate(0.2) contrast(0.5)
}
.wiz-step {
margin-top: 4px;
}
.inline-center {
display: inline-flex;
align-items: center;
font-size: 64px;
}

View File

@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
import { pauseFor, RELATIVE_URL } from '@start9labs/shared'
import { RELATIVE_URL } from '@start9labs/shared'
import { DOCUMENT } from '@angular/common'
import { WINDOW } from '@ng-web-apis/common'
@@ -11,9 +11,6 @@ import { WINDOW } from '@ng-web-apis/common'
styleUrls: ['./ca-wizard.component.scss'],
})
export class CAWizardComponent {
downloadClicked = false
instructionsClicked = false
polling = false
caTrusted = false
constructor(
@@ -25,15 +22,12 @@ export class CAWizardComponent {
) {}
async ngOnInit() {
if (!this.config.isSecure()) {
await this.testHttps().catch(e =>
console.warn('Failed Https connection attempt'),
)
}
}
download() {
this.downloadClicked = true
this.document.getElementById('install-cert')?.click()
}
@@ -43,21 +37,10 @@ export class CAWizardComponent {
'_blank',
'noreferrer',
)
this.instructionsClicked = true
this.startDaemon()
}
private async startDaemon(): Promise<void> {
this.polling = true
while (this.polling) {
try {
await this.testHttps()
this.polling = false
} catch (e) {
console.warn('Failed Https connection attempt')
await pauseFor(2000)
}
}
refresh() {
this.document.location.reload()
}
launchHttps() {
@@ -68,8 +51,6 @@ export class CAWizardComponent {
private async testHttps() {
const url = `https://${this.document.location.host}${this.relativeUrl}`
await this.api.echo({ message: 'ping' }, url).then(() => {
this.downloadClicked = true
this.instructionsClicked = true
this.caTrusted = true
})
}