mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Compare commits
9 Commits
v0.4.0-alp
...
v0.4.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24eb27f005 | ||
|
|
009d76ea35 | ||
|
|
6e8a425eb1 | ||
|
|
66188d791b | ||
|
|
015ff02d71 | ||
|
|
10bfaf5415 | ||
|
|
e3e0b85e0c | ||
|
|
ad0632892e | ||
|
|
f26791ba39 |
100
.github/workflows/start-tunnel.yaml
vendored
Normal file
100
.github/workflows/start-tunnel.yaml
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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
|
||||||
11
.github/workflows/startos-iso.yaml
vendored
11
.github/workflows/startos-iso.yaml
vendored
@@ -67,8 +67,13 @@ jobs:
|
|||||||
"ALL": ["x86_64", "aarch64"]
|
"ALL": ["x86_64", "aarch64"]
|
||||||
}')[github.event.inputs.platform || 'ALL']
|
}')[github.event.inputs.platform || 'ALL']
|
||||||
}}
|
}}
|
||||||
runs-on: ${{ fromJson('["ubuntu-22.04", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
runs-on: ${{ fromJson('["ubuntu-latest", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||||
steps:
|
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: |
|
- run: |
|
||||||
sudo mount -t tmpfs tmpfs .
|
sudo mount -t tmpfs tmpfs .
|
||||||
if: ${{ github.event.inputs.runner == 'fast' }}
|
if: ${{ github.event.inputs.runner == 'fast' }}
|
||||||
@@ -134,7 +139,7 @@ jobs:
|
|||||||
${{
|
${{
|
||||||
fromJson(
|
fromJson(
|
||||||
format(
|
format(
|
||||||
'["ubuntu-22.04", "{0}"]',
|
'["ubuntu-latest", "{0}"]',
|
||||||
fromJson('{
|
fromJson('{
|
||||||
"x86_64": "buildjet-8vcpu-ubuntu-2204",
|
"x86_64": "buildjet-8vcpu-ubuntu-2204",
|
||||||
"x86_64-nonfree": "buildjet-8vcpu-ubuntu-2204",
|
"x86_64-nonfree": "buildjet-8vcpu-ubuntu-2204",
|
||||||
@@ -267,7 +272,7 @@ jobs:
|
|||||||
index:
|
index:
|
||||||
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||||
needs: [image]
|
needs: [image]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- run: >-
|
- run: >-
|
||||||
curl "https://${{
|
curl "https://${{
|
||||||
|
|||||||
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
@@ -17,7 +17,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Run Automated Tests
|
name: Run Automated Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -40,7 +40,6 @@ STARTOS_TARGETS := $(STARTD_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_
|
|||||||
fi')
|
fi')
|
||||||
REGISTRY_TARGETS := core/target/$(RUST_ARCH)-unknown-linux-musl/$(PROFILE)/registrybox core/startos/start-registryd.service
|
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
|
TUNNEL_TARGETS := core/target/$(RUST_ARCH)-unknown-linux-musl/$(PROFILE)/tunnelbox core/startos/start-tunneld.service
|
||||||
REBUILD_TYPES = 1
|
|
||||||
|
|
||||||
ifeq ($(REMOTE),)
|
ifeq ($(REMOTE),)
|
||||||
mkdir = mkdir -p $1
|
mkdir = mkdir -p $1
|
||||||
@@ -63,7 +62,7 @@ endif
|
|||||||
|
|
||||||
.DELETE_ON_ERROR:
|
.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
|
.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
|
||||||
|
|
||||||
all: $(STARTOS_TARGETS)
|
all: $(STARTOS_TARGETS)
|
||||||
|
|
||||||
@@ -277,10 +276,9 @@ container-runtime/node_modules/.package-lock.json: container-runtime/package-loc
|
|||||||
npm --prefix container-runtime ci
|
npm --prefix container-runtime ci
|
||||||
touch container-runtime/node_modules/.package-lock.json
|
touch container-runtime/node_modules/.package-lock.json
|
||||||
|
|
||||||
sdk/base/lib/osBindings/index.ts: $(shell if [ "$(REBUILD_TYPES)" -ne 0 ]; then echo core/startos/bindings/index.ts; fi)
|
ts-bindings: core/startos/bindings/index.ts
|
||||||
mkdir -p sdk/base/lib/osBindings
|
mkdir -p sdk/base/lib/osBindings
|
||||||
rsync -ac --delete core/startos/bindings/ 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)
|
core/startos/bindings/index.ts: $(call ls-files, core) $(ENVIRONMENT_FILE)
|
||||||
rm -rf core/startos/bindings
|
rm -rf core/startos/bindings
|
||||||
|
|||||||
201
agents/VERSION_BUMP.md
Normal file
201
agents/VERSION_BUMP.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# 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,4 +1,3 @@
|
|||||||
- grub-common
|
|
||||||
- grub-efi
|
- grub-efi
|
||||||
+ parted
|
+ parted
|
||||||
+ raspberrypi-net-mods
|
+ raspberrypi-net-mods
|
||||||
|
|||||||
@@ -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
|
apply_rule OUTPUT -p tcp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||||
|
|
||||||
if [ "$UNDO" = 1 ]; then
|
if [ "$UNDO" = 1 ]; then
|
||||||
conntrack -D -p tcp -d $sip --dport $sport
|
conntrack -D -p tcp -d $sip --dport $sport || true # conntrack returns exit 1 if no connections are active
|
||||||
fi
|
fi
|
||||||
@@ -61,7 +61,7 @@ fi
|
|||||||
|
|
||||||
chroot /media/startos/next bash -e << "EOF"
|
chroot /media/startos/next bash -e << "EOF"
|
||||||
|
|
||||||
if dpkg -s grub-common 2>&1 > /dev/null; then
|
if [ -f /boot/grub/grub.cfg ]; then
|
||||||
grub-install /dev/$(eval $(lsblk -o MOUNTPOINT,PKNAME -P | grep 'MOUNTPOINT="/media/startos/root"') && echo $PKNAME)
|
grub-install /dev/$(eval $(lsblk -o MOUNTPOINT,PKNAME -P | grep 'MOUNTPOINT="/media/startos/root"') && echo $PKNAME)
|
||||||
update-grub
|
update-grub
|
||||||
fi
|
fi
|
||||||
@@ -70,7 +70,7 @@ EOF
|
|||||||
|
|
||||||
sync
|
sync
|
||||||
|
|
||||||
umount -R /media/startos/next
|
umount -Rl /media/startos/next
|
||||||
umount /media/startos/upper
|
umount /media/startos/upper
|
||||||
umount /media/startos/lower
|
umount /media/startos/lower
|
||||||
|
|
||||||
|
|||||||
2
container-runtime/package-lock.json
generated
2
container-runtime/package-lock.json
generated
@@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
"../sdk/dist": {
|
"../sdk/dist": {
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.43",
|
"version": "0.4.0-beta.44",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^3.0.0",
|
"@iarna/toml": "^3.0.0",
|
||||||
|
|||||||
@@ -158,6 +158,8 @@ export class RpcListener {
|
|||||||
|
|
||||||
this.unixSocketServer.listen(SOCKET_PATH)
|
this.unixSocketServer.listen(SOCKET_PATH)
|
||||||
|
|
||||||
|
console.log("Listening on %s", SOCKET_PATH)
|
||||||
|
|
||||||
this.unixSocketServer.on("connection", (s) => {
|
this.unixSocketServer.on("connection", (s) => {
|
||||||
let id: IdType = null
|
let id: IdType = null
|
||||||
const captureId = <X>(x: X) => {
|
const captureId = <X>(x: X) => {
|
||||||
|
|||||||
2
core/Cargo.lock
generated
2
core/Cargo.lock
generated
@@ -7908,7 +7908,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "start-os"
|
name = "start-os"
|
||||||
version = "0.4.0-alpha.13"
|
version = "0.4.0-alpha.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.7.5",
|
"aes 0.7.5",
|
||||||
"arti-client",
|
"arti-client",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ if tty -s; then
|
|||||||
USE_TTY="-it"
|
USE_TTY="-it"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
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'
|
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'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
else
|
else
|
||||||
if [ "$PROFILE" != "debug"]; then
|
if [ "$PROFILE" != "debug"]; then
|
||||||
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
|
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
|
||||||
PROFILE=debug
|
PROFILE=debug
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ license = "MIT"
|
|||||||
name = "start-os"
|
name = "start-os"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/Start9Labs/start-os"
|
repository = "https://github.com/Start9Labs/start-os"
|
||||||
version = "0.4.0-alpha.13" # VERSION_BUMP
|
version = "0.4.0-alpha.15" # VERSION_BUMP
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "startos"
|
name = "startos"
|
||||||
|
|||||||
@@ -260,11 +260,7 @@ impl NetworkInterfaceInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn secure(&self) -> bool {
|
pub fn secure(&self) -> bool {
|
||||||
self.secure.unwrap_or_else(|| {
|
self.secure.unwrap_or(false)
|
||||||
self.ip_info.as_ref().map_or(false, |ip_info| {
|
|
||||||
ip_info.device_type == Some(NetworkInterfaceType::Wireguard)
|
|
||||||
}) && !self.public()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use crate::registry::context::{RegistryContext, RegistryUrlParams};
|
|||||||
use crate::registry::package::get::GetPackageResponse;
|
use crate::registry::package::get::GetPackageResponse;
|
||||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||||
use crate::s9pk::manifest::PackageId;
|
use crate::s9pk::manifest::PackageId;
|
||||||
|
use crate::s9pk::v2::SIG_CONTEXT;
|
||||||
use crate::upload::upload;
|
use crate::upload::upload;
|
||||||
use crate::util::Never;
|
use crate::util::Never;
|
||||||
use crate::util::io::open_file;
|
use crate::util::io::open_file;
|
||||||
@@ -154,6 +155,8 @@ pub async fn install(
|
|||||||
})?
|
})?
|
||||||
.s9pk;
|
.s9pk;
|
||||||
|
|
||||||
|
asset.validate(SIG_CONTEXT, asset.all_signers())?;
|
||||||
|
|
||||||
let progress_tracker = FullProgressTracker::new();
|
let progress_tracker = FullProgressTracker::new();
|
||||||
let download_progress = progress_tracker.add_phase("Downloading".into(), Some(100));
|
let download_progress = progress_tracker.add_phase("Downloading".into(), Some(100));
|
||||||
let download = ctx
|
let download = ctx
|
||||||
|
|||||||
@@ -366,6 +366,7 @@ impl LxcContainer {
|
|||||||
}
|
}
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
}
|
}
|
||||||
|
tracing::info!("Connected to socket in {:?}", started.elapsed());
|
||||||
Ok(UnixRpcClient::new(sock_path))
|
Ok(UnixRpcClient::new(sock_path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,10 +217,15 @@ where
|
|||||||
.write_all(&buffered)
|
.write_all(&buffered)
|
||||||
.await
|
.await
|
||||||
.with_kind(ErrorKind::Network)?;
|
.with_kind(ErrorKind::Network)?;
|
||||||
return Ok(Some((
|
let stream = match mid.into_stream(Arc::new(cfg)).await {
|
||||||
metadata,
|
Ok(stream) => Box::pin(stream) as AcceptStream,
|
||||||
Box::pin(mid.into_stream(Arc::new(cfg)).await?) as AcceptStream,
|
Err(e) => {
|
||||||
)));
|
tracing::trace!("Error completing TLS handshake: {e}");
|
||||||
|
tracing::trace!("{e:?}");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Ok(Some((metadata, stream)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|||||||
@@ -649,16 +649,6 @@ async fn torctl(
|
|||||||
.invoke(ErrorKind::Tor)
|
.invoke(ErrorKind::Tor)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let logs = journalctl(
|
|
||||||
LogSource::Unit(SYSTEMD_UNIT),
|
|
||||||
Some(0),
|
|
||||||
None,
|
|
||||||
Some("0"),
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut tcp_stream = None;
|
let mut tcp_stream = None;
|
||||||
for _ in 0..60 {
|
for _ in 0..60 {
|
||||||
if let Ok(conn) = TcpStream::connect(tor_control).await {
|
if let Ok(conn) = TcpStream::connect(tor_control).await {
|
||||||
@@ -720,7 +710,7 @@ async fn torctl(
|
|||||||
ErrorKind::Tor,
|
ErrorKind::Tor,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok((connection, logs))
|
Ok(connection)
|
||||||
};
|
};
|
||||||
let pre_handler = async {
|
let pre_handler = async {
|
||||||
while let Some(command) = recv.recv().await {
|
while let Some(command) = recv.recv().await {
|
||||||
@@ -745,7 +735,7 @@ async fn torctl(
|
|||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut connection, mut logs) = tokio::select! {
|
let mut connection = tokio::select! {
|
||||||
res = bootstrap => res?,
|
res = bootstrap => res?,
|
||||||
res = pre_handler => return res,
|
res = pre_handler => return res,
|
||||||
};
|
};
|
||||||
@@ -851,46 +841,59 @@ async fn torctl(
|
|||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let log_parser = async {
|
let log_parser = async {
|
||||||
while let Some(log) = logs.try_next().await? {
|
loop {
|
||||||
for (regex, severity) in &*LOG_REGEXES {
|
let mut logs = journalctl(
|
||||||
if regex.is_match(&log.message) {
|
LogSource::Unit(SYSTEMD_UNIT),
|
||||||
let (check, wipe_state) = match severity {
|
Some(0),
|
||||||
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
|
None,
|
||||||
ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state),
|
Some("0"),
|
||||||
};
|
false,
|
||||||
let addr = hck_key.public().get_onion_address().to_string();
|
true,
|
||||||
if !check
|
)
|
||||||
|| TcpStream::connect(tor_socks)
|
.await?;
|
||||||
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
while let Some(log) = logs.try_next().await? {
|
||||||
.and_then(|mut tor_socks| async move {
|
for (regex, severity) in &*LOG_REGEXES {
|
||||||
tokio::time::timeout(
|
if regex.is_match(&log.message) {
|
||||||
Duration::from_secs(30),
|
let (check, wipe_state) = match severity {
|
||||||
socks5_impl::client::connect(&mut tor_socks, (addr, 80), None)
|
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
|
||||||
.map_err(|e| Error::new(e, ErrorKind::Tor)),
|
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))
|
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||||
.await?
|
.and_then(|mut tor_socks| async move {
|
||||||
})
|
tokio::time::timeout(
|
||||||
.await
|
Duration::from_secs(30),
|
||||||
.with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down"))
|
socks5_impl::client::connect(
|
||||||
.log_err()
|
&mut tor_socks,
|
||||||
.is_some()
|
(addr, 80),
|
||||||
{
|
None,
|
||||||
if wipe_state {
|
)
|
||||||
Command::new("systemctl")
|
.map_err(|e| Error::new(e, ErrorKind::Tor)),
|
||||||
.arg("stop")
|
)
|
||||||
.arg("tor")
|
.map_err(|e| Error::new(e, ErrorKind::Tor))
|
||||||
.invoke(ErrorKind::Tor)
|
.await?
|
||||||
.await?;
|
})
|
||||||
tokio::fs::remove_dir_all("/var/lib/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));
|
||||||
}
|
}
|
||||||
return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Err(Error::new(eyre!("Log stream terminated"), ErrorKind::Tor))
|
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
let health_checker = async {
|
let health_checker = async {
|
||||||
let mut last_success = Instant::now();
|
let mut last_success = Instant::now();
|
||||||
@@ -960,20 +963,23 @@ impl TorControl {
|
|||||||
_thread: tokio::spawn(async move {
|
_thread: tokio::spawn(async move {
|
||||||
let wipe_state = AtomicBool::new(false);
|
let wipe_state = AtomicBool::new(false);
|
||||||
let mut health_timeout = Duration::from_secs(STARTING_HEALTH_TIMEOUT);
|
let mut health_timeout = Duration::from_secs(STARTING_HEALTH_TIMEOUT);
|
||||||
while let Err(e) = torctl(
|
loop {
|
||||||
tor_control,
|
if let Err(e) = torctl(
|
||||||
tor_socks,
|
tor_control,
|
||||||
&mut recv,
|
tor_socks,
|
||||||
&mut thread_services,
|
&mut recv,
|
||||||
&wipe_state,
|
&mut thread_services,
|
||||||
&mut health_timeout,
|
&wipe_state,
|
||||||
)
|
&mut health_timeout,
|
||||||
.await
|
)
|
||||||
{
|
.await
|
||||||
tracing::error!("{e}: Restarting tor");
|
{
|
||||||
tracing::debug!("{e:?}");
|
tracing::error!("TorControl : {e}");
|
||||||
|
tracing::debug!("{e:?}");
|
||||||
|
}
|
||||||
|
tracing::info!("Restarting Tor");
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
tracing::info!("TorControl is shut down.")
|
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
send,
|
send,
|
||||||
|
|||||||
@@ -39,6 +39,23 @@ pub struct AddTunnelParams {
|
|||||||
public: bool,
|
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(
|
pub async fn add_tunnel(
|
||||||
ctx: RpcContext,
|
ctx: RpcContext,
|
||||||
AddTunnelParams {
|
AddTunnelParams {
|
||||||
@@ -86,7 +103,7 @@ pub async fn add_tunnel(
|
|||||||
|
|
||||||
let tmpdir = TmpDir::new().await?;
|
let tmpdir = TmpDir::new().await?;
|
||||||
let conf = tmpdir.join(&iface).with_extension("conf");
|
let conf = tmpdir.join(&iface).with_extension("conf");
|
||||||
write_file_atomic(&conf, &config).await?;
|
write_file_atomic(&conf, &sanitize_config(&config)).await?;
|
||||||
Command::new("nmcli")
|
Command::new("nmcli")
|
||||||
.arg("connection")
|
.arg("connection")
|
||||||
.arg("import")
|
.arg("import")
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ impl FullProgressTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn progress_bar_task(&self, name: &str) -> NonDetachingJoinHandle<()> {
|
pub fn progress_bar_task(&self, name: &str) -> NonDetachingJoinHandle<()> {
|
||||||
let mut stream = self.stream(None);
|
let mut stream = self.stream(Some(Duration::from_millis(200)));
|
||||||
let mut bar = PhasedProgressBar::new(name);
|
let mut bar = PhasedProgressBar::new(name);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(progress) = stream.next().await {
|
while let Some(progress) = stream.next().await {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
@@ -84,6 +85,26 @@ impl RegistryAsset<MerkleArchiveCommitment> {
|
|||||||
)
|
)
|
||||||
.await
|
.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 {
|
pub struct BufferedHttpSource {
|
||||||
@@ -91,6 +112,19 @@ pub struct BufferedHttpSource {
|
|||||||
file: UploadingFile,
|
file: UploadingFile,
|
||||||
}
|
}
|
||||||
impl BufferedHttpSource {
|
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(
|
pub async fn new(
|
||||||
client: Client,
|
client: Client,
|
||||||
url: Url,
|
url: Url,
|
||||||
@@ -103,6 +137,9 @@ impl BufferedHttpSource {
|
|||||||
file,
|
file,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub async fn wait_for_buffered(&self) -> Result<(), Error> {
|
||||||
|
self.file.wait_for_complete().await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl ArchiveSource for BufferedHttpSource {
|
impl ArchiveSource for BufferedHttpSource {
|
||||||
type FetchReader = <UploadingFile as ArchiveSource>::FetchReader;
|
type FetchReader = <UploadingFile as ArchiveSource>::FetchReader;
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use exver::{ExtendedVersion, VersionRange};
|
use exver::{ExtendedVersion, VersionRange};
|
||||||
use imbl_value::InternedString;
|
use helpers::to_tmp_path;
|
||||||
|
use imbl_value::{InternedString, json};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use models::PackageId;
|
use models::PackageId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
use crate::context::CliContext;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::progress::{FullProgressTracker, ProgressUnits};
|
||||||
use crate::registry::context::RegistryContext;
|
use crate::registry::context::RegistryContext;
|
||||||
use crate::registry::device_info::DeviceInfo;
|
use crate::registry::device_info::DeviceInfo;
|
||||||
use crate::registry::package::index::{PackageIndex, PackageVersionInfo};
|
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::VersionString;
|
||||||
|
use crate::util::io::TrackingIO;
|
||||||
use crate::util::serde::{WithIoFormat, display_serializable};
|
use crate::util::serde::{WithIoFormat, display_serializable};
|
||||||
|
use crate::util::tui::choose;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS, ValueEnum,
|
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS, ValueEnum,
|
||||||
@@ -352,8 +360,7 @@ pub fn display_package_info(
|
|||||||
info: Value,
|
info: Value,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if let Some(format) = params.format {
|
if let Some(format) = params.format {
|
||||||
display_serializable(format, info);
|
return display_serializable(format, info);
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = params.rest.id {
|
if let Some(_) = params.rest.id {
|
||||||
@@ -387,3 +394,90 @@ pub fn display_package_info(
|
|||||||
}
|
}
|
||||||
Ok(())
|
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};
|
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async, from_fn_async_local};
|
||||||
|
|
||||||
use crate::context::CliContext;
|
use crate::context::CliContext;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@@ -54,6 +54,12 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
|||||||
.with_about("List installation candidate package(s)")
|
.with_about("List installation candidate package(s)")
|
||||||
.with_call_remote::<CliContext>(),
|
.with_call_remote::<CliContext>(),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
"download",
|
||||||
|
from_fn_async_local(get::cli_download)
|
||||||
|
.no_display()
|
||||||
|
.with_about("Download an s9pk"),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
"category",
|
"category",
|
||||||
category::category_api::<C>()
|
category::category_api::<C>()
|
||||||
|
|||||||
@@ -106,7 +106,9 @@ pub struct ExecParams {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pty_size: Option<TermSize>,
|
pty_size: Option<TermSize>,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
env: Option<PathBuf>,
|
env: Vec<String>,
|
||||||
|
#[arg(long)]
|
||||||
|
env_file: Option<PathBuf>,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
workdir: Option<PathBuf>,
|
workdir: Option<PathBuf>,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
@@ -119,6 +121,7 @@ impl ExecParams {
|
|||||||
fn exec(&self) -> Result<(), Error> {
|
fn exec(&self) -> Result<(), Error> {
|
||||||
let ExecParams {
|
let ExecParams {
|
||||||
env,
|
env,
|
||||||
|
env_file,
|
||||||
workdir,
|
workdir,
|
||||||
user,
|
user,
|
||||||
chroot,
|
chroot,
|
||||||
@@ -131,14 +134,15 @@ impl ExecParams {
|
|||||||
ErrorKind::InvalidRequest,
|
ErrorKind::InvalidRequest,
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let env_string = if let Some(env) = &env {
|
let env_string = if let Some(env_file) = &env_file {
|
||||||
std::fs::read_to_string(env)
|
std::fs::read_to_string(env_file)
|
||||||
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("read {env:?}")))?
|
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("read {env:?}")))?
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
let env = env_string
|
let env = env_string
|
||||||
.lines()
|
.lines()
|
||||||
|
.chain(env.iter().map(|l| l.as_str()))
|
||||||
.map(|l| l.trim())
|
.map(|l| l.trim())
|
||||||
.filter_map(|l| l.split_once("="))
|
.filter_map(|l| l.split_once("="))
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
@@ -199,6 +203,7 @@ pub fn launch(
|
|||||||
force_stderr_tty,
|
force_stderr_tty,
|
||||||
pty_size,
|
pty_size,
|
||||||
env,
|
env,
|
||||||
|
env_file,
|
||||||
workdir,
|
workdir,
|
||||||
user,
|
user,
|
||||||
chroot,
|
chroot,
|
||||||
@@ -294,8 +299,11 @@ pub fn launch(
|
|||||||
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
||||||
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
||||||
cmd = cmd.arg("subcontainer").arg("launch-init");
|
cmd = cmd.arg("subcontainer").arg("launch-init");
|
||||||
if let Some(env) = env {
|
for env in env {
|
||||||
cmd = cmd.arg("--env").arg(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(workdir) = workdir {
|
if let Some(workdir) = workdir {
|
||||||
cmd = cmd.arg("--workdir").arg(workdir);
|
cmd = cmd.arg("--workdir").arg(workdir);
|
||||||
@@ -349,8 +357,11 @@ pub fn launch(
|
|||||||
} else {
|
} else {
|
||||||
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
||||||
cmd.arg("subcontainer").arg("launch-init");
|
cmd.arg("subcontainer").arg("launch-init");
|
||||||
if let Some(env) = env {
|
for env in env {
|
||||||
cmd.arg("--env").arg(env);
|
cmd.arg("-e").arg(env);
|
||||||
|
}
|
||||||
|
if let Some(env_file) = env_file {
|
||||||
|
cmd.arg("--env-file").arg(env_file);
|
||||||
}
|
}
|
||||||
if let Some(workdir) = workdir {
|
if let Some(workdir) = workdir {
|
||||||
cmd.arg("--workdir").arg(workdir);
|
cmd.arg("--workdir").arg(workdir);
|
||||||
@@ -441,6 +452,7 @@ pub fn exec(
|
|||||||
force_stderr_tty,
|
force_stderr_tty,
|
||||||
pty_size,
|
pty_size,
|
||||||
env,
|
env,
|
||||||
|
env_file,
|
||||||
workdir,
|
workdir,
|
||||||
user,
|
user,
|
||||||
chroot,
|
chroot,
|
||||||
@@ -544,8 +556,11 @@ pub fn exec(
|
|||||||
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
|
||||||
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
|
||||||
cmd = cmd.arg("subcontainer").arg("exec-command");
|
cmd = cmd.arg("subcontainer").arg("exec-command");
|
||||||
if let Some(env) = env {
|
for env in env {
|
||||||
cmd = cmd.arg("--env").arg(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(workdir) = workdir {
|
if let Some(workdir) = workdir {
|
||||||
cmd = cmd.arg("--workdir").arg(workdir);
|
cmd = cmd.arg("--workdir").arg(workdir);
|
||||||
@@ -599,8 +614,11 @@ pub fn exec(
|
|||||||
} else {
|
} else {
|
||||||
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
let mut cmd = StdCommand::new("/usr/bin/start-container");
|
||||||
cmd.arg("subcontainer").arg("exec-command");
|
cmd.arg("subcontainer").arg("exec-command");
|
||||||
if let Some(env) = env {
|
for env in env {
|
||||||
cmd.arg("--env").arg(env);
|
cmd.arg("-e").arg(env);
|
||||||
|
}
|
||||||
|
if let Some(env_file) = env_file {
|
||||||
|
cmd.arg("--env-file").arg(env_file);
|
||||||
}
|
}
|
||||||
if let Some(workdir) = workdir {
|
if let Some(workdir) = workdir {
|
||||||
cmd.arg("--workdir").arg(workdir);
|
cmd.arg("--workdir").arg(workdir);
|
||||||
|
|||||||
@@ -885,7 +885,7 @@ pub async fn attach(
|
|||||||
.arg("start-container")
|
.arg("start-container")
|
||||||
.arg("subcontainer")
|
.arg("subcontainer")
|
||||||
.arg("exec")
|
.arg("exec")
|
||||||
.arg("--env")
|
.arg("--env-file")
|
||||||
.arg(
|
.arg(
|
||||||
Path::new("/media/startos/images")
|
Path::new("/media/startos/images")
|
||||||
.join(image_id)
|
.join(image_id)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ use crate::util::rpc_client::UnixRpcClient;
|
|||||||
use crate::volume::data_dir;
|
use crate::volume::data_dir;
|
||||||
use crate::{ARCH, DATA_DIR, PACKAGE_DATA};
|
use crate::{ARCH, DATA_DIR, PACKAGE_DATA};
|
||||||
|
|
||||||
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
|
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ServiceState {
|
pub struct ServiceState {
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ impl ServiceMap {
|
|||||||
match Service::load(ctx, id, disposition).await {
|
match Service::load(ctx, id, disposition).await {
|
||||||
Ok(s) => *service = s.into(),
|
Ok(s) => *service = s.into(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
tracing::error!("Error loading service: {e}");
|
||||||
|
tracing::debug!("{e:?}");
|
||||||
let e = ErrorData::from(e);
|
let e = ErrorData::from(e);
|
||||||
ctx.db
|
ctx.db
|
||||||
.mutate(|db| {
|
.mutate(|db| {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use imbl::HashMap;
|
|||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use patch_db::HasModel;
|
use patch_db::HasModel;
|
||||||
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
@@ -113,27 +113,12 @@ impl AuthContext for TunnelContext {
|
|||||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS, Parser)]
|
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS, Parser)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[model = "Model<Self>"]
|
#[model = "Model<Self>"]
|
||||||
#[ts(export)]
|
|
||||||
pub struct SignerInfo {
|
pub struct SignerInfo {
|
||||||
pub name: InternedString,
|
pub name: InternedString,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
||||||
ParentHandler::new()
|
crate::auth::auth::<C, TunnelContext>()
|
||||||
.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", from_fn_async(set_password_rpc).no_cli())
|
||||||
.subcommand(
|
.subcommand(
|
||||||
"set-password",
|
"set-password",
|
||||||
@@ -173,19 +158,15 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
|||||||
.with_display_serializable()
|
.with_display_serializable()
|
||||||
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||||
use prettytable::*;
|
use prettytable::*;
|
||||||
|
|
||||||
if let Some(format) = params.format {
|
if let Some(format) = params.format {
|
||||||
return display_serializable(format, res);
|
return display_serializable(format, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.add_row(row![bc => "NAME", "KEY"]);
|
table.add_row(row![bc => "NAME", "KEY"]);
|
||||||
for (key, info) in res {
|
for (key, info) in res {
|
||||||
table.add_row(row![info.name, key]);
|
table.add_row(row![info.name, key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.print_tty(false)?;
|
table.print_tty(false)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.with_about("List authorized keys")
|
.with_about("List authorized keys")
|
||||||
@@ -194,7 +175,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddKeyParams {
|
pub struct AddKeyParams {
|
||||||
pub name: InternedString,
|
pub name: InternedString,
|
||||||
@@ -216,7 +197,7 @@ pub async fn add_key(
|
|||||||
.result
|
.result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveKeyParams {
|
pub struct RemoveKeyParams {
|
||||||
pub key: AnyVerifyingKey,
|
pub key: AnyVerifyingKey,
|
||||||
@@ -240,7 +221,7 @@ pub async fn list_keys(ctx: TunnelContext) -> Result<HashMap<AnyVerifyingKey, Si
|
|||||||
ctx.db.peek().await.into_auth_pubkeys().de()
|
ctx.db.peek().await.into_auth_pubkeys().de()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||||
pub struct SetPasswordParams {
|
pub struct SetPasswordParams {
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
|
use std::path::Path;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
@@ -56,6 +57,7 @@ struct Progress {
|
|||||||
tracker: PhaseProgressTrackerHandle,
|
tracker: PhaseProgressTrackerHandle,
|
||||||
expected_size: Option<u64>,
|
expected_size: Option<u64>,
|
||||||
written: u64,
|
written: u64,
|
||||||
|
complete: bool,
|
||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
}
|
}
|
||||||
impl Progress {
|
impl Progress {
|
||||||
@@ -111,9 +113,7 @@ impl Progress {
|
|||||||
}
|
}
|
||||||
async fn ready(watch: &mut watch::Receiver<Self>) -> Result<(), Error> {
|
async fn ready(watch: &mut watch::Receiver<Self>) -> Result<(), Error> {
|
||||||
match &*watch
|
match &*watch
|
||||||
.wait_for(|progress| {
|
.wait_for(|progress| progress.error.is_some() || progress.complete)
|
||||||
progress.error.is_some() || Some(progress.written) == progress.expected_size
|
|
||||||
})
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::new(
|
Error::new(
|
||||||
@@ -126,8 +126,9 @@ impl Progress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn complete(&mut self) -> bool {
|
fn complete(&mut self) -> bool {
|
||||||
|
let mut changed = !self.complete;
|
||||||
self.tracker.complete();
|
self.tracker.complete();
|
||||||
match self {
|
changed |= match self {
|
||||||
Self {
|
Self {
|
||||||
expected_size: Some(size),
|
expected_size: Some(size),
|
||||||
written,
|
written,
|
||||||
@@ -165,18 +166,21 @@ impl Progress {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
};
|
||||||
|
self.complete = true;
|
||||||
|
changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UploadingFile {
|
pub struct UploadingFile {
|
||||||
tmp_dir: Arc<TmpDir>,
|
tmp_dir: Option<Arc<TmpDir>>,
|
||||||
file: MultiCursorFile,
|
file: MultiCursorFile,
|
||||||
progress: watch::Receiver<Progress>,
|
progress: watch::Receiver<Progress>,
|
||||||
}
|
}
|
||||||
impl UploadingFile {
|
impl UploadingFile {
|
||||||
pub async fn new(
|
pub async fn with_path(
|
||||||
|
path: impl AsRef<Path>,
|
||||||
mut progress: PhaseProgressTrackerHandle,
|
mut progress: PhaseProgressTrackerHandle,
|
||||||
) -> Result<(UploadHandle, Self), Error> {
|
) -> Result<(UploadHandle, Self), Error> {
|
||||||
progress.set_units(Some(ProgressUnits::Bytes));
|
progress.set_units(Some(ProgressUnits::Bytes));
|
||||||
@@ -185,25 +189,35 @@ impl UploadingFile {
|
|||||||
expected_size: None,
|
expected_size: None,
|
||||||
written: 0,
|
written: 0,
|
||||||
error: None,
|
error: None,
|
||||||
|
complete: false,
|
||||||
});
|
});
|
||||||
let tmp_dir = Arc::new(TmpDir::new().await?);
|
let file = create_file(path).await?;
|
||||||
let file = create_file(tmp_dir.join("upload.tmp")).await?;
|
|
||||||
let uploading = Self {
|
let uploading = Self {
|
||||||
tmp_dir: tmp_dir.clone(),
|
tmp_dir: None,
|
||||||
file: MultiCursorFile::open(&file).await?,
|
file: MultiCursorFile::open(&file).await?,
|
||||||
progress: progress.1,
|
progress: progress.1,
|
||||||
};
|
};
|
||||||
Ok((
|
Ok((
|
||||||
UploadHandle {
|
UploadHandle {
|
||||||
tmp_dir,
|
tmp_dir: None,
|
||||||
file,
|
file,
|
||||||
progress: progress.0,
|
progress: progress.0,
|
||||||
},
|
},
|
||||||
uploading,
|
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> {
|
pub async fn delete(self) -> Result<(), Error> {
|
||||||
if let Ok(tmp_dir) = Arc::try_unwrap(self.tmp_dir) {
|
if let Some(Ok(tmp_dir)) = self.tmp_dir.map(Arc::try_unwrap) {
|
||||||
tmp_dir.delete().await?;
|
tmp_dir.delete().await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -234,7 +248,7 @@ impl ArchiveSource for UploadingFile {
|
|||||||
|
|
||||||
#[pin_project::pin_project(project = UploadingFileReaderProjection)]
|
#[pin_project::pin_project(project = UploadingFileReaderProjection)]
|
||||||
pub struct UploadingFileReader {
|
pub struct UploadingFileReader {
|
||||||
tmp_dir: Arc<TmpDir>,
|
tmp_dir: Option<Arc<TmpDir>>,
|
||||||
position: u64,
|
position: u64,
|
||||||
to_seek: Option<SeekFrom>,
|
to_seek: Option<SeekFrom>,
|
||||||
#[pin]
|
#[pin]
|
||||||
@@ -330,7 +344,7 @@ impl AsyncSeek for UploadingFileReader {
|
|||||||
|
|
||||||
#[pin_project::pin_project(PinnedDrop)]
|
#[pin_project::pin_project(PinnedDrop)]
|
||||||
pub struct UploadHandle {
|
pub struct UploadHandle {
|
||||||
tmp_dir: Arc<TmpDir>,
|
tmp_dir: Option<Arc<TmpDir>>,
|
||||||
#[pin]
|
#[pin]
|
||||||
file: File,
|
file: File,
|
||||||
progress: watch::Sender<Progress>,
|
progress: watch::Sender<Progress>,
|
||||||
@@ -377,6 +391,9 @@ impl UploadHandle {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Err(e) = self.file.sync_all().await {
|
||||||
|
self.progress.send_if_modified(|p| p.handle_error(&e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[pin_project::pinned_drop]
|
#[pin_project::pinned_drop]
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ mod v0_4_0_alpha_10;
|
|||||||
mod v0_4_0_alpha_11;
|
mod v0_4_0_alpha_11;
|
||||||
mod v0_4_0_alpha_12;
|
mod v0_4_0_alpha_12;
|
||||||
mod v0_4_0_alpha_13;
|
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_13::Version; // VERSION_BUMP
|
pub type Current = v0_4_0_alpha_15::Version; // VERSION_BUMP
|
||||||
|
|
||||||
impl Current {
|
impl Current {
|
||||||
#[instrument(skip(self, db))]
|
#[instrument(skip(self, db))]
|
||||||
@@ -169,7 +171,9 @@ enum Version {
|
|||||||
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::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_11(Wrapper<v0_4_0_alpha_11::Version>),
|
||||||
V0_4_0_alpha_12(Wrapper<v0_4_0_alpha_12::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>), // VERSION_BUMP
|
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
|
||||||
Other(exver::Version),
|
Other(exver::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +229,9 @@ impl Version {
|
|||||||
Self::V0_4_0_alpha_10(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_11(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_4_0_alpha_12(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)), // VERSION_BUMP
|
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::Other(v) => {
|
Self::Other(v) => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
eyre!("unknown version {v}"),
|
eyre!("unknown version {v}"),
|
||||||
@@ -273,7 +279,9 @@ impl Version {
|
|||||||
Version::V0_4_0_alpha_10(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_11(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_4_0_alpha_12(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_BUMP
|
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::Other(x) => x.clone(),
|
Version::Other(x) => x.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use exver::{PreReleaseSegment, VersionRange};
|
|||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
|
|
||||||
use super::v0_3_5::V0_3_0_COMPAT;
|
use super::v0_3_5::V0_3_0_COMPAT;
|
||||||
use super::{VersionT, v0_4_0_alpha_11};
|
use super::{v0_4_0_alpha_11, VersionT};
|
||||||
use crate::net::tor::TorSecretKey;
|
use crate::net::tor::TorSecretKey;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -75,7 +75,10 @@ impl VersionT for Version {
|
|||||||
}
|
}
|
||||||
fix_host(&mut db["public"]["serverInfo"]["network"]["host"])?;
|
fix_host(&mut db["public"]["serverInfo"]["network"]["host"])?;
|
||||||
|
|
||||||
db["private"]["keyStore"]["localCerts"] = db["private"]["keyStore"]["local_certs"].clone();
|
if db["private"]["keyStore"]["localCerts"].is_null() {
|
||||||
|
db["private"]["keyStore"]["localCerts"] =
|
||||||
|
db["private"]["keyStore"]["local_certs"].clone();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Null)
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
|
|||||||
37
core/startos/src/version/v0_4_0_alpha_14.rs
Normal file
37
core/startos/src/version/v0_4_0_alpha_14.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use exver::{PreReleaseSegment, VersionRange};
|
||||||
|
|
||||||
|
use super::v0_3_5::V0_3_0_COMPAT;
|
||||||
|
use super::{VersionT, v0_4_0_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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
37
core/startos/src/version/v0_4_0_alpha_15.rs
Normal file
37
core/startos/src/version/v0_4_0_alpha_15.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use exver::{PreReleaseSegment, VersionRange};
|
||||||
|
|
||||||
|
use super::v0_3_5::V0_3_0_COMPAT;
|
||||||
|
use super::{VersionT, v0_4_0_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,6 +205,7 @@ fi
|
|||||||
useradd --shell /bin/bash -G startos -m start9
|
useradd --shell /bin/bash -G startos -m start9
|
||||||
echo start9:embassy | chpasswd
|
echo start9:embassy | chpasswd
|
||||||
usermod -aG sudo start9
|
usermod -aG sudo start9
|
||||||
|
usermod -aG systemd-journal start9
|
||||||
|
|
||||||
echo "start9 ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/010_start9-nopasswd"
|
echo "start9 ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee "/etc/sudoers.d/010_start9-nopasswd"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// 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 }
|
export type SignerInfo = {
|
||||||
|
name: string
|
||||||
|
contact: Array<ContactInfo>
|
||||||
|
keys: Array<AnyVerifyingKey>
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { knownProtocols } from "../interfaces/Host"
|
|||||||
import { AddressInfo, Host, Hostname, HostnameInfo } from "../types"
|
import { AddressInfo, Host, Hostname, HostnameInfo } from "../types"
|
||||||
import { Effects } from "../Effects"
|
import { Effects } from "../Effects"
|
||||||
import { DropGenerator, DropPromise } from "./Drop"
|
import { DropGenerator, DropPromise } from "./Drop"
|
||||||
import { IPV6_LINK_LOCAL } from "./ip"
|
import { IpAddress, IPV6_LINK_LOCAL } from "./ip"
|
||||||
|
|
||||||
export type UrlString = string
|
export type UrlString = string
|
||||||
export type HostId = string
|
export type HostId = string
|
||||||
@@ -17,7 +17,15 @@ export const getHostname = (url: string): Hostname | null => {
|
|||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterKinds = "onion" | "local" | "domain" | "ip" | "ipv4" | "ipv6"
|
type FilterKinds =
|
||||||
|
| "onion"
|
||||||
|
| "local"
|
||||||
|
| "domain"
|
||||||
|
| "ip"
|
||||||
|
| "ipv4"
|
||||||
|
| "ipv6"
|
||||||
|
| "localhost"
|
||||||
|
| "link-local"
|
||||||
export type Filter = {
|
export type Filter = {
|
||||||
visibility?: "public" | "private"
|
visibility?: "public" | "private"
|
||||||
kind?: FilterKinds | FilterKinds[]
|
kind?: FilterKinds | FilterKinds[]
|
||||||
@@ -72,6 +80,12 @@ type FilterReturnTy<F extends Filter> = F extends {
|
|||||||
: Exclude<HostnameInfo, FilterReturnTy<E>>
|
: Exclude<HostnameInfo, FilterReturnTy<E>>
|
||||||
: HostnameInfo
|
: HostnameInfo
|
||||||
|
|
||||||
|
const defaultFilter = {
|
||||||
|
exclude: {
|
||||||
|
kind: ["localhost", "link-local"] as ("localhost" | "link-local")[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
type Formats = "hostname-info" | "urlstring" | "url"
|
type Formats = "hostname-info" | "urlstring" | "url"
|
||||||
type FormatReturnTy<
|
type FormatReturnTy<
|
||||||
F extends Filter,
|
F extends Filter,
|
||||||
@@ -92,8 +106,11 @@ export type Filled = {
|
|||||||
sslUrl: UrlString | null
|
sslUrl: UrlString | null
|
||||||
}
|
}
|
||||||
|
|
||||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
filter: <
|
||||||
filter: F,
|
F extends Filter = typeof defaultFilter,
|
||||||
|
Format extends Formats = "urlstring",
|
||||||
|
>(
|
||||||
|
filter?: F,
|
||||||
format?: Format,
|
format?: Format,
|
||||||
) => FormatReturnTy<F, Format>[]
|
) => FormatReturnTy<F, Format>[]
|
||||||
|
|
||||||
@@ -215,7 +232,13 @@ function filterRec(
|
|||||||
h.kind === "ip" &&
|
h.kind === "ip" &&
|
||||||
h.hostname.kind === "domain") ||
|
h.hostname.kind === "domain") ||
|
||||||
(kind.has("ipv4") && h.kind === "ip" && h.hostname.kind === "ipv4") ||
|
(kind.has("ipv4") && h.kind === "ip" && h.hostname.kind === "ipv4") ||
|
||||||
(kind.has("ipv6") && h.kind === "ip" && h.hostname.kind === "ipv6")),
|
(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)))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,11 +262,14 @@ export const filledAddress = (
|
|||||||
...addressInfo,
|
...addressInfo,
|
||||||
hostnames,
|
hostnames,
|
||||||
toUrls,
|
toUrls,
|
||||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
filter: <
|
||||||
filter: F,
|
F extends Filter = typeof defaultFilter,
|
||||||
|
Format extends Formats = "urlstring",
|
||||||
|
>(
|
||||||
|
filter?: F,
|
||||||
format?: Format,
|
format?: Format,
|
||||||
) => {
|
) => {
|
||||||
const filtered = filterRec(hostnames, filter, false)
|
const filtered = filterRec(hostnames, filter ?? defaultFilter, false)
|
||||||
let res: FormatReturnTy<F, Format>[] = filtered as any
|
let res: FormatReturnTy<F, Format>[] = filtered as any
|
||||||
if (format === "hostname-info") return res
|
if (format === "hostname-info") return res
|
||||||
const urls = filtered.flatMap(toUrlArray)
|
const urls = filtered.flatMap(toUrlArray)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ import {
|
|||||||
} from "../../base/lib/inits"
|
} from "../../base/lib/inits"
|
||||||
import { DropGenerator } from "../../base/lib/util/Drop"
|
import { DropGenerator } from "../../base/lib/util/Drop"
|
||||||
|
|
||||||
export const OSVersion = testTypeVersion("0.4.0-alpha.13")
|
export const OSVersion = testTypeVersion("0.4.0-alpha.15")
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type AnyNeverCond<T extends any[], Then, Else> =
|
type AnyNeverCond<T extends any[], Then, Else> =
|
||||||
|
|||||||
@@ -410,12 +410,17 @@ export class SubContainerOwned<
|
|||||||
workdir = options.cwd
|
workdir = options.cwd
|
||||||
delete 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(
|
const child = cp.spawn(
|
||||||
"start-container",
|
"start-container",
|
||||||
[
|
[
|
||||||
"subcontainer",
|
"subcontainer",
|
||||||
"exec",
|
"exec",
|
||||||
`--env=/media/startos/images/${this.imageId}.env`,
|
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||||
`--user=${user}`,
|
`--user=${user}`,
|
||||||
`--workdir=${workdir}`,
|
`--workdir=${workdir}`,
|
||||||
...extra,
|
...extra,
|
||||||
@@ -530,6 +535,11 @@ export class SubContainerOwned<
|
|||||||
workdir = options.cwd
|
workdir = options.cwd
|
||||||
delete 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()
|
await this.killLeader()
|
||||||
this.leaderExited = false
|
this.leaderExited = false
|
||||||
this.leader = cp.spawn(
|
this.leader = cp.spawn(
|
||||||
@@ -537,7 +547,7 @@ export class SubContainerOwned<
|
|||||||
[
|
[
|
||||||
"subcontainer",
|
"subcontainer",
|
||||||
"launch",
|
"launch",
|
||||||
`--env=/media/startos/images/${this.imageId}.env`,
|
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||||
`--user=${user}`,
|
`--user=${user}`,
|
||||||
`--workdir=${workdir}`,
|
`--workdir=${workdir}`,
|
||||||
...extra,
|
...extra,
|
||||||
@@ -574,12 +584,17 @@ export class SubContainerOwned<
|
|||||||
workdir = options.cwd
|
workdir = options.cwd
|
||||||
delete 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(
|
return cp.spawn(
|
||||||
"start-container",
|
"start-container",
|
||||||
[
|
[
|
||||||
"subcontainer",
|
"subcontainer",
|
||||||
"exec",
|
"exec",
|
||||||
`--env=/media/startos/images/${this.imageId}.env`,
|
`--env-file=/media/startos/images/${this.imageId}.env`,
|
||||||
`--user=${user}`,
|
`--user=${user}`,
|
||||||
`--workdir=${workdir}`,
|
`--workdir=${workdir}`,
|
||||||
...extra,
|
...extra,
|
||||||
|
|||||||
11
sdk/package/package-lock.json
generated
11
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.43",
|
"version": "0.4.0-beta.44",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.43",
|
"version": "0.4.0-beta.44",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^3.0.0",
|
"@iarna/toml": "^3.0.0",
|
||||||
@@ -98,6 +98,7 @@
|
|||||||
"integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
|
"integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.26.0",
|
"@babel/code-frame": "^7.26.0",
|
||||||
@@ -1643,6 +1644,7 @@
|
|||||||
"integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
|
"integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.20.0"
|
||||||
}
|
}
|
||||||
@@ -1944,6 +1946,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001669",
|
"caniuse-lite": "^1.0.30001669",
|
||||||
"electron-to-chromium": "^1.5.41",
|
"electron-to-chromium": "^1.5.41",
|
||||||
@@ -3053,6 +3056,7 @@
|
|||||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "^29.7.0",
|
"@jest/core": "^29.7.0",
|
||||||
"@jest/types": "^29.6.3",
|
"@jest/types": "^29.6.3",
|
||||||
@@ -4157,6 +4161,7 @@
|
|||||||
"integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==",
|
"integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^10.0.0",
|
"commander": "^10.0.0",
|
||||||
"source-map-generator": "0.8.0"
|
"source-map-generator": "0.8.0"
|
||||||
@@ -4833,6 +4838,7 @@
|
|||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
@@ -4953,6 +4959,7 @@
|
|||||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.43",
|
"version": "0.4.0-beta.44",
|
||||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||||
"main": "./package/lib/index.js",
|
"main": "./package/lib/index.js",
|
||||||
"types": "./package/lib/index.d.ts",
|
"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",
|
"name": "startos-ui",
|
||||||
"version": "0.4.0-alpha.13",
|
"version": "0.4.0-alpha.15",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "startos-ui",
|
"name": "startos-ui",
|
||||||
"version": "0.4.0-alpha.13",
|
"version": "0.4.0-alpha.15",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^20.3.0",
|
"@angular/animations": "^20.3.0",
|
||||||
@@ -25,18 +25,18 @@
|
|||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@start9labs/argon2": "^0.3.0",
|
"@start9labs/argon2": "^0.3.0",
|
||||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||||
"@taiga-ui/addon-charts": "4.55.0",
|
"@taiga-ui/addon-charts": "4.62.0",
|
||||||
"@taiga-ui/addon-commerce": "4.55.0",
|
"@taiga-ui/addon-commerce": "4.62.0",
|
||||||
"@taiga-ui/addon-mobile": "4.55.0",
|
"@taiga-ui/addon-mobile": "4.62.0",
|
||||||
"@taiga-ui/addon-table": "4.55.0",
|
"@taiga-ui/addon-table": "4.62.0",
|
||||||
"@taiga-ui/cdk": "4.55.0",
|
"@taiga-ui/cdk": "4.62.0",
|
||||||
"@taiga-ui/core": "4.55.0",
|
"@taiga-ui/core": "4.62.0",
|
||||||
"@taiga-ui/dompurify": "4.1.11",
|
"@taiga-ui/dompurify": "4.1.11",
|
||||||
"@taiga-ui/event-plugins": "4.7.0",
|
"@taiga-ui/event-plugins": "4.7.0",
|
||||||
"@taiga-ui/experimental": "4.55.0",
|
"@taiga-ui/experimental": "4.62.0",
|
||||||
"@taiga-ui/icons": "4.55.0",
|
"@taiga-ui/icons": "4.62.0",
|
||||||
"@taiga-ui/kit": "4.55.0",
|
"@taiga-ui/kit": "4.62.0",
|
||||||
"@taiga-ui/layout": "4.55.0",
|
"@taiga-ui/layout": "4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "4.9.0",
|
"@taiga-ui/polymorpheus": "4.9.0",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
@@ -3950,9 +3950,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-charts": {
|
"node_modules/@taiga-ui/addon-charts": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.62.0.tgz",
|
||||||
"integrity": "sha512-Rwgcc7NaLm75GsHYhqbO4jgNhD1bGbA7Kk0sNFE+Tgz9+V3ARXMuBw7C3cU9UxLSFnOsXz9RYLosmZ3jAVlyuQ==",
|
"integrity": "sha512-tCysUpzEHwRhK/p9hopkt0Jw4jcgA2cF8CYK8mDntghC+fNLnqCVUcrqFIC5plGabAo00WMEz+X+KyGvwvKaVg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -3961,15 +3961,15 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0"
|
"@taiga-ui/polymorpheus": "^4.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-commerce": {
|
"node_modules/@taiga-ui/addon-commerce": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.62.0.tgz",
|
||||||
"integrity": "sha512-eOOBkIJSsagtRkpRZ04xlL8ePIP01d4Xo264zSTg2SRxD6vwR/7/QJlf9108BvIJv/jfTpmFukLwSB9LazqmCw==",
|
"integrity": "sha512-J4+bdHeDe2d7Uh8NNObLl4LzBhWLCdzxNHXPac1bMGB+3gX751Htc9px37FkVZlQGnxQATCbxAVXj7Zjveq/QQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3979,22 +3979,22 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@angular/forms": ">=16.0.0",
|
"@angular/forms": ">=16.0.0",
|
||||||
"@maskito/angular": "^3.10.3",
|
"@maskito/angular": "^3.11.1",
|
||||||
"@maskito/core": "^3.10.3",
|
"@maskito/core": "^3.11.1",
|
||||||
"@maskito/kit": "^3.10.3",
|
"@maskito/kit": "^3.11.1",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/i18n": "^4.55.0",
|
"@taiga-ui/i18n": "^4.62.0",
|
||||||
"@taiga-ui/kit": "^4.55.0",
|
"@taiga-ui/kit": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-mobile": {
|
"node_modules/@taiga-ui/addon-mobile": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.62.0.tgz",
|
||||||
"integrity": "sha512-NQRozVKcXLs9/rd/s1yI7T5rIokzwHQ5IN/c3NLBUEka1iKUr1ZTch+g9CHJf8GTVB0uAwWKNCgX5LxtiSI5zg==",
|
"integrity": "sha512-seIBG4utgLq2xDJu+YDzksOsVi/V6vsTbm2bljgM1fIBZInbhqk95YOIFZDU9JXT1/vIShcqetavg1vHD1wdkQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4004,18 +4004,18 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/kit": "^4.55.0",
|
"@taiga-ui/kit": "^4.62.0",
|
||||||
"@taiga-ui/layout": "^4.55.0",
|
"@taiga-ui/layout": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-table": {
|
"node_modules/@taiga-ui/addon-table": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.62.0.tgz",
|
||||||
"integrity": "sha512-K5qpOS0UQLDqruJXPNDic8scCRvO3oAN/ZCPQ9XOGDrcvydvo4AKUwjKRPj+pZ/z0ulxbwAAruFFFCvRNnbzaA==",
|
"integrity": "sha512-0rolnsO1puYwUK17si5OOpzFxiziS6/OSbpLOSKrVrMkCgsWCoNDvpgPIwtwS5Mq3iF5cwLfUPbDQM8saG7wxQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4024,18 +4024,18 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/i18n": "^4.55.0",
|
"@taiga-ui/i18n": "^4.62.0",
|
||||||
"@taiga-ui/kit": "^4.55.0",
|
"@taiga-ui/kit": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/cdk": {
|
"node_modules/@taiga-ui/cdk": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.62.0.tgz",
|
||||||
"integrity": "sha512-vA5nGyx+YIHR1xZeq5D9gSqTRQg74qhe1AOt5FlqFOC0P4LvmLkNg3De7AeahXALNSeRz/DYcqI7WuGo6xpcLQ==",
|
"integrity": "sha512-KWPXEbCHtRp7aIet1L3PySdXpo5Aay4L/36jDzjiFZ/bcbuD2cY/3S2l68zpgv6ZksZA94DuCuaamSEwQIAtPw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4065,9 +4065,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/core": {
|
"node_modules/@taiga-ui/core": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.62.0.tgz",
|
||||||
"integrity": "sha512-Z2ATVNmEAlHEk2cgs/tnS6qZML87IchkPDeRl6HQfBT2fjYVjh1oCzXL07t86Lv6tpvkllyUVqoBCTSvDXs9kA==",
|
"integrity": "sha512-PQW10hFH50g8PgnJpPa/ZrGMWljhIsBHad/utvalmlv8wXQY24i8T1BjrGIOFPOjzs20NEwLOICHf7KdZUtiuA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4082,9 +4082,9 @@
|
|||||||
"@angular/router": ">=16.0.0",
|
"@angular/router": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/event-plugins": "^4.7.0",
|
"@taiga-ui/event-plugins": "^4.7.0",
|
||||||
"@taiga-ui/i18n": "^4.55.0",
|
"@taiga-ui/i18n": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
@@ -4120,9 +4120,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/experimental": {
|
"node_modules/@taiga-ui/experimental": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.62.0.tgz",
|
||||||
"integrity": "sha512-3zq2BTl+fE/N/tEr74TzWbyzT5OoS9YEApKobJehKivW5XxZmF/MDRWp45kSe4jDtVbJ2ueI0Jn8h0BDNykkcg==",
|
"integrity": "sha512-EiL5wJ+9LSf0BfZcFX6ioCavLfx26v0BCOUXh52Rtczp85Uh2qTDt2feM0oBDB+0Kj74/+wqqiKi+s3B8ZV3WA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4130,19 +4130,19 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@taiga-ui/addon-commerce": "^4.55.0",
|
"@taiga-ui/addon-commerce": "^4.62.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/kit": "^4.55.0",
|
"@taiga-ui/kit": "^4.62.0",
|
||||||
"@taiga-ui/layout": "^4.55.0",
|
"@taiga-ui/layout": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/i18n": {
|
"node_modules/@taiga-ui/i18n": {
|
||||||
"version": "4.59.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.62.0.tgz",
|
||||||
"integrity": "sha512-IxPzqkORJlSqagvUdmqBL9fuvfRm/Ca/W/bCDEK2GN/4QtSZ0yFzAyQdWduoIJubqyEPMXRbXZGc7WBtDgAMIQ==",
|
"integrity": "sha512-84hD1nI26EAYd5RUhFKxbg+8WKYhc0GBHyf8wfi15xuwaT6oh2gbJx7pNTlGN3klH4CeDB9HF998tkhieevqQw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4155,18 +4155,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/icons": {
|
"node_modules/@taiga-ui/icons": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.62.0.tgz",
|
||||||
"integrity": "sha512-sYqSG9wgUcwHBrDRnMhLCMEkvAAw/SZrvvq0jdY2oWGmKwAj/6WBt+wA+jnFkDDKEZ7mjzdPIQffpVaUjSwsiw==",
|
"integrity": "sha512-vD+bJk3Wot/+NcbdPwAJGBnqXG6T1OJVeg2IkaEE6DBixwdwDpukZWiV9asXyXiJkyEpG2Ar7SASvdCYZEVlxw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/kit": {
|
"node_modules/@taiga-ui/kit": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.62.0.tgz",
|
||||||
"integrity": "sha512-xTvi7viI+wI2ifPv2bsf8prhYWWS4g1lbx059jXV3f5Cttc0Xg6DEb6xpaQOf4loBkcrP2FzkA4njACUuiouzw==",
|
"integrity": "sha512-tdEaXJTks1PZQJAwMiVQTZrtCpaLIYV6T9VdVPZUKAJXq7K6J2kcD0oIISjwE9rqgLVwqytMZrwHx1nSRzkb/A==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4177,25 +4177,25 @@
|
|||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@angular/forms": ">=16.0.0",
|
"@angular/forms": ">=16.0.0",
|
||||||
"@angular/router": ">=16.0.0",
|
"@angular/router": ">=16.0.0",
|
||||||
"@maskito/angular": "^3.10.3",
|
"@maskito/angular": "^3.11.1",
|
||||||
"@maskito/core": "^3.10.3",
|
"@maskito/core": "^3.11.1",
|
||||||
"@maskito/kit": "^3.10.3",
|
"@maskito/kit": "^3.11.1",
|
||||||
"@maskito/phone": "^3.10.3",
|
"@maskito/phone": "^3.11.1",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||||
"@ng-web-apis/resize-observer": "^4.12.0",
|
"@ng-web-apis/resize-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/i18n": "^4.55.0",
|
"@taiga-ui/i18n": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/layout": {
|
"node_modules/@taiga-ui/layout": {
|
||||||
"version": "4.55.0",
|
"version": "4.62.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.62.0.tgz",
|
||||||
"integrity": "sha512-C+e4gudZwjIc46VITil5vySas1FPpfe+D4uwLRggJOTuUosZlZHBuc51v91wCCc0pL0Xfu+TD0s8W9kRd1sQHA==",
|
"integrity": "sha512-xd8eLLeR5FE3RhnVMGl1QlC3JXXJLsLAAASpBf9DQsTt+YBBl8BQt/cXGbBcJecC2mJLZlS6zytSkMTHY7VAhw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4204,9 +4204,9 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@taiga-ui/cdk": "^4.55.0",
|
"@taiga-ui/cdk": "^4.62.0",
|
||||||
"@taiga-ui/core": "^4.55.0",
|
"@taiga-ui/core": "^4.62.0",
|
||||||
"@taiga-ui/kit": "^4.55.0",
|
"@taiga-ui/kit": "^4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "startos-ui",
|
"name": "startos-ui",
|
||||||
"version": "0.4.0-alpha.13",
|
"version": "0.4.0-alpha.15",
|
||||||
"author": "Start9 Labs, Inc",
|
"author": "Start9 Labs, Inc",
|
||||||
"homepage": "https://start9.com/",
|
"homepage": "https://start9.com/",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -49,18 +49,18 @@
|
|||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@start9labs/argon2": "^0.3.0",
|
"@start9labs/argon2": "^0.3.0",
|
||||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||||
"@taiga-ui/addon-charts": "4.55.0",
|
"@taiga-ui/addon-charts": "4.62.0",
|
||||||
"@taiga-ui/addon-commerce": "4.55.0",
|
"@taiga-ui/addon-commerce": "4.62.0",
|
||||||
"@taiga-ui/addon-mobile": "4.55.0",
|
"@taiga-ui/addon-mobile": "4.62.0",
|
||||||
"@taiga-ui/addon-table": "4.55.0",
|
"@taiga-ui/addon-table": "4.62.0",
|
||||||
"@taiga-ui/cdk": "4.55.0",
|
"@taiga-ui/cdk": "4.62.0",
|
||||||
"@taiga-ui/core": "4.55.0",
|
"@taiga-ui/core": "4.62.0",
|
||||||
"@taiga-ui/dompurify": "4.1.11",
|
"@taiga-ui/dompurify": "4.1.11",
|
||||||
"@taiga-ui/event-plugins": "4.7.0",
|
"@taiga-ui/event-plugins": "4.7.0",
|
||||||
"@taiga-ui/experimental": "4.55.0",
|
"@taiga-ui/experimental": "4.62.0",
|
||||||
"@taiga-ui/icons": "4.55.0",
|
"@taiga-ui/icons": "4.62.0",
|
||||||
"@taiga-ui/kit": "4.55.0",
|
"@taiga-ui/kit": "4.62.0",
|
||||||
"@taiga-ui/layout": "4.55.0",
|
"@taiga-ui/layout": "4.62.0",
|
||||||
"@taiga-ui/polymorpheus": "4.9.0",
|
"@taiga-ui/polymorpheus": "4.9.0",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import { MarketplaceItemComponent } from './item.component'
|
|||||||
|
|
||||||
<div class="background-border box-shadow-lg shadow-color-light">
|
<div class="background-border box-shadow-lg shadow-color-light">
|
||||||
<div class="box-container">
|
<div class="box-container">
|
||||||
|
<h2 class="additional-detail-title">{{ 'Description' | i18n }}</h2>
|
||||||
<p [innerHTML]="pkg().description.long"></p>
|
<p [innerHTML]="pkg().description.long"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ import { MarketplacePkgBase } from '../../../types'
|
|||||||
selector: 'marketplace-dep-item',
|
selector: 'marketplace-dep-item',
|
||||||
template: `
|
template: `
|
||||||
<div class="outer-container">
|
<div class="outer-container">
|
||||||
<tui-avatar class="dep-img" size="l" [src]="getImage(dep.key)" />
|
<tui-avatar
|
||||||
|
appearance="action-grayscale"
|
||||||
|
class="dep-img"
|
||||||
|
size="l"
|
||||||
|
[src]="getImage(dep.key)"
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<tui-line-clamp [linesLimit]="2" [content]="titleContent" />
|
<tui-line-clamp [linesLimit]="2" [content]="titleContent" />
|
||||||
<ng-template #titleContent>
|
<ng-template #titleContent>
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import { MarketplacePkg } from '../../types'
|
|||||||
[queryParams]="{ id: pkg.id, flavor: pkg.flavor }"
|
[queryParams]="{ id: pkg.id, flavor: pkg.flavor }"
|
||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
>
|
>
|
||||||
<tui-avatar [src]="pkg.icon | trustUrl" />
|
<tui-avatar
|
||||||
|
appearance="action-grayscale"
|
||||||
|
[src]="pkg.icon | trustUrl"
|
||||||
|
/>
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
{{ pkg.title }}
|
{{ pkg.title }}
|
||||||
<span tuiSubtitle>{{ pkg.version }}</span>
|
<span tuiSubtitle>{{ pkg.version }}</span>
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h3 style="color: #f8546a; font-weight: bold">Important!</h3>
|
<h2 style="font-variant-caps: all-small-caps">
|
||||||
|
Root Certificate Authority
|
||||||
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Download your server's Root CA and
|
Download your server's Root CA and
|
||||||
<a
|
<a
|
||||||
@@ -47,7 +49,7 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
|||||||
path="/user-manual/trust-ca.html"
|
path="/user-manual/trust-ca.html"
|
||||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||||
>
|
>
|
||||||
follow the instructions
|
follow instructions
|
||||||
</a>
|
</a>
|
||||||
to establish a secure connection with your server.
|
to establish a secure connection with your server.
|
||||||
</p>
|
</p>
|
||||||
@@ -84,15 +86,15 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<h2 style="font-variant-caps: all-small-caps">
|
<h2 style="font-variant-caps: all-small-caps">
|
||||||
Access from home (LAN)
|
Permanent Local Address
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Visit the address below when you are connected to the same WiFi or
|
You must be connected to the same Local Area Network (LAN) as your
|
||||||
Local Area Network (LAN) as your server.
|
server to access this address.
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
style="
|
style="
|
||||||
padding: 16px;
|
padding: 16px 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -100,33 +102,6 @@ import { DocsLinkDirective } from '@start9labs/shared'
|
|||||||
>
|
>
|
||||||
<code id="lan-addr"></code>
|
<code id="lan-addr"></code>
|
||||||
</p>
|
</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>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { DownloadHTMLService, ErrorService } from '@start9labs/shared'
|
import { DownloadHTMLService, ErrorService } from '@start9labs/shared'
|
||||||
import { TuiButton, TuiIcon, TuiSurface } from '@taiga-ui/core'
|
import { TuiButton, TuiIcon, TuiLoader, TuiSurface } from '@taiga-ui/core'
|
||||||
import { TuiCardLarge } from '@taiga-ui/layout'
|
import { TuiCardLarge } from '@taiga-ui/layout'
|
||||||
import { DocumentationComponent } from 'src/app/components/documentation.component'
|
import { DocumentationComponent } from 'src/app/components/documentation.component'
|
||||||
import { MatrixComponent } from 'src/app/components/matrix.component'
|
import { MatrixComponent } from 'src/app/components/matrix.component'
|
||||||
@@ -31,10 +31,16 @@ import { StateService } from 'src/app/services/state.service'
|
|||||||
<h3>You can now safely unplug your old StartOS data drive</h3>
|
<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()">
|
<button tuiCardLarge tuiSurface="floating" (click)="download()">
|
||||||
<strong class="caps">Download address info</strong>
|
<strong class="caps">Download address info</strong>
|
||||||
<span>
|
<span>
|
||||||
start.local was for setup purposes only. It will no longer work.
|
For future reference, this file contains your server's permanent
|
||||||
|
local address, as well as its Root Certificate Authority (Root CA).
|
||||||
</span>
|
</span>
|
||||||
<strong class="caps">
|
<strong class="caps">
|
||||||
Download
|
Download
|
||||||
@@ -48,17 +54,18 @@ import { StateService } from 'src/app/services/state.service'
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
[attr.href]="disableLogin ? null : lanAddress"
|
[attr.href]="disableLogin ? null : lanAddress"
|
||||||
>
|
>
|
||||||
<strong class="caps">Trust your Root CA</strong>
|
|
||||||
<span>
|
<span>
|
||||||
In the new tab, follow instructions to trust your server's Root CA
|
In the new tab, follow instructions to trust your server's Root CA
|
||||||
and log in.
|
and log in.
|
||||||
</span>
|
</span>
|
||||||
<strong class="caps">
|
<strong class="caps">
|
||||||
Open
|
Open Local Address
|
||||||
<tui-icon icon="@tui.external-link" />
|
<tui-icon icon="@tui.external-link" />
|
||||||
</strong>
|
</strong>
|
||||||
</a>
|
</a>
|
||||||
<app-documentation hidden [lanAddress]="lanAddress" />
|
<app-documentation hidden [lanAddress]="lanAddress" />
|
||||||
|
} @else {
|
||||||
|
<tui-loader />
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
`,
|
`,
|
||||||
@@ -97,6 +104,10 @@ import { StateService } from 'src/app/services/state.service'
|
|||||||
opacity: var(--tui-disabled-opacity);
|
opacity: var(--tui-disabled-opacity);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
imports: [
|
imports: [
|
||||||
TuiCardLarge,
|
TuiCardLarge,
|
||||||
@@ -105,6 +116,7 @@ import { StateService } from 'src/app/services/state.service'
|
|||||||
TuiSurface,
|
TuiSurface,
|
||||||
MatrixComponent,
|
MatrixComponent,
|
||||||
DocumentationComponent,
|
DocumentationComponent,
|
||||||
|
TuiLoader,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class SuccessPage implements AfterViewInit {
|
export default class SuccessPage implements AfterViewInit {
|
||||||
@@ -117,7 +129,6 @@ export default class SuccessPage implements AfterViewInit {
|
|||||||
|
|
||||||
readonly stateService = inject(StateService)
|
readonly stateService = inject(StateService)
|
||||||
|
|
||||||
torAddresses?: string[]
|
|
||||||
lanAddress?: string
|
lanAddress?: string
|
||||||
cert?: string
|
cert?: string
|
||||||
disableLogin = this.stateService.setupType === 'fresh'
|
disableLogin = this.stateService.setupType === 'fresh'
|
||||||
@@ -127,10 +138,8 @@ export default class SuccessPage implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
download() {
|
download() {
|
||||||
const torElem = this.document.getElementById('tor-addr')
|
|
||||||
const lanElem = this.document.getElementById('lan-addr')
|
const lanElem = this.document.getElementById('lan-addr')
|
||||||
|
|
||||||
if (torElem) torElem.innerHTML = this.torAddresses?.join('\n') || ''
|
|
||||||
if (lanElem) lanElem.innerHTML = this.lanAddress || ''
|
if (lanElem) lanElem.innerHTML = this.lanAddress || ''
|
||||||
|
|
||||||
this.document
|
this.document
|
||||||
@@ -155,9 +164,6 @@ export default class SuccessPage implements AfterViewInit {
|
|||||||
try {
|
try {
|
||||||
const ret = await this.api.complete()
|
const ret = await this.api.complete()
|
||||||
if (!this.stateService.kiosk) {
|
if (!this.stateService.kiosk) {
|
||||||
this.torAddresses = ret.torAddresses.map(a =>
|
|
||||||
a.replace(/^https:/, 'http:'),
|
|
||||||
)
|
|
||||||
this.lanAddress = ret.lanAddress.replace(/^https:/, 'http:')
|
this.lanAddress = ret.lanAddress.replace(/^https:/, 'http:')
|
||||||
this.cert = ret.rootCa
|
this.cert = ret.rootCa
|
||||||
|
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ export default {
|
|||||||
512: 'Der Kiosk-Modus ist auf diesem Gerät nicht verfügbar',
|
512: 'Der Kiosk-Modus ist auf diesem Gerät nicht verfügbar',
|
||||||
513: 'Aktivieren',
|
513: 'Aktivieren',
|
||||||
514: 'Deaktivieren',
|
514: 'Deaktivieren',
|
||||||
515: 'Du verwendest derzeit einen Kiosk. Wenn du den Kiosk-Modus deaktivierst, wird die Verbindung zum Kiosk getrennt.',
|
515: 'Diese Änderung wird nach dem nächsten Neustart wirksam',
|
||||||
516: 'Empfohlen',
|
516: 'Empfohlen',
|
||||||
517: 'Möchten Sie diese Aufgabe wirklich verwerfen?',
|
517: 'Möchten Sie diese Aufgabe wirklich verwerfen?',
|
||||||
518: 'Verwerfen',
|
518: 'Verwerfen',
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ export const ENGLISH = {
|
|||||||
'Kiosk Mode is unavailable on this device': 512,
|
'Kiosk Mode is unavailable on this device': 512,
|
||||||
'Enable': 513,
|
'Enable': 513,
|
||||||
'Disable': 514,
|
'Disable': 514,
|
||||||
'You are currently using a kiosk. Disabling Kiosk Mode will result in the kiosk disconnecting.': 515,
|
'This change will take effect after the next boot': 515,
|
||||||
'Recommended': 516, // as in, we recommend this
|
'Recommended': 516, // as in, we recommend this
|
||||||
'Are you sure you want to dismiss this task?': 517,
|
'Are you sure you want to dismiss this task?': 517,
|
||||||
'Dismiss': 518, // as in, dismiss or delete a task
|
'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',
|
512: 'El modo quiosco no está disponible en este dispositivo',
|
||||||
513: 'Activar',
|
513: 'Activar',
|
||||||
514: 'Desactivar',
|
514: 'Desactivar',
|
||||||
515: 'Actualmente estás utilizando un quiosco. Desactivar el modo quiosco provocará su desconexión.',
|
515: 'Este cambio tendrá efecto después del próximo inicio',
|
||||||
516: 'Recomendado',
|
516: 'Recomendado',
|
||||||
517: '¿Estás seguro de que deseas descartar esta tarea?',
|
517: '¿Estás seguro de que deseas descartar esta tarea?',
|
||||||
518: 'Descartar',
|
518: 'Descartar',
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ export default {
|
|||||||
512: 'Le mode kiosque n’est pas disponible sur cet appareil',
|
512: 'Le mode kiosque n’est pas disponible sur cet appareil',
|
||||||
513: 'Activer',
|
513: 'Activer',
|
||||||
514: 'Désactiver',
|
514: 'Désactiver',
|
||||||
515: 'Vous utilisez actuellement un kiosque. Désactiver le mode kiosque entraînera sa déconnexion.',
|
515: 'Ce changement va prendre effet après le prochain démarrage',
|
||||||
516: 'Recommandé',
|
516: 'Recommandé',
|
||||||
517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?',
|
517: 'Êtes-vous sûr de vouloir ignorer cette tâche ?',
|
||||||
518: 'Ignorer',
|
518: 'Ignorer',
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ export default {
|
|||||||
512: 'Tryb kiosku jest niedostępny na tym urządzeniu',
|
512: 'Tryb kiosku jest niedostępny na tym urządzeniu',
|
||||||
513: 'Włącz',
|
513: 'Włącz',
|
||||||
514: 'Wyłącz',
|
514: 'Wyłącz',
|
||||||
515: 'Obecnie używasz kiosku. Wyłączenie trybu kiosku spowoduje jego rozłączenie.',
|
515: 'Ta zmiana zacznie obowiązywać po następnym uruchomieniu',
|
||||||
516: 'Zalecane',
|
516: 'Zalecane',
|
||||||
517: 'Czy na pewno chcesz odrzucić to zadanie?',
|
517: 'Czy na pewno chcesz odrzucić to zadanie?',
|
||||||
518: 'Odrzuć',
|
518: 'Odrzuć',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { I18N, i18nKey } from './i18n.providers'
|
|||||||
export class i18nPipe implements PipeTransform {
|
export class i18nPipe implements PipeTransform {
|
||||||
private readonly i18n = inject(I18N)
|
private readonly i18n = inject(I18N)
|
||||||
|
|
||||||
transform(englishKey: i18nKey | null | undefined): string {
|
transform(englishKey: i18nKey | null | undefined | ''): string {
|
||||||
englishKey = englishKey || ('' as i18nKey)
|
englishKey = englishKey || ('' as i18nKey)
|
||||||
|
|
||||||
return this.i18n()?.[ENGLISH[englishKey]] || englishKey
|
return this.i18n()?.[ENGLISH[englishKey]] || englishKey
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ import { HintPipe } from '../pipes/hint.pipe'
|
|||||||
[(ngModel)]="selected"
|
[(ngModel)]="selected"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<tui-data-list-wrapper *tuiTextfieldDropdown new [items]="items" />
|
@if (!mobile) {
|
||||||
|
<tui-data-list-wrapper *tuiTextfieldDropdown new [items]="items" />
|
||||||
|
}
|
||||||
@if (spec | hint; as hint) {
|
@if (spec | hint; as hint) {
|
||||||
<tui-icon [tuiTooltip]="hint" />
|
<tui-icon [tuiTooltip]="hint" />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
tuiButtonOptionsProvider,
|
tuiButtonOptionsProvider,
|
||||||
TuiDataList,
|
TuiDataList,
|
||||||
TuiDropdown,
|
TuiDropdown,
|
||||||
|
TuiIcon,
|
||||||
TuiTextfield,
|
TuiTextfield,
|
||||||
} from '@taiga-ui/core'
|
} from '@taiga-ui/core'
|
||||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||||
@@ -27,13 +28,9 @@ import { InterfaceComponent } from '../interface.component'
|
|||||||
selector: 'td[actions]',
|
selector: 'td[actions]',
|
||||||
template: `
|
template: `
|
||||||
<div class="desktop">
|
<div class="desktop">
|
||||||
<button
|
<button tuiIconButton appearance="flat-grayscale" (click)="viewDetails()">
|
||||||
tuiIconButton
|
|
||||||
appearance="flat-grayscale"
|
|
||||||
iconStart="@tui.info"
|
|
||||||
(click)="viewDetails()"
|
|
||||||
>
|
|
||||||
{{ 'Address details' | i18n }}
|
{{ 'Address details' | i18n }}
|
||||||
|
<tui-icon class="info" icon="@tui.info" background="@tui.info-filled" />
|
||||||
</button>
|
</button>
|
||||||
@if (interface.value()?.type === 'ui') {
|
@if (interface.value()?.type === 'ui') {
|
||||||
<a
|
<a
|
||||||
@@ -113,6 +110,19 @@ import { InterfaceComponent } from '../interface.component'
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(.uncommon-hidden) .desktop {
|
||||||
|
height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background: var(--tui-status-info);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
mask-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -127,7 +137,14 @@ import { InterfaceComponent } from '../interface.component'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
imports: [TuiButton, TuiDropdown, TuiDataList, i18nPipe, TuiTextfield],
|
imports: [
|
||||||
|
TuiButton,
|
||||||
|
TuiDropdown,
|
||||||
|
TuiDataList,
|
||||||
|
i18nPipe,
|
||||||
|
TuiTextfield,
|
||||||
|
TuiIcon,
|
||||||
|
],
|
||||||
providers: [tuiButtonOptionsProvider({ appearance: 'icon' })],
|
providers: [tuiButtonOptionsProvider({ appearance: 'icon' })],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core'
|
import { ChangeDetectionStrategy, Component, input } from '@angular/core'
|
||||||
import { i18nPipe } from '@start9labs/shared'
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
|
import { TuiButton } from '@taiga-ui/core'
|
||||||
import { TuiAccordion } from '@taiga-ui/experimental'
|
import { TuiAccordion } from '@taiga-ui/experimental'
|
||||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
import { TuiElasticContainer, TuiSkeleton } from '@taiga-ui/kit'
|
||||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
||||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
|
|
||||||
@@ -12,91 +13,79 @@ import { InterfaceAddressItemComponent } from './item.component'
|
|||||||
selector: 'section[addresses]',
|
selector: 'section[addresses]',
|
||||||
template: `
|
template: `
|
||||||
<header>{{ 'Addresses' | i18n }}</header>
|
<header>{{ 'Addresses' | i18n }}</header>
|
||||||
<table [appTable]="['Type', 'Access', 'Gateway', 'URL', null]">
|
<tui-elastic-container>
|
||||||
@for (address of addresses()?.common; track $index) {
|
<table [appTable]="['Type', 'Access', 'Gateway', 'URL', null]">
|
||||||
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
@for (address of addresses()?.common; track $index) {
|
||||||
} @empty {
|
<tr [address]="address" [isRunning]="isRunning()"></tr>
|
||||||
@if (addresses()) {
|
} @empty {
|
||||||
<tr>
|
@if (addresses()) {
|
||||||
<td colspan="5">
|
|
||||||
<app-placeholder icon="@tui.list-x">
|
|
||||||
{{ 'No addresses' | i18n }}
|
|
||||||
</app-placeholder>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
} @else {
|
|
||||||
@for (_ of [0, 1]; track $index) {
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">
|
<td colspan="5">
|
||||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
<app-placeholder icon="@tui.list-x">
|
||||||
|
{{ 'No addresses' | i18n }}
|
||||||
|
</app-placeholder>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
} @else {
|
||||||
|
@for (_ of [0, 1]; track $index) {
|
||||||
|
<tr>
|
||||||
|
<td colspan="6">
|
||||||
|
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
<tbody [class.uncommon-hidden]="!uncommon">
|
||||||
</table>
|
@if (addresses()?.uncommon?.length && uncommon) {
|
||||||
@if (addresses()?.uncommon?.length) {
|
<tr [style.background]="'var(--tui-background-neutral-1)'">
|
||||||
<tui-accordion>
|
<td colspan="5"></td>
|
||||||
<tui-expand>
|
</tr>
|
||||||
<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
|
|
||||||
}
|
}
|
||||||
</button>
|
@for (address of addresses()?.uncommon; track $index) {
|
||||||
</tui-accordion>
|
<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>
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
tui-accordion {
|
.g-table:has(caption) {
|
||||||
border-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[tuiAccordion],
|
[tuiButton] {
|
||||||
tui-expand {
|
width: 100%;
|
||||||
box-shadow: none;
|
border-top-left-radius: 0;
|
||||||
padding: 0;
|
border-top-right-radius: 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' },
|
host: { class: 'g-card' },
|
||||||
imports: [
|
imports: [
|
||||||
|
TuiSkeleton,
|
||||||
|
TuiButton,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
InterfaceAddressItemComponent,
|
InterfaceAddressItemComponent,
|
||||||
TuiAccordion,
|
TuiElasticContainer,
|
||||||
TuiSkeleton,
|
|
||||||
],
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,27 +8,31 @@ import { TuiBadge } from '@taiga-ui/kit'
|
|||||||
selector: 'tr[address]',
|
selector: 'tr[address]',
|
||||||
template: `
|
template: `
|
||||||
@if (address(); as address) {
|
@if (address(); as address) {
|
||||||
<td>{{ address.type }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
@if (address.access === 'public') {
|
<div class="wrapper">{{ address.type }}</div>
|
||||||
<tui-badge size="s" appearance="primary-success">
|
</td>
|
||||||
{{ 'public' | i18n }}
|
<td>
|
||||||
</tui-badge>
|
<div class="wrapper">
|
||||||
} @else if (address.access === 'private') {
|
@if (address.access === 'public') {
|
||||||
<tui-badge size="s" appearance="primary-destructive">
|
<tui-badge size="s" appearance="primary-success">
|
||||||
{{ 'private' | i18n }}
|
{{ 'public' | i18n }}
|
||||||
</tui-badge>
|
</tui-badge>
|
||||||
} @else {
|
} @else if (address.access === 'private') {
|
||||||
-
|
<tui-badge size="s" appearance="primary-destructive">
|
||||||
}
|
{{ 'private' | i18n }}
|
||||||
|
</tui-badge>
|
||||||
|
} @else {
|
||||||
|
-
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td [style.order]="-1">
|
<td [style.order]="-1">
|
||||||
<div [title]="address.gatewayName">
|
<div class="wrapper" [title]="address.gatewayName">
|
||||||
{{ address.gatewayName || '-' }}
|
{{ address.gatewayName || '-' }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div [title]="address.url">{{ address.url }}</div>
|
<div class="wrapper" [title]="address.url">{{ address.url }}</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
actions
|
actions
|
||||||
@@ -48,6 +52,18 @@ import { TuiBadge } from '@taiga-ui/kit'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(.uncommon-hidden) {
|
||||||
|
.wrapper {
|
||||||
|
height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding-block: 0;
|
||||||
|
border: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody><ng-content /></tbody>
|
<tbody><ng-content /></tbody>
|
||||||
|
<ng-content select="tbody" />
|
||||||
|
<ng-content select="caption" />
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
:host:has(app-placeholder) thead {
|
:host:has(app-placeholder) thead {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { ToManifestPipe } from '../../../pipes/to-manifest'
|
|||||||
[queryParams]="services[d.key] ? {} : { search: d.key }"
|
[queryParams]="services[d.key] ? {} : { search: d.key }"
|
||||||
[class.error]="getError(d.key)"
|
[class.error]="getError(d.key)"
|
||||||
>
|
>
|
||||||
<tui-avatar>
|
<tui-avatar appearance="action-grayscale">
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
[src]="
|
[src]="
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
inject,
|
computed,
|
||||||
Input,
|
input,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
import { T } from '@start9labs/start-sdk'
|
|
||||||
import { TuiLoader } from '@taiga-ui/core'
|
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 { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
||||||
import { InstallingInfo } from 'src/app/services/patch-db/data-model'
|
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||||
import {
|
import {
|
||||||
|
getInstalledPrimaryStatus,
|
||||||
PrimaryRendering,
|
PrimaryRendering,
|
||||||
PrimaryStatus,
|
|
||||||
} from 'src/app/services/pkg-status-rendering.service'
|
} from 'src/app/services/pkg-status-rendering.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -19,23 +19,27 @@ import {
|
|||||||
template: `
|
template: `
|
||||||
<header>{{ 'Status' | i18n }}</header>
|
<header>{{ 'Status' | i18n }}</header>
|
||||||
<div>
|
<div>
|
||||||
@if (installingInfo) {
|
@if (info()) {
|
||||||
<h3>
|
<h3>
|
||||||
<tui-loader size="s" [inheritColor]="true" />
|
<tui-loader size="s" [inheritColor]="true" />
|
||||||
{{ 'Installing' | i18n }}
|
{{ 'Installing' | i18n }}
|
||||||
<span class="loading-dots"></span>
|
<span class="loading-dots"></span>
|
||||||
{{ getText(installingInfo.progress.overall) | i18n }}
|
{{ info() | i18n }}
|
||||||
</h3>
|
</h3>
|
||||||
} @else {
|
} @else {
|
||||||
<h3 [class]="class">
|
<h3 [class]="class()">
|
||||||
{{ text | i18n }}
|
{{ text() || 'Unknown' | i18n }}
|
||||||
@if (text === 'Task Required') {
|
@if (text() === 'Task Required') {
|
||||||
<small>{{ 'See below' | i18n }}</small>
|
<small>{{ 'See below' | i18n }}</small>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (rendering?.showDots) {
|
@if (rendering().showDots) {
|
||||||
<span class="loading-dots"></span>
|
<span class="loading-dots"></span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if ($any(pkg().status)?.started; as started) {
|
||||||
|
<service-uptime [started]="started" />
|
||||||
|
}
|
||||||
</h3>
|
</h3>
|
||||||
}
|
}
|
||||||
<ng-content />
|
<ng-content />
|
||||||
@@ -76,6 +80,12 @@ import {
|
|||||||
margin: 0 0.25rem -0.125rem 0;
|
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-context(tui-root._mobile) {
|
||||||
:host {
|
:host {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -94,32 +104,33 @@ import {
|
|||||||
small {
|
small {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
service-uptime {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
host: { class: 'g-card' },
|
host: { class: 'g-card' },
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [TuiLoader, i18nPipe],
|
imports: [TuiLoader, i18nPipe, ServiceUptimeComponent],
|
||||||
})
|
})
|
||||||
export class ServiceStatusComponent {
|
export class ServiceStatusComponent {
|
||||||
@Input({ required: true })
|
readonly pkg = input.required<PackageDataEntry>()
|
||||||
status?: PrimaryStatus
|
readonly connected = input(false)
|
||||||
|
|
||||||
@Input()
|
protected readonly status = computed((pkg = this.pkg()) =>
|
||||||
installingInfo?: InstallingInfo
|
pkg?.stateInfo.state === 'installed'
|
||||||
|
? getInstalledPrimaryStatus(pkg)
|
||||||
|
: pkg?.stateInfo.state,
|
||||||
|
)
|
||||||
|
|
||||||
@Input()
|
protected readonly rendering = computed(() => PrimaryRendering[this.status()])
|
||||||
connected = false
|
protected readonly text = computed(
|
||||||
|
() => this.connected() && this.rendering().display,
|
||||||
|
)
|
||||||
|
|
||||||
private readonly i18n = inject(i18nPipe)
|
protected readonly class = computed(() => {
|
||||||
|
switch (this.connected() && this.rendering().color) {
|
||||||
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':
|
case 'danger':
|
||||||
return 'g-negative'
|
return 'g-negative'
|
||||||
case 'warning':
|
case 'warning':
|
||||||
@@ -131,13 +142,10 @@ export class ServiceStatusComponent {
|
|||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
get rendering() {
|
protected readonly info = computed(
|
||||||
return this.status && PrimaryRendering[this.status]
|
(progress = this.pkg().stateInfo.installingInfo?.progress.overall) =>
|
||||||
}
|
progress ? getProgressText(progress) : '',
|
||||||
|
)
|
||||||
getText(progress: T.Progress): i18nKey {
|
|
||||||
return getProgressText(progress)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
|||||||
selector: 'tr[task]',
|
selector: 'tr[task]',
|
||||||
template: `
|
template: `
|
||||||
<td tuiFade class="row">
|
<td tuiFade class="row">
|
||||||
<tui-avatar size="xs">
|
<tui-avatar appearance="action-grayscale" size="xs">
|
||||||
<img [src]="pkg()?.icon || fallback()?.icon" alt="" />
|
<img [src]="pkg()?.icon || fallback()?.icon" alt="" />
|
||||||
</tui-avatar>
|
</tui-avatar>
|
||||||
<span>{{ title() || fallback()?.title }}</span>
|
<span>{{ title() || fallback()?.title }}</span>
|
||||||
@@ -79,6 +79,10 @@ import { getManifest } from 'src/app/utils/get-package-data'
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td:not(:last-child) {
|
||||||
|
padding-inline-end: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
td:last-child {
|
td:last-child {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ import { distinctUntilChanged } from 'rxjs/operators'
|
|||||||
color: var(--tui-text-primary);
|
color: var(--tui-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(table) {
|
:host-context(table),
|
||||||
|
:host-context(service-status) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ export default class ServiceActionsRoute {
|
|||||||
.pipe(
|
.pipe(
|
||||||
filter(pkg => pkg.stateInfo.state === 'installed'),
|
filter(pkg => pkg.stateInfo.state === 'installed'),
|
||||||
map(pkg => {
|
map(pkg => {
|
||||||
const specialGroup = Object.values(pkg.actions).some(
|
const specialGroup = Object.values(pkg.actions).some(a => !!a.group)
|
||||||
pkg => !!pkg.group,
|
|
||||||
)
|
|
||||||
? 'Other'
|
? 'Other'
|
||||||
: 'General'
|
: 'General'
|
||||||
return {
|
return {
|
||||||
@@ -90,9 +88,15 @@ export default class ServiceActionsRoute {
|
|||||||
group: action.group || specialGroup,
|
group: action.group || specialGroup,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a.group === specialGroup) return 1
|
if (a.group === specialGroup && b.group !== specialGroup)
|
||||||
if (b.group === specialGroup) return -1
|
return 1
|
||||||
return a.group.localeCompare(b.group) // Optional: sort others alphabetically
|
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
|
||||||
})
|
})
|
||||||
.reduce<
|
.reduce<
|
||||||
Record<
|
Record<
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ const INACTIVE: PrimaryStatus[] = [
|
|||||||
</div>
|
</div>
|
||||||
<aside class="g-aside">
|
<aside class="g-aside">
|
||||||
<header tuiCell routerLink="./">
|
<header tuiCell routerLink="./">
|
||||||
<tui-avatar><img alt="" [src]="service()?.icon" /></tui-avatar>
|
<tui-avatar appearance="action-grayscale">
|
||||||
|
<img alt="" [src]="service()?.icon" />
|
||||||
|
</tui-avatar>
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
<strong tuiFade>{{ manifest()?.title }}</strong>
|
<strong tuiFade>{{ manifest()?.title }}</strong>
|
||||||
<span tuiSubtitle>{{ manifest()?.version }}</span>
|
<span tuiSubtitle>{{ manifest()?.version }}</span>
|
||||||
|
|||||||
@@ -39,11 +39,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
|||||||
} @else if (installing()) {
|
} @else if (installing()) {
|
||||||
<service-install-progress [pkg]="pkg" />
|
<service-install-progress [pkg]="pkg" />
|
||||||
} @else if (installed()) {
|
} @else if (installed()) {
|
||||||
<service-status
|
<service-status [connected]="!!connected()" [pkg]="pkg">
|
||||||
[connected]="!!connected()"
|
|
||||||
[installingInfo]="pkg.stateInfo.installingInfo"
|
|
||||||
[status]="status()"
|
|
||||||
>
|
|
||||||
@if (connected()) {
|
@if (connected()) {
|
||||||
<service-controls [pkg]="pkg" [status]="status()" />
|
<service-controls [pkg]="pkg" [status]="status()" />
|
||||||
}
|
}
|
||||||
@@ -51,10 +47,8 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
|||||||
|
|
||||||
@if (status() !== 'backingUp') {
|
@if (status() !== 'backingUp') {
|
||||||
<service-health-checks [checks]="health()" />
|
<service-health-checks [checks]="health()" />
|
||||||
<service-uptime
|
<service-uptime class="g-card" [started]="$any(pkg.status).started" />
|
||||||
class="g-card"
|
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
|
||||||
[started]="$any(pkg.status)?.started"
|
|
||||||
/>
|
|
||||||
|
|
||||||
@if (errors() | async; as errors) {
|
@if (errors() | async; as errors) {
|
||||||
<service-dependencies
|
<service-dependencies
|
||||||
@@ -63,7 +57,6 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
|||||||
[errors]="errors"
|
[errors]="errors"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<service-interfaces [pkg]="pkg" [disabled]="status() !== 'running'" />
|
|
||||||
|
|
||||||
<service-tasks
|
<service-tasks
|
||||||
#tasks="elementRef"
|
#tasks="elementRef"
|
||||||
@@ -91,7 +84,7 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
} @else if (removing()) {
|
} @else if (removing()) {
|
||||||
<service-status [connected]="!!connected()" [status]="status()" />
|
<service-status [connected]="!!connected()" [pkg]="pkg" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -139,6 +132,10 @@ import { ServiceUptimeComponent } from '../components/uptime.component'
|
|||||||
> * {
|
> * {
|
||||||
grid-column: span 1;
|
grid-column: span 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
service-uptime {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
host: { class: 'g-subpage' },
|
host: { class: 'g-subpage' },
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
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 { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
import { AuthorityItemComponent } from './item.component'
|
import { AuthorityItemComponent } from './item.component'
|
||||||
import { AuthorityService } from './authority.service'
|
import { AuthorityService } from './authority.service'
|
||||||
@@ -12,15 +10,11 @@ import { AuthorityService } from './authority.service'
|
|||||||
<tr [authority]="{ name: 'Local Root CA' }"></tr>
|
<tr [authority]="{ name: 'Local Root CA' }"></tr>
|
||||||
@for (authority of authorityService.authorities(); track $index) {
|
@for (authority of authorityService.authorities(); track $index) {
|
||||||
<tr [authority]="authority"></tr>
|
<tr [authority]="authority"></tr>
|
||||||
} @empty {
|
|
||||||
<td [attr.colspan]="4">
|
|
||||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
|
||||||
</td>
|
|
||||||
}
|
}
|
||||||
</table>
|
</table>
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [TuiSkeleton, i18nPipe, TableComponent, AuthorityItemComponent],
|
imports: [TableComponent, AuthorityItemComponent],
|
||||||
})
|
})
|
||||||
export class AuthoritiesTableComponent {
|
export class AuthoritiesTableComponent {
|
||||||
protected readonly authorityService = inject(AuthorityService)
|
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) {
|
@for (pkg of pkgs() | keyvalue; track $index) {
|
||||||
@if (backupProgress()?.[pkg.key]; as progress) {
|
@if (backupProgress()?.[pkg.key]; as progress) {
|
||||||
<div tuiCell>
|
<div tuiCell>
|
||||||
<tui-avatar>
|
<tui-avatar appearance="action-grayscale">
|
||||||
<img alt="" [src]="pkg.value.icon" />
|
<img alt="" [src]="pkg.value.icon" />
|
||||||
</tui-avatar>
|
</tui-avatar>
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
inject,
|
inject,
|
||||||
INJECTOR,
|
INJECTOR,
|
||||||
DOCUMENT,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
@@ -151,7 +150,7 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@if (server.kiosk !== null) {
|
@if (server.kiosk !== null) {
|
||||||
<button tuiButton appearance="primary" (click)="tryToggleKiosk()">
|
<button tuiButton appearance="primary" (click)="toggleKiosk()">
|
||||||
{{ server.kiosk ? ('Disable' | i18n) : ('Enable' | i18n) }}
|
{{ server.kiosk ? ('Disable' | i18n) : ('Enable' | i18n) }}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -242,7 +241,6 @@ export default class SystemGeneralComponent {
|
|||||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
private readonly isTor = inject(ConfigService).isTor()
|
private readonly isTor = inject(ConfigService).isTor()
|
||||||
private readonly document = inject(DOCUMENT)
|
|
||||||
private readonly dialog = inject(DialogService)
|
private readonly dialog = inject(DialogService)
|
||||||
private readonly i18n = inject(i18nPipe)
|
private readonly i18n = inject(i18nPipe)
|
||||||
private readonly injector = inject(INJECTOR)
|
private readonly injector = inject(INJECTOR)
|
||||||
@@ -326,28 +324,6 @@ export default class SystemGeneralComponent {
|
|||||||
.subscribe(() => this.resetTor(this.wipe))
|
.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() {
|
async onRepair() {
|
||||||
this.dialog
|
this.dialog
|
||||||
.openConfirm({
|
.openConfirm({
|
||||||
@@ -370,7 +346,7 @@ export default class SystemGeneralComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async toggleKiosk() {
|
async toggleKiosk() {
|
||||||
const kiosk = this.server()?.kiosk
|
const kiosk = this.server()?.kiosk
|
||||||
|
|
||||||
const loader = this.loader
|
const loader = this.loader
|
||||||
@@ -379,6 +355,11 @@ export default class SystemGeneralComponent {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.api.toggleKiosk(!kiosk)
|
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) {
|
} catch (e: any) {
|
||||||
this.errorService.handleError(e)
|
this.errorService.handleError(e)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
viewChild,
|
||||||
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { RouterLink } from '@angular/router'
|
import { RouterLink } from '@angular/router'
|
||||||
import { verify } from '@start9labs/argon2'
|
import { verify } from '@start9labs/argon2'
|
||||||
import {
|
import {
|
||||||
DialogService,
|
|
||||||
ErrorService,
|
ErrorService,
|
||||||
i18nKey,
|
i18nKey,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
LoadingService,
|
LoadingService,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
import { ISB } from '@start9labs/start-sdk'
|
import { ISB } from '@start9labs/start-sdk'
|
||||||
import { TuiButton, TuiTitle } from '@taiga-ui/core'
|
import { TuiAlertService, TuiButton, TuiTitle } from '@taiga-ui/core'
|
||||||
import { TuiHeader } from '@taiga-ui/layout'
|
import { TuiHeader } from '@taiga-ui/layout'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { from } from 'rxjs'
|
import { from } from 'rxjs'
|
||||||
@@ -70,13 +74,14 @@ import { getServerInfo } from 'src/app/utils/get-server-info'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class SystemPasswordComponent {
|
export default class SystemPasswordComponent {
|
||||||
private readonly dialog = inject(DialogService)
|
private readonly alerts = inject(TuiAlertService)
|
||||||
private readonly loader = inject(LoadingService)
|
private readonly loader = inject(LoadingService)
|
||||||
private readonly errorService = inject(ErrorService)
|
private readonly errorService = inject(ErrorService)
|
||||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
private readonly i18n = inject(i18nPipe)
|
private readonly i18n = inject(i18nPipe)
|
||||||
|
|
||||||
|
readonly form = viewChild(FormComponent)
|
||||||
readonly spec = toSignal(from(configBuilderToSpec(this.passwordSpec())))
|
readonly spec = toSignal(from(configBuilderToSpec(this.passwordSpec())))
|
||||||
readonly buttons = [
|
readonly buttons = [
|
||||||
{
|
{
|
||||||
@@ -119,7 +124,12 @@ export default class SystemPasswordComponent {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.api.resetPassword({ oldPassword, newPassword })
|
await this.api.resetPassword({ oldPassword, newPassword })
|
||||||
this.dialog.openAlert('Password changed').subscribe()
|
this.form()?.form.reset()
|
||||||
|
this.alerts
|
||||||
|
.open(this.i18n.transform('Password changed'), {
|
||||||
|
appearance: 'positive',
|
||||||
|
})
|
||||||
|
.subscribe()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.errorService.handleError(e)
|
this.errorService.handleError(e)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ import UpdatesComponent from './updates.component'
|
|||||||
<tr (click)="expanded.set(!expanded())">
|
<tr (click)="expanded.set(!expanded())">
|
||||||
<td>
|
<td>
|
||||||
<div [style.gap.rem]="0.75" [style.padding-inline-end.rem]="1">
|
<div [style.gap.rem]="0.75" [style.padding-inline-end.rem]="1">
|
||||||
<tui-avatar size="s"><img alt="" [src]="item().icon" /></tui-avatar>
|
<tui-avatar appearance="action-grayscale" size="s">
|
||||||
|
<img alt="" [src]="item().icon" />
|
||||||
|
</tui-avatar>
|
||||||
<span tuiTitle [style.margin]="'-0.125rem 0 0'">
|
<span tuiTitle [style.margin]="'-0.125rem 0 0'">
|
||||||
<b tuiFade>{{ item().title }}</b>
|
<b tuiFade>{{ item().title }}</b>
|
||||||
<span tuiSubtitle tuiFade class="mobile">
|
<span tuiSubtitle tuiFade class="mobile">
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ interface UpdatesData {
|
|||||||
[class.g-secondary]="current()?.url !== registry.url"
|
[class.g-secondary]="current()?.url !== registry.url"
|
||||||
(click)="current.set(registry)"
|
(click)="current.set(registry)"
|
||||||
>
|
>
|
||||||
<tui-avatar>
|
<tui-avatar appearance="action-grayscale">
|
||||||
<store-icon [url]="registry.url" />
|
<store-icon [url]="registry.url" />
|
||||||
</tui-avatar>
|
</tui-avatar>
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export namespace Mock {
|
|||||||
squashfs: {
|
squashfs: {
|
||||||
aarch64: {
|
aarch64: {
|
||||||
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.13/startos-0.4.0-alpha.13-33ae46f~dev_aarch64.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_aarch64.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
||||||
size: 1343500288,
|
size: 1343500288,
|
||||||
@@ -122,7 +122,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
'aarch64-nonfree': {
|
'aarch64-nonfree': {
|
||||||
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.13/startos-0.4.0-alpha.13-33ae46f~dev_aarch64-nonfree.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
||||||
size: 1653075968,
|
size: 1653075968,
|
||||||
@@ -134,7 +134,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
raspberrypi: {
|
raspberrypi: {
|
||||||
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.13/startos-0.4.0-alpha.13-33ae46f~dev_raspberrypi.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_raspberrypi.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
||||||
size: 1490731008,
|
size: 1490731008,
|
||||||
@@ -146,7 +146,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
x86_64: {
|
x86_64: {
|
||||||
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.13/startos-0.4.0-alpha.13-33ae46f~dev_x86_64.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.14/startos-0.4.0-alpha.14-33ae46f~dev_x86_64.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
||||||
size: 1411657728,
|
size: 1411657728,
|
||||||
@@ -158,7 +158,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
'x86_64-nonfree': {
|
'x86_64-nonfree': {
|
||||||
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.13/startos-0.4.0-alpha.13-33ae46f~dev_x86_64-nonfree.squashfs',
|
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',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
||||||
size: 1731035136,
|
size: 1731035136,
|
||||||
@@ -385,7 +385,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -420,7 +420,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -465,7 +465,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -500,7 +500,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -547,7 +547,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release to 0.17.5',
|
releaseNotes: 'Upstream release to 0.17.5',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -595,7 +595,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release to 0.17.4',
|
releaseNotes: 'Upstream release to 0.17.4',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -647,7 +647,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -682,7 +682,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -727,7 +727,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release and minor fixes.',
|
releaseNotes: 'Upstream release and minor fixes.',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -775,7 +775,7 @@ export namespace Mock {
|
|||||||
marketingSite: '',
|
marketingSite: '',
|
||||||
releaseNotes: 'Upstream release and minor fixes.',
|
releaseNotes: 'Upstream release and minor fixes.',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.43',
|
sdkVersion: '0.4.0-beta.44',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: PROXY_ICON,
|
icon: PROXY_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user