* add support for idmapped mounts to start-sdk

* misc fixes

* misc fixes

* add default to textarea

* fix iptables masquerade rule

* fix textarea types

* more fixes

* better logging for rsync

* fix tty size

* fix wg conf generation for android

* disable file mounts on dependencies

* mostly there, some styling issues (#3069)

* mostly there, some styling issues

* fix: address comments (#3070)

* fix: address comments

* fix: fix

* show SSL for any address with secure protocol and ssl added

* better sorting and messaging

---------

Co-authored-by: Alex Inkin <alexander@inkin.ru>

* fixes for nextcloud

* allow sidebar navigation during service state traansitions

* wip: x-forwarded headers

* implement x-forwarded-for proxy

* lowercase domain names and fix warning popover bug

* fix http2 websockets

* fix websocket retry behavior

* add arch filters to s9pk pack

* use docker for start-cli install

* add version range to package signer on registry

* fix rcs < 0

* fix user information parsing

* refactor service interface getters

* disable idmaps

* build fixes

* update docker login action

* streamline build

* add start-cli workflow

* rename

* riscv64gc

* fix ui packing

* no default features on cli

* make cli depend on GIT_HASH

* more build fixes

* more build fixes

* interpolate arch within dockerfile

* fix tests

* add launch ui to service page plus other small improvements (#3075)

* add launch ui to service page plus other small improvements

* revert translation disable

* add spinner to service list if service is health and loading

* chore: some visual tune up

* chore: update Taiga UI

---------

Co-authored-by: waterplea <alexander@inkin.ru>

* fix backups

* feat: use arm hosted runners and don't fail when apt package does not exist (#3076)

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Remco Ros <remcoros@live.nl>
This commit is contained in:
Aiden McClelland
2025-12-15 13:30:50 -07:00
committed by GitHub
parent b945243d1a
commit 0430e0f930
148 changed files with 2572 additions and 1761 deletions

View File

@@ -27,6 +27,7 @@ const allowedStatuses = {
'stopping',
'starting',
'backing-up',
'task-required',
]),
}

View File

@@ -1395,6 +1395,7 @@ export namespace Mock {
name: 'Color',
required: false,
default: null,
immutable: true,
}),
datetime: ISB.Value.datetime({
name: 'Datetime',
@@ -1481,6 +1482,7 @@ export namespace Mock {
description:
'<ul><li>determines whether your node is running on testnet or mainnet</li></ul><script src="fake"></script>',
warning: 'Chain will have to resync!',
immutable: true,
}),
'object-list': ISB.Value.list(
ISB.List.obj(
@@ -1598,6 +1600,8 @@ export namespace Mock {
option1: 'option1',
option2: 'option2',
option3: 'option3',
option4:
'https://qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm12345.onion',
},
disabled: ['option2'],
})),
@@ -1610,6 +1614,7 @@ export namespace Mock {
default: 7,
integer: false,
units: 'BTC',
placeholder: 'Is it 237?',
min: -100,
max: 100,
}),

View File

