mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Compare commits
5 Commits
v0.4.0-bet
...
fix/open-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc857e028f | ||
|
|
c03bf166f1 | ||
|
|
9373f40e82 | ||
|
|
f181b9a9f1 | ||
|
|
2ea2879317 |
@@ -156,6 +156,7 @@ export class RpcListener {
|
||||
|
||||
this.unixSocketServer.on("connection", (s) => {
|
||||
let id: IdType = null
|
||||
let lineBuffer = ""
|
||||
const captureId = <X>(x: X) => {
|
||||
if (hasId(x)) id = x.id
|
||||
return x
|
||||
@@ -190,32 +191,31 @@ export class RpcListener {
|
||||
)
|
||||
}
|
||||
}
|
||||
s.on("data", (a) =>
|
||||
Promise.resolve(a)
|
||||
.then((b) => b.toString())
|
||||
.then((buf) => {
|
||||
for (let s of buf.split("\n")) {
|
||||
if (s)
|
||||
Promise.resolve(s)
|
||||
.then(logData("dataIn"))
|
||||
.then(jsonParse)
|
||||
.then(captureId)
|
||||
.then((x) => this.dealWithInput(x))
|
||||
.catch(mapError)
|
||||
.then(logData("response"))
|
||||
.then(writeDataToSocket)
|
||||
.then((_) => {
|
||||
if (this.shouldExit) {
|
||||
process.exit(0)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`Major error in socket handling: ${e}`)
|
||||
console.debug(`Data in: ${a.toString()}`)
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
s.on("data", (a) => {
|
||||
lineBuffer += a.toString()
|
||||
const lines = lineBuffer.split("\n")
|
||||
lineBuffer = lines.pop()! // keep incomplete trailing chunk in buffer
|
||||
for (const line of lines) {
|
||||
if (line)
|
||||
Promise.resolve(line)
|
||||
.then(logData("dataIn"))
|
||||
.then(jsonParse)
|
||||
.then(captureId)
|
||||
.then((x) => this.dealWithInput(x))
|
||||
.catch(mapError)
|
||||
.then(logData("response"))
|
||||
.then(writeDataToSocket)
|
||||
.then((_) => {
|
||||
if (this.shouldExit) {
|
||||
process.exit(0)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`Major error in socket handling: ${e}`)
|
||||
console.debug(`Data in: ${line}`)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
2
core/Cargo.lock
generated
2
core/Cargo.lock
generated
@@ -6476,7 +6476,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "start-os"
|
||||
version = "0.4.0-beta.1"
|
||||
version = "0.4.0-beta.2"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"async-acme",
|
||||
|
||||
@@ -15,7 +15,7 @@ license = "MIT"
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.4.0-beta.1" # VERSION_BUMP
|
||||
version = "0.4.0-beta.2" # VERSION_BUMP
|
||||
|
||||
[lib]
|
||||
name = "startos"
|
||||
|
||||
@@ -134,20 +134,18 @@ pub async fn list_service_interfaces(
|
||||
.expect("valid json pointer");
|
||||
let mut watch = context.seed.ctx.db.watch(ptr).await;
|
||||
|
||||
let Some(res) = from_value(watch.peek_and_mark_seen()?)? else {
|
||||
return Ok(BTreeMap::new());
|
||||
};
|
||||
let res: Option<_> = from_value(watch.peek_and_mark_seen()?)?;
|
||||
|
||||
if let Some(callback) = callback {
|
||||
let callback = callback.register(&context.seed.persistent_container);
|
||||
context.seed.ctx.callbacks.add_list_service_interfaces(
|
||||
package_id.clone(),
|
||||
watch.typed::<BTreeMap<ServiceInterfaceId, ServiceInterface>>(),
|
||||
watch.typed::<Option<BTreeMap<ServiceInterfaceId, ServiceInterface>>>(),
|
||||
CallbackHandler::new(&context, callback),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
Ok(res.unwrap_or_default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)]
|
||||
|
||||
@@ -65,8 +65,9 @@ mod v0_4_0_alpha_22;
|
||||
mod v0_4_0_alpha_23;
|
||||
mod v0_4_0_beta_0;
|
||||
mod v0_4_0_beta_1;
|
||||
mod v0_4_0_beta_2;
|
||||
|
||||
pub type Current = v0_4_0_beta_1::Version; // VERSION_BUMP
|
||||
pub type Current = v0_4_0_beta_2::Version; // VERSION_BUMP
|
||||
|
||||
impl Current {
|
||||
#[instrument(skip(self, db))]
|
||||
@@ -199,7 +200,8 @@ enum Version {
|
||||
V0_4_0_alpha_22(Wrapper<v0_4_0_alpha_22::Version>),
|
||||
V0_4_0_alpha_23(Wrapper<v0_4_0_alpha_23::Version>),
|
||||
V0_4_0_beta_0(Wrapper<v0_4_0_beta_0::Version>),
|
||||
V0_4_0_beta_1(Wrapper<v0_4_0_beta_1::Version>), // VERSION_BUMP
|
||||
V0_4_0_beta_1(Wrapper<v0_4_0_beta_1::Version>),
|
||||
V0_4_0_beta_2(Wrapper<v0_4_0_beta_2::Version>), // VERSION_BUMP
|
||||
Other(exver::Version),
|
||||
}
|
||||
|
||||
@@ -267,7 +269,8 @@ impl Version {
|
||||
Self::V0_4_0_alpha_22(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_23(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_beta_0(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_beta_1(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::V0_4_0_beta_1(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_beta_2(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::Other(v) => {
|
||||
return Err(Error::new(
|
||||
eyre!("unknown version {v}"),
|
||||
@@ -327,7 +330,8 @@ impl Version {
|
||||
Version::V0_4_0_alpha_22(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_23(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_beta_0(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_beta_1(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::V0_4_0_beta_1(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_beta_2(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::Other(x) => x.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
37
core/src/version/v0_4_0_beta_2.rs
Normal file
37
core/src/version/v0_4_0_beta_2.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{VersionT, v0_4_0_beta_1};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_beta_2: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("beta".into()), 2.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_beta_1::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_beta_2.clone()
|
||||
}
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<Value, Error> {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-beta.1",
|
||||
"version": "0.4.0-beta.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-beta.1",
|
||||
"version": "0.4.0-beta.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^21.2.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-beta.1",
|
||||
"version": "0.4.0-beta.2",
|
||||
"author": "Start9 Labs, Inc",
|
||||
"homepage": "https://start9.com/",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -77,10 +77,8 @@ import { DomainHealthService } from './domain-health.service'
|
||||
<span>{{ address.url | tuiObfuscate: 'mask' }}</span>
|
||||
} @else {
|
||||
<span [title]="address.url">
|
||||
@if (urlParts(); as parts) {
|
||||
{{ parts.prefix }}
|
||||
<b>{{ parts.hostname }}</b>
|
||||
{{ parts.suffix }}
|
||||
@if (urlHtml(); as html) {
|
||||
<span [innerHTML]="html"></span>
|
||||
} @else {
|
||||
{{ address.url }}
|
||||
}
|
||||
@@ -246,15 +244,14 @@ export class InterfaceAddressItemComponent {
|
||||
this.address()?.masked && this.currentlyMasked() ? 'mask' : 'none',
|
||||
)
|
||||
|
||||
readonly urlParts = computed(() => {
|
||||
readonly urlHtml = computed(() => {
|
||||
const { url, hostnameInfo } = this.address()
|
||||
const idx = url.indexOf(hostnameInfo.hostname)
|
||||
if (idx === -1) return null
|
||||
return {
|
||||
prefix: url.slice(0, idx),
|
||||
hostname: hostnameInfo.hostname,
|
||||
suffix: url.slice(idx + hostnameInfo.hostname.length),
|
||||
}
|
||||
const prefix = url.slice(0, idx)
|
||||
const hostname = hostnameInfo.hostname
|
||||
const suffix = url.slice(idx + hostname.length)
|
||||
return `${prefix}<b>${hostname}</b>${suffix}`
|
||||
})
|
||||
|
||||
typeAppearance(kind: string): string {
|
||||
|
||||
@@ -291,11 +291,25 @@ export class InterfaceService {
|
||||
matching = mdns.format('urlstring')[0]
|
||||
onLan = true
|
||||
break
|
||||
case 'domain':
|
||||
matching = publicDomains.format('urlstring')[0]
|
||||
break
|
||||
case 'tor':
|
||||
matching = addresses
|
||||
.filter({
|
||||
pluginId: 'tor',
|
||||
})
|
||||
.format('urlstring')[0]
|
||||
break
|
||||
case 'wan-ipv4':
|
||||
matching = wanIp.format('urlstring')[0]
|
||||
break
|
||||
}
|
||||
|
||||
if (matching) return matching
|
||||
if (onLan && bestPrivate) return bestPrivate
|
||||
if (bestPublic) return bestPublic
|
||||
if (bestPrivate) return bestPrivate
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import { InterfaceService } from '../../../components/interfaces/interface.servi
|
||||
tuiChevron
|
||||
tuiDropdownAuto
|
||||
tuiDropdownLimitWidth="fixed"
|
||||
[disabled]="!hasAnyHref()"
|
||||
[tuiDropdown]="content"
|
||||
>
|
||||
{{ 'Open UI' | i18n }}
|
||||
@@ -62,6 +63,7 @@ import { InterfaceService } from '../../../components/interfaces/interface.servi
|
||||
rel="noreferrer"
|
||||
iconEnd="@tui.external-link"
|
||||
[attr.href]="getHref(i)"
|
||||
[class.disabled]="!getHref(i)"
|
||||
(click)="close()"
|
||||
>
|
||||
{{ i.name }}
|
||||
@@ -69,12 +71,13 @@ import { InterfaceService } from '../../../components/interfaces/interface.servi
|
||||
}
|
||||
</tui-data-list>
|
||||
</ng-template>
|
||||
} @else if (interfaces()[0]) {
|
||||
} @else if (interfaces()[0]; as ui) {
|
||||
<button
|
||||
tuiButton
|
||||
appearance="primary-grayscale"
|
||||
iconStart="@tui.external-link"
|
||||
(click)="openUI(interfaces()[0]!)"
|
||||
[disabled]="!getHref(ui)"
|
||||
(click)="openUI(ui)"
|
||||
>
|
||||
{{ 'Open UI' | i18n }}
|
||||
</button>
|
||||
@@ -163,6 +166,10 @@ export class ServiceControlsComponent {
|
||||
),
|
||||
)
|
||||
|
||||
readonly hasAnyHref = computed(() =>
|
||||
this.interfaces().some(i => !!this.getHref(i)),
|
||||
)
|
||||
|
||||
getHref(ui: T.ServiceInterface): string {
|
||||
const host = this.pkg().hosts[ui.addressInfo.hostId]
|
||||
if (!host) return ''
|
||||
@@ -170,6 +177,9 @@ export class ServiceControlsComponent {
|
||||
}
|
||||
|
||||
openUI(ui: T.ServiceInterface) {
|
||||
this.document.defaultView?.open(this.getHref(ui), '_blank', 'noreferrer')
|
||||
const href = this.getHref(ui)
|
||||
if (href) {
|
||||
this.document.defaultView?.open(href, '_blank', 'noreferrer')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1224,8 +1224,8 @@ export class MockApiService extends ApiService {
|
||||
},
|
||||
'p2p-interface': {
|
||||
name: 'P2P Interface',
|
||||
result: 'waiting',
|
||||
message: 'Chain State',
|
||||
result: 'success',
|
||||
message: null,
|
||||
},
|
||||
'rpc-interface': {
|
||||
name: 'RPC Interface',
|
||||
|
||||
@@ -497,6 +497,21 @@ export const mockPatchData: DataModel = {
|
||||
suffix: '',
|
||||
},
|
||||
},
|
||||
'admin-ui': {
|
||||
id: 'admin-ui',
|
||||
masked: false,
|
||||
name: 'Admin UI',
|
||||
description: 'An admin panel for managing your Bitcoin node',
|
||||
type: 'ui',
|
||||
addressInfo: {
|
||||
username: null,
|
||||
hostId: 'abcdefg',
|
||||
internalPort: 80,
|
||||
scheme: 'http',
|
||||
sslScheme: 'https',
|
||||
suffix: '/admin',
|
||||
},
|
||||
},
|
||||
rpc: {
|
||||
id: 'rpc',
|
||||
masked: true,
|
||||
|
||||
Reference in New Issue
Block a user