Frontend fixes/improvements (#2950)

* fix Tor logs actually fetching od logs

* chore: switch from `mime-types` to `mime` for browser environment support (#2951)

* change V2 s9pk title to Legacy

* show warning for domains when not public, disable launch too

---------

Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Mariusz Kogen <k0gen@pm.me>
This commit is contained in:
Matt Hill
2025-05-23 10:45:06 -06:00
committed by GitHub
parent b40849f672
commit b1f9f90fec
15 changed files with 49 additions and 59 deletions

View File

@@ -190,7 +190,7 @@ impl TryFrom<ManifestV1> for Manifest {
let default_url = value.upstream_repo.clone();
Ok(Self {
id: value.id,
title: format!("{} (Compatibility Mode)", value.title).into(),
title: format!("{} (Legacy)", value.title).into(),
version: ExtendedVersion::from(
exver::emver::Version::from_str(&value.version)
.with_kind(ErrorKind::Deserialization)?,

View File

@@ -6,7 +6,7 @@ import {
PackageId,
} from "../osBindings"
import { ArrayBufferReader, MerkleArchive } from "./merkleArchive"
import mime from "mime-types"
import mime from "mime"
import { DirectoryContents } from "./merkleArchive/directoryContents"
import { FileContents } from "./merkleArchive/fileContents"
@@ -60,14 +60,13 @@ export class S9pk {
async icon(): Promise<DataUrl> {
const iconName = Object.keys(this.archive.contents.contents).find(
(name) =>
name.startsWith("icon.") &&
(mime.contentType(name) || null)?.startsWith("image/"),
name.startsWith("icon.") && mime.getType(name)?.startsWith("image/"),
)
if (!iconName) {
throw new Error("no icon found in archive")
}
return (
`data:${mime.contentType(iconName)};base64,` +
`data:${mime.getType(iconName)};base64,` +
Buffer.from(
await this.archive.contents.getPath([iconName])!.verifiedFileContents(),
).toString("base64")
@@ -91,12 +90,11 @@ export class S9pk {
if (!dir || !(dir.contents instanceof DirectoryContents)) return null
const iconName = Object.keys(dir.contents.contents).find(
(name) =>
name.startsWith("icon.") &&
(mime.contentType(name) || null)?.startsWith("image/"),
name.startsWith("icon.") && mime.getType(name)?.startsWith("image/"),
)
if (!iconName) return null
return (
`data:${mime.contentType(iconName)};base64,` +
`data:${mime.getType(iconName)};base64,` +
Buffer.from(
await dir.contents.getPath([iconName])!.verifiedFileContents(),
).toString("base64")

View File

@@ -12,14 +12,13 @@
"@noble/hashes": "^1.7.2",
"deep-equality-data-structures": "^1.5.0",
"isomorphic-fetch": "^3.0.0",
"mime-types": "^3.0.1",
"mime": "^4.0.7",
"ts-matches": "^6.3.2",
"yaml": "^2.7.1"
},
"devDependencies": {
"@types/jest": "^29.4.0",
"@types/lodash.merge": "^4.6.2",
"@types/mime-types": "^2.1.4",
"jest": "^29.4.3",
"peggy": "^3.0.2",
"prettier": "^3.2.5",
@@ -1609,13 +1608,6 @@
"@types/lodash": "*"
}
},
"node_modules/@types/mime-types": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
"integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.10.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz",
@@ -3770,25 +3762,19 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"node_modules/mime": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz",
"integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">= 0.6"
"node": ">=16"
}
},
"node_modules/mimic-fn": {

View File

@@ -25,7 +25,7 @@
"@noble/curves": "^1.8.2",
"@noble/hashes": "^1.7.2",
"isomorphic-fetch": "^3.0.0",
"mime-types": "^3.0.1",
"mime": "^4.0.7",
"ts-matches": "^6.3.2",
"yaml": "^2.7.1",
"deep-equality-data-structures": "^1.5.0"
@@ -39,7 +39,6 @@
"devDependencies": {
"@types/jest": "^29.4.0",
"@types/lodash.merge": "^4.6.2",
"@types/mime-types": "^2.1.4",
"jest": "^29.4.3",
"peggy": "^3.0.2",
"prettier": "^3.2.5",

View File

@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { ActivatedRoute, Data } from '@angular/router'
import { ActivatedRoute } from '@angular/router'
import { TuiDialogContext, TuiLoader, TuiNotification } from '@taiga-ui/core'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'

View File

@@ -518,4 +518,5 @@ export default {
516: 'Empfohlen',
517: 'Möchten Sie diese Aufgabe wirklich verwerfen?',
518: 'Verwerfen',
519: 'Um Clearnet-Domains zu veröffentlichen, musst du oben auf „Öffentlich machen“ klicken.',
} satisfies i18n

View File

@@ -517,4 +517,5 @@ export const ENGLISH = {
'Recommended': 516, // as in, we recommend this
'Are you sure you want to dismiss this task?': 517,
'Dismiss': 518, // as in, dismiss or delete a task
'To publish clearnet domains, you must click "Make Public", above.': 519,
} as const

View File

@@ -518,4 +518,5 @@ export default {
516: 'Recomendado',
517: '¿Estás seguro de que deseas descartar esta tarea?',
518: 'Descartar',
519: 'Para publicar dominios en clearnet, debes hacer clic en "Hacer público" arriba.',
} satisfies i18n

View File

@@ -518,4 +518,5 @@ export default {
516: 'Recommandé',
517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?',
518: 'Ignorer',
519: 'Pour publier des domaines clearnet, vous devez cliquer sur « Rendre public » ci-dessus.',
} satisfies i18n

View File

@@ -518,4 +518,5 @@ export default {
516: 'Zalecane',
517: 'Czy na pewno chcesz odrzucić to zadanie?',
518: 'Odrzuć',
519: 'Aby opublikować domeny w clearnet, kliknij „Upublicznij” powyżej.',
} satisfies i18n

View File

@@ -19,6 +19,7 @@ import {
TuiDataList,
TuiIcon,
TuiLink,
TuiNotification,
} from '@taiga-ui/core'
import { TuiTooltip } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
@@ -66,12 +67,25 @@ type ClearnetForm = {
</a>
</ng-template>
@if (clearnet().length) {
<button tuiButton iconStart="@tui.plus" (click)="add()">
<button
tuiButton
iconStart="@tui.plus"
[style.margin-inline-start]="'auto'"
(click)="add()"
>
{{ 'Add' | i18n }}
</button>
}
</header>
@if (clearnet().length) {
@if (!isPublic()) {
<tui-notification appearance="negative" [style.margin-bottom]="'1rem'">
{{
'To publish clearnet domains, you must click "Make Public", above.'
| i18n
}}
</tui-notification>
}
<table [appTable]="['ACME', 'URL', null]">
@for (address of clearnet(); track $index) {
<tr>
@@ -79,7 +93,11 @@ type ClearnetForm = {
{{ interface.value().addSsl ? (address.acme | acme) : '-' }}
</td>
<td>{{ address.url | mask }}</td>
<td actions [href]="address.url" [disabled]="!isRunning()">
<td
actions
[href]="address.url"
[disabled]="!isRunning() || !isPublic()"
>
@if (address.isDomain) {
<button
tuiIconButton
@@ -129,6 +147,7 @@ type ClearnetForm = {
InterfaceActionsComponent,
i18nPipe,
DocsLinkDirective,
TuiNotification,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -143,6 +162,7 @@ export class InterfaceClearnetComponent {
readonly clearnet = input.required<readonly ClearnetAddress[]>()
readonly isRunning = input.required<boolean>()
readonly isPublic = input.required<boolean>()
readonly acme = toSignal(
inject<PatchDB<DataModel>>(PatchDB)

View File

@@ -27,6 +27,7 @@ import { MappedServiceInterface } from './interface.utils'
</button>
<section
[clearnet]="value().addresses.clearnet"
[isPublic]="value().public"
[isRunning]="isRunning()"
></section>
<section [tor]="value().addresses.tor" [isRunning]="isRunning()"></section>

View File

@@ -3,7 +3,6 @@ import { i18nPipe } from '@start9labs/shared'
import { LogsComponent } from 'src/app/routes/portal/components/logs/logs.component'
import { RR } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { LogsHeaderComponent } from '../components/header.component'
@Component({
@@ -31,10 +30,4 @@ export default class SystemKernelComponent {
protected readonly fetch = (params: RR.GetServerLogsReq) =>
this.api.getKernelLogs(params)
log = {
title: 'Kernel Logs',
subtitle: 'Diagnostics for drivers and other kernel processes',
icon: '@tui.square-chevron-right',
}
}

View File

@@ -30,10 +30,4 @@ export default class SystemOSComponent {
protected readonly fetch = (params: RR.GetServerLogsReq) =>
this.api.getServerLogs(params)
log = {
title: 'Kernel Logs',
subtitle: 'Diagnostics for drivers and other kernel processes',
icon: '@tui.square-chevron-right',
}
}

View File

@@ -26,14 +26,8 @@ export default class SystemOSComponent {
private readonly api = inject(ApiService)
protected readonly follow = (params: RR.FollowServerLogsReq) =>
this.api.followServerLogs(params)
this.api.followTorLogs(params)
protected readonly fetch = (params: RR.GetServerLogsReq) =>
this.api.getServerLogs(params)
log = {
title: 'Kernel Logs',
subtitle: 'Diagnostics for drivers and other kernel processes',
icon: '@tui.square-chevron-right',
}
this.api.getTorLogs(params)
}