@@ -1089,7 +1089,7 @@ export class MockApiService extends ApiService {
health: {
'ephemeral-health-check': {
name: 'Ephemeral Health Check',
result: 'starting',
result: 'success',
message: null,
},
'unnecessary-health-check': {

View File

@@ -47,6 +47,7 @@ export const mockPatchData: DataModel = {
addSsl: {
preferredExternalPort: 443,
alpn: { specified: ['http/1.1', 'h2'] },
addXForwardedHeaders: false,
},
secure: null,
},

View File

@@ -44,7 +44,7 @@ export interface DependencyErrorTaskRequired {
export type DependencyErrorHealthChecksFailed = {
type: 'healthChecksFailed'
check: T.NamedHealthCheckResult
check?: T.NamedHealthCheckResult
}
export type DependencyErrorTransitive = {
@@ -164,10 +164,10 @@ export class DepErrorService {
}
// health check failure
if (depStatus === 'running' && currentDep?.kind === 'running') {
if (currentDep?.kind === 'running') {
for (let id of currentDep.healthChecks) {
const check = dep.statusInfo.health[id]
if (check && check?.result !== 'success') {
if (check?.result !== 'success') {
return {
type: 'healthChecksFailed',
check,

View File

@@ -93,8 +93,12 @@ export class FormService {
}
return this.formBuilder.control(value, stringValidators(spec))
case 'textarea':
value = currentValue || null
return this.formBuilder.control(value, textareaValidators(spec))
if (currentValue !== undefined) {
value = currentValue
} else {
value = spec.default || null
}
return this.formBuilder.control(value, stringValidators(spec))
case 'number':
if (currentValue !== undefined) {
value = currentValue
@@ -156,7 +160,7 @@ export class FormService {
// }
function stringValidators(
spec: IST.ValueSpecText | IST.ListValueSpecText,
spec: IST.ValueSpecText | IST.ValueSpecTextarea | IST.ListValueSpecText,
): ValidatorFn[] {
const validators: ValidatorFn[] = []
@@ -173,18 +177,6 @@ function stringValidators(
return validators
}
function textareaValidators(spec: IST.ValueSpecTextarea): ValidatorFn[] {
const validators: ValidatorFn[] = []
if (spec.required) {
validators.push(Validators.required)
}
validators.push(textLengthInRange(spec.minLength, spec.maxLength))
return validators
}
function colorValidators({ required }: IST.ValueSpecColor): ValidatorFn[] {
const validators: ValidatorFn[] = [Validators.pattern(/^#[0-9a-f]{6}$/i)]

View File

@@ -6,12 +6,9 @@ import {
MARKDOWN,
} from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom, merge, of, shareReplay, Subject } from 'rxjs'
import { merge, of, shareReplay, Subject } from 'rxjs'
import { REPORT } from 'src/app/components/backup-report.component'
import {
ServerNotification,
ServerNotifications,
} from 'src/app/services/api/api.types'
import { ServerNotification } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { DataModel } from 'src/app/services/patch-db/data-model'
@@ -28,16 +25,6 @@ export class NotificationService {
this.localUnreadCount$,
).pipe(shareReplay(1))
async markSeen(notifications: ServerNotifications) {
const ids = notifications.filter(n => !n.seen).map(n => n.id)
this.updateCount(-ids.length)
this.api
.markSeenNotifications({ ids })
.catch(e => this.errorService.handleError(e))
}
async markSeenAll(latestId: number) {
this.localUnreadCount$.next(0)
@@ -46,24 +33,6 @@ export class NotificationService {
.catch(e => this.errorService.handleError(e))
}
async markUnseen(notifications: ServerNotifications) {
const ids = notifications.filter(n => n.seen).map(n => n.id)
this.updateCount(ids.length)
this.api
.markUnseenNotifications({ ids })
.catch(e => this.errorService.handleError(e))
}
async remove(notifications: ServerNotifications): Promise<void> {
this.updateCount(-notifications.filter(n => !n.seen).length)
this.api
.deleteNotifications({ ids: notifications.map(n => n.id) })
.catch(e => this.errorService.handleError(e))
}
getColor(notification: ServerNotification<number>): string {
switch (notification.level) {
case 'info':
@@ -95,7 +64,6 @@ export class NotificationService {
viewModal(notification: ServerNotification<number>, full = false) {
const { data, createdAt, code, title, message } = notification
this.markSeen([notification])
if (code === 1) {
// Backup Report
@@ -116,10 +84,4 @@ export class NotificationService {
.subscribe()
}
}
private async updateCount(toAdjust: number) {
const currentCount = await firstValueFrom(this.unreadCount$)
this.localUnreadCount$.next(Math.max(currentCount + toAdjust, 0))
}
}

View File

@@ -1,6 +1,6 @@
import { inject, Injectable, InjectionToken } from '@angular/core'
import { Dump, Revision, Update } from 'patch-db-client'
import { BehaviorSubject, EMPTY, Observable } from 'rxjs'
import { BehaviorSubject, EMPTY, Observable, throwError, timer } from 'rxjs'
import {
bufferTime,
catchError,
@@ -40,7 +40,7 @@ export class PatchDbSource extends Observable<Update<DataModel>[]> {
),
),
catchError((_, original$) => {
this.state.retrigger()
this.state.retrigger(false, 2000)
return this.state.pipe(
skip(1), // skipping previous value stored due to shareReplay

View File

@@ -93,7 +93,9 @@ export class StateService extends Observable<RR.ServerState | null> {
super(subscriber => this.stream$.subscribe(subscriber))
// Retrigger on offline
this.network$.pipe(filter(v => !v)).subscribe(() => this.retrigger())
this.network$
.pipe(filter(v => !v))
.subscribe(() => this.retrigger(false, 2000))
// Show toasts
this.trigger$
@@ -109,8 +111,8 @@ export class StateService extends Observable<RR.ServerState | null> {
.subscribe()
}
retrigger(gracefully = false) {
this.trigger$.next(gracefully)
retrigger(gracefully: boolean, delay: number) {
setTimeout(() => this.trigger$.next(gracefully), delay)
}
private handleState(state: RR.ServerState): void {