mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 20:43:41 +00:00
Compare commits
11 Commits
v0.4.0-alp
...
feature/de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc9db9f2b7 | ||
|
|
7210f43f50 | ||
|
|
df636b7a78 | ||
|
|
10c14b4d0a | ||
|
|
1bf610a853 | ||
|
|
b4d82b82a9 | ||
|
|
7c5ba45f6a | ||
|
|
f83df5682c | ||
|
|
bfdab897ab | ||
|
|
29c97fcbb0 | ||
|
|
e7847d0e88 |
100
.github/workflows/start-tunnel.yaml
vendored
100
.github/workflows/start-tunnel.yaml
vendored
@@ -1,100 +0,0 @@
|
||||
name: Start-Tunnel
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
type: choice
|
||||
description: Environment
|
||||
options:
|
||||
- NONE
|
||||
- dev
|
||||
- unstable
|
||||
- dev-unstable
|
||||
runner:
|
||||
type: choice
|
||||
description: Runner
|
||||
options:
|
||||
- standard
|
||||
- fast
|
||||
arch:
|
||||
type: choice
|
||||
description: Architecture
|
||||
options:
|
||||
- ALL
|
||||
- x86_64
|
||||
- aarch64
|
||||
- riscv64
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- next/*
|
||||
|
||||
env:
|
||||
NODEJS_VERSION: "24.11.0"
|
||||
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev''))[github.event.inputs.environment == ''NONE''] }}'
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
name: Build Debian Package
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
arch: >-
|
||||
${{
|
||||
fromJson('{
|
||||
"x86_64": ["x86_64"],
|
||||
"aarch64": ["aarch64"],
|
||||
"riscv64": ["riscv64"],
|
||||
"ALL": ["x86_64", "aarch64", "riscv64"]
|
||||
}')[github.event.inputs.platform || 'ALL']
|
||||
}}
|
||||
runs-on: ${{ fromJson('["ubuntu-latest", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||
steps:
|
||||
- name: Cleaning up unnecessary files
|
||||
run: |
|
||||
sudo apt-get remove --purge -y google-chrome-stable firefox mono-devel
|
||||
sudo apt-get autoremove -y
|
||||
sudo apt-get clean
|
||||
|
||||
- run: |
|
||||
sudo mount -t tmpfs tmpfs .
|
||||
if: ${{ github.event.inputs.runner == 'fast' }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODEJS_VERSION }}
|
||||
|
||||
- name: Set up docker QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Configure sccache
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
|
||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||
|
||||
- name: Make
|
||||
run: make tunnel-deb
|
||||
env:
|
||||
PLATFORM: ${{ matrix.arch }}
|
||||
SCCACHE_GHA_ENABLED: on
|
||||
SCCACHE_GHA_VERSION: 0
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: start-tunnel_${{ matrix.arch }}.deb
|
||||
path: results/start-tunnel-*_${{ matrix.arch }}.deb
|
||||
17
.github/workflows/startos-iso.yaml
vendored
17
.github/workflows/startos-iso.yaml
vendored
@@ -67,13 +67,8 @@ jobs:
|
||||
"ALL": ["x86_64", "aarch64"]
|
||||
}')[github.event.inputs.platform || 'ALL']
|
||||
}}
|
||||
runs-on: ${{ fromJson('["ubuntu-latest", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||
runs-on: ${{ fromJson('["ubuntu-22.04", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||
steps:
|
||||
- name: Cleaning up unnecessary files
|
||||
run: |
|
||||
sudo apt-get remove --purge -y google-chrome-stable firefox mono-devel
|
||||
sudo apt-get autoremove -y
|
||||
sudo apt-get clean
|
||||
- run: |
|
||||
sudo mount -t tmpfs tmpfs .
|
||||
if: ${{ github.event.inputs.runner == 'fast' }}
|
||||
@@ -107,6 +102,12 @@ jobs:
|
||||
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
|
||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||
|
||||
- name: Use Beta Toolchain
|
||||
run: rustup default beta
|
||||
|
||||
- name: Setup Cross
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross
|
||||
|
||||
- name: Make
|
||||
run: make ARCH=${{ matrix.arch }} compiled-${{ matrix.arch }}.tar
|
||||
env:
|
||||
@@ -139,7 +140,7 @@ jobs:
|
||||
${{
|
||||
fromJson(
|
||||
format(
|
||||
'["ubuntu-latest", "{0}"]',
|
||||
'["ubuntu-22.04", "{0}"]',
|
||||
fromJson('{
|
||||
"x86_64": "buildjet-8vcpu-ubuntu-2204",
|
||||
"x86_64-nonfree": "buildjet-8vcpu-ubuntu-2204",
|
||||
@@ -272,7 +273,7 @@ jobs:
|
||||
index:
|
||||
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||
needs: [image]
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: >-
|
||||
curl "https://${{
|
||||
|
||||
8
.github/workflows/test.yaml
vendored
8
.github/workflows/test.yaml
vendored
@@ -17,7 +17,7 @@ env:
|
||||
jobs:
|
||||
test:
|
||||
name: Run Automated Tests
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -27,5 +27,11 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ env.NODEJS_VERSION }}
|
||||
|
||||
- name: Use Beta Toolchain
|
||||
run: rustup default beta
|
||||
|
||||
- name: Setup Cross
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross
|
||||
|
||||
- name: Build And Run Tests
|
||||
run: make test
|
||||
|
||||
6
Makefile
6
Makefile
@@ -40,6 +40,7 @@ STARTOS_TARGETS := $(STARTD_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_
|
||||
fi')
|
||||
REGISTRY_TARGETS := core/target/$(RUST_ARCH)-unknown-linux-musl/$(PROFILE)/registrybox core/startos/start-registryd.service
|
||||
TUNNEL_TARGETS := core/target/$(RUST_ARCH)-unknown-linux-musl/$(PROFILE)/tunnelbox core/startos/start-tunneld.service
|
||||
REBUILD_TYPES = 1
|
||||
|
||||
ifeq ($(REMOTE),)
|
||||
mkdir = mkdir -p $1
|
||||
@@ -62,7 +63,7 @@ endif
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: all metadata install clean format cli uis ui reflash deb $(IMAGE_TYPE) squashfs wormhole wormhole-deb test test-core test-sdk test-container-runtime registry install-registry tunnel install-tunnel ts-bindings
|
||||
.PHONY: all metadata install clean format cli uis ui reflash deb $(IMAGE_TYPE) squashfs wormhole wormhole-deb test test-core test-sdk test-container-runtime registry install-registry tunnel install-tunnel
|
||||
|
||||
all: $(STARTOS_TARGETS)
|
||||
|
||||
@@ -276,9 +277,10 @@ container-runtime/node_modules/.package-lock.json: container-runtime/package-loc
|
||||
npm --prefix container-runtime ci
|
||||
touch container-runtime/node_modules/.package-lock.json
|
||||
|
||||
ts-bindings: core/startos/bindings/index.ts
|
||||
sdk/base/lib/osBindings/index.ts: $(shell if [ "$(REBUILD_TYPES)" -ne 0 ]; then echo core/startos/bindings/index.ts; fi)
|
||||
mkdir -p sdk/base/lib/osBindings
|
||||
rsync -ac --delete core/startos/bindings/ sdk/base/lib/osBindings/
|
||||
touch sdk/base/lib/osBindings/index.ts
|
||||
|
||||
core/startos/bindings/index.ts: $(call ls-files, core) $(ENVIRONMENT_FILE)
|
||||
rm -rf core/startos/bindings
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
# StartTunnel
|
||||
|
||||
A self-hosted WireGuard VPN optimized for creating VLANs and reverse tunneling to personal servers.
|
||||
A self-hosted Wiregaurd VPN optimized for creating VLANs and reverse tunneling to personal servers.
|
||||
|
||||
You can think of StartTunnel as "virtual router in the cloud".
|
||||
You can think of StartTunnel as "virtual router in the cloud"
|
||||
|
||||
Use it for private remote access to self-hosted services running on a personal server, or to expose self-hosted services to the public Internet without revealing the host server's IP address.
|
||||
|
||||
## Features
|
||||
|
||||
- **Create Subnets**: Each subnet creates a private, virtual local area network (VLAN), similar to the LAN created by a home router.
|
||||
|
||||
- **Add Devices**: When you add a device (server, phone, laptop) to a subnet, it receives a LAN IP address on that subnet as well as a unique WireGuard config that must be copied, downloaded, or scanned into the device.
|
||||
|
||||
- **Forward Ports**: Forwarding a port creates a "reverse tunnel", exposing a specific port on a specific device to the public Internet.
|
||||
Use it for private, remote access, to self-hosted services running on a personal server, or to expose self-hosted services to the public Internet without revealing the host server's IP address.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -27,7 +19,7 @@ Use it for private remote access to self-hosted services running on a personal s
|
||||
1. Rent a low cost VPS. For most use cases, the cheapest option should be enough.
|
||||
|
||||
- It must have a dedicated public IP address.
|
||||
- For compute (CPU), memory (RAM), and storage (disk), choose the minimum spec.
|
||||
- For (CPU), memory (RAM), and storage (disk), choose the minimum spec.
|
||||
- For transfer (bandwidth), it depends on (1) your use case and (2) your home Internet's _upload_ speed. Even if you intend to serve large files or stream content from your server, there is no reason to pay for speeds that exceed your home Internet's upload speed.
|
||||
|
||||
1. Provision the VPS with the latest version of Debian.
|
||||
@@ -37,17 +29,11 @@ Use it for private remote access to self-hosted services running on a personal s
|
||||
1. Install StartTunnel:
|
||||
|
||||
```sh
|
||||
TMP_DIR=$(mktemp -d) && (cd $TMP_DIR && wget https://github.com/Start9Labs/start-os/releases/download/v0.4.0-alpha.12/start-tunnel-0.4.0-alpha.12-unknown.dev_$(uname -m).deb && apt-get install -y ./start-tunnel-0.4.0-alpha.12-unknown.dev_$(uname -m).deb) && rm -rf $TMP_DIR && systemctl start start-tunneld && echo "Installation Succeeded"
|
||||
TMP_DIR=$(mktemp -d) && (cd $TMP_DIR && wget https://github.com/Start9Labs/start-os/releases/download/v0.4.0-alpha.12/start-tunnel-0.4.0-alpha.12-68f401b_$(uname -m).deb && apt-get install -y ./start-tunnel-0.4.0-alpha.12-68f401b_$(uname -m).deb) && rm -rf $TMP_DIR && systemctl start start-tunneld && echo "Installation Succeeded"
|
||||
```
|
||||
|
||||
5. [Initialize the web interface](#web-interface) (recommended)
|
||||
|
||||
## Updating
|
||||
|
||||
```sh
|
||||
TMP_DIR=$(mktemp -d) && (cd $TMP_DIR && wget https://github.com/Start9Labs/start-os/releases/download/v0.4.0-alpha.12/start-tunnel-0.4.0-alpha.12-unknown.dev_$(uname -m).deb && apt-get install --reinstall -y ./start-tunnel-0.4.0-alpha.12-unknown.dev_$(uname -m).deb) && rm -rf $TMP_DIR && systemctl daemon-reload && systemctl restart start-tunneld && echo "Update Succeeded"
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
By default, StartTunnel is managed via the `start-tunnel` command line interface, which is self-documented.
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
# StartOS Version Bump Guide
|
||||
|
||||
This document explains how to bump the StartOS version across the entire codebase.
|
||||
|
||||
## Overview
|
||||
|
||||
When bumping from version `X.Y.Z-alpha.N` to `X.Y.Z-alpha.N+1`, you need to update files in multiple locations across the repository. The `// VERSION_BUMP` comment markers indicate where changes are needed.
|
||||
|
||||
## Files to Update
|
||||
|
||||
### 1. Core Rust Crate Version
|
||||
|
||||
**File: `core/startos/Cargo.toml`**
|
||||
|
||||
Update the version string (line ~18):
|
||||
|
||||
```toml
|
||||
version = "0.4.0-alpha.15" # VERSION_BUMP
|
||||
```
|
||||
|
||||
**File: `core/Cargo.lock`**
|
||||
|
||||
This file is auto-generated. After updating `Cargo.toml`, run:
|
||||
|
||||
```bash
|
||||
cd core
|
||||
cargo check
|
||||
```
|
||||
|
||||
This will update the version in `Cargo.lock` automatically.
|
||||
|
||||
### 2. Create New Version Migration Module
|
||||
|
||||
**File: `core/startos/src/version/vX_Y_Z_alpha_N+1.rs`**
|
||||
|
||||
Create a new version file by copying the previous version and updating:
|
||||
|
||||
```rust
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{VersionT, v0_4_0_alpha_14}; // Update to previous version
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_15: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("alpha".into()), 15.into()] // Update number
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_alpha_14::Version; // Update to previous version
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_alpha_15.clone() // Update version name
|
||||
}
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<Value, Error> {
|
||||
// Add migration logic here if needed
|
||||
Ok(Value::Null)
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
// Add rollback logic here if needed
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Update Version Module Registry
|
||||
|
||||
**File: `core/startos/src/version/mod.rs`**
|
||||
|
||||
Make changes in **5 locations**:
|
||||
|
||||
#### Location 1: Module Declaration (~line 57)
|
||||
|
||||
Add the new module after the previous version:
|
||||
|
||||
```rust
|
||||
mod v0_4_0_alpha_14;
|
||||
mod v0_4_0_alpha_15; // Add this
|
||||
```
|
||||
|
||||
#### Location 2: Current Type Alias (~line 59)
|
||||
|
||||
Update the `Current` type and move the `// VERSION_BUMP` comment:
|
||||
|
||||
```rust
|
||||
pub type Current = v0_4_0_alpha_15::Version; // VERSION_BUMP
|
||||
```
|
||||
|
||||
#### Location 3: Version Enum (~line 175)
|
||||
|
||||
Remove `// VERSION_BUMP` from the previous version, add new variant, add comment:
|
||||
|
||||
```rust
|
||||
V0_4_0_alpha_14(Wrapper<v0_4_0_alpha_14::Version>),
|
||||
V0_4_0_alpha_15(Wrapper<v0_4_0_alpha_15::Version>), // VERSION_BUMP
|
||||
Other(exver::Version),
|
||||
```
|
||||
|
||||
#### Location 4: as_version_t() Match (~line 233)
|
||||
|
||||
Remove `// VERSION_BUMP`, add new match arm, add comment:
|
||||
|
||||
```rust
|
||||
Self::V0_4_0_alpha_14(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_15(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::Other(v) => {
|
||||
```
|
||||
|
||||
#### Location 5: as_exver() Match (~line 284, inside #[cfg(test)])
|
||||
|
||||
Remove `// VERSION_BUMP`, add new match arm, add comment:
|
||||
|
||||
```rust
|
||||
Version::V0_4_0_alpha_14(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_15(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::Other(x) => x.clone(),
|
||||
```
|
||||
|
||||
### 4. SDK TypeScript Version
|
||||
|
||||
**File: `sdk/package/lib/StartSdk.ts`**
|
||||
|
||||
Update the OSVersion constant (~line 64):
|
||||
|
||||
```typescript
|
||||
export const OSVersion = testTypeVersion("0.4.0-alpha.15");
|
||||
```
|
||||
|
||||
### 5. Web UI Package Version
|
||||
|
||||
**File: `web/package.json`**
|
||||
|
||||
Update the version field:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.15",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**File: `web/package-lock.json`**
|
||||
|
||||
This file is auto-generated, but it's faster to update manually. Find all instances of "startos-ui" and update the version field.
|
||||
|
||||
## Verification Step
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
## VERSION_BUMP Comment Pattern
|
||||
|
||||
The `// VERSION_BUMP` comment serves as a marker for where to make changes next time:
|
||||
|
||||
- Always **remove** it from the old location
|
||||
- **Add** the new version entry
|
||||
- **Move** the comment to mark the new location
|
||||
|
||||
This pattern helps you quickly find all the places that need updating in the next version bump.
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
- [ ] Update `core/startos/Cargo.toml` version
|
||||
- [ ] Create new `core/startos/src/version/vX_Y_Z_alpha_N+1.rs` file
|
||||
- [ ] Update `core/startos/src/version/mod.rs` in 5 locations
|
||||
- [ ] Run `cargo check` to update `core/Cargo.lock`
|
||||
- [ ] Update `sdk/package/lib/StartSdk.ts` OSVersion
|
||||
- [ ] Update `web/package.json` and `web/package-lock.json` version
|
||||
- [ ] Verify all changes compile/build successfully
|
||||
|
||||
## Migration Logic
|
||||
|
||||
The `up()` and `down()` methods in the version file handle database migrations:
|
||||
|
||||
- **up()**: Migrates the database from the previous version to this version
|
||||
- **down()**: Rolls back from this version to the previous version
|
||||
- **pre_up()**: Runs before migration, useful for pre-migration checks or data gathering
|
||||
|
||||
If no migration is needed, return `Ok(Value::Null)` for `up()` and `Ok(())` for `down()`.
|
||||
|
||||
For complex migrations, you may need to:
|
||||
|
||||
1. Update `type PreUpRes` to pass data between `pre_up()` and `up()`
|
||||
2. Implement database transformations in the `up()` method
|
||||
3. Implement reverse transformations in `down()` for rollback support
|
||||
@@ -1,3 +1,4 @@
|
||||
- grub-common
|
||||
- grub-efi
|
||||
+ parted
|
||||
+ raspberrypi-net-mods
|
||||
|
||||
@@ -95,7 +95,6 @@ if [ "$CHROOT_RES" -eq 0 ]; then
|
||||
|
||||
echo 'Upgrading...'
|
||||
|
||||
rm -f /media/startos/images/next.squashfs
|
||||
if ! time mksquashfs /media/startos/next /media/startos/images/next.squashfs -b 4096 -comp gzip; then
|
||||
umount -l /media/startos/next
|
||||
umount -l /media/startos/upper
|
||||
|
||||
@@ -25,5 +25,5 @@ apply_rule PREROUTING -p tcp -d $sip --dport $sport -j DNAT --to-destination $di
|
||||
apply_rule OUTPUT -p tcp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
|
||||
if [ "$UNDO" = 1 ]; then
|
||||
conntrack -D -p tcp -d $sip --dport $sport || true # conntrack returns exit 1 if no connections are active
|
||||
conntrack -D -p tcp -d $sip --dport $sport
|
||||
fi
|
||||
@@ -61,7 +61,7 @@ fi
|
||||
|
||||
chroot /media/startos/next bash -e << "EOF"
|
||||
|
||||
if [ -f /boot/grub/grub.cfg ]; then
|
||||
if dpkg -s grub-common 2>&1 > /dev/null; then
|
||||
grub-install /dev/$(eval $(lsblk -o MOUNTPOINT,PKNAME -P | grep 'MOUNTPOINT="/media/startos/root"') && echo $PKNAME)
|
||||
update-grub
|
||||
fi
|
||||
@@ -70,7 +70,7 @@ EOF
|
||||
|
||||
sync
|
||||
|
||||
umount -Rl /media/startos/next
|
||||
umount -R /media/startos/next
|
||||
umount /media/startos/upper
|
||||
umount /media/startos/lower
|
||||
|
||||
|
||||
2
container-runtime/package-lock.json
generated
2
container-runtime/package-lock.json
generated
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"../sdk/dist": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.44",
|
||||
"version": "0.4.0-beta.43",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^3.0.0",
|
||||
|
||||
@@ -158,8 +158,6 @@ export class RpcListener {
|
||||
|
||||
this.unixSocketServer.listen(SOCKET_PATH)
|
||||
|
||||
console.log("Listening on %s", SOCKET_PATH)
|
||||
|
||||
this.unixSocketServer.on("connection", (s) => {
|
||||
let id: IdType = null
|
||||
const captureId = <X>(x: X) => {
|
||||
|
||||
2
core/Cargo.lock
generated
2
core/Cargo.lock
generated
@@ -7908,7 +7908,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "start-os"
|
||||
version = "0.4.0-alpha.15"
|
||||
version = "0.4.0-alpha.12"
|
||||
dependencies = [
|
||||
"aes 0.7.5",
|
||||
"arti-client",
|
||||
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${ARCH:-}" ]; then
|
||||
@@ -66,6 +61,6 @@ fi
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo zigbuild --manifest-path=./core/Cargo.toml $BUILD_FLAGS --no-default-features --features $FEATURE_ARGS --locked --bin start-cli --target=$TARGET
|
||||
if [ "$(ls -nd "core/target/$TARGET/$PROFILE/start-cli" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
if [ "$(ls -nd "core/target/$TARGET/release/start-cli" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
rust-zig-builder sh -c "cd core && chown -R $UID:$UID target && chown -R $UID:$UID /root/.cargo"
|
||||
fi
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
@@ -41,6 +36,6 @@ fi
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo zigbuild --manifest-path=./core/Cargo.toml $BUILD_FLAGS --no-default-features --features cli-container,$FEATURES --locked --bin containerbox --target=$RUST_ARCH-unknown-linux-musl
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/$PROFILE/containerbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/release/containerbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
rust-zig-builder sh -c "chown -R $UID:$UID core/target && chown -R $UID:$UID /root/.cargo"
|
||||
fi
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
@@ -41,6 +36,6 @@ fi
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo zigbuild --manifest-path=./core/Cargo.toml $BUILD_FLAGS --no-default-features --features cli-registry,registry,$FEATURES --locked --bin registrybox --target=$RUST_ARCH-unknown-linux-musl
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/$PROFILE/registrybox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/release/registrybox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
rust-zig-builder sh -c "chown -R $UID:$UID core/target && chown -R $UID:$UID /root/.cargo"
|
||||
fi
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
@@ -41,6 +36,6 @@ fi
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo zigbuild --manifest-path=./core/Cargo.toml $BUILD_FLAGS --no-default-features --features cli,startd,$FEATURES --locked --bin startbox --target=$RUST_ARCH-unknown-linux-musl
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/$PROFILE/startbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/release/startbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
rust-zig-builder sh -c "chown -R $UID:$UID core/target && chown -R $UID:$UID /root/.cargo"
|
||||
fi
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
|
||||
@@ -10,11 +10,6 @@ shopt -s expand_aliases
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
@@ -41,6 +36,6 @@ fi
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo zigbuild --manifest-path=./core/Cargo.toml $BUILD_FLAGS --no-default-features --features cli-tunnel,tunnel,$FEATURES --locked --bin tunnelbox --target=$RUST_ARCH-unknown-linux-musl
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/$PROFILE/tunnelbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
if [ "$(ls -nd "core/target/$RUST_ARCH-unknown-linux-musl/release/tunnelbox" | awk '{ print $3 }')" != "$UID" ]; then
|
||||
rust-zig-builder sh -c "chown -R $UID:$UID core/target && chown -R $UID:$UID /root/.cargo"
|
||||
fi
|
||||
@@ -5,4 +5,4 @@ if tty -s; then
|
||||
USE_TTY="-it"
|
||||
fi
|
||||
|
||||
alias 'rust-zig-builder'='docker run '"$USE_TTY"' --rm -e "RUSTFLAGS=$RUSTFLAGS" -e "AWS_LC_SYS_CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_musl=/root/cmake-overrides/toolchain-riscv64-musl-clang.cmake" -e SCCACHE_GHA_ENABLED -e SCCACHE_GHA_VERSION -e ACTIONS_RESULTS_URL -e ACTIONS_RUNTIME_TOKEN -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$HOME/.cache/sccache":/root/.cache/sccache -v "$(pwd)":/workdir -w /workdir -P start9/cargo-zigbuild'
|
||||
alias 'rust-zig-builder'='docker run '"$USE_TTY"' --rm -e "RUSTFLAGS=$RUSTFLAGS" -e "CFLAGS=-D_FORTIFY_SOURCE=2" -e "CXXFLAGS=-D_FORTIFY_SOURCE=2" -e SCCACHE_GHA_ENABLED -e SCCACHE_GHA_VERSION -e ACTIONS_RESULTS_URL -e ACTIONS_RUNTIME_TOKEN -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$HOME/.cache/sccache":/root/.cache/sccache -v "$(pwd)":/workdir -w /workdir -P start9/cargo-zigbuild'
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
source ./builder-alias.sh
|
||||
|
||||
set -ea
|
||||
shopt -s expand_aliases
|
||||
|
||||
PROFILE=${PROFILE:-release}
|
||||
if [ "${PROFILE}" = "release" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
else
|
||||
if [ "$PROFILE" != "debug"]; then
|
||||
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||
PROFILE=debug
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
@@ -38,8 +31,8 @@ if [[ "${ENVIRONMENT}" =~ (^|-)console($|-) ]]; then
|
||||
RUSTFLAGS="--cfg tokio_unstable"
|
||||
fi
|
||||
|
||||
source ./core/builder-alias.sh
|
||||
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-zig-builder cargo test --manifest-path=./core/Cargo.toml $BUILD_FLAGS --features=test,$FEATURES --workspace --locked -- --skip export_bindings_
|
||||
rust-zig-builder sh -c "chown -R $UID:$UID core/target && chown -R $UID:$UID /root/.cargo"
|
||||
cross test --manifest-path=./core/Cargo.toml $BUILD_FLAGS --features=test,$FEATURES --workspace --locked --target=$ARCH-unknown-linux-musl -- --skip export_bindings_
|
||||
@@ -15,7 +15,7 @@ license = "MIT"
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.4.0-alpha.15" # VERSION_BUMP
|
||||
version = "0.4.0-alpha.12" # VERSION_BUMP
|
||||
|
||||
[lib]
|
||||
name = "startos"
|
||||
|
||||
@@ -260,7 +260,11 @@ impl NetworkInterfaceInfo {
|
||||
}
|
||||
|
||||
pub fn secure(&self) -> bool {
|
||||
self.secure.unwrap_or(false)
|
||||
self.secure.unwrap_or_else(|| {
|
||||
self.ip_info.as_ref().map_or(false, |ip_info| {
|
||||
ip_info.device_type == Some(NetworkInterfaceType::Wireguard)
|
||||
}) && !self.public()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ use crate::registry::context::{RegistryContext, RegistryUrlParams};
|
||||
use crate::registry::package::get::GetPackageResponse;
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::s9pk::v2::SIG_CONTEXT;
|
||||
use crate::upload::upload;
|
||||
use crate::util::Never;
|
||||
use crate::util::io::open_file;
|
||||
@@ -155,8 +154,6 @@ pub async fn install(
|
||||
})?
|
||||
.s9pk;
|
||||
|
||||
asset.validate(SIG_CONTEXT, asset.all_signers())?;
|
||||
|
||||
let progress_tracker = FullProgressTracker::new();
|
||||
let download_progress = progress_tracker.add_phase("Downloading".into(), Some(100));
|
||||
let download = ctx
|
||||
|
||||
@@ -366,7 +366,6 @@ impl LxcContainer {
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
tracing::info!("Connected to socket in {:?}", started.elapsed());
|
||||
Ok(UnixRpcClient::new(sock_path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,54 +438,60 @@ async fn watcher(
|
||||
loop {
|
||||
until
|
||||
.run(async {
|
||||
let devices = netman_proxy.all_devices().await?;
|
||||
ensure_code!(
|
||||
!devices.is_empty(),
|
||||
ErrorKind::Network,
|
||||
"NetworkManager returned no devices. Trying again..."
|
||||
);
|
||||
let mut ifaces = BTreeSet::new();
|
||||
let mut jobs = Vec::new();
|
||||
for device in devices {
|
||||
use futures::future::Either;
|
||||
|
||||
let device_proxy =
|
||||
device::DeviceProxy::new(&connection, device.clone()).await?;
|
||||
let iface = InternedString::intern(device_proxy.ip_interface().await?);
|
||||
if iface.is_empty() {
|
||||
loop {
|
||||
let devices = netman_proxy.all_devices().await?;
|
||||
if devices.is_empty() {
|
||||
tracing::warn!(
|
||||
"NetworkManager returned no devices. Trying again..."
|
||||
);
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
let iface: GatewayId = iface.into();
|
||||
if watch_activation.peek(|a| a.contains_key(&iface)) {
|
||||
jobs.push(Either::Left(watch_activated(
|
||||
let mut ifaces = BTreeSet::new();
|
||||
let mut jobs = Vec::new();
|
||||
for device in devices {
|
||||
use futures::future::Either;
|
||||
|
||||
let device_proxy =
|
||||
device::DeviceProxy::new(&connection, device.clone()).await?;
|
||||
let iface =
|
||||
InternedString::intern(device_proxy.ip_interface().await?);
|
||||
if iface.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let iface: GatewayId = iface.into();
|
||||
if watch_activation.peek(|a| a.contains_key(&iface)) {
|
||||
jobs.push(Either::Left(watch_activated(
|
||||
&connection,
|
||||
device_proxy.clone(),
|
||||
iface.clone(),
|
||||
&watch_activation,
|
||||
)));
|
||||
}
|
||||
|
||||
jobs.push(Either::Right(watch_ip(
|
||||
&connection,
|
||||
device_proxy.clone(),
|
||||
iface.clone(),
|
||||
&watch_activation,
|
||||
&watch_ip_info,
|
||||
)));
|
||||
ifaces.insert(iface);
|
||||
}
|
||||
|
||||
jobs.push(Either::Right(watch_ip(
|
||||
&connection,
|
||||
device_proxy.clone(),
|
||||
iface.clone(),
|
||||
&watch_ip_info,
|
||||
)));
|
||||
ifaces.insert(iface);
|
||||
}
|
||||
|
||||
watch_ip_info.send_if_modified(|m| {
|
||||
let mut changed = false;
|
||||
for (iface, info) in OrdMapIterMut::from(m) {
|
||||
if !ifaces.contains(iface) {
|
||||
info.ip_info = None;
|
||||
changed = true;
|
||||
watch_ip_info.send_if_modified(|m| {
|
||||
let mut changed = false;
|
||||
for (iface, info) in OrdMapIterMut::from(m) {
|
||||
if !ifaces.contains(iface) {
|
||||
info.ip_info = None;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
changed
|
||||
});
|
||||
futures::future::try_join_all(jobs).await?;
|
||||
changed
|
||||
});
|
||||
futures::future::try_join_all(jobs).await?;
|
||||
|
||||
break;
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -240,16 +240,12 @@ impl CertPair {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn root_ca_start_time() -> SystemTime {
|
||||
if check_time_is_synchronized()
|
||||
.await
|
||||
.log_err()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
pub async fn root_ca_start_time() -> Result<SystemTime, Error> {
|
||||
Ok(if check_time_is_synchronized().await? {
|
||||
SystemTime::now()
|
||||
} else {
|
||||
*SOURCE_DATE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1;
|
||||
|
||||
@@ -217,15 +217,10 @@ where
|
||||
.write_all(&buffered)
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?;
|
||||
let stream = match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(stream) => Box::pin(stream) as AcceptStream,
|
||||
Err(e) => {
|
||||
tracing::trace!("Error completing TLS handshake: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
return Ok(Some((metadata, stream)));
|
||||
return Ok(Some((
|
||||
metadata,
|
||||
Box::pin(mid.into_stream(Arc::new(cfg)).await?) as AcceptStream,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
|
||||
@@ -649,6 +649,16 @@ async fn torctl(
|
||||
.invoke(ErrorKind::Tor)
|
||||
.await?;
|
||||
|
||||
let logs = journalctl(
|
||||
LogSource::Unit(SYSTEMD_UNIT),
|
||||
Some(0),
|
||||
None,
|
||||
Some("0"),
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tcp_stream = None;
|
||||
for _ in 0..60 {
|
||||
if let Ok(conn) = TcpStream::connect(tor_control).await {
|
||||
@@ -710,7 +720,7 @@ async fn torctl(
|
||||
ErrorKind::Tor,
|
||||
));
|
||||
}
|
||||
Ok(connection)
|
||||
Ok((connection, logs))
|
||||
};
|
||||
let pre_handler = async {
|
||||
while let Some(command) = recv.recv().await {
|
||||
@@ -735,7 +745,7 @@ async fn torctl(
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut connection = tokio::select! {
|
||||
let (mut connection, mut logs) = tokio::select! {
|
||||
res = bootstrap => res?,
|
||||
res = pre_handler => return res,
|
||||
};
|
||||
@@ -841,59 +851,46 @@ async fn torctl(
|
||||
Ok(())
|
||||
};
|
||||
let log_parser = async {
|
||||
loop {
|
||||
let mut logs = journalctl(
|
||||
LogSource::Unit(SYSTEMD_UNIT),
|
||||
Some(0),
|
||||
None,
|
||||
Some("0"),
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
while let Some(log) = logs.try_next().await? {
|
||||
for (regex, severity) in &*LOG_REGEXES {
|
||||
if regex.is_match(&log.message) {
|
||||
let (check, wipe_state) = match severity {
|
||||
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
|
||||
ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state),
|
||||
};
|
||||
let addr = hck_key.public().get_onion_address().to_string();
|
||||
if !check
|
||||
|| TcpStream::connect(tor_socks)
|
||||
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||
.and_then(|mut tor_socks| async move {
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(30),
|
||||
socks5_impl::client::connect(
|
||||
&mut tor_socks,
|
||||
(addr, 80),
|
||||
None,
|
||||
)
|
||||
while let Some(log) = logs.try_next().await? {
|
||||
for (regex, severity) in &*LOG_REGEXES {
|
||||
if regex.is_match(&log.message) {
|
||||
let (check, wipe_state) = match severity {
|
||||
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
|
||||
ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state),
|
||||
};
|
||||
let addr = hck_key.public().get_onion_address().to_string();
|
||||
if !check
|
||||
|| TcpStream::connect(tor_socks)
|
||||
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||
.and_then(|mut tor_socks| async move {
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(30),
|
||||
socks5_impl::client::connect(&mut tor_socks, (addr, 80), None)
|
||||
.map_err(|e| Error::new(e, ErrorKind::Tor)),
|
||||
)
|
||||
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||
.await?
|
||||
})
|
||||
.await
|
||||
.with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down"))
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
if wipe_state {
|
||||
Command::new("systemctl")
|
||||
.arg("stop")
|
||||
.arg("tor")
|
||||
.invoke(ErrorKind::Tor)
|
||||
.await?;
|
||||
tokio::fs::remove_dir_all("/var/lib/tor").await?;
|
||||
}
|
||||
return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor));
|
||||
)
|
||||
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||
.await?
|
||||
})
|
||||
.await
|
||||
.with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down"))
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
if wipe_state {
|
||||
Command::new("systemctl")
|
||||
.arg("stop")
|
||||
.arg("tor")
|
||||
.invoke(ErrorKind::Tor)
|
||||
.await?;
|
||||
tokio::fs::remove_dir_all("/var/lib/tor").await?;
|
||||
}
|
||||
return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Err(Error::new(eyre!("Log stream terminated"), ErrorKind::Tor))
|
||||
Ok(())
|
||||
};
|
||||
let health_checker = async {
|
||||
let mut last_success = Instant::now();
|
||||
@@ -963,23 +960,20 @@ impl TorControl {
|
||||
_thread: tokio::spawn(async move {
|
||||
let wipe_state = AtomicBool::new(false);
|
||||
let mut health_timeout = Duration::from_secs(STARTING_HEALTH_TIMEOUT);
|
||||
loop {
|
||||
if let Err(e) = torctl(
|
||||
tor_control,
|
||||
tor_socks,
|
||||
&mut recv,
|
||||
&mut thread_services,
|
||||
&wipe_state,
|
||||
&mut health_timeout,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!("TorControl : {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
}
|
||||
tracing::info!("Restarting Tor");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
while let Err(e) = torctl(
|
||||
tor_control,
|
||||
tor_socks,
|
||||
&mut recv,
|
||||
&mut thread_services,
|
||||
&wipe_state,
|
||||
&mut health_timeout,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!("{e}: Restarting tor");
|
||||
tracing::debug!("{e:?}");
|
||||
}
|
||||
tracing::info!("TorControl is shut down.")
|
||||
})
|
||||
.into(),
|
||||
send,
|
||||
|
||||
@@ -39,23 +39,6 @@ pub struct AddTunnelParams {
|
||||
public: bool,
|
||||
}
|
||||
|
||||
fn sanitize_config(config: &str) -> String {
|
||||
let mut res = String::with_capacity(config.len());
|
||||
for line in config.lines() {
|
||||
if line
|
||||
.trim()
|
||||
.strip_prefix("AllowedIPs")
|
||||
.map_or(false, |l| l.trim().starts_with("="))
|
||||
{
|
||||
res.push_str("AllowedIPs = 0.0.0.0/0, ::/0");
|
||||
} else {
|
||||
res.push_str(line);
|
||||
}
|
||||
res.push('\n');
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub async fn add_tunnel(
|
||||
ctx: RpcContext,
|
||||
AddTunnelParams {
|
||||
@@ -103,7 +86,7 @@ pub async fn add_tunnel(
|
||||
|
||||
let tmpdir = TmpDir::new().await?;
|
||||
let conf = tmpdir.join(&iface).with_extension("conf");
|
||||
write_file_atomic(&conf, &sanitize_config(&config)).await?;
|
||||
write_file_atomic(&conf, &config).await?;
|
||||
Command::new("nmcli")
|
||||
.arg("connection")
|
||||
.arg("import")
|
||||
|
||||
@@ -353,7 +353,7 @@ impl FullProgressTracker {
|
||||
}
|
||||
}
|
||||
pub fn progress_bar_task(&self, name: &str) -> NonDetachingJoinHandle<()> {
|
||||
let mut stream = self.stream(Some(Duration::from_millis(200)));
|
||||
let mut stream = self.stream(None);
|
||||
let mut bar = PhasedProgressBar::new(name);
|
||||
tokio::spawn(async move {
|
||||
while let Some(progress) = stream.next().await {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -85,26 +84,6 @@ impl RegistryAsset<MerkleArchiveCommitment> {
|
||||
)
|
||||
.await
|
||||
}
|
||||
pub async fn download_to(
|
||||
&self,
|
||||
path: impl AsRef<Path>,
|
||||
client: Client,
|
||||
progress: PhaseProgressTrackerHandle,
|
||||
) -> Result<
|
||||
(
|
||||
S9pk<Section<Arc<BufferedHttpSource>>>,
|
||||
Arc<BufferedHttpSource>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
let source = Arc::new(
|
||||
BufferedHttpSource::with_path(path, client, self.url.clone(), progress).await?,
|
||||
);
|
||||
Ok((
|
||||
S9pk::deserialize(&source, Some(&self.commitment)).await?,
|
||||
source,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufferedHttpSource {
|
||||
@@ -112,19 +91,6 @@ pub struct BufferedHttpSource {
|
||||
file: UploadingFile,
|
||||
}
|
||||
impl BufferedHttpSource {
|
||||
pub async fn with_path(
|
||||
path: impl AsRef<Path>,
|
||||
client: Client,
|
||||
url: Url,
|
||||
progress: PhaseProgressTrackerHandle,
|
||||
) -> Result<Self, Error> {
|
||||
let (mut handle, file) = UploadingFile::with_path(path, progress).await?;
|
||||
let response = client.get(url).send().await?;
|
||||
Ok(Self {
|
||||
_download: tokio::spawn(async move { handle.download(response).await }).into(),
|
||||
file,
|
||||
})
|
||||
}
|
||||
pub async fn new(
|
||||
client: Client,
|
||||
url: Url,
|
||||
@@ -137,9 +103,6 @@ impl BufferedHttpSource {
|
||||
file,
|
||||
})
|
||||
}
|
||||
pub async fn wait_for_buffered(&self) -> Result<(), Error> {
|
||||
self.file.wait_for_complete().await
|
||||
}
|
||||
}
|
||||
impl ArchiveSource for BufferedHttpSource {
|
||||
type FetchReader = <UploadingFile as ArchiveSource>::FetchReader;
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use exver::{ExtendedVersion, VersionRange};
|
||||
use helpers::to_tmp_path;
|
||||
use imbl_value::{InternedString, json};
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use models::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgressTracker, ProgressUnits};
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::device_info::DeviceInfo;
|
||||
use crate::registry::package::index::{PackageIndex, PackageVersionInfo};
|
||||
use crate::s9pk::merkle_archive::source::ArchiveSource;
|
||||
use crate::s9pk::v2::SIG_CONTEXT;
|
||||
use crate::util::VersionString;
|
||||
use crate::util::io::TrackingIO;
|
||||
use crate::util::serde::{WithIoFormat, display_serializable};
|
||||
use crate::util::tui::choose;
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS, ValueEnum,
|
||||
@@ -360,7 +352,8 @@ pub fn display_package_info(
|
||||
info: Value,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(format) = params.format {
|
||||
return display_serializable(format, info);
|
||||
display_serializable(format, info);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(_) = params.rest.id {
|
||||
@@ -394,90 +387,3 @@ pub fn display_package_info(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliDownloadParams {
|
||||
pub id: PackageId,
|
||||
#[arg(long, short = 'v')]
|
||||
#[ts(type = "string | null")]
|
||||
pub target_version: Option<VersionRange>,
|
||||
#[arg(short, long)]
|
||||
pub dest: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub async fn cli_download(
|
||||
ctx: CliContext,
|
||||
CliDownloadParams {
|
||||
ref id,
|
||||
target_version,
|
||||
dest,
|
||||
}: CliDownloadParams,
|
||||
) -> Result<(), Error> {
|
||||
let progress_tracker = FullProgressTracker::new();
|
||||
let mut fetching_progress = progress_tracker.add_phase("Fetching".into(), Some(1));
|
||||
let download_progress = progress_tracker.add_phase("Downloading".into(), Some(100));
|
||||
let mut verify_progress = progress_tracker.add_phase("Verifying".into(), Some(10));
|
||||
|
||||
let progress = progress_tracker.progress_bar_task("Downloading S9PK...");
|
||||
|
||||
fetching_progress.start();
|
||||
let mut res: GetPackageResponse = from_value(
|
||||
ctx.call_remote::<RegistryContext>(
|
||||
"package.get",
|
||||
json!({
|
||||
"id": &id,
|
||||
"targetVersion": &target_version,
|
||||
}),
|
||||
)
|
||||
.await?,
|
||||
)?;
|
||||
let PackageVersionInfo { s9pk, .. } = match res.best.len() {
|
||||
0 => {
|
||||
return Err(Error::new(
|
||||
eyre!(
|
||||
"Could not find a version of {id} that satisfies {}",
|
||||
target_version.unwrap_or(VersionRange::Any)
|
||||
),
|
||||
ErrorKind::NotFound,
|
||||
));
|
||||
}
|
||||
1 => res.best.pop_first().unwrap().1,
|
||||
_ => {
|
||||
let choices = res.best.keys().cloned().collect::<Vec<_>>();
|
||||
let version = choose(
|
||||
&format!("Multiple flavors of {id} available. Choose a version to download:"),
|
||||
&choices,
|
||||
)
|
||||
.await?;
|
||||
res.best.remove(version).unwrap()
|
||||
}
|
||||
};
|
||||
s9pk.validate(SIG_CONTEXT, s9pk.all_signers())?;
|
||||
fetching_progress.complete();
|
||||
|
||||
let dest = dest.unwrap_or_else(|| Path::new(".").join(id).with_extension("s9pk"));
|
||||
let dest_tmp = to_tmp_path(&dest).with_kind(ErrorKind::Filesystem)?;
|
||||
let (mut parsed, source) = s9pk
|
||||
.download_to(&dest_tmp, ctx.client.clone(), download_progress)
|
||||
.await?;
|
||||
if let Some(size) = source.size().await {
|
||||
verify_progress.set_total(size);
|
||||
}
|
||||
verify_progress.set_units(Some(ProgressUnits::Bytes));
|
||||
let mut progress_sink = verify_progress.writer(tokio::io::sink());
|
||||
parsed
|
||||
.serialize(&mut TrackingIO::new(0, &mut progress_sink), true)
|
||||
.await?;
|
||||
progress_sink.into_inner().1.complete();
|
||||
|
||||
source.wait_for_buffered().await?;
|
||||
tokio::fs::rename(dest_tmp, dest).await?;
|
||||
|
||||
progress_tracker.complete();
|
||||
progress.await.unwrap();
|
||||
|
||||
println!("Download Complete");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async, from_fn_async_local};
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
@@ -54,12 +54,6 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
||||
.with_about("List installation candidate package(s)")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"download",
|
||||
from_fn_async_local(get::cli_download)
|
||||
.no_display()
|
||||
.with_about("Download an s9pk"),
|
||||
)
|
||||
.subcommand(
|
||||
"category",
|
||||
category::category_api::<C>()
|
||||
|
||||
@@ -106,9 +106,7 @@ pub struct ExecParams {
|
||||
#[arg(long)]
|
||||
pty_size: Option<TermSize>,
|
||||
#[arg(short, long)]
|
||||
env: Vec<String>,
|
||||
#[arg(long)]
|
||||
env_file: Option<PathBuf>,
|
||||
env: Option<PathBuf>,
|
||||
#[arg(short, long)]
|
||||
workdir: Option<PathBuf>,
|
||||
#[arg(short, long)]
|
||||
@@ -121,7 +119,6 @@ impl ExecParams {
|
||||
fn exec(&self) -> Result<(), Error> {
|
||||
let ExecParams {
|
||||
env,
|
||||
env_file,
|
||||
workdir,
|
||||
user,
|
||||
chroot,
|
||||
@@ -134,15 +131,14 @@ impl ExecParams {
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
};
|
||||
let env_string = if let Some(env_file) = &env_file {
|
||||
std::fs::read_to_string(env_file)
|
||||
let env_string = if let Some(env) = &env {
|
||||
std::fs::read_to_string(env)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("read {env:?}")))?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let env = env_string
|
||||
.lines()
|
||||
.chain(env.iter().map(|l| l.as_str()))
|
||||
.map(|l| l.trim())
|
||||
.filter_map(|l| l.split_once("="))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
@@ -203,7 +199,6 @@ pub fn launch(
|
||||
force_stderr_tty,
|
||||
pty_size,
|
||||
env,
|
||||
env_file,
|
||||
workdir,
|
||||
user,
|
||||
chroot,
|
||||
@@ -299,11 +294,8 @@ pub fn launch(
|
||||
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
||||
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
||||
cmd = cmd.arg("subcontainer").arg("launch-init");
|
||||
for env in env {
|
||||
cmd = cmd.arg("-e").arg(env)
|
||||
}
|
||||
if let Some(env_file) = env_file {
|
||||
cmd = cmd.arg("--env-file").arg(env_file);
|
||||
if let Some(env) = env {
|
||||
cmd = cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd = cmd.arg("--workdir").arg(workdir);
|
||||
@@ -357,11 +349,8 @@ pub fn launch(
|
||||
} else {
|
||||
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
||||
cmd.arg("subcontainer").arg("launch-init");
|
||||
for env in env {
|
||||
cmd.arg("-e").arg(env);
|
||||
}
|
||||
if let Some(env_file) = env_file {
|
||||
cmd.arg("--env-file").arg(env_file);
|
||||
if let Some(env) = env {
|
||||
cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd.arg("--workdir").arg(workdir);
|
||||
@@ -452,7 +441,6 @@ pub fn exec(
|
||||
force_stderr_tty,
|
||||
pty_size,
|
||||
env,
|
||||
env_file,
|
||||
workdir,
|
||||
user,
|
||||
chroot,
|
||||
@@ -556,11 +544,8 @@ pub fn exec(
|
||||
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
||||
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
||||
cmd = cmd.arg("subcontainer").arg("exec-command");
|
||||
for env in env {
|
||||
cmd = cmd.arg("-e").arg(env);
|
||||
}
|
||||
if let Some(env_file) = env_file {
|
||||
cmd = cmd.arg("--env-file").arg(env_file);
|
||||
if let Some(env) = env {
|
||||
cmd = cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd = cmd.arg("--workdir").arg(workdir);
|
||||
@@ -614,11 +599,8 @@ pub fn exec(
|
||||
} else {
|
||||
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
||||
cmd.arg("subcontainer").arg("exec-command");
|
||||
for env in env {
|
||||
cmd.arg("-e").arg(env);
|
||||
}
|
||||
if let Some(env_file) = env_file {
|
||||
cmd.arg("--env-file").arg(env_file);
|
||||
if let Some(env) = env {
|
||||
cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd.arg("--workdir").arg(workdir);
|
||||
|
||||
@@ -885,7 +885,7 @@ pub async fn attach(
|
||||
.arg("start-container")
|
||||
.arg("subcontainer")
|
||||
.arg("exec")
|
||||
.arg("--env-file")
|
||||
.arg("--env")
|
||||
.arg(
|
||||
Path::new("/media/startos/images")
|
||||
.join(image_id)
|
||||
|
||||
@@ -43,7 +43,7 @@ use crate::util::rpc_client::UnixRpcClient;
|
||||
use crate::volume::data_dir;
|
||||
use crate::{ARCH, DATA_DIR, PACKAGE_DATA};
|
||||
|
||||
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServiceState {
|
||||
|
||||
@@ -117,8 +117,6 @@ impl ServiceMap {
|
||||
match Service::load(ctx, id, disposition).await {
|
||||
Ok(s) => *service = s.into(),
|
||||
Err(e) => {
|
||||
tracing::error!("Error loading service: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
let e = ErrorData::from(e);
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
|
||||
@@ -499,7 +499,7 @@ async fn fresh_setup(
|
||||
..
|
||||
}: SetupExecuteProgress,
|
||||
) -> Result<(SetupResult, RpcContext), Error> {
|
||||
let account = AccountInfo::new(start_os_password, root_ca_start_time().await)?;
|
||||
let account = AccountInfo::new(start_os_password, root_ca_start_time().await?)?;
|
||||
let db = ctx.db().await?;
|
||||
let kiosk = Some(kiosk.unwrap_or(true)).filter(|_| &*PLATFORM != "raspberrypi");
|
||||
sync_kiosk(kiosk).await?;
|
||||
|
||||
@@ -3,7 +3,7 @@ use imbl::HashMap;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use patch_db::HasModel;
|
||||
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
@@ -113,12 +113,27 @@ impl AuthContext for TunnelContext {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct SignerInfo {
|
||||
pub name: InternedString,
|
||||
}
|
||||
|
||||
pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
||||
crate::auth::auth::<C, TunnelContext>()
|
||||
ParentHandler::new()
|
||||
.subcommand(
|
||||
"login",
|
||||
from_fn_async(crate::auth::login_impl::<TunnelContext>)
|
||||
.with_metadata("login", Value::Bool(true))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"logout",
|
||||
from_fn_async(crate::auth::logout::<TunnelContext>)
|
||||
.with_metadata("get_session", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Log out of current auth session")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand("set-password", from_fn_async(set_password_rpc).no_cli())
|
||||
.subcommand(
|
||||
"set-password",
|
||||
@@ -158,15 +173,19 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||
use prettytable::*;
|
||||
|
||||
if let Some(format) = params.format {
|
||||
return display_serializable(format, res);
|
||||
}
|
||||
|
||||
let mut table = Table::new();
|
||||
table.add_row(row![bc => "NAME", "KEY"]);
|
||||
for (key, info) in res {
|
||||
table.add_row(row![info.name, key]);
|
||||
}
|
||||
|
||||
table.print_tty(false)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_about("List authorized keys")
|
||||
@@ -175,7 +194,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddKeyParams {
|
||||
pub name: InternedString,
|
||||
@@ -197,7 +216,7 @@ pub async fn add_key(
|
||||
.result
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveKeyParams {
|
||||
pub key: AnyVerifyingKey,
|
||||
@@ -221,7 +240,7 @@ pub async fn list_keys(ctx: TunnelContext) -> Result<HashMap<AnyVerifyingKey, Si
|
||||
ctx.db.peek().await.into_auth_pubkeys().de()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct SetPasswordParams {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use hickory_client::proto::rr::rdata::cert;
|
||||
use imbl_value::{InternedString, json};
|
||||
use itertools::Itertools;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
@@ -11,6 +12,7 @@ use rpc_toolkit::{
|
||||
Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async, from_fn_async_local,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio_rustls::rustls::ServerConfig;
|
||||
use tokio_rustls::rustls::crypto::CryptoProvider;
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
@@ -18,8 +20,7 @@ use tokio_rustls::rustls::server::ClientHello;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::net::ssl::{SANInfo, root_ca_start_time};
|
||||
use crate::net::ssl::SANInfo;
|
||||
use crate::net::tls::TlsHandler;
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
@@ -133,7 +134,7 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"generate-certificate",
|
||||
from_fn_async(generate_certificate)
|
||||
.with_about("Generate a certificate to use for the webserver")
|
||||
.with_about("Generate a self signed certificaet to use for the webserver")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -285,21 +286,11 @@ pub struct GenerateCertParams {
|
||||
pub async fn generate_certificate(
|
||||
ctx: TunnelContext,
|
||||
GenerateCertParams { subject }: GenerateCertParams,
|
||||
) -> Result<Pem<Vec<X509>>, Error> {
|
||||
) -> Result<Pem<X509>, Error> {
|
||||
let saninfo = SANInfo::new(&subject.into_iter().collect());
|
||||
|
||||
let root_key = crate::net::ssl::generate_key()?;
|
||||
let root_cert = crate::net::ssl::make_root_cert(
|
||||
&root_key,
|
||||
&Hostname("start-tunnel".into()),
|
||||
root_ca_start_time().await,
|
||||
)?;
|
||||
let int_key = crate::net::ssl::generate_key()?;
|
||||
let int_cert = crate::net::ssl::make_int_cert((&root_key, &root_cert), &int_key)?;
|
||||
|
||||
let key = crate::net::ssl::generate_key()?;
|
||||
let cert = crate::net::ssl::make_leaf_cert((&int_key, &int_cert), (&key, &saninfo))?;
|
||||
let chain = Pem(vec![cert, int_cert, root_cert]);
|
||||
let cert = crate::net::ssl::make_self_signed((&key, &saninfo))?;
|
||||
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
@@ -307,13 +298,13 @@ pub async fn generate_certificate(
|
||||
.as_certificate_mut()
|
||||
.ser(&Some(TunnelCertData {
|
||||
key: Pem(key),
|
||||
cert: chain.clone(),
|
||||
cert: Pem(vec![cert.clone()]),
|
||||
}))
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
|
||||
Ok(chain)
|
||||
Ok(Pem(cert))
|
||||
}
|
||||
|
||||
pub async fn get_certificate(ctx: TunnelContext) -> Result<Option<Pem<Vec<X509>>>, Error> {
|
||||
@@ -510,12 +501,8 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
let cert = from_value::<Pem<Vec<X509>>>(
|
||||
ctx.call_remote::<TunnelContext>("web.get-certificate", json!({}))
|
||||
.await?,
|
||||
)?
|
||||
.0
|
||||
.pop()
|
||||
.map(Pem)
|
||||
.or_not_found("certificate in chain")?;
|
||||
println!("📝 Root SSL Certificate:");
|
||||
)?;
|
||||
println!("📝 SSL Certificate:");
|
||||
print!("{cert}");
|
||||
|
||||
println!(concat!(
|
||||
@@ -607,7 +594,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
impl std::fmt::Display for Choice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Generate => write!(f, "Generate an SSL certificate"),
|
||||
Self::Generate => write!(f, "Generate a Self Signed Certificate"),
|
||||
Self::Provide => write!(f, "Provide your own certificate and key"),
|
||||
}
|
||||
}
|
||||
@@ -615,7 +602,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
|
||||
let options = vec![Choice::Generate, Choice::Provide];
|
||||
let choice = choose(
|
||||
concat!(
|
||||
"Select whether to generate an SSL certificate ",
|
||||
"Select whether to autogenerate a self-signed SSL certificate ",
|
||||
"or provide your own certificate and key:"
|
||||
),
|
||||
&options,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::env::consts::ARCH;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -19,6 +20,12 @@ use ts_rs::TS;
|
||||
|
||||
use crate::PLATFORM;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::disk::mount::filesystem::MountType;
|
||||
use crate::disk::mount::filesystem::bind::Bind;
|
||||
use crate::disk::mount::filesystem::block_dev::BlockDev;
|
||||
use crate::disk::mount::filesystem::efivarfs::EfiVarFs;
|
||||
use crate::disk::mount::filesystem::overlayfs::OverlayGuard;
|
||||
use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard};
|
||||
use crate::notifications::{NotificationLevel, notify};
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{
|
||||
@@ -269,6 +276,7 @@ async fn maybe_do_update(
|
||||
download_phase.set_total(asset.commitment.size);
|
||||
download_phase.set_units(Some(ProgressUnits::Bytes));
|
||||
let reverify_phase = progress.add_phase("Reverifying File".into(), Some(10));
|
||||
let sync_boot_phase = progress.add_phase("Syncing Boot Files".into(), Some(1));
|
||||
let finalize_phase = progress.add_phase("Finalizing Update".into(), Some(1));
|
||||
|
||||
let start_progress = progress.snapshot();
|
||||
@@ -324,6 +332,7 @@ async fn maybe_do_update(
|
||||
prune_phase,
|
||||
download_phase,
|
||||
reverify_phase,
|
||||
sync_boot_phase,
|
||||
finalize_phase,
|
||||
},
|
||||
)
|
||||
@@ -380,6 +389,7 @@ struct UpdateProgressHandles {
|
||||
prune_phase: PhaseProgressTrackerHandle,
|
||||
download_phase: PhaseProgressTrackerHandle,
|
||||
reverify_phase: PhaseProgressTrackerHandle,
|
||||
sync_boot_phase: PhaseProgressTrackerHandle,
|
||||
finalize_phase: PhaseProgressTrackerHandle,
|
||||
}
|
||||
|
||||
@@ -392,6 +402,7 @@ async fn do_update(
|
||||
mut prune_phase,
|
||||
mut download_phase,
|
||||
mut reverify_phase,
|
||||
mut sync_boot_phase,
|
||||
mut finalize_phase,
|
||||
}: UpdateProgressHandles,
|
||||
) -> Result<(), Error> {
|
||||
@@ -426,7 +437,7 @@ async fn do_update(
|
||||
dst.save().await.with_kind(ErrorKind::Filesystem)?;
|
||||
reverify_phase.complete();
|
||||
|
||||
finalize_phase.start();
|
||||
sync_boot_phase.start();
|
||||
Command::new("unsquashfs")
|
||||
.arg("-n")
|
||||
.arg("-f")
|
||||
@@ -441,9 +452,18 @@ async fn do_update(
|
||||
|
||||
Command::new("/usr/lib/startos/scripts/upgrade")
|
||||
.env("CHECKSUM", &checksum)
|
||||
.arg(&path)
|
||||
.invoke(ErrorKind::Grub)
|
||||
.await?;
|
||||
sync_boot_phase.complete();
|
||||
|
||||
finalize_phase.start();
|
||||
Command::new("ln")
|
||||
.arg("-rsf")
|
||||
.arg(&path)
|
||||
.arg("/media/startos/config/current.rootfs")
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
|
||||
finalize_phase.complete();
|
||||
|
||||
progress.complete();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::io::SeekFrom;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
@@ -57,7 +56,6 @@ struct Progress {
|
||||
tracker: PhaseProgressTrackerHandle,
|
||||
expected_size: Option<u64>,
|
||||
written: u64,
|
||||
complete: bool,
|
||||
error: Option<Error>,
|
||||
}
|
||||
impl Progress {
|
||||
@@ -113,7 +111,9 @@ impl Progress {
|
||||
}
|
||||
async fn ready(watch: &mut watch::Receiver<Self>) -> Result<(), Error> {
|
||||
match &*watch
|
||||
.wait_for(|progress| progress.error.is_some() || progress.complete)
|
||||
.wait_for(|progress| {
|
||||
progress.error.is_some() || Some(progress.written) == progress.expected_size
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
@@ -126,9 +126,8 @@ impl Progress {
|
||||
}
|
||||
}
|
||||
fn complete(&mut self) -> bool {
|
||||
let mut changed = !self.complete;
|
||||
self.tracker.complete();
|
||||
changed |= match self {
|
||||
match self {
|
||||
Self {
|
||||
expected_size: Some(size),
|
||||
written,
|
||||
@@ -166,21 +165,18 @@ impl Progress {
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
self.complete = true;
|
||||
changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UploadingFile {
|
||||
tmp_dir: Option<Arc<TmpDir>>,
|
||||
tmp_dir: Arc<TmpDir>,
|
||||
file: MultiCursorFile,
|
||||
progress: watch::Receiver<Progress>,
|
||||
}
|
||||
impl UploadingFile {
|
||||
pub async fn with_path(
|
||||
path: impl AsRef<Path>,
|
||||
pub async fn new(
|
||||
mut progress: PhaseProgressTrackerHandle,
|
||||
) -> Result<(UploadHandle, Self), Error> {
|
||||
progress.set_units(Some(ProgressUnits::Bytes));
|
||||
@@ -189,35 +185,25 @@ impl UploadingFile {
|
||||
expected_size: None,
|
||||
written: 0,
|
||||
error: None,
|
||||
complete: false,
|
||||
});
|
||||
let file = create_file(path).await?;
|
||||
let tmp_dir = Arc::new(TmpDir::new().await?);
|
||||
let file = create_file(tmp_dir.join("upload.tmp")).await?;
|
||||
let uploading = Self {
|
||||
tmp_dir: None,
|
||||
tmp_dir: tmp_dir.clone(),
|
||||
file: MultiCursorFile::open(&file).await?,
|
||||
progress: progress.1,
|
||||
};
|
||||
Ok((
|
||||
UploadHandle {
|
||||
tmp_dir: None,
|
||||
tmp_dir,
|
||||
file,
|
||||
progress: progress.0,
|
||||
},
|
||||
uploading,
|
||||
))
|
||||
}
|
||||
pub async fn new(progress: PhaseProgressTrackerHandle) -> Result<(UploadHandle, Self), Error> {
|
||||
let tmp_dir = Arc::new(TmpDir::new().await?);
|
||||
let (mut handle, mut file) = Self::with_path(tmp_dir.join("upload.tmp"), progress).await?;
|
||||
handle.tmp_dir = Some(tmp_dir.clone());
|
||||
file.tmp_dir = Some(tmp_dir);
|
||||
Ok((handle, file))
|
||||
}
|
||||
pub async fn wait_for_complete(&self) -> Result<(), Error> {
|
||||
Progress::ready(&mut self.progress.clone()).await
|
||||
}
|
||||
pub async fn delete(self) -> Result<(), Error> {
|
||||
if let Some(Ok(tmp_dir)) = self.tmp_dir.map(Arc::try_unwrap) {
|
||||
if let Ok(tmp_dir) = Arc::try_unwrap(self.tmp_dir) {
|
||||
tmp_dir.delete().await?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -248,7 +234,7 @@ impl ArchiveSource for UploadingFile {
|
||||
|
||||
#[pin_project::pin_project(project = UploadingFileReaderProjection)]
|
||||
pub struct UploadingFileReader {
|
||||
tmp_dir: Option<Arc<TmpDir>>,
|
||||
tmp_dir: Arc<TmpDir>,
|
||||
position: u64,
|
||||
to_seek: Option<SeekFrom>,
|
||||
#[pin]
|
||||
@@ -344,7 +330,7 @@ impl AsyncSeek for UploadingFileReader {
|
||||
|
||||
#[pin_project::pin_project(PinnedDrop)]
|
||||
pub struct UploadHandle {
|
||||
tmp_dir: Option<Arc<TmpDir>>,
|
||||
tmp_dir: Arc<TmpDir>,
|
||||
#[pin]
|
||||
file: File,
|
||||
progress: watch::Sender<Progress>,
|
||||
@@ -391,9 +377,6 @@ impl UploadHandle {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.file.sync_all().await {
|
||||
self.progress.send_if_modified(|p| p.handle_error(&e));
|
||||
}
|
||||
}
|
||||
}
|
||||
#[pin_project::pinned_drop]
|
||||
|
||||
@@ -52,11 +52,8 @@ mod v0_4_0_alpha_9;
|
||||
mod v0_4_0_alpha_10;
|
||||
mod v0_4_0_alpha_11;
|
||||
mod v0_4_0_alpha_12;
|
||||
mod v0_4_0_alpha_13;
|
||||
mod v0_4_0_alpha_14;
|
||||
mod v0_4_0_alpha_15;
|
||||
|
||||
pub type Current = v0_4_0_alpha_15::Version; // VERSION_BUMP
|
||||
pub type Current = v0_4_0_alpha_12::Version; // VERSION_BUMP
|
||||
|
||||
impl Current {
|
||||
#[instrument(skip(self, db))]
|
||||
@@ -170,10 +167,7 @@ enum Version {
|
||||
V0_4_0_alpha_9(Wrapper<v0_4_0_alpha_9::Version>),
|
||||
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::Version>),
|
||||
V0_4_0_alpha_11(Wrapper<v0_4_0_alpha_11::Version>),
|
||||
V0_4_0_alpha_12(Wrapper<v0_4_0_alpha_12::Version>),
|
||||
V0_4_0_alpha_13(Wrapper<v0_4_0_alpha_13::Version>),
|
||||
V0_4_0_alpha_14(Wrapper<v0_4_0_alpha_14::Version>),
|
||||
V0_4_0_alpha_15(Wrapper<v0_4_0_alpha_15::Version>), // VERSION_BUMP
|
||||
V0_4_0_alpha_12(Wrapper<v0_4_0_alpha_12::Version>), // VERSION_BUMP
|
||||
Other(exver::Version),
|
||||
}
|
||||
|
||||
@@ -228,10 +222,7 @@ impl Version {
|
||||
Self::V0_4_0_alpha_9(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_10(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_11(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_12(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_13(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_14(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_4_0_alpha_15(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::V0_4_0_alpha_12(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::Other(v) => {
|
||||
return Err(Error::new(
|
||||
eyre!("unknown version {v}"),
|
||||
@@ -278,10 +269,7 @@ impl Version {
|
||||
Version::V0_4_0_alpha_9(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_10(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_11(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_12(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_13(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_14(Wrapper(x)) => x.semver(),
|
||||
Version::V0_4_0_alpha_15(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::V0_4_0_alpha_12(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::Other(x) => x.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use exver::{PreReleaseSegment, VersionRange};
|
||||
use imbl_value::InternedString;
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{v0_4_0_alpha_11, VersionT};
|
||||
use super::{VersionT, v0_4_0_alpha_11};
|
||||
use crate::net::tor::TorSecretKey;
|
||||
use crate::prelude::*;
|
||||
|
||||
@@ -75,10 +75,7 @@ impl VersionT for Version {
|
||||
}
|
||||
fix_host(&mut db["public"]["serverInfo"]["network"]["host"])?;
|
||||
|
||||
if db["private"]["keyStore"]["localCerts"].is_null() {
|
||||
db["private"]["keyStore"]["localCerts"] =
|
||||
db["private"]["keyStore"]["local_certs"].clone();
|
||||
}
|
||||
db["private"]["keyStore"]["localCerts"] = db["private"]["keyStore"]["local_certs"].clone();
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{VersionT, v0_4_0_alpha_12};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_13: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("alpha".into()), 13.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_alpha_12::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_alpha_13.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(())
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{VersionT, v0_4_0_alpha_13};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_14: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("alpha".into()), 14.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_alpha_13::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_alpha_14.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(())
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{VersionT, v0_4_0_alpha_14};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_15: exver::Version = exver::Version::new(
|
||||
[0, 4, 0],
|
||||
[PreReleaseSegment::String("alpha".into()), 15.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_4_0_alpha_14::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_4_0_alpha_15.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(())
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,6 @@ fi
|
||||
useradd --shell /bin/bash -G startos -m start9
|
||||
echo start9:embassy | chpasswd
|
||||
usermod -aG sudo start9
|
||||
usermod -aG systemd-journal start9
|
||||
|
||||
echo "start9 ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/010_start9-nopasswd"
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AnyVerifyingKey } from "./AnyVerifyingKey"
|
||||
import type { ContactInfo } from "./ContactInfo"
|
||||
|
||||
export type SignerInfo = {
|
||||
name: string
|
||||
contact: Array<ContactInfo>
|
||||
keys: Array<AnyVerifyingKey>
|
||||
}
|
||||
export type SignerInfo = { name: string }
|
||||
|
||||
@@ -3,7 +3,7 @@ import { knownProtocols } from "../interfaces/Host"
|
||||
import { AddressInfo, Host, Hostname, HostnameInfo } from "../types"
|
||||
import { Effects } from "../Effects"
|
||||
import { DropGenerator, DropPromise } from "./Drop"
|
||||
import { IpAddress, IPV6_LINK_LOCAL } from "./ip"
|
||||
import { IPV6_LINK_LOCAL } from "./ip"
|
||||
|
||||
export type UrlString = string
|
||||
export type HostId = string
|
||||
@@ -17,15 +17,7 @@ export const getHostname = (url: string): Hostname | null => {
|
||||
return last
|
||||
}
|
||||
|
||||
type FilterKinds =
|
||||
| "onion"
|
||||
| "local"
|
||||
| "domain"
|
||||
| "ip"
|
||||
| "ipv4"
|
||||
| "ipv6"
|
||||
| "localhost"
|
||||
| "link-local"
|
||||
type FilterKinds = "onion" | "local" | "domain" | "ip" | "ipv4" | "ipv6"
|
||||
export type Filter = {
|
||||
visibility?: "public" | "private"
|
||||
kind?: FilterKinds | FilterKinds[]
|
||||
@@ -80,12 +72,6 @@ type FilterReturnTy<F extends Filter> = F extends {
|
||||
: Exclude<HostnameInfo, FilterReturnTy<E>>
|
||||
: HostnameInfo
|
||||
|
||||
const defaultFilter = {
|
||||
exclude: {
|
||||
kind: ["localhost", "link-local"] as ("localhost" | "link-local")[],
|
||||
},
|
||||
}
|
||||
|
||||
type Formats = "hostname-info" | "urlstring" | "url"
|
||||
type FormatReturnTy<
|
||||
F extends Filter,
|
||||
@@ -106,11 +92,8 @@ export type Filled = {
|
||||
sslUrl: UrlString | null
|
||||
}
|
||||
|
||||
filter: <
|
||||
F extends Filter = typeof defaultFilter,
|
||||
Format extends Formats = "urlstring",
|
||||
>(
|
||||
filter?: F,
|
||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
||||
filter: F,
|
||||
format?: Format,
|
||||
) => FormatReturnTy<F, Format>[]
|
||||
|
||||
@@ -232,13 +215,7 @@ function filterRec(
|
||||
h.kind === "ip" &&
|
||||
h.hostname.kind === "domain") ||
|
||||
(kind.has("ipv4") && h.kind === "ip" && h.hostname.kind === "ipv4") ||
|
||||
(kind.has("ipv6") && h.kind === "ip" && h.hostname.kind === "ipv6") ||
|
||||
(kind.has("localhost") &&
|
||||
["localhost", "127.0.0.1", "[::1]"].includes(h.hostname.value)) ||
|
||||
(kind.has("link-local") &&
|
||||
h.kind === "ip" &&
|
||||
h.hostname.kind === "ipv6" &&
|
||||
IPV6_LINK_LOCAL.contains(IpAddress.parse(h.hostname.value)))),
|
||||
(kind.has("ipv6") && h.kind === "ip" && h.hostname.kind === "ipv6")),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -262,14 +239,11 @@ export const filledAddress = (
|
||||
...addressInfo,
|
||||
hostnames,
|
||||
toUrls,
|
||||
filter: <
|
||||
F extends Filter = typeof defaultFilter,
|
||||
Format extends Formats = "urlstring",
|
||||
>(
|
||||
filter?: F,
|
||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
||||
filter: F,
|
||||
format?: Format,
|
||||
) => {
|
||||
const filtered = filterRec(hostnames, filter ?? defaultFilter, false)
|
||||
const filtered = filterRec(hostnames, filter, false)
|
||||
let res: FormatReturnTy<F, Format>[] = filtered as any
|
||||
if (format === "hostname-info") return res
|
||||
const urls = filtered.flatMap(toUrlArray)
|
||||
|
||||
@@ -61,7 +61,7 @@ import {
|
||||
} from "../../base/lib/inits"
|
||||
import { DropGenerator } from "../../base/lib/util/Drop"
|
||||
|
||||
export const OSVersion = testTypeVersion("0.4.0-alpha.15")
|
||||
export const OSVersion = testTypeVersion("0.4.0-alpha.12")
|
||||
|
||||
// prettier-ignore
|
||||
type AnyNeverCond<T extends any[], Then, Else> =
|
||||
|
||||
@@ -410,17 +410,12 @@ export class SubContainerOwned<
|
||||
workdir = options.cwd
|
||||
delete options.cwd
|
||||
}
|
||||
if (options?.env) {
|
||||
for (let [k, v] of Object.entries(options.env)) {
|
||||
extra.push(`--env=${k}=${v}`)
|
||||
}
|
||||
}
|
||||
const child = cp.spawn(
|
||||
"start-container",
|
||||
[
|
||||
"subcontainer",
|
||||
"exec",
|
||||
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||
`--env=/media/startos/images/${this.imageId}.env`,
|
||||
`--user=${user}`,
|
||||
`--workdir=${workdir}`,
|
||||
...extra,
|
||||
@@ -535,11 +530,6 @@ export class SubContainerOwned<
|
||||
workdir = options.cwd
|
||||
delete options.cwd
|
||||
}
|
||||
if (options?.env) {
|
||||
for (let [k, v] of Object.entries(options.env)) {
|
||||
extra.push(`--env=${k}=${v}`)
|
||||
}
|
||||
}
|
||||
await this.killLeader()
|
||||
this.leaderExited = false
|
||||
this.leader = cp.spawn(
|
||||
@@ -547,7 +537,7 @@ export class SubContainerOwned<
|
||||
[
|
||||
"subcontainer",
|
||||
"launch",
|
||||
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||
`--env=/media/startos/images/${this.imageId}.env`,
|
||||
`--user=${user}`,
|
||||
`--workdir=${workdir}`,
|
||||
...extra,
|
||||
@@ -584,17 +574,12 @@ export class SubContainerOwned<
|
||||
workdir = options.cwd
|
||||
delete options.cwd
|
||||
}
|
||||
if (options?.env) {
|
||||
for (let [k, v] of Object.entries(options.env)) {
|
||||
extra.push(`--env=${k}=${v}`)
|
||||
}
|
||||
}
|
||||
return cp.spawn(
|
||||
"start-container",
|
||||
[
|
||||
"subcontainer",
|
||||
"exec",
|
||||
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||
`--env=/media/startos/images/${this.imageId}.env`,
|
||||
`--user=${user}`,
|
||||
`--workdir=${workdir}`,
|
||||
...extra,
|
||||
|
||||
11
sdk/package/package-lock.json
generated
11
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.44",
|
||||
"version": "0.4.0-beta.43",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.44",
|
||||
"version": "0.4.0-beta.43",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^3.0.0",
|
||||
@@ -98,7 +98,6 @@
|
||||
"integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.26.0",
|
||||
@@ -1644,7 +1643,6 @@
|
||||
"integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
@@ -1946,7 +1944,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001669",
|
||||
"electron-to-chromium": "^1.5.41",
|
||||
@@ -3056,7 +3053,6 @@
|
||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -4161,7 +4157,6 @@
|
||||
"integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^10.0.0",
|
||||
"source-map-generator": "0.8.0"
|
||||
@@ -4838,7 +4833,6 @@
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
@@ -4959,7 +4953,6 @@
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.44",
|
||||
"version": "0.4.0-beta.43",
|
||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||
"main": "./package/lib/index.js",
|
||||
"types": "./package/lib/index.d.ts",
|
||||
|
||||
158
web/package-lock.json
generated
158
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.15",
|
||||
"version": "0.4.0-alpha.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.15",
|
||||
"version": "0.4.0-alpha.12",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^20.3.0",
|
||||
@@ -25,18 +25,18 @@
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"@start9labs/argon2": "^0.3.0",
|
||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||
"@taiga-ui/addon-charts": "4.62.0",
|
||||
"@taiga-ui/addon-commerce": "4.62.0",
|
||||
"@taiga-ui/addon-mobile": "4.62.0",
|
||||
"@taiga-ui/addon-table": "4.62.0",
|
||||
"@taiga-ui/cdk": "4.62.0",
|
||||
"@taiga-ui/core": "4.62.0",
|
||||
"@taiga-ui/addon-charts": "4.55.0",
|
||||
"@taiga-ui/addon-commerce": "4.55.0",
|
||||
"@taiga-ui/addon-mobile": "4.55.0",
|
||||
"@taiga-ui/addon-table": "4.55.0",
|
||||
"@taiga-ui/cdk": "4.55.0",
|
||||
"@taiga-ui/core": "4.55.0",
|
||||
"@taiga-ui/dompurify": "4.1.11",
|
||||
"@taiga-ui/event-plugins": "4.7.0",
|
||||
"@taiga-ui/experimental": "4.62.0",
|
||||
"@taiga-ui/icons": "4.62.0",
|
||||
"@taiga-ui/kit": "4.62.0",
|
||||
"@taiga-ui/layout": "4.62.0",
|
||||
"@taiga-ui/experimental": "4.55.0",
|
||||
"@taiga-ui/icons": "4.55.0",
|
||||
"@taiga-ui/kit": "4.55.0",
|
||||
"@taiga-ui/layout": "4.55.0",
|
||||
"@taiga-ui/polymorpheus": "4.9.0",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"base64-js": "^1.5.1",
|
||||
@@ -3950,9 +3950,9 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@taiga-ui/addon-charts": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.62.0.tgz",
|
||||
"integrity": "sha512-tCysUpzEHwRhK/p9hopkt0Jw4jcgA2cF8CYK8mDntghC+fNLnqCVUcrqFIC5plGabAo00WMEz+X+KyGvwvKaVg==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.55.0.tgz",
|
||||
"integrity": "sha512-Rwgcc7NaLm75GsHYhqbO4jgNhD1bGbA7Kk0sNFE+Tgz9+V3ARXMuBw7C3cU9UxLSFnOsXz9RYLosmZ3jAVlyuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": ">=2.8.1"
|
||||
@@ -3961,15 +3961,15 @@
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ng-web-apis/common": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/addon-commerce": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.62.0.tgz",
|
||||
"integrity": "sha512-J4+bdHeDe2d7Uh8NNObLl4LzBhWLCdzxNHXPac1bMGB+3gX751Htc9px37FkVZlQGnxQATCbxAVXj7Zjveq/QQ==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.55.0.tgz",
|
||||
"integrity": "sha512-eOOBkIJSsagtRkpRZ04xlL8ePIP01d4Xo264zSTg2SRxD6vwR/7/QJlf9108BvIJv/jfTpmFukLwSB9LazqmCw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -3979,22 +3979,22 @@
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@angular/forms": ">=16.0.0",
|
||||
"@maskito/angular": "^3.11.1",
|
||||
"@maskito/core": "^3.11.1",
|
||||
"@maskito/kit": "^3.11.1",
|
||||
"@maskito/angular": "^3.10.3",
|
||||
"@maskito/core": "^3.10.3",
|
||||
"@maskito/kit": "^3.10.3",
|
||||
"@ng-web-apis/common": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/i18n": "^4.62.0",
|
||||
"@taiga-ui/kit": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/i18n": "^4.55.0",
|
||||
"@taiga-ui/kit": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/addon-mobile": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.62.0.tgz",
|
||||
"integrity": "sha512-seIBG4utgLq2xDJu+YDzksOsVi/V6vsTbm2bljgM1fIBZInbhqk95YOIFZDU9JXT1/vIShcqetavg1vHD1wdkQ==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.55.0.tgz",
|
||||
"integrity": "sha512-NQRozVKcXLs9/rd/s1yI7T5rIokzwHQ5IN/c3NLBUEka1iKUr1ZTch+g9CHJf8GTVB0uAwWKNCgX5LxtiSI5zg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": ">=2.8.1"
|
||||
@@ -4004,18 +4004,18 @@
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ng-web-apis/common": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/kit": "^4.62.0",
|
||||
"@taiga-ui/layout": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/kit": "^4.55.0",
|
||||
"@taiga-ui/layout": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/addon-table": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.62.0.tgz",
|
||||
"integrity": "sha512-0rolnsO1puYwUK17si5OOpzFxiziS6/OSbpLOSKrVrMkCgsWCoNDvpgPIwtwS5Mq3iF5cwLfUPbDQM8saG7wxQ==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.55.0.tgz",
|
||||
"integrity": "sha512-K5qpOS0UQLDqruJXPNDic8scCRvO3oAN/ZCPQ9XOGDrcvydvo4AKUwjKRPj+pZ/z0ulxbwAAruFFFCvRNnbzaA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": ">=2.8.1"
|
||||
@@ -4024,18 +4024,18 @@
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/i18n": "^4.62.0",
|
||||
"@taiga-ui/kit": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/i18n": "^4.55.0",
|
||||
"@taiga-ui/kit": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/cdk": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.62.0.tgz",
|
||||
"integrity": "sha512-KWPXEbCHtRp7aIet1L3PySdXpo5Aay4L/36jDzjiFZ/bcbuD2cY/3S2l68zpgv6ZksZA94DuCuaamSEwQIAtPw==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.55.0.tgz",
|
||||
"integrity": "sha512-vA5nGyx+YIHR1xZeq5D9gSqTRQg74qhe1AOt5FlqFOC0P4LvmLkNg3De7AeahXALNSeRz/DYcqI7WuGo6xpcLQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4065,9 +4065,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/core": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.62.0.tgz",
|
||||
"integrity": "sha512-PQW10hFH50g8PgnJpPa/ZrGMWljhIsBHad/utvalmlv8wXQY24i8T1BjrGIOFPOjzs20NEwLOICHf7KdZUtiuA==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.55.0.tgz",
|
||||
"integrity": "sha512-Z2ATVNmEAlHEk2cgs/tnS6qZML87IchkPDeRl6HQfBT2fjYVjh1oCzXL07t86Lv6tpvkllyUVqoBCTSvDXs9kA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4082,9 +4082,9 @@
|
||||
"@angular/router": ">=16.0.0",
|
||||
"@ng-web-apis/common": "^4.12.0",
|
||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/event-plugins": "^4.7.0",
|
||||
"@taiga-ui/i18n": "^4.62.0",
|
||||
"@taiga-ui/i18n": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
@@ -4120,9 +4120,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/experimental": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.62.0.tgz",
|
||||
"integrity": "sha512-EiL5wJ+9LSf0BfZcFX6ioCavLfx26v0BCOUXh52Rtczp85Uh2qTDt2feM0oBDB+0Kj74/+wqqiKi+s3B8ZV3WA==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.55.0.tgz",
|
||||
"integrity": "sha512-3zq2BTl+fE/N/tEr74TzWbyzT5OoS9YEApKobJehKivW5XxZmF/MDRWp45kSe4jDtVbJ2ueI0Jn8h0BDNykkcg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": ">=2.8.1"
|
||||
@@ -4130,19 +4130,19 @@
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@taiga-ui/addon-commerce": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/kit": "^4.62.0",
|
||||
"@taiga-ui/layout": "^4.62.0",
|
||||
"@taiga-ui/addon-commerce": "^4.55.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/kit": "^4.55.0",
|
||||
"@taiga-ui/layout": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/i18n": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.62.0.tgz",
|
||||
"integrity": "sha512-84hD1nI26EAYd5RUhFKxbg+8WKYhc0GBHyf8wfi15xuwaT6oh2gbJx7pNTlGN3klH4CeDB9HF998tkhieevqQw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.59.0.tgz",
|
||||
"integrity": "sha512-IxPzqkORJlSqagvUdmqBL9fuvfRm/Ca/W/bCDEK2GN/4QtSZ0yFzAyQdWduoIJubqyEPMXRbXZGc7WBtDgAMIQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4155,18 +4155,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/icons": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.62.0.tgz",
|
||||
"integrity": "sha512-vD+bJk3Wot/+NcbdPwAJGBnqXG6T1OJVeg2IkaEE6DBixwdwDpukZWiV9asXyXiJkyEpG2Ar7SASvdCYZEVlxw==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.55.0.tgz",
|
||||
"integrity": "sha512-sYqSG9wgUcwHBrDRnMhLCMEkvAAw/SZrvvq0jdY2oWGmKwAj/6WBt+wA+jnFkDDKEZ7mjzdPIQffpVaUjSwsiw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/kit": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.62.0.tgz",
|
||||
"integrity": "sha512-tdEaXJTks1PZQJAwMiVQTZrtCpaLIYV6T9VdVPZUKAJXq7K6J2kcD0oIISjwE9rqgLVwqytMZrwHx1nSRzkb/A==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.55.0.tgz",
|
||||
"integrity": "sha512-xTvi7viI+wI2ifPv2bsf8prhYWWS4g1lbx059jXV3f5Cttc0Xg6DEb6xpaQOf4loBkcrP2FzkA4njACUuiouzw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4177,25 +4177,25 @@
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@angular/forms": ">=16.0.0",
|
||||
"@angular/router": ">=16.0.0",
|
||||
"@maskito/angular": "^3.11.1",
|
||||
"@maskito/core": "^3.11.1",
|
||||
"@maskito/kit": "^3.11.1",
|
||||
"@maskito/phone": "^3.11.1",
|
||||
"@maskito/angular": "^3.10.3",
|
||||
"@maskito/core": "^3.10.3",
|
||||
"@maskito/kit": "^3.10.3",
|
||||
"@maskito/phone": "^3.10.3",
|
||||
"@ng-web-apis/common": "^4.12.0",
|
||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||
"@ng-web-apis/resize-observer": "^4.12.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/i18n": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/i18n": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@taiga-ui/layout": {
|
||||
"version": "4.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.62.0.tgz",
|
||||
"integrity": "sha512-xd8eLLeR5FE3RhnVMGl1QlC3JXXJLsLAAASpBf9DQsTt+YBBl8BQt/cXGbBcJecC2mJLZlS6zytSkMTHY7VAhw==",
|
||||
"version": "4.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.55.0.tgz",
|
||||
"integrity": "sha512-C+e4gudZwjIc46VITil5vySas1FPpfe+D4uwLRggJOTuUosZlZHBuc51v91wCCc0pL0Xfu+TD0s8W9kRd1sQHA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -4204,9 +4204,9 @@
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=16.0.0",
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@taiga-ui/cdk": "^4.62.0",
|
||||
"@taiga-ui/core": "^4.62.0",
|
||||
"@taiga-ui/kit": "^4.62.0",
|
||||
"@taiga-ui/cdk": "^4.55.0",
|
||||
"@taiga-ui/core": "^4.55.0",
|
||||
"@taiga-ui/kit": "^4.55.0",
|
||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.4.0-alpha.15",
|
||||
"version": "0.4.0-alpha.12",
|
||||
"author": "Start9 Labs, Inc",
|
||||
"homepage": "https://start9.com/",
|
||||
"license": "MIT",
|
||||
@@ -49,18 +49,18 @@
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"@start9labs/argon2": "^0.3.0",
|
||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||
"@taiga-ui/addon-charts": "4.62.0",
|
||||
"@taiga-ui/addon-commerce": "4.62.0",
|
||||
"@taiga-ui/addon-mobile": "4.62.0",
|
||||
"@taiga-ui/addon-table": "4.62.0",
|
||||
"@taiga-ui/cdk": "4.62.0",
|
||||
"@taiga-ui/core": "4.62.0",
|
||||
"@taiga-ui/addon-charts": "4.55.0",
|
||||
"@taiga-ui/addon-commerce": "4.55.0",
|
||||
"@taiga-ui/addon-mobile": "4.55.0",
|
||||
"@taiga-ui/addon-table": "4.55.0",
|
||||
"@taiga-ui/cdk": "4.55.0",
|
||||
"@taiga-ui/core": "4.55.0",
|
||||
"@taiga-ui/dompurify": "4.1.11",
|
||||
"@taiga-ui/event-plugins": "4.7.0",
|
||||
"@taiga-ui/experimental": "4.62.0",
|
||||
"@taiga-ui/icons": "4.62.0",
|
||||
"@taiga-ui/kit": "4.62.0",
|
||||
"@taiga-ui/layout": "4.62.0",
|
||||
"@taiga-ui/experimental": "4.55.0",
|
||||
"@taiga-ui/icons": "4.55.0",
|
||||
"@taiga-ui/kit": "4.55.0",
|
||||
"@taiga-ui/layout": "4.55.0",
|
||||
"@taiga-ui/polymorpheus": "4.9.0",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"base64-js": "^1.5.1",
|
||||
|
||||
@@ -70,7 +70,6 @@ import { MarketplaceItemComponent } from './item.component'
|
||||
|
||||
<div class="background-border box-shadow-lg shadow-color-light">
|
||||
<div class="box-container">
|
||||
<h2 class="additional-detail-title">{{ 'Description' | i18n }}</h2>
|
||||
<p [innerHTML]="pkg().description.long"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,12 +10,7 @@ import { MarketplacePkgBase } from '../../../types'
|
||||
selector: 'marketplace-dep-item',
|
||||
template: `
|
||||
<div class="outer-container">
|
||||
<tui-avatar
|
||||
appearance="action-grayscale"
|
||||
class="dep-img"
|
||||
size="l"
|
||||
[src]="getImage(dep.key)"
|
||||
/>
|
||||
<tui-avatar class="dep-img" size="l" [src]="getImage(dep.key)" />
|
||||
<div>
|
||||
<tui-line-clamp [linesLimit]="2" [content]="titleContent" />
|
||||
<ng-template #titleContent>
|
||||
|
||||
@@ -21,10 +21,7 @@ import { MarketplacePkg } from '../../types'
|
||||
[queryParams]="{ id: pkg.id, flavor: pkg.flavor }"
|
||||
queryParamsHandling="merge"
|
||||
>
|
||||
<tui-avatar
|
||||
appearance="action-grayscale"
|
||||
[src]="pkg.icon | trustUrl"
|
||||
/>
|
||||
<tui-avatar [src]="pkg.icon | trustUrl" />
|
||||
<span tuiTitle>
|
||||
{{ pkg.title }}
|
||||
<span tuiSubtitle>{{ pkg.version }}</span>
|
||||
|
||||
@@ -39,9 +39,7 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<h2 style="font-variant-caps: all-small-caps">
|
||||
Root Certificate Authority
|
||||
</h2>
|
||||
<h3 style="color: #f8546a; font-weight: bold">Important!</h3>
|
||||
<p>
|
||||
Download your server's Root CA and
|
||||
<a
|
||||
@@ -49,7 +47,7 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
||||
path="/user-manual/trust-ca.html"
|
||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||
>
|
||||
follow instructions
|
||||
follow the instructions
|
||||
</a>
|
||||
to establish a secure connection with your server.
|
||||
</p>
|
||||
@@ -86,15 +84,15 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
||||
"
|
||||
>
|
||||
<h2 style="font-variant-caps: all-small-caps">
|
||||
Permanent Local Address
|
||||
Access from home (LAN)
|
||||
</h2>
|
||||
<p>
|
||||
You must be connected to the same Local Area Network (LAN) as your
|
||||
server to access this address.
|
||||
Visit the address below when you are connected to the same WiFi or
|
||||
Local Area Network (LAN) as your server.
|
||||
</p>
|
||||
<p
|
||||
style="
|
||||
padding: 16px 0;
|
||||
padding: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
overflow: auto;
|
||||
@@ -102,6 +100,33 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
||||
>
|
||||
<code id="lan-addr"></code>
|
||||
</p>
|
||||
|
||||
<h2 style="font-variant-caps: all-small-caps">
|
||||
Access on the go (Tor)
|
||||
</h2>
|
||||
<p>Visit the address below when you are away from home.</p>
|
||||
<p>
|
||||
<span style="font-weight: bold">Note:</span>
|
||||
This address will only work from a Tor-enabled browser.
|
||||
<a
|
||||
docsLink
|
||||
path="/user-manual/connecting-remotely/tor.html"
|
||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||
>
|
||||
Follow the instructions
|
||||
</a>
|
||||
to get setup.
|
||||
</p>
|
||||
<p
|
||||
style="
|
||||
padding: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
overflow: auto;
|
||||
"
|
||||
>
|
||||
<code id="tor-addr"></code>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
DOCUMENT,
|
||||
} from '@angular/core'
|
||||
import { DownloadHTMLService, ErrorService } from '@start9labs/shared'
|
||||
import { TuiButton, TuiIcon, TuiLoader, TuiSurface } from '@taiga-ui/core'
|
||||
import { TuiButton, TuiIcon, TuiSurface } from '@taiga-ui/core'
|
||||
import { TuiCardLarge } from '@taiga-ui/layout'
|
||||
import { DocumentationComponent } from 'src/app/components/documentation.component'
|
||||
import { MatrixComponent } from 'src/app/components/matrix.component'
|
||||
@@ -31,16 +31,10 @@ import { StateService } from 'src/app/services/state.service'
|
||||
<h3>You can now safely unplug your old StartOS data drive</h3>
|
||||
}
|
||||
|
||||
<h3>
|
||||
http://start.local was for setup purposes only. It will no longer
|
||||
work.
|
||||
</h3>
|
||||
|
||||
<button tuiCardLarge tuiSurface="floating" (click)="download()">
|
||||
<strong class="caps">Download address info</strong>
|
||||
<span>
|
||||
For future reference, this file contains your server's permanent
|
||||
local address, as well as its Root Certificate Authority (Root CA).
|
||||
start.local was for setup purposes only. It will no longer work.
|
||||
</span>
|
||||
<strong class="caps">
|
||||
Download
|
||||
@@ -54,18 +48,17 @@ import { StateService } from 'src/app/services/state.service'
|
||||
target="_blank"
|
||||
[attr.href]="disableLogin ? null : lanAddress"
|
||||
>
|
||||
<strong class="caps">Trust your Root CA</strong>
|
||||
<span>
|
||||
In the new tab, follow instructions to trust your server's Root CA
|
||||
and log in.
|
||||
</span>
|
||||
<strong class="caps">
|
||||
Open Local Address
|
||||
Open
|
||||
<tui-icon icon="@tui.external-link" />
|
||||
</strong>
|
||||
</a>
|
||||
<app-documentation hidden [lanAddress]="lanAddress" />
|
||||
} @else {
|
||||
<tui-loader />
|
||||
}
|
||||
</section>
|
||||
`,
|
||||
@@ -104,10 +97,6 @@ import { StateService } from 'src/app/services/state.service'
|
||||
opacity: var(--tui-disabled-opacity);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: left;
|
||||
}
|
||||
`,
|
||||
imports: [
|
||||
TuiCardLarge,
|
||||
@@ -116,7 +105,6 @@ import { StateService } from 'src/app/services/state.service'
|
||||
TuiSurface,
|
||||
MatrixComponent,
|
||||
DocumentationComponent,
|
||||
TuiLoader,
|
||||
],
|
||||
})
|
||||
export default class SuccessPage implements AfterViewInit {
|
||||
@@ -129,6 +117,7 @@ export default class SuccessPage implements AfterViewInit {
|
||||
|
||||
readonly stateService = inject(StateService)
|
||||
|
||||
torAddresses?: string[]
|
||||
lanAddress?: string
|
||||
cert?: string
|
||||
disableLogin = this.stateService.setupType === 'fresh'
|
||||
@@ -138,8 +127,10 @@ export default class SuccessPage implements AfterViewInit {
|
||||
}
|
||||
|
||||
download() {
|
||||
const torElem = this.document.getElementById('tor-addr')
|
||||
const lanElem = this.document.getElementById('lan-addr')
|
||||
|
||||
if (torElem) torElem.innerHTML = this.torAddresses?.join('\n') || ''
|
||||
if (lanElem) lanElem.innerHTML = this.lanAddress || ''
|
||||
|
||||
this.document
|
||||
@@ -164,6 +155,9 @@ export default class SuccessPage implements AfterViewInit {
|
||||
try {
|
||||
const ret = await this.api.complete()
|
||||
if (!this.stateService.kiosk) {
|
||||
this.torAddresses = ret.torAddresses.map(a =>
|
||||
a.replace(/^https:/, 'http:'),
|
||||
)
|
||||
this.lanAddress = ret.lanAddress.replace(/^https:/, 'http:')
|
||||
this.cert = ret.rootCa
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ export default {
|
||||
512: 'Der Kiosk-Modus ist auf diesem Gerät nicht verfügbar',
|
||||
513: 'Aktivieren',
|
||||
514: 'Deaktivieren',
|
||||
515: 'Diese Änderung wird nach dem nächsten Neustart wirksam',
|
||||
515: 'Du verwendest derzeit einen Kiosk. Wenn du den Kiosk-Modus deaktivierst, wird die Verbindung zum Kiosk getrennt.',
|
||||
516: 'Empfohlen',
|
||||
517: 'Möchten Sie diese Aufgabe wirklich verwerfen?',
|
||||
518: 'Verwerfen',
|
||||
|
||||
@@ -483,7 +483,7 @@ export const ENGLISH = {
|
||||
'Kiosk Mode is unavailable on this device': 512,
|
||||
'Enable': 513,
|
||||
'Disable': 514,
|
||||
'This change will take effect after the next boot': 515,
|
||||
'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.': 515,
|
||||
'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
|
||||
|
||||
@@ -484,7 +484,7 @@ export default {
|
||||
512: 'El modo quiosco no está disponible en este dispositivo',
|
||||
513: 'Activar',
|
||||
514: 'Desactivar',
|
||||
515: 'Este cambio tendrá efecto después del próximo inicio',
|
||||
515: 'Actualmente estás utilizando un quiosco. Desactivar el modo quiosco provocará su desconexión.',
|
||||
516: 'Recomendado',
|
||||
517: '¿Estás seguro de que deseas descartar esta tarea?',
|
||||
518: 'Descartar',
|
||||
|
||||
@@ -484,7 +484,7 @@ export default {
|
||||
512: 'Le mode kiosque n’est pas disponible sur cet appareil',
|
||||
513: 'Activer',
|
||||
514: 'Désactiver',
|
||||
515: 'Ce changement va prendre effet après le prochain démarrage',
|
||||
515: 'Vous utilisez actuellement un kiosque. Désactiver le mode kiosque entraînera sa déconnexion.',
|
||||
516: 'Recommandé',
|
||||
517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?',
|
||||
518: 'Ignorer',
|
||||
|
||||
@@ -484,7 +484,7 @@ export default {
|
||||
512: 'Tryb kiosku jest niedostępny na tym urządzeniu',
|
||||
513: 'Włącz',
|
||||
514: 'Wyłącz',
|
||||
515: 'Ta zmiana zacznie obowiązywać po następnym uruchomieniu',
|
||||
515: 'Obecnie używasz kiosku. Wyłączenie trybu kiosku spowoduje jego rozłączenie.',
|
||||
516: 'Zalecane',
|
||||
517: 'Czy na pewno chcesz odrzucić to zadanie?',
|
||||
518: 'Odrzuć',
|
||||
|
||||
@@ -10,7 +10,7 @@ import { I18N, i18nKey } from './i18n.providers'
|
||||
export class i18nPipe implements PipeTransform {
|
||||
private readonly i18n = inject(I18N)
|
||||
|
||||
transform(englishKey: i18nKey | null | undefined | ''): string {
|
||||
transform(englishKey: i18nKey | null | undefined): string {
|
||||
englishKey = englishKey || ('' as i18nKey)
|
||||
|
||||
return this.i18n()?.[ENGLISH[englishKey]] || englishKey
|
||||
|
||||
@@ -40,9 +40,7 @@ import { HintPipe } from '../pipes/hint.pipe'
|
||||
[(ngModel)]="selected"
|
||||
/>
|
||||
}
|
||||
@if (!mobile) {
|
||||
<tui-data-list-wrapper *tuiTextfieldDropdown new [items]="items" />
|
||||
}
|
||||
<tui-data-list-wrapper *tuiTextfieldDropdown new [items]="items" />
|
||||
@if (spec | hint; as hint) {
|
||||
<tui-icon [tuiTooltip]="hint" />
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
tuiButtonOptionsProvider,
|
||||
TuiDataList,
|
||||
TuiDropdown,
|
||||
TuiIcon,
|
||||
TuiTextfield,
|
||||
} from '@taiga-ui/core'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
@@ -28,9 +27,13 @@ import { InterfaceComponent } from '../interface.component'
|
||||
selector: 'td[actions]',
|
||||
template: `
|
||||
<div class="desktop">
|
||||
<button tuiIconButton appearance="flat-grayscale" (click)="viewDetails()">
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat-grayscale"
|
||||
iconStart="@tui.info"
|
||||
(click)="viewDetails()"
|
||||
>
|
||||
{{ 'Address details' | i18n }}
|
||||
<tui-icon class="info" icon="@tui.info" background="@tui.info-filled" />
|
||||
</button>
|
||||
@if (interface.value()?.type === 'ui') {
|
||||
<a
|
||||
@@ -110,19 +113,6 @@ import { InterfaceComponent } from '../interface.component'
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:host-context(.uncommon-hidden) .desktop {
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.info {
|
||||
background: var(--tui-status-info);
|
||||
|
||||
&::after {
|
||||
mask-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
display: none;
|
||||
}
|
||||
@@ -137,14 +127,7 @@ import { InterfaceComponent } from '../interface.component'
|
||||
}
|
||||
}
|
||||
`,
|
||||
imports: [
|
||||
TuiButton,
|
||||
TuiDropdown,
|
||||
TuiDataList,
|
||||
i18nPipe,
|
||||
TuiTextfield,
|
||||
TuiIcon,
|
||||
],
|
||||
imports: [TuiButton, TuiDropdown, TuiDataList, i18nPipe, TuiTextfield],
|
||||
providers: [tuiButtonOptionsProvider({ appearance: 'icon' })],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { TuiButton } from '@taiga-ui/core'
|
||||
import { TuiAccordion } from '@taiga-ui/experimental'
|
||||
import { TuiElasticContainer, TuiSkeleton } from '@taiga-ui/kit'
|
||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||
|
||||
@@ -13,79 +12,91 @@ import { InterfaceAddressItemComponent } from './item.component'
|
||||
selector: 'section[addresses]',
|
||||
template: `
|
||||
<header>{{ 'Addresses' | i18n }}</header>
|
||||
<tui-elastic-container>
|
||||
<table [appTable]="['Type', 'Access', 'Gateway', 'URL', null]">
|
||||
@for (address of addresses()?.common; track $index) {
|
||||
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
||||
} @empty {
|
||||
@if (addresses()) {
|
||||
<table [appTable]="['Type', 'Access', 'Gateway', 'URL', null]">
|
||||
@for (address of addresses()?.common; track $index) {
|
||||
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
||||
} @empty {
|
||||
@if (addresses()) {
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<app-placeholder icon="@tui.list-x">
|
||||
{{ 'No addresses' | i18n }}
|
||||
</app-placeholder>
|
||||
</td>
|
||||
</tr>
|
||||
} @else {
|
||||
@for (_ of [0, 1]; track $index) {
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<app-placeholder icon="@tui.list-x">
|
||||
{{ 'No addresses' | i18n }}
|
||||
</app-placeholder>
|
||||
<td colspan="6">
|
||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
} @else {
|
||||
@for (_ of [0, 1]; track $index) {
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
}
|
||||
</table>
|
||||
@if (addresses()?.uncommon?.length) {
|
||||
<tui-accordion>
|
||||
<tui-expand>
|
||||
<hr />
|
||||
<table class="g-table">
|
||||
@for (address of addresses()?.uncommon; track $index) {
|
||||
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
||||
}
|
||||
</table>
|
||||
</tui-expand>
|
||||
<button
|
||||
appearance="secondary-grayscale"
|
||||
iconEnd=""
|
||||
[(tuiAccordion)]="uncommon"
|
||||
>
|
||||
@if (uncommon) {
|
||||
Hide uncommon
|
||||
} @else {
|
||||
Show uncommon
|
||||
}
|
||||
}
|
||||
<tbody [class.uncommon-hidden]="!uncommon">
|
||||
@if (addresses()?.uncommon?.length && uncommon) {
|
||||
<tr [style.background]="'var(--tui-background-neutral-1)'">
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
}
|
||||
@for (address of addresses()?.uncommon; track $index) {
|
||||
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
||||
}
|
||||
</tbody>
|
||||
@if (addresses()?.uncommon?.length) {
|
||||
<caption [style.caption-side]="'bottom'">
|
||||
<button
|
||||
tuiButton
|
||||
size="m"
|
||||
appearance="secondary-grayscale"
|
||||
(click)="uncommon = !uncommon"
|
||||
>
|
||||
@if (uncommon) {
|
||||
Hide uncommon
|
||||
} @else {
|
||||
Show uncommon
|
||||
}
|
||||
</button>
|
||||
</caption>
|
||||
}
|
||||
</table>
|
||||
</tui-elastic-container>
|
||||
</button>
|
||||
</tui-accordion>
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
.g-table:has(caption) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
tui-accordion {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
[tuiButton] {
|
||||
width: 100%;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
[tuiAccordion],
|
||||
tui-expand {
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[tuiAccordion] {
|
||||
justify-content: center;
|
||||
height: 3rem;
|
||||
border-radius: 0 0 var(--tui-radius-m) var(--tui-radius-m) !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 0;
|
||||
height: 0.25rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
:host-context(tui-root._mobile) {
|
||||
[tuiAccordion] {
|
||||
margin: 0.5rem 0;
|
||||
border-radius: var(--tui-radius-m) !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
host: { class: 'g-card' },
|
||||
imports: [
|
||||
TuiSkeleton,
|
||||
TuiButton,
|
||||
TableComponent,
|
||||
PlaceholderComponent,
|
||||
i18nPipe,
|
||||
InterfaceAddressItemComponent,
|
||||
TuiElasticContainer,
|
||||
TuiAccordion,
|
||||
TuiSkeleton,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
|
||||
@@ -8,31 +8,27 @@ import { TuiBadge } from '@taiga-ui/kit'
|
||||
selector: 'tr[address]',
|
||||
template: `
|
||||
@if (address(); as address) {
|
||||
<td>{{ address.type }}</td>
|
||||
<td>
|
||||
<div class="wrapper">{{ address.type }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wrapper">
|
||||
@if (address.access === 'public') {
|
||||
<tui-badge size="s" appearance="primary-success">
|
||||
{{ 'public' | i18n }}
|
||||
</tui-badge>
|
||||
} @else if (address.access === 'private') {
|
||||
<tui-badge size="s" appearance="primary-destructive">
|
||||
{{ 'private' | i18n }}
|
||||
</tui-badge>
|
||||
} @else {
|
||||
-
|
||||
}
|
||||
</div>
|
||||
@if (address.access === 'public') {
|
||||
<tui-badge size="s" appearance="primary-success">
|
||||
{{ 'public' | i18n }}
|
||||
</tui-badge>
|
||||
} @else if (address.access === 'private') {
|
||||
<tui-badge size="s" appearance="primary-destructive">
|
||||
{{ 'private' | i18n }}
|
||||
</tui-badge>
|
||||
} @else {
|
||||
-
|
||||
}
|
||||
</td>
|
||||
<td [style.order]="-1">
|
||||
<div class="wrapper" [title]="address.gatewayName">
|
||||
<div [title]="address.gatewayName">
|
||||
{{ address.gatewayName || '-' }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="wrapper" [title]="address.url">{{ address.url }}</div>
|
||||
<div [title]="address.url">{{ address.url }}</div>
|
||||
</td>
|
||||
<td
|
||||
actions
|
||||
@@ -52,18 +48,6 @@ import { TuiBadge } from '@taiga-ui/kit'
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.uncommon-hidden) {
|
||||
.wrapper {
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
td {
|
||||
padding-block: 0;
|
||||
border: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -13,8 +13,6 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><ng-content /></tbody>
|
||||
<ng-content select="tbody" />
|
||||
<ng-content select="caption" />
|
||||
`,
|
||||
styles: `
|
||||
:host:has(app-placeholder) thead {
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ToManifestPipe } from '../../../pipes/to-manifest'
|
||||
[queryParams]="services[d.key] ? {} : { search: d.key }"
|
||||
[class.error]="getError(d.key)"
|
||||
>
|
||||
<tui-avatar appearance="action-grayscale">
|
||||
<tui-avatar>
|
||||
<img
|
||||
alt=""
|
||||
[src]="
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { TuiLoader } from '@taiga-ui/core'
|
||||
import { ServiceUptimeComponent } from 'src/app/routes/portal/routes/services/components/uptime.component'
|
||||
import { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
||||
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { InstallingInfo } from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
getInstalledPrimaryStatus,
|
||||
PrimaryRendering,
|
||||
PrimaryStatus,
|
||||
} from 'src/app/services/pkg-status-rendering.service'
|
||||
|
||||
@Component({
|
||||
@@ -19,27 +19,23 @@ import {
|
||||
template: `
|
||||
<header>{{ 'Status' | i18n }}</header>
|
||||
<div>
|
||||
@if (info()) {
|
||||
@if (installingInfo) {
|
||||
<h3>
|
||||
<tui-loader size="s" [inheritColor]="true" />
|
||||
{{ 'Installing' | i18n }}
|
||||
<span class="loading-dots"></span>
|
||||
{{ info() | i18n }}
|
||||
{{ getText(installingInfo.progress.overall) | i18n }}
|
||||
</h3>
|
||||
} @else {
|
||||
<h3 [class]="class()">
|
||||
{{ text() || 'Unknown' | i18n }}
|
||||
@if (text() === 'Task Required') {
|
||||
<h3 [class]="class">
|
||||
{{ text | i18n }}
|
||||
@if (text === 'Task Required') {
|
||||
<small>{{ 'See below' | i18n }}</small>
|
||||
}
|
||||
|
||||
@if (rendering().showDots) {
|
||||
@if (rendering?.showDots) {
|
||||
<span class="loading-dots"></span>
|
||||
}
|
||||
|
||||
@if ($any(pkg().status)?.started; as started) {
|
||||
<service-uptime [started]="started" />
|
||||
}
|
||||
</h3>
|
||||
}
|
||||
<ng-content />
|
||||
@@ -80,12 +76,6 @@ import {
|
||||
margin: 0 0.25rem -0.125rem 0;
|
||||
}
|
||||
|
||||
service-uptime {
|
||||
display: none;
|
||||
width: fit-content;
|
||||
margin: 0.5rem 0.125rem;
|
||||
}
|
||||
|
||||
:host-context(tui-root._mobile) {
|
||||
:host {
|
||||
min-height: 0;
|
||||
@@ -104,33 +94,32 @@ import {
|
||||
small {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
service-uptime {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
`,
|
||||
host: { class: 'g-card' },
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiLoader, i18nPipe, ServiceUptimeComponent],
|
||||
imports: [TuiLoader, i18nPipe],
|
||||
})
|
||||
export class ServiceStatusComponent {
|
||||
readonly pkg = input.required<PackageDataEntry>()
|
||||
readonly connected = input(false)
|
||||
@Input({ required: true })
|
||||
status?: PrimaryStatus
|
||||
|
||||
protected readonly status = computed((pkg = this.pkg()) =>
|
||||
pkg?.stateInfo.state === 'installed'
|
||||
? getInstalledPrimaryStatus(pkg)
|
||||
: pkg?.stateInfo.state,
|
||||
)
|
||||
@Input()
|
||||
installingInfo?: InstallingInfo
|
||||
|
||||
protected readonly rendering = computed(() => PrimaryRendering[this.status()])
|
||||
protected readonly text = computed(
|
||||
() => this.connected() && this.rendering().display,
|
||||
)
|
||||
@Input()
|
||||
connected = false
|
||||
|
||||
protected readonly class = computed(() => {
|
||||
switch (this.connected() && this.rendering().color) {
|
||||
private readonly i18n = inject(i18nPipe)
|
||||
|
||||
get text(): i18nKey {
|
||||
return this.connected ? this.rendering?.display || 'Unknown' : 'Unknown'
|
||||
}
|
||||
|
||||
get class(): string | null {
|
||||
if (!this.connected) return null
|
||||
|
||||
switch (this.rendering?.color) {
|
||||
case 'danger':
|
||||
return 'g-negative'
|
||||
case 'warning':
|
||||
@@ -142,10 +131,13 @@ export class ServiceStatusComponent {
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected readonly info = computed(
|
||||
(progress = this.pkg().stateInfo.installingInfo?.progress.overall) =>
|
||||
progress ? getProgressText(progress) : '',
|
||||
)
|
||||
get rendering() {
|
||||
return this.status && PrimaryRendering[this.status]
|
||||
}
|
||||
|
||||
getText(progress: T.Progress): i18nKey {
|
||||
return getProgressText(progress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
||||
selector: 'tr[task]',
|
||||
template: `
|
||||
<td tuiFade class="row">
|
||||
<tui-avatar appearance="action-grayscale" size="xs">
|
||||
<tui-avatar size="xs">
|
||||
<img [src]="pkg()?.icon || fallback()?.icon" alt="" />
|
||||
</tui-avatar>
|
||||
<span>{{ title() || fallback()?.title }}</span>
|
||||
@@ -79,10 +79,6 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
td:not(:last-child) {
|
||||
padding-inline-end: 1.5rem;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
|
||||
@@ -66,8 +66,7 @@ import { distinctUntilChanged } from 'rxjs/operators'
|
||||
color: var(--tui-text-primary);
|
||||
}
|
||||
|
||||
:host-context(table),
|
||||
:host-context(service-status) {
|
||||
:host-context(table) {
|
||||
padding: 0;
|
||||
|
||||
header {
|
||||
|
||||
@@ -73,7 +73,9 @@ export default class ServiceActionsRoute {
|
||||
.pipe(
|
||||
filter(pkg => pkg.stateInfo.state === 'installed'),
|
||||
map(pkg => {
|
||||
const specialGroup = Object.values(pkg.actions).some(a => !!a.group)
|
||||
const specialGroup = Object.values(pkg.actions).some(
|
||||
pkg => !!pkg.group,
|
||||
)
|
||||
? 'Other'
|
||||
: 'General'
|
||||
return {
|
||||
@@ -88,15 +90,9 @@ export default class ServiceActionsRoute {
|
||||
group: action.group || specialGroup,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.group === specialGroup && b.group !== specialGroup)
|
||||
return 1
|
||||
if (b.group === specialGroup && a.group !== specialGroup)
|
||||
return -1
|
||||
|
||||
const groupCompare = a.group.localeCompare(b.group) // sort groups lexicographically
|
||||
if (groupCompare !== 0) return groupCompare
|
||||
|
||||
return a.id.localeCompare(b.id) // sort actions within groups lexicographically
|
||||
if (a.group === specialGroup) return 1
|
||||
if (b.group === specialGroup) return -1
|
||||
return a.group.localeCompare(b.group) // Optional: sort others alphabetically
|
||||
})
|
||||
.reduce<
|
||||
Record<
|
||||
|
||||
@@ -47,9 +47,7 @@ const INACTIVE: PrimaryStatus[] = [
|
||||
</div>
|
||||
<aside class="g-aside">
|
||||
<header tuiCell routerLink="./">
|
||||
<tui-avatar appearance="action-grayscale">
|
||||
<img alt="" [src]="service()?.icon" />
|
||||
</tui-avatar>
|
||||
<tui-avatar><img alt="" [src]="service()?.icon" /></tui-avatar>
|
||||
<span tuiTitle>
|
||||
<strong tuiFade>{{ manifest()?.title }}</strong>
|
||||
<span tuiSubtitle>{{ manifest()?.version }}</span>
|
||||
|
||||
@@ -39,7 +39,11 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
} @else if (installing()) {
|
||||
<service-install-progress [pkg]="pkg" />
|
||||
} @else if (installed()) {
|
||||
<service-status [connected]="!!connected()" [pkg]="pkg">
|
||||
<service-status
|
||||
[connected]="!!connected()"
|
||||
[installingInfo]="pkg.stateInfo.installingInfo"
|
||||
[status]="status()"
|
||||
>
|
||||
@if (connected()) {
|
||||
<service-controls [pkg]="pkg" [status]="status()" />
|
||||
}
|
||||
@@ -47,8 +51,10 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
|
||||
@if (status() !== 'backingUp') {
|
||||
<service-health-checks [checks]="health()" />
|
||||
<service-uptime class="g-card" [started]="$any(pkg.status).started" />
|
||||
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
|
||||
<service-uptime
|
||||
class="g-card"
|
||||
[started]="$any(pkg.status)?.started"
|
||||
/>
|
||||
|
||||
@if (errors() | async; as errors) {
|
||||
<service-dependencies
|
||||
@@ -57,6 +63,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
[errors]="errors"
|
||||
/>
|
||||
}
|
||||
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
|
||||
|
||||
<service-tasks
|
||||
#tasks="elementRef"
|
||||
@@ -84,7 +91,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
</button>
|
||||
}
|
||||
} @else if (removing()) {
|
||||
<service-status [connected]="!!connected()" [pkg]="pkg" />
|
||||
<service-status [connected]="!!connected()" [status]="status()" />
|
||||
}
|
||||
}
|
||||
`,
|
||||
@@ -132,10 +139,6 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
||||
> * {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
service-uptime {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
host: { class: 'g-subpage' },
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||
import { AuthorityItemComponent } from './item.component'
|
||||
import { AuthorityService } from './authority.service'
|
||||
@@ -10,11 +12,15 @@ import { AuthorityService } from './authority.service'
|
||||
<tr [authority]="{ name: 'Local Root CA' }"></tr>
|
||||
@for (authority of authorityService.authorities(); track $index) {
|
||||
<tr [authority]="authority"></tr>
|
||||
} @empty {
|
||||
<td [attr.colspan]="4">
|
||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||
</td>
|
||||
}
|
||||
</table>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TableComponent, AuthorityItemComponent],
|
||||
imports: [TuiSkeleton, i18nPipe, TableComponent, AuthorityItemComponent],
|
||||
})
|
||||
export class AuthoritiesTableComponent {
|
||||
protected readonly authorityService = inject(AuthorityService)
|
||||
|
||||
@@ -18,7 +18,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
@for (pkg of pkgs() | keyvalue; track $index) {
|
||||
@if (backupProgress()?.[pkg.key]; as progress) {
|
||||
<div tuiCell>
|
||||
<tui-avatar appearance="action-grayscale">
|
||||
<tui-avatar>
|
||||
<img alt="" [src]="pkg.value.icon" />
|
||||
</tui-avatar>
|
||||
<span tuiTitle>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Component,
|
||||
inject,
|
||||
INJECTOR,
|
||||
DOCUMENT,
|
||||
} from '@angular/core'
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
@@ -150,7 +151,7 @@ import { SystemWipeComponent } from './wipe.component'
|
||||
</span>
|
||||
</span>
|
||||
@if (server.kiosk !== null) {
|
||||
<button tuiButton appearance="primary" (click)="toggleKiosk()">
|
||||
<button tuiButton appearance="primary" (click)="tryToggleKiosk()">
|
||||
{{ server.kiosk ? ('Disable' | i18n) : ('Enable' | i18n) }}
|
||||
</button>
|
||||
}
|
||||
@@ -241,6 +242,7 @@ export default class SystemGeneralComponent {
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly isTor = inject(ConfigService).isTor()
|
||||
private readonly document = inject(DOCUMENT)
|
||||
private readonly dialog = inject(DialogService)
|
||||
private readonly i18n = inject(i18nPipe)
|
||||
private readonly injector = inject(INJECTOR)
|
||||
@@ -324,6 +326,28 @@ export default class SystemGeneralComponent {
|
||||
.subscribe(() => this.resetTor(this.wipe))
|
||||
}
|
||||
|
||||
async tryToggleKiosk() {
|
||||
if (
|
||||
this.server()?.kiosk &&
|
||||
['localhost', '127.0.0.1'].includes(this.document.location.hostname)
|
||||
) {
|
||||
return this.dialog
|
||||
.openConfirm({
|
||||
label: 'Warning',
|
||||
data: {
|
||||
content:
|
||||
'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.',
|
||||
yes: 'Disable',
|
||||
no: 'Cancel',
|
||||
},
|
||||
})
|
||||
.pipe(filter(Boolean))
|
||||
.subscribe(async () => this.toggleKiosk())
|
||||
}
|
||||
|
||||
this.toggleKiosk()
|
||||
}
|
||||
|
||||
async onRepair() {
|
||||
this.dialog
|
||||
.openConfirm({
|
||||
@@ -346,7 +370,7 @@ export default class SystemGeneralComponent {
|
||||
})
|
||||
}
|
||||
|
||||
async toggleKiosk() {
|
||||
private async toggleKiosk() {
|
||||
const kiosk = this.server()?.kiosk
|
||||
|
||||
const loader = this.loader
|
||||
@@ -355,11 +379,6 @@ export default class SystemGeneralComponent {
|
||||
|
||||
try {
|
||||
await this.api.toggleKiosk(!kiosk)
|
||||
this.dialog
|
||||
.openAlert('This change will take effect after the next boot', {
|
||||
label: 'Restart to apply',
|
||||
})
|
||||
.subscribe()
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
viewChild,
|
||||
} from '@angular/core'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { verify } from '@start9labs/argon2'
|
||||
import {
|
||||
DialogService,
|
||||
ErrorService,
|
||||
i18nKey,
|
||||
i18nPipe,
|
||||
LoadingService,
|
||||
} from '@start9labs/shared'
|
||||
import { ISB } from '@start9labs/start-sdk'
|
||||
import { TuiAlertService, TuiButton, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiButton, TuiTitle } from '@taiga-ui/core'
|
||||
import { TuiHeader } from '@taiga-ui/layout'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { from } from 'rxjs'
|
||||
@@ -74,14 +70,13 @@ import { getServerInfo } from 'src/app/utils/get-server-info'
|
||||
],
|
||||
})
|
||||
export default class SystemPasswordComponent {
|
||||
private readonly alerts = inject(TuiAlertService)
|
||||
private readonly dialog = inject(DialogService)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly i18n = inject(i18nPipe)
|
||||
|
||||
readonly form = viewChild(FormComponent)
|
||||
readonly spec = toSignal(from(configBuilderToSpec(this.passwordSpec())))
|
||||
readonly buttons = [
|
||||
{
|
||||
@@ -124,12 +119,7 @@ export default class SystemPasswordComponent {
|
||||
|
||||
try {
|
||||
await this.api.resetPassword({ oldPassword, newPassword })
|
||||
this.form()?.form.reset()
|
||||
this.alerts
|
||||
.open(this.i18n.transform('Password changed'), {
|
||||
appearance: 'positive',
|
||||
})
|
||||
.subscribe()
|
||||
this.dialog.openAlert('Password changed').subscribe()
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
|
||||
@@ -45,9 +45,7 @@ import UpdatesComponent from './updates.component'
|
||||
<tr (click)="expanded.set(!expanded())">
|
||||
<td>
|
||||
<div [style.gap.rem]="0.75" [style.padding-inline-end.rem]="1">
|
||||
<tui-avatar appearance="action-grayscale" size="s">
|
||||
<img alt="" [src]="item().icon" />
|
||||
</tui-avatar>
|
||||
<tui-avatar size="s"><img alt="" [src]="item().icon" /></tui-avatar>
|
||||
<span tuiTitle [style.margin]="'-0.125rem 0 0'">
|
||||
<b tuiFade>{{ item().title }}</b>
|
||||
<span tuiSubtitle tuiFade class="mobile">
|
||||
|
||||
@@ -69,7 +69,7 @@ interface UpdatesData {
|
||||
[class.g-secondary]="current()?.url !== registry.url"
|
||||
(click)="current.set(registry)"
|
||||
>
|
||||
<tui-avatar appearance="action-grayscale">
|
||||
<tui-avatar>
|
||||
<store-icon [url]="registry.url" />
|
||||
</tui-avatar>
|
||||
<span tuiTitle>
|
||||
|
||||
@@ -110,7 +110,7 @@ export namespace Mock {
|
||||
squashfs: {
|
||||
aarch64: {
|
||||
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_aarch64.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.12/startos-0.4.0-alpha.12-33ae46f~dev_aarch64.squashfs',
|
||||
commitment: {
|
||||
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
||||
size: 1343500288,
|
||||
@@ -122,7 +122,7 @@ export namespace Mock {
|
||||
},
|
||||
'aarch64-nonfree': {
|
||||
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.12/startos-0.4.0-alpha.12-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||
commitment: {
|
||||
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
||||
size: 1653075968,
|
||||
@@ -134,7 +134,7 @@ export namespace Mock {
|
||||
},
|
||||
raspberrypi: {
|
||||
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_raspberrypi.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.12/startos-0.4.0-alpha.12-33ae46f~dev_raspberrypi.squashfs',
|
||||
commitment: {
|
||||
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
||||
size: 1490731008,
|
||||
@@ -146,7 +146,7 @@ export namespace Mock {
|
||||
},
|
||||
x86_64: {
|
||||
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_x86_64.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.12/startos-0.4.0-alpha.12-33ae46f~dev_x86_64.squashfs',
|
||||
commitment: {
|
||||
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
||||
size: 1411657728,
|
||||
@@ -158,7 +158,7 @@ export namespace Mock {
|
||||
},
|
||||
'x86_64-nonfree': {
|
||||
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_x86_64-nonfree.squashfs',
|
||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.12/startos-0.4.0-alpha.12-33ae46f~dev_x86_64-nonfree.squashfs',
|
||||
commitment: {
|
||||
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
||||
size: 1731035136,
|
||||
@@ -385,7 +385,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -420,7 +420,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -465,7 +465,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -500,7 +500,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -547,7 +547,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.5',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -595,7 +595,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release to 0.17.4',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -647,7 +647,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoin.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -682,7 +682,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://bitcoinknots.org',
|
||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: BTC_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -727,7 +727,7 @@ export namespace Mock {
|
||||
docsUrl: 'https://lightning.engineering/',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: LND_ICON,
|
||||
sourceVersion: null,
|
||||
@@ -775,7 +775,7 @@ export namespace Mock {
|
||||
marketingSite: '',
|
||||
releaseNotes: 'Upstream release and minor fixes.',
|
||||
osVersion: '0.3.6',
|
||||
sdkVersion: '0.4.0-beta.44',
|
||||
sdkVersion: '0.4.0-beta.43',
|
||||
gitHash: 'fakehash',
|
||||
icon: PROXY_ICON,
|
||||
sourceVersion: null,
|
||||
|
||||
Reference in New Issue
Block a user