mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
merge from master and fix typescript errors
This commit is contained in:
240
.github/workflows/startos-iso.yaml
vendored
240
.github/workflows/startos-iso.yaml
vendored
@@ -12,9 +12,9 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- unstable
|
- unstable
|
||||||
- dev-unstable
|
- dev-unstable
|
||||||
- podman
|
- docker
|
||||||
- dev-podman
|
- dev-docker
|
||||||
- dev-unstable-podman
|
- dev-unstable-docker
|
||||||
runner:
|
runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: Runner
|
description: Runner
|
||||||
@@ -31,6 +31,13 @@ on:
|
|||||||
- aarch64
|
- aarch64
|
||||||
- aarch64-nonfree
|
- aarch64-nonfree
|
||||||
- raspberrypi
|
- raspberrypi
|
||||||
|
deploy:
|
||||||
|
type: choice
|
||||||
|
description: Deploy
|
||||||
|
options:
|
||||||
|
- NONE
|
||||||
|
- alpha
|
||||||
|
- beta
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@@ -42,11 +49,55 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
NODEJS_VERSION: "18.15.0"
|
NODEJS_VERSION: "18.15.0"
|
||||||
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev-podman''))[github.event.inputs.environment == ''NONE''] }}'
|
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev''))[github.event.inputs.environment == ''NONE''] }}'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
all:
|
compile:
|
||||||
name: Build
|
name: Compile Base Binaries
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
arch: >-
|
||||||
|
${{
|
||||||
|
fromJson('{
|
||||||
|
"x86_64": ["x86_64"],
|
||||||
|
"x86_64-nonfree": ["x86_64"],
|
||||||
|
"aarch64": ["aarch64"],
|
||||||
|
"aarch64-nonfree": ["aarch64"],
|
||||||
|
"raspberrypi": ["aarch64"],
|
||||||
|
"ALL": ["x86_64", "aarch64"]
|
||||||
|
}')[github.event.inputs.platform || 'ALL']
|
||||||
|
}}
|
||||||
|
runs-on: ${{ fromJson('["ubuntu-22.04", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
sudo mount -t tmpfs tmpfs .
|
||||||
|
if: ${{ github.event.inputs.runner == 'fast' }}
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ env.NODEJS_VERSION }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Make
|
||||||
|
run: make ARCH=${{ matrix.arch }} compiled-${{ matrix.arch }}.tar
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: compiled-${{ matrix.arch }}.tar
|
||||||
|
path: compiled-${{ matrix.arch }}.tar
|
||||||
|
image:
|
||||||
|
name: Build Image
|
||||||
|
needs: [compile]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -68,86 +119,30 @@ jobs:
|
|||||||
format(
|
format(
|
||||||
'["ubuntu-22.04", "{0}"]',
|
'["ubuntu-22.04", "{0}"]',
|
||||||
fromJson('{
|
fromJson('{
|
||||||
"x86_64": ["buildjet-32vcpu-ubuntu-2204", "buildjet-32vcpu-ubuntu-2204"],
|
"x86_64": "buildjet-8vcpu-ubuntu-2204",
|
||||||
"x86_64-nonfree": ["buildjet-32vcpu-ubuntu-2204", "buildjet-32vcpu-ubuntu-2204"],
|
"x86_64-nonfree": "buildjet-8vcpu-ubuntu-2204",
|
||||||
"aarch64": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
"aarch64": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||||
"aarch64-nonfree": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
"aarch64-nonfree": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||||
"raspberrypi": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
"raspberrypi": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||||
}')[matrix.platform][github.event.inputs.platform == matrix.platform]
|
}')[matrix.platform]
|
||||||
)
|
)
|
||||||
)[github.event.inputs.runner == 'fast']
|
)[github.event.inputs.runner == 'fast']
|
||||||
}}
|
}}
|
||||||
|
env:
|
||||||
|
ARCH: >-
|
||||||
|
${{
|
||||||
|
fromJson('{
|
||||||
|
"x86_64": "x86_64",
|
||||||
|
"x86_64-nonfree": "x86_64",
|
||||||
|
"aarch64": "aarch64",
|
||||||
|
"aarch64-nonfree": "aarch64",
|
||||||
|
"raspberrypi": "aarch64",
|
||||||
|
}')[matrix.platform]
|
||||||
|
}}
|
||||||
steps:
|
steps:
|
||||||
- name: Free space
|
|
||||||
run: df -h && rm -rf /opt/hostedtoolcache* && df -h
|
|
||||||
if: ${{ github.event.inputs.runner != 'fast' }}
|
|
||||||
|
|
||||||
- run: |
|
|
||||||
sudo mount -t tmpfs tmpfs .
|
|
||||||
if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree' || github.event.inputs.platform == matrix.platform) }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: Start9Labs/embassy-os-deb
|
|
||||||
path: embassy-os-deb
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
path: embassy-os-deb/embassyos-0.3.x
|
|
||||||
|
|
||||||
- run: |
|
|
||||||
cp -r debian embassyos-0.3.x/
|
|
||||||
VERSION=0.3.x ./control.sh
|
|
||||||
cp embassyos-0.3.x/backend/startd.service embassyos-0.3.x/debian/embassyos.startd.service
|
|
||||||
working-directory: embassy-os-deb
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODEJS_VERSION }}
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: /var/lib/docker
|
|
||||||
key: ${{ runner.os }}-${{ matrix.platform }}-docker-cache
|
|
||||||
|
|
||||||
- name: Get npm cache directory
|
|
||||||
id: npm-cache-dir
|
|
||||||
run: |
|
|
||||||
echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
id: npm-cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-node-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install \
|
|
||||||
debmake \
|
|
||||||
debhelper-compat \
|
|
||||||
crossbuild-essential-arm64
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Run dpkg build
|
|
||||||
working-directory: embassy-os-deb
|
|
||||||
run: "make VERSION=0.3.x TAG=${{ github.ref_name }}"
|
|
||||||
env:
|
|
||||||
OS_ARCH: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: Start9Labs/startos-image-recipes
|
|
||||||
path: startos-image-recipes
|
|
||||||
ref: feature/podman
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -166,52 +161,77 @@ jobs:
|
|||||||
- run: sudo mount -t tmpfs tmpfs /var/tmp/debspawn
|
- run: sudo mount -t tmpfs tmpfs /var/tmp/debspawn
|
||||||
if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree') }}
|
if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree') }}
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- name: Download compiled artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: /var/lib/debspawn
|
name: compiled-${{ env.ARCH }}.tar
|
||||||
key: ${{ runner.os }}-${{ matrix.platform }}-debspawn-init
|
|
||||||
|
|
||||||
- run: "mkdir -p startos-image-recipes/overlays/deb"
|
- name: Extract compiled artifacts
|
||||||
|
run: tar -xvf compiled-${{ env.ARCH }}.tar
|
||||||
|
|
||||||
- run: "mv embassy-os-deb/embassyos_0.3.x-1_*.deb startos-image-recipes/overlays/deb/"
|
- name: Prevent rebuild of compiled artifacts
|
||||||
|
run: |
|
||||||
- run: "sudo rm -rf embassy-os-deb ${{ steps.npm-cache-dir.outputs.dir }} $HOME/.cargo"
|
mkdir -p frontend/dist/raw
|
||||||
|
PLATFORM=${{ matrix.platform }} make -t compiled-${{ env.ARCH }}.tar
|
||||||
|
|
||||||
- name: Run iso build
|
- name: Run iso build
|
||||||
working-directory: startos-image-recipes
|
run: PLATFORM=${{ matrix.platform }} make iso
|
||||||
run: |
|
if: ${{ matrix.platform != 'raspberrypi' }}
|
||||||
./run-local-build.sh ${{ matrix.platform }}
|
|
||||||
|
- name: Run img build
|
||||||
|
run: PLATFORM=${{ matrix.platform }} make img
|
||||||
|
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.platform }}.squashfs
|
name: ${{ matrix.platform }}.squashfs
|
||||||
path: startos-image-recipes/results/*.squashfs
|
path: results/*.squashfs
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.platform }}.iso
|
name: ${{ matrix.platform }}.iso
|
||||||
path: startos-image-recipes/results/*.iso
|
path: results/*.iso
|
||||||
if: ${{ matrix.platform != 'raspberrypi' }}
|
if: ${{ matrix.platform != 'raspberrypi' }}
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
path: start-os
|
|
||||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
|
||||||
|
|
||||||
- run: "mv startos-image-recipes/results/startos-*_raspberrypi.squashfs start-os/startos.raspberrypi.squashfs"
|
|
||||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
|
||||||
|
|
||||||
- run: rm -rf startos-image-recipes
|
|
||||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
|
||||||
|
|
||||||
- name: Build image
|
|
||||||
working-directory: start-os
|
|
||||||
run: make startos_raspberrypi.img
|
|
||||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: raspberrypi.img
|
name: ${{ matrix.platform }}.img
|
||||||
path: start-os/startos-*_raspberrypi.img
|
path: results/*.img
|
||||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||||
|
|
||||||
|
- name: Upload OTA to registry
|
||||||
|
run: >-
|
||||||
|
PLATFORM=${{ matrix.platform }} make upload-ota TARGET="${{
|
||||||
|
fromJson('{
|
||||||
|
"alpha": "alpha-registry-x.start9.com",
|
||||||
|
"beta": "beta-registry.start9.com",
|
||||||
|
}')[github.event.inputs.deploy]
|
||||||
|
}}" KEY="${{
|
||||||
|
fromJson(
|
||||||
|
format('{{
|
||||||
|
"alpha": "{0}",
|
||||||
|
"beta": "{1}",
|
||||||
|
}}', secrets.ALPHA_INDEX_KEY, secrets.BETA_INDEX_KEY)
|
||||||
|
)[github.event.inputs.deploy]
|
||||||
|
}}"
|
||||||
|
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||||
|
|
||||||
|
index:
|
||||||
|
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||||
|
needs: [image]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- run: >-
|
||||||
|
curl "https://${{
|
||||||
|
fromJson('{
|
||||||
|
"alpha": "alpha-registry-x.start9.com",
|
||||||
|
"beta": "beta-registry.start9.com",
|
||||||
|
}')[github.event.inputs.deploy]
|
||||||
|
}}:8443/resync.cgi?key=${{
|
||||||
|
fromJson(
|
||||||
|
format('{{
|
||||||
|
"alpha": "{0}",
|
||||||
|
"beta": "{1}",
|
||||||
|
}}', secrets.ALPHA_INDEX_KEY, secrets.BETA_INDEX_KEY)
|
||||||
|
)[github.event.inputs.deploy]
|
||||||
|
}}"
|
||||||
|
|||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -16,13 +16,15 @@ deploy_web.sh
|
|||||||
secrets.db
|
secrets.db
|
||||||
.vscode/
|
.vscode/
|
||||||
/cargo-deps/**/*
|
/cargo-deps/**/*
|
||||||
|
/PLATFORM.txt
|
||||||
/ENVIRONMENT.txt
|
/ENVIRONMENT.txt
|
||||||
/GIT_HASH.txt
|
/GIT_HASH.txt
|
||||||
/VERSION.txt
|
/VERSION.txt
|
||||||
/embassyos-*.tar.gz
|
|
||||||
/eos-*.tar.gz
|
/eos-*.tar.gz
|
||||||
/*.deb
|
/*.deb
|
||||||
/target
|
/target
|
||||||
/*.squashfs
|
/*.squashfs
|
||||||
/debian
|
/results
|
||||||
/DEBIAN
|
/dpkg-workdir
|
||||||
|
/compiled.tar
|
||||||
|
/compiled-*.tar
|
||||||
5607
Cargo.lock
generated
5607
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
163
Makefile
163
Makefile
@@ -1,25 +1,31 @@
|
|||||||
OS_ARCH := $(shell echo "${OS_ARCH}")
|
PLATFORM_FILE := $(shell ./check-platform.sh)
|
||||||
ARCH := $(shell if [ "$(OS_ARCH)" = "raspberrypi" ]; then echo aarch64; else echo $(OS_ARCH) | sed 's/-nonfree$$//g'; fi)
|
ENVIRONMENT_FILE := $(shell ./check-environment.sh)
|
||||||
ENVIRONMENT_FILE = $(shell ./check-environment.sh)
|
GIT_HASH_FILE := $(shell ./check-git-hash.sh)
|
||||||
GIT_HASH_FILE = $(shell ./check-git-hash.sh)
|
VERSION_FILE := $(shell ./check-version.sh)
|
||||||
VERSION_FILE = $(shell ./check-version.sh)
|
BASENAME := $(shell ./basename.sh)
|
||||||
|
PLATFORM := $(shell if [ -f ./PLATFORM.txt ]; then cat ./PLATFORM.txt; else echo unknown; fi)
|
||||||
|
ARCH := $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo aarch64; else echo $(PLATFORM) | sed 's/-nonfree$$//g'; fi)
|
||||||
|
IMAGE_TYPE=$(shell if [ "$(PLATFORM)" = raspberrypi ]; then echo img; else echo iso; fi)
|
||||||
EMBASSY_BINS := backend/target/$(ARCH)-unknown-linux-gnu/release/startbox libs/target/aarch64-unknown-linux-musl/release/embassy_container_init libs/target/x86_64-unknown-linux-musl/release/embassy_container_init
|
EMBASSY_BINS := backend/target/$(ARCH)-unknown-linux-gnu/release/startbox libs/target/aarch64-unknown-linux-musl/release/embassy_container_init libs/target/x86_64-unknown-linux-musl/release/embassy_container_init
|
||||||
EMBASSY_UIS := frontend/dist/raw/ui frontend/dist/raw/setup-wizard frontend/dist/raw/diagnostic-ui frontend/dist/raw/install-wizard
|
EMBASSY_UIS := frontend/dist/raw/ui frontend/dist/raw/setup-wizard frontend/dist/raw/diagnostic-ui frontend/dist/raw/install-wizard
|
||||||
BUILD_SRC := $(shell find build)
|
BUILD_SRC := $(shell git ls-files build) build/lib/depends build/lib/conflicts
|
||||||
|
DEBIAN_SRC := $(shell git ls-files debian/)
|
||||||
|
IMAGE_RECIPE_SRC := $(shell git ls-files image-recipe/)
|
||||||
EMBASSY_SRC := backend/startd.service $(BUILD_SRC)
|
EMBASSY_SRC := backend/startd.service $(BUILD_SRC)
|
||||||
COMPAT_SRC := $(shell find system-images/compat/ -not -path 'system-images/compat/target/*' -and -not -name *.tar -and -not -name target)
|
COMPAT_SRC := $(shell git ls-files system-images/compat/)
|
||||||
UTILS_SRC := $(shell find system-images/utils/ -not -name *.tar)
|
UTILS_SRC := $(shell git ls-files system-images/utils/)
|
||||||
BINFMT_SRC := $(shell find system-images/binfmt/ -not -name *.tar)
|
BINFMT_SRC := $(shell git ls-files system-images/binfmt/)
|
||||||
BACKEND_SRC := $(shell find backend/src) $(shell find backend/migrations) $(shell find patch-db/*/src) $(shell find libs/*/src) libs/*/Cargo.toml backend/Cargo.toml backend/Cargo.lock frontend/dist/static
|
BACKEND_SRC := $(shell git ls-files backend) $(shell git ls-files --recurse-submodules patch-db) $(shell git ls-files libs) frontend/dist/static
|
||||||
FRONTEND_SHARED_SRC := $(shell find frontend/projects/shared) $(shell ls -p frontend/ | grep -v / | sed 's/^/frontend\//g') frontend/package.json frontend/node_modules frontend/config.json patch-db/client/dist frontend/patchdb-ui-seed.json
|
FRONTEND_SHARED_SRC := $(shell git ls-files frontend/projects/shared) $(shell ls -p frontend/ | grep -v / | sed 's/^/frontend\//g') frontend/node_modules frontend/config.json patch-db/client/dist frontend/patchdb-ui-seed.json
|
||||||
FRONTEND_UI_SRC := $(shell find frontend/projects/ui)
|
FRONTEND_UI_SRC := $(shell git ls-files frontend/projects/ui)
|
||||||
FRONTEND_SETUP_WIZARD_SRC := $(shell find frontend/projects/setup-wizard)
|
FRONTEND_SETUP_WIZARD_SRC := $(shell git ls-files frontend/projects/setup-wizard)
|
||||||
FRONTEND_DIAGNOSTIC_UI_SRC := $(shell find frontend/projects/diagnostic-ui)
|
FRONTEND_DIAGNOSTIC_UI_SRC := $(shell git ls-files frontend/projects/diagnostic-ui)
|
||||||
FRONTEND_INSTALL_WIZARD_SRC := $(shell find frontend/projects/install-wizard)
|
FRONTEND_INSTALL_WIZARD_SRC := $(shell git ls-files frontend/projects/install-wizard)
|
||||||
PATCH_DB_CLIENT_SRC := $(shell find patch-db/client -not -path patch-db/client/dist -and -not -path patch-db/client/node_modules)
|
PATCH_DB_CLIENT_SRC := $(shell git ls-files --recurse-submodules patch-db/client)
|
||||||
GZIP_BIN := $(shell which pigz || which gzip)
|
GZIP_BIN := $(shell which pigz || which gzip)
|
||||||
TAR_BIN := $(shell which gtar || which tar)
|
TAR_BIN := $(shell which gtar || which tar)
|
||||||
ALL_TARGETS := $(EMBASSY_BINS) system-images/compat/docker-images/$(ARCH).tar system-images/utils/docker-images/$(ARCH).tar system-images/binfmt/docker-images/$(ARCH).tar $(EMBASSY_SRC) $(shell if [ "$(OS_ARCH)" = "raspberrypi" ]; then echo cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep; fi) $(shell /bin/bash -c 'if [[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]; then echo cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console; fi') $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE)
|
COMPILED_TARGETS := $(EMBASSY_BINS) system-images/compat/docker-images/$(ARCH).tar system-images/utils/docker-images/$(ARCH).tar system-images/binfmt/docker-images/$(ARCH).tar
|
||||||
|
ALL_TARGETS := $(EMBASSY_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE) $(COMPILED_TARGETS) $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep; fi) $(shell /bin/bash -c 'if [[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]; then echo cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console; fi') $(PLATFORM_FILE)
|
||||||
|
|
||||||
ifeq ($(REMOTE),)
|
ifeq ($(REMOTE),)
|
||||||
mkdir = mkdir -p $1
|
mkdir = mkdir -p $1
|
||||||
@@ -40,14 +46,14 @@ define cp
|
|||||||
endef
|
endef
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.DELETE_ON_ERROR:
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
.PHONY: all gzip install clean format sdk snapshots frontends ui backend reflash startos_raspberrypi.img sudo wormhole
|
.PHONY: all metadata install clean format sdk snapshots frontends ui backend reflash deb $(IMAGE_TYPE) squashfs sudo wormhole
|
||||||
|
|
||||||
all: $(ALL_TARGETS)
|
all: $(ALL_TARGETS)
|
||||||
|
|
||||||
|
metadata: $(VERSION_FILE) $(PLATFORM_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE)
|
||||||
|
|
||||||
sudo:
|
sudo:
|
||||||
sudo true
|
sudo true
|
||||||
|
|
||||||
@@ -64,9 +70,13 @@ clean:
|
|||||||
rm -rf patch-db/client/dist
|
rm -rf patch-db/client/dist
|
||||||
rm -rf patch-db/target
|
rm -rf patch-db/target
|
||||||
rm -rf cargo-deps
|
rm -rf cargo-deps
|
||||||
rm ENVIRONMENT.txt
|
rm -rf dpkg-workdir
|
||||||
rm GIT_HASH.txt
|
rm -rf image-recipe/deb
|
||||||
rm VERSION.txt
|
rm -rf results
|
||||||
|
rm -f ENVIRONMENT.txt
|
||||||
|
rm -f PLATFORM.txt
|
||||||
|
rm -f GIT_HASH.txt
|
||||||
|
rm -f VERSION.txt
|
||||||
|
|
||||||
format:
|
format:
|
||||||
cd backend && cargo +nightly fmt
|
cd backend && cargo +nightly fmt
|
||||||
@@ -75,8 +85,20 @@ format:
|
|||||||
sdk:
|
sdk:
|
||||||
cd backend/ && ./install-sdk.sh
|
cd backend/ && ./install-sdk.sh
|
||||||
|
|
||||||
startos_raspberrypi.img: $(BUILD_SRC) startos.raspberrypi.squashfs $(VERSION_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) | sudo
|
deb: results/$(BASENAME).deb
|
||||||
./build/raspberrypi/make-image.sh
|
|
||||||
|
debian/control: build/lib/depends build/lib/conflicts
|
||||||
|
./debuild/control.sh
|
||||||
|
|
||||||
|
results/$(BASENAME).deb: dpkg-build.sh $(DEBIAN_SRC) $(VERSION_FILE) $(PLATFORM_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE)
|
||||||
|
PLATFORM=$(PLATFORM) ./dpkg-build.sh
|
||||||
|
|
||||||
|
$(IMAGE_TYPE): results/$(BASENAME).$(IMAGE_TYPE)
|
||||||
|
|
||||||
|
squashfs: results/$(BASENAME).squashfs
|
||||||
|
|
||||||
|
results/$(BASENAME).$(IMAGE_TYPE) results/$(BASENAME).squashfs: $(IMAGE_RECIPE_SRC) results/$(BASENAME).deb
|
||||||
|
./image-recipe/run-local-build.sh "results/$(BASENAME).deb"
|
||||||
|
|
||||||
# For creating os images. DO NOT USE
|
# For creating os images. DO NOT USE
|
||||||
install: $(ALL_TARGETS)
|
install: $(ALL_TARGETS)
|
||||||
@@ -88,58 +110,68 @@ install: $(ALL_TARGETS)
|
|||||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/start-deno)
|
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/start-deno)
|
||||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/avahi-alias)
|
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/avahi-alias)
|
||||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/embassy-cli)
|
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/embassy-cli)
|
||||||
if [ "$(OS_ARCH)" = "raspberrypi" ]; then $(call cp,cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep,$(DESTDIR)/usr/bin/pi-beep); fi
|
if [ "$(PLATFORM)" = "raspberrypi" ]; then $(call cp,cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep,$(DESTDIR)/usr/bin/pi-beep); fi
|
||||||
if /bin/bash -c '[[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]'; then $(call cp,cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console,$(DESTDIR)/usr/bin/tokio-console); fi
|
if /bin/bash -c '[[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]'; then $(call cp,cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console,$(DESTDIR)/usr/bin/tokio-console); fi
|
||||||
|
|
||||||
|
$(call mkdir,$(DESTDIR)/lib/systemd/system)
|
||||||
|
$(call cp,backend/startd.service,$(DESTDIR)/lib/systemd/system/startd.service)
|
||||||
|
|
||||||
$(call mkdir,$(DESTDIR)/usr/lib)
|
$(call mkdir,$(DESTDIR)/usr/lib)
|
||||||
$(call rm,$(DESTDIR)/usr/lib/embassy)
|
$(call rm,$(DESTDIR)/usr/lib/startos)
|
||||||
$(call cp,build/lib,$(DESTDIR)/usr/lib/embassy)
|
$(call cp,build/lib,$(DESTDIR)/usr/lib/startos)
|
||||||
|
|
||||||
$(call cp,ENVIRONMENT.txt,$(DESTDIR)/usr/lib/embassy/ENVIRONMENT.txt)
|
$(call cp,PLATFORM.txt,$(DESTDIR)/usr/lib/startos/PLATFORM.txt)
|
||||||
$(call cp,GIT_HASH.txt,$(DESTDIR)/usr/lib/embassy/GIT_HASH.txt)
|
$(call cp,ENVIRONMENT.txt,$(DESTDIR)/usr/lib/startos/ENVIRONMENT.txt)
|
||||||
$(call cp,VERSION.txt,$(DESTDIR)/usr/lib/embassy/VERSION.txt)
|
$(call cp,GIT_HASH.txt,$(DESTDIR)/usr/lib/startos/GIT_HASH.txt)
|
||||||
|
$(call cp,VERSION.txt,$(DESTDIR)/usr/lib/startos/VERSION.txt)
|
||||||
|
|
||||||
$(call mkdir,$(DESTDIR)/usr/lib/embassy/container)
|
$(call mkdir,$(DESTDIR)/usr/lib/startos/container)
|
||||||
$(call cp,libs/target/aarch64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/embassy/container/embassy_container_init.arm64)
|
$(call cp,libs/target/aarch64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/startos/container/embassy_container_init.arm64)
|
||||||
$(call cp,libs/target/x86_64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/embassy/container/embassy_container_init.amd64)
|
$(call cp,libs/target/x86_64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/startos/container/embassy_container_init.amd64)
|
||||||
|
|
||||||
$(call mkdir,$(DESTDIR)/usr/lib/embassy/system-images)
|
$(call mkdir,$(DESTDIR)/usr/lib/startos/system-images)
|
||||||
$(call cp,system-images/compat/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/compat.tar)
|
$(call cp,system-images/compat/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/compat.tar)
|
||||||
$(call cp,system-images/utils/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/utils.tar)
|
$(call cp,system-images/utils/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/utils.tar)
|
||||||
$(call cp,system-images/binfmt/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/binfmt.tar)
|
$(call cp,system-images/binfmt/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/binfmt.tar)
|
||||||
|
|
||||||
update-overlay:
|
update-overlay: $(ALL_TARGETS)
|
||||||
@echo "\033[33m!!! THIS WILL ONLY REFLASH YOUR DEVICE IN MEMORY !!!\033[0m"
|
@echo "\033[33m!!! THIS WILL ONLY REFLASH YOUR DEVICE IN MEMORY !!!\033[0m"
|
||||||
@echo "\033[33mALL CHANGES WILL BE REVERTED IF YOU RESTART THE DEVICE\033[0m"
|
@echo "\033[33mALL CHANGES WILL BE REVERTED IF YOU RESTART THE DEVICE\033[0m"
|
||||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||||
@if [ "`ssh $(REMOTE) 'cat /usr/lib/embassy/VERSION.txt'`" != "`cat ./VERSION.txt`" ]; then >&2 echo "StartOS requires migrations: update-overlay is unavailable." && false; fi
|
@if [ "`ssh $(REMOTE) 'cat /usr/lib/startos/VERSION.txt'`" != "`cat ./VERSION.txt`" ]; then >&2 echo "StartOS requires migrations: update-overlay is unavailable." && false; fi
|
||||||
$(call ssh,"sudo systemctl stop startd")
|
$(call ssh,"sudo systemctl stop startd")
|
||||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) OS_ARCH=$(OS_ARCH)
|
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) PLATFORM=$(PLATFORM)
|
||||||
$(call ssh,"sudo systemctl start startd")
|
$(call ssh,"sudo systemctl start startd")
|
||||||
|
|
||||||
wormhole: backend/target/$(ARCH)-unknown-linux-gnu/release/startbox
|
wormhole: backend/target/$(ARCH)-unknown-linux-gnu/release/startbox
|
||||||
@wormhole send backend/target/$(ARCH)-unknown-linux-gnu/release/startbox 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/embassy/scripts/chroot-and-upgrade \"cd /usr/bin && rm startbox && wormhole receive --accept-file %s && chmod +x startbox\"\n", $$3 }'
|
@wormhole send backend/target/$(ARCH)-unknown-linux-gnu/release/startbox 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade \"cd /usr/bin && rm startbox && wormhole receive --accept-file %s && chmod +x startbox\"\n", $$3 }'
|
||||||
|
|
||||||
update:
|
update: $(ALL_TARGETS)
|
||||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||||
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
||||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next OS_ARCH=$(OS_ARCH)
|
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next PLATFORM=$(PLATFORM)
|
||||||
$(call ssh,'sudo NO_SYNC=1 /media/embassy/next/usr/lib/embassy/scripts/chroot-and-upgrade "apt-get install -y $(shell cat ./build/lib/depends)"')
|
$(call ssh,'sudo NO_SYNC=1 /media/embassy/next/usr/lib/startos/scripts/chroot-and-upgrade "apt-get install -y $(shell cat ./build/lib/depends)"')
|
||||||
|
|
||||||
emulate-reflash:
|
emulate-reflash: $(ALL_TARGETS)
|
||||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||||
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
||||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next OS_ARCH=$(OS_ARCH)
|
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next PLATFORM=$(PLATFORM)
|
||||||
$(call ssh,"sudo touch /media/embassy/config/upgrade && sudo rm -f /media/embassy/config/disk.guid && sudo sync && sudo reboot")
|
$(call ssh,"sudo touch /media/embassy/config/upgrade && sudo rm -f /media/embassy/config/disk.guid && sudo sync && sudo reboot")
|
||||||
|
|
||||||
system-images/compat/docker-images/aarch64.tar system-images/compat/docker-images/x86_64.tar: $(COMPAT_SRC) backend/Cargo.lock | sudo
|
upload-ota: results/$(BASENAME).squashfs
|
||||||
cd system-images/compat && make && touch docker-images/*.tar
|
TARGET=$(TARGET) KEY=$(KEY) ./upload-ota.sh
|
||||||
|
|
||||||
system-images/utils/docker-images/aarch64.tar system-images/utils/docker-images/x86_64.tar: $(UTILS_SRC) | sudo
|
build/lib/depends build/lib/conflicts: build/dpkg-deps/*
|
||||||
cd system-images/utils && make && touch docker-images/*.tar
|
build/dpkg-deps/generate.sh
|
||||||
|
|
||||||
system-images/binfmt/docker-images/aarch64.tar system-images/binfmt/docker-images/x86_64.tar: $(BINFMT_SRC) | sudo
|
system-images/compat/docker-images/$(ARCH).tar: $(COMPAT_SRC) backend/Cargo.lock
|
||||||
cd system-images/binfmt && make && touch docker-images/*.tar
|
cd system-images/compat && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||||
|
|
||||||
|
system-images/utils/docker-images/$(ARCH).tar: $(UTILS_SRC)
|
||||||
|
cd system-images/utils && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||||
|
|
||||||
|
system-images/binfmt/docker-images/$(ARCH).tar: $(BINFMT_SRC)
|
||||||
|
cd system-images/binfmt && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||||
|
|
||||||
snapshots: libs/snapshot_creator/Cargo.toml
|
snapshots: libs/snapshot_creator/Cargo.toml
|
||||||
cd libs/ && ./build-v8-snapshot.sh
|
cd libs/ && ./build-v8-snapshot.sh
|
||||||
@@ -152,30 +184,26 @@ $(EMBASSY_BINS): $(BACKEND_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) frontend/pa
|
|||||||
frontend/node_modules: frontend/package.json
|
frontend/node_modules: frontend/package.json
|
||||||
npm --prefix frontend ci
|
npm --prefix frontend ci
|
||||||
|
|
||||||
frontend/dist/raw/ui: $(FRONTEND_UI_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
frontend/dist/raw/ui: $(FRONTEND_UI_SRC) $(FRONTEND_SHARED_SRC)
|
||||||
npm --prefix frontend run build:ui
|
npm --prefix frontend run build:ui
|
||||||
|
|
||||||
frontend/dist/raw/setup-wizard: $(FRONTEND_SETUP_WIZARD_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
frontend/dist/raw/setup-wizard: $(FRONTEND_SETUP_WIZARD_SRC) $(FRONTEND_SHARED_SRC)
|
||||||
npm --prefix frontend run build:setup
|
npm --prefix frontend run build:setup
|
||||||
|
|
||||||
frontend/dist/raw/diagnostic-ui: $(FRONTEND_DIAGNOSTIC_UI_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
frontend/dist/raw/diagnostic-ui: $(FRONTEND_DIAGNOSTIC_UI_SRC) $(FRONTEND_SHARED_SRC)
|
||||||
npm --prefix frontend run build:dui
|
npm --prefix frontend run build:dui
|
||||||
|
|
||||||
frontend/dist/raw/install-wizard: $(FRONTEND_INSTALL_WIZARD_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
frontend/dist/raw/install-wizard: $(FRONTEND_INSTALL_WIZARD_SRC) $(FRONTEND_SHARED_SRC)
|
||||||
npm --prefix frontend run build:install-wiz
|
npm --prefix frontend run build:install-wiz
|
||||||
|
|
||||||
frontend/dist/static: $(EMBASSY_UIS)
|
frontend/dist/static: $(EMBASSY_UIS) $(ENVIRONMENT_FILE)
|
||||||
./compress-uis.sh
|
./compress-uis.sh
|
||||||
|
|
||||||
frontend/config.json: $(GIT_HASH_FILE) frontend/config-sample.json
|
frontend/config.json: $(GIT_HASH_FILE) frontend/config-sample.json
|
||||||
jq '.useMocks = false' frontend/config-sample.json > frontend/config.json
|
jq '.useMocks = false' frontend/config-sample.json | jq '.gitHash = "$(shell cat GIT_HASH.txt)"' > frontend/config.json
|
||||||
jq '.packageArch = "$(ARCH)"' frontend/config.json > frontend/config.json.tmp
|
|
||||||
jq '.osArch = "$(OS_ARCH)"' frontend/config.json.tmp > frontend/config.json
|
|
||||||
rm frontend/config.json.tmp
|
|
||||||
npm --prefix frontend run-script build-config
|
|
||||||
|
|
||||||
frontend/patchdb-ui-seed.json: frontend/package.json
|
frontend/patchdb-ui-seed.json: frontend/package.json
|
||||||
jq '."ack-welcome" = $(shell yq '.version' frontend/package.json)' frontend/patchdb-ui-seed.json > ui-seed.tmp
|
jq '."ack-welcome" = $(shell jq '.version' frontend/package.json)' frontend/patchdb-ui-seed.json > ui-seed.tmp
|
||||||
mv ui-seed.tmp frontend/patchdb-ui-seed.json
|
mv ui-seed.tmp frontend/patchdb-ui-seed.json
|
||||||
|
|
||||||
patch-db/client/node_modules: patch-db/client/package.json
|
patch-db/client/node_modules: patch-db/client/package.json
|
||||||
@@ -186,7 +214,7 @@ patch-db/client/dist: $(PATCH_DB_CLIENT_SRC) patch-db/client/node_modules
|
|||||||
npm --prefix frontend run build:deps
|
npm --prefix frontend run build:deps
|
||||||
|
|
||||||
# used by github actions
|
# used by github actions
|
||||||
backend-$(ARCH).tar: $(EMBASSY_BINS)
|
compiled-$(ARCH).tar: $(COMPILED_TARGETS) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE)
|
||||||
tar -cvf $@ $^
|
tar -cvf $@ $^
|
||||||
|
|
||||||
# this is a convenience step to build all frontends - it is not referenced elsewhere in this file
|
# this is a convenience step to build all frontends - it is not referenced elsewhere in this file
|
||||||
@@ -195,10 +223,7 @@ frontends: $(EMBASSY_UIS)
|
|||||||
# this is a convenience step to build the UI
|
# this is a convenience step to build the UI
|
||||||
ui: frontend/dist/raw/ui
|
ui: frontend/dist/raw/ui
|
||||||
|
|
||||||
# used by github actions
|
cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep:
|
||||||
backend: $(EMBASSY_BINS)
|
|
||||||
|
|
||||||
cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep: | sudo
|
|
||||||
ARCH=aarch64 ./build-cargo-dep.sh pi-beep
|
ARCH=aarch64 ./build-cargo-dep.sh pi-beep
|
||||||
|
|
||||||
cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console: | sudo
|
cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console: | sudo
|
||||||
|
|||||||
14
backend/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json
generated
Normal file
14
backend/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM network_keys WHERE package = $1",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d"
|
||||||
|
}
|
||||||
24
backend/Cargo.lock
generated
24
backend/Cargo.lock
generated
@@ -1906,28 +1906,6 @@ version = "0.28.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "git-version"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899"
|
|
||||||
dependencies = [
|
|
||||||
"git-version-macro",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "git-version-macro"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-hack",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -4970,12 +4948,12 @@ dependencies = [
|
|||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"divrem",
|
"divrem",
|
||||||
"ed25519 2.2.3",
|
"ed25519 2.2.3",
|
||||||
|
"ed25519-dalek 1.0.1",
|
||||||
"ed25519-dalek 2.0.0",
|
"ed25519-dalek 2.0.0",
|
||||||
"embassy_container_init",
|
"embassy_container_init",
|
||||||
"emver",
|
"emver",
|
||||||
"fd-lock-rs",
|
"fd-lock-rs",
|
||||||
"futures",
|
"futures",
|
||||||
"git-version",
|
|
||||||
"gpt",
|
"gpt",
|
||||||
"helpers",
|
"helpers",
|
||||||
"hex",
|
"hex",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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.3.5"
|
version = "0.3.5"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "startos"
|
name = "startos"
|
||||||
@@ -31,7 +32,7 @@ cli = []
|
|||||||
daemon = []
|
daemon = []
|
||||||
default = ["cli", "sdk", "daemon", "js_engine"]
|
default = ["cli", "sdk", "daemon", "js_engine"]
|
||||||
dev = []
|
dev = []
|
||||||
podman = []
|
docker = []
|
||||||
sdk = []
|
sdk = []
|
||||||
unstable = ["console-subscriber", "tokio/tracing"]
|
unstable = ["console-subscriber", "tokio/tracing"]
|
||||||
|
|
||||||
@@ -66,18 +67,17 @@ divrem = "1.0.0"
|
|||||||
ed25519 = { version = "2.2.3", features = ["pkcs8", "pem", "alloc"] }
|
ed25519 = { version = "2.2.3", features = ["pkcs8", "pem", "alloc"] }
|
||||||
ed25519-dalek = { version = "2.0.0", features = [
|
ed25519-dalek = { version = "2.0.0", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"hazmat",
|
|
||||||
"zeroize",
|
"zeroize",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"digest",
|
"digest",
|
||||||
] }
|
] }
|
||||||
|
ed25519-dalek-v1 = { package = "ed25519-dalek", version = "1" }
|
||||||
embassy_container_init = { path = "../libs/embassy_container_init" }
|
embassy_container_init = { path = "../libs/embassy_container_init" }
|
||||||
emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [
|
emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
fd-lock-rs = "0.1.4"
|
fd-lock-rs = "0.1.4"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
git-version = "0.3.5"
|
|
||||||
gpt = "3.1.0"
|
gpt = "3.1.0"
|
||||||
helpers = { path = "../libs/helpers" }
|
helpers = { path = "../libs/helpers" }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
|||||||
@@ -3,11 +3,6 @@
|
|||||||
set -e
|
set -e
|
||||||
shopt -s expand_aliases
|
shopt -s expand_aliases
|
||||||
|
|
||||||
if [ -z "$OS_ARCH" ]; then
|
|
||||||
>&2 echo '$OS_ARCH is required'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$ARCH" ]; then
|
if [ -z "$ARCH" ]; then
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
fi
|
fi
|
||||||
@@ -23,27 +18,17 @@ if tty -s; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
FLAGS=""
|
FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')"
|
||||||
RUSTFLAGS=""
|
RUSTFLAGS=""
|
||||||
if [[ "$ENVIRONMENT" =~ (^|-)podman($|-) ]]; then
|
|
||||||
FLAGS="podman,$FLAGS"
|
|
||||||
fi
|
|
||||||
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
|
||||||
FLAGS="unstable,$FLAGS"
|
|
||||||
RUSTFLAGS="$RUSTFLAGS --cfg tokio_unstable"
|
|
||||||
fi
|
|
||||||
if [[ "$ENVIRONMENT" =~ (^|-)dev($|-) ]]; then
|
|
||||||
FLAGS="dev,$FLAGS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
||||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
alias 'rust-musl-builder'='docker run $USE_TTY --rm -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
fail=
|
fail=
|
||||||
echo "FLAGS=\"$FLAGS\""
|
echo "FEATURES=\"$FEATURES\""
|
||||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FLAGS --locked --target=$ARCH-unknown-linux-gnu)"
|
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FEATURES --locked --target=$ARCH-unknown-linux-gnu)"
|
||||||
if test $? -ne 0; then
|
if test $? -ne 0; then
|
||||||
fail=true
|
fail=true
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ fi
|
|||||||
frontend="../frontend/dist/static"
|
frontend="../frontend/dist/static"
|
||||||
[ -d "$frontend" ] || mkdir -p "$frontend"
|
[ -d "$frontend" ] || mkdir -p "$frontend"
|
||||||
|
|
||||||
if [ -z "$OS_ARCH" ]; then
|
if [ -z "$PLATFORM" ]; then
|
||||||
export OS_ARCH=$(uname -m)
|
export PLATFORM=$(uname -m)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cargo install --path=. --no-default-features --features=js_engine,sdk,cli --locked
|
cargo install --path=. --no-default-features --features=js_engine,sdk,cli --locked
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use digest::Digest;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use ed25519_dalek::SecretKey;
|
use ed25519_dalek::SecretKey;
|
||||||
use openssl::pkey::{PKey, Private};
|
use openssl::pkey::{PKey, Private};
|
||||||
use openssl::x509::X509;
|
use openssl::x509::X509;
|
||||||
@@ -14,7 +15,7 @@ fn hash_password(password: &str) -> Result<String, Error> {
|
|||||||
argon2::hash_encoded(
|
argon2::hash_encoded(
|
||||||
password.as_bytes(),
|
password.as_bytes(),
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::default(),
|
&argon2::Config::rfc9106_low_mem(),
|
||||||
)
|
)
|
||||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)
|
.with_kind(crate::ErrorKind::PasswordHashGeneration)
|
||||||
}
|
}
|
||||||
@@ -29,11 +30,11 @@ pub struct AccountInfo {
|
|||||||
pub root_ca_cert: X509,
|
pub root_ca_cert: X509,
|
||||||
}
|
}
|
||||||
impl AccountInfo {
|
impl AccountInfo {
|
||||||
pub fn new(password: &str) -> Result<Self, Error> {
|
pub fn new(password: &str, start_time: SystemTime) -> Result<Self, Error> {
|
||||||
let server_id = generate_id();
|
let server_id = generate_id();
|
||||||
let hostname = generate_hostname();
|
let hostname = generate_hostname();
|
||||||
let root_ca_key = generate_key()?;
|
let root_ca_key = generate_key()?;
|
||||||
let root_ca_cert = make_root_cert(&root_ca_key, &hostname)?;
|
let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
server_id,
|
server_id,
|
||||||
hostname,
|
hostname,
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ fn gen_pwd() {
|
|||||||
argon2::hash_encoded(
|
argon2::hash_encoded(
|
||||||
b"testing1234",
|
b"testing1234",
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::default()
|
&argon2::Config::rfc9106_low_mem()
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ pub async fn recover_full_embassy(
|
|||||||
os_backup.account.password = argon2::hash_encoded(
|
os_backup.account.password = argon2::hash_encoded(
|
||||||
embassy_password.as_bytes(),
|
embassy_password.as_bytes(),
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::default(),
|
&argon2::Config::rfc9106_low_mem(),
|
||||||
)
|
)
|
||||||
.with_kind(ErrorKind::PasswordHashGeneration)?;
|
.with_kind(ErrorKind::PasswordHashGeneration)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
use clap::Arg;
|
|
||||||
use rpc_toolkit::command;
|
|
||||||
use rpc_toolkit::run_cli;
|
|
||||||
use rpc_toolkit::yajrc::RpcError;
|
use rpc_toolkit::yajrc::RpcError;
|
||||||
|
use rpc_toolkit::{command, run_cli, Context};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::context::CliContext;
|
|
||||||
use crate::procedure::js_scripts::ExecuteArgs;
|
use crate::procedure::js_scripts::ExecuteArgs;
|
||||||
use crate::s9pk::manifest::PackageId;
|
use crate::s9pk::manifest::PackageId;
|
||||||
use crate::util::logger::EmbassyLogger;
|
|
||||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable};
|
use crate::util::serde::{display_serializable, parse_stdin_deserializable};
|
||||||
use crate::version::{Current, VersionT};
|
use crate::version::{Current, VersionT};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
@@ -16,6 +12,9 @@ lazy_static::lazy_static! {
|
|||||||
static ref VERSION_STRING: String = Current::new().semver().to_string();
|
static ref VERSION_STRING: String = Current::new().semver().to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DenoContext;
|
||||||
|
impl Context for DenoContext {}
|
||||||
|
|
||||||
#[command(subcommands(execute, sandbox))]
|
#[command(subcommands(execute, sandbox))]
|
||||||
fn deno_api() -> Result<(), Error> {
|
fn deno_api() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -70,13 +69,11 @@ impl PackageLogger {
|
|||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
use tracing_subscriber::{fmt, EnvFilter};
|
use tracing_subscriber::{fmt, EnvFilter};
|
||||||
|
|
||||||
let filter_layer = EnvFilter::builder()
|
let filter_layer = EnvFilter::default().add_directive(
|
||||||
.with_default_directive(
|
format!("{}=warn", std::module_path!().split("::").next().unwrap())
|
||||||
format!("{}=info", std::module_path!().split("::").next().unwrap())
|
.parse()
|
||||||
.parse()
|
.unwrap(),
|
||||||
.unwrap(),
|
);
|
||||||
)
|
|
||||||
.from_env_lossy();
|
|
||||||
let fmt_layer = fmt::layer().with_writer(std::io::stderr).with_target(true);
|
let fmt_layer = fmt::layer().with_writer(std::io::stderr).with_target(true);
|
||||||
let journald_layer = tracing_journald::layer()
|
let journald_layer = tracing_journald::layer()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -103,16 +100,8 @@ fn inner_main() -> Result<(), Error> {
|
|||||||
command: deno_api,
|
command: deno_api,
|
||||||
app: app => app
|
app: app => app
|
||||||
.name("StartOS Deno Executor")
|
.name("StartOS Deno Executor")
|
||||||
.version(&**VERSION_STRING)
|
.version(&**VERSION_STRING),
|
||||||
.arg(
|
context: _m => DenoContext,
|
||||||
clap::Arg::with_name("config")
|
|
||||||
.short('c')
|
|
||||||
.long("config")
|
|
||||||
.takes_value(true),
|
|
||||||
),
|
|
||||||
context: matches => {
|
|
||||||
CliContext::init(matches)?
|
|
||||||
},
|
|
||||||
exit: |e: RpcError| {
|
exit: |e: RpcError| {
|
||||||
match e.data {
|
match e.data {
|
||||||
Some(Value::String(s)) => eprintln!("{}: {}", e.message, s),
|
Some(Value::String(s)) => eprintln!("{}: {}", e.message, s),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::net::web_server::WebServer;
|
|||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
use crate::sound::CHIME;
|
use crate::sound::CHIME;
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
|
use crate::{Error, ErrorKind, ResultExt, PLATFORM};
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||||
@@ -30,19 +30,19 @@ async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Er
|
|||||||
|
|
||||||
Command::new("ln")
|
Command::new("ln")
|
||||||
.arg("-sf")
|
.arg("-sf")
|
||||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||||
.arg("/usr/local/bin/apt")
|
.arg("/usr/local/bin/apt")
|
||||||
.invoke(crate::ErrorKind::OpenSsh)
|
.invoke(crate::ErrorKind::OpenSsh)
|
||||||
.await?;
|
.await?;
|
||||||
Command::new("ln")
|
Command::new("ln")
|
||||||
.arg("-sf")
|
.arg("-sf")
|
||||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||||
.arg("/usr/local/bin/apt-get")
|
.arg("/usr/local/bin/apt-get")
|
||||||
.invoke(crate::ErrorKind::OpenSsh)
|
.invoke(crate::ErrorKind::OpenSsh)
|
||||||
.await?;
|
.await?;
|
||||||
Command::new("ln")
|
Command::new("ln")
|
||||||
.arg("-sf")
|
.arg("-sf")
|
||||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||||
.arg("/usr/local/bin/aptitude")
|
.arg("/usr/local/bin/aptitude")
|
||||||
.invoke(crate::ErrorKind::OpenSsh)
|
.invoke(crate::ErrorKind::OpenSsh)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -177,7 +177,7 @@ async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
|
|||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||||
if OS_ARCH == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
|
if &*PLATFORM == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
|
||||||
tokio::fs::remove_file(STANDBY_MODE_PATH).await?;
|
tokio::fs::remove_file(STANDBY_MODE_PATH).await?;
|
||||||
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
|
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
|
||||||
crate::sound::SHUTDOWN.play().await?;
|
crate::sound::SHUTDOWN.play().await?;
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ use rpc_toolkit::command;
|
|||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::s9pk::manifest::{PackageId};
|
use crate::s9pk::manifest::PackageId;
|
||||||
use crate::util::display_none;
|
use crate::util::display_none;
|
||||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -105,7 +105,7 @@ where
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
timeout: &Option<Duration>,
|
timeout: &Option<Duration>,
|
||||||
) -> Result<Value, Self::Error> {
|
) -> Result<Value, Self::Error> {
|
||||||
self.gen_with(self.default_spec().borrow(), rng, timeout)
|
self.gen_with(self.default_spec(), rng, timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use serde::Deserialize;
|
|||||||
use sqlx::postgres::PgConnectOptions;
|
use sqlx::postgres::PgConnectOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
||||||
|
use tokio::time::Instant;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use super::setup::CURRENT_SECRET;
|
use super::setup::CURRENT_SECRET;
|
||||||
@@ -29,7 +30,7 @@ use crate::install::cleanup::{cleanup_failed, uninstall};
|
|||||||
use crate::manager::ManagerMap;
|
use crate::manager::ManagerMap;
|
||||||
use crate::middleware::auth::HashSessionToken;
|
use crate::middleware::auth::HashSessionToken;
|
||||||
use crate::net::net_controller::NetController;
|
use crate::net::net_controller::NetController;
|
||||||
use crate::net::ssl::SslManager;
|
use crate::net::ssl::{root_ca_start_time, SslManager};
|
||||||
use crate::net::wifi::WpaCli;
|
use crate::net::wifi::WpaCli;
|
||||||
use crate::notifications::NotificationManager;
|
use crate::notifications::NotificationManager;
|
||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
@@ -123,6 +124,7 @@ pub struct RpcContextSeed {
|
|||||||
pub current_secret: Arc<Jwk>,
|
pub current_secret: Arc<Jwk>,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub hardware: Hardware,
|
pub hardware: Hardware,
|
||||||
|
pub start_time: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Hardware {
|
pub struct Hardware {
|
||||||
@@ -158,7 +160,7 @@ impl RpcContext {
|
|||||||
base.dns_bind
|
base.dns_bind
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]),
|
.unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]),
|
||||||
SslManager::new(&account)?,
|
SslManager::new(&account, root_ca_start_time().await?)?,
|
||||||
&account.hostname,
|
&account.hostname,
|
||||||
&account.key,
|
&account.key,
|
||||||
)
|
)
|
||||||
@@ -214,6 +216,7 @@ impl RpcContext {
|
|||||||
.build()
|
.build()
|
||||||
.with_kind(crate::ErrorKind::ParseUrl)?,
|
.with_kind(crate::ErrorKind::ParseUrl)?,
|
||||||
hardware: Hardware { devices, ram },
|
hardware: Hardware { devices, ram },
|
||||||
|
start_time: Instant::now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let res = Self(seed.clone());
|
let res = Self(seed.clone());
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use emver::VersionRange;
|
use emver::VersionRange;
|
||||||
|
use imbl_value::InternedString;
|
||||||
use ipnet::{Ipv4Net, Ipv6Net};
|
use ipnet::{Ipv4Net, Ipv6Net};
|
||||||
use isocountry::CountryCode;
|
use isocountry::CountryCode;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@@ -24,6 +25,7 @@ use crate::s9pk::manifest::{Manifest, PackageId};
|
|||||||
use crate::status::Status;
|
use crate::status::Status;
|
||||||
use crate::util::Version;
|
use crate::util::Version;
|
||||||
use crate::version::{Current, VersionT};
|
use crate::version::{Current, VersionT};
|
||||||
|
use crate::{ARCH, PLATFORM};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
@@ -40,6 +42,8 @@ impl Database {
|
|||||||
let lan_address = account.hostname.lan_address().parse().unwrap();
|
let lan_address = account.hostname.lan_address().parse().unwrap();
|
||||||
Database {
|
Database {
|
||||||
server_info: ServerInfo {
|
server_info: ServerInfo {
|
||||||
|
arch: get_arch(),
|
||||||
|
platform: get_platform(),
|
||||||
id: account.server_id.clone(),
|
id: account.server_id.clone(),
|
||||||
version: Current::new().semver().into(),
|
version: Current::new().semver().into(),
|
||||||
hostname: account.hostname.no_dot_host_name(),
|
hostname: account.hostname.no_dot_host_name(),
|
||||||
@@ -55,6 +59,8 @@ impl Database {
|
|||||||
backup_progress: None,
|
backup_progress: None,
|
||||||
updated: false,
|
updated: false,
|
||||||
update_progress: None,
|
update_progress: None,
|
||||||
|
shutting_down: false,
|
||||||
|
restarting: false,
|
||||||
},
|
},
|
||||||
wifi: WifiInfo {
|
wifi: WifiInfo {
|
||||||
ssids: Vec::new(),
|
ssids: Vec::new(),
|
||||||
@@ -77,8 +83,8 @@ impl Database {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|x| format!("{x:X}"))
|
.map(|x| format!("{x:X}"))
|
||||||
.join(":"),
|
.join(":"),
|
||||||
system_start_time: Utc::now().to_rfc3339(),
|
ntp_synced: false,
|
||||||
zram: false,
|
zram: true,
|
||||||
},
|
},
|
||||||
package_data: AllPackageData::default(),
|
package_data: AllPackageData::default(),
|
||||||
lan_port_forwards: LanPortForwards::new(),
|
lan_port_forwards: LanPortForwards::new(),
|
||||||
@@ -90,10 +96,22 @@ impl Database {
|
|||||||
|
|
||||||
pub type DatabaseModel = Model<Database>;
|
pub type DatabaseModel = Model<Database>;
|
||||||
|
|
||||||
|
fn get_arch() -> InternedString {
|
||||||
|
(*ARCH).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_platform() -> InternedString {
|
||||||
|
(&*PLATFORM).into()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[model = "Model<Self>"]
|
#[model = "Model<Self>"]
|
||||||
pub struct ServerInfo {
|
pub struct ServerInfo {
|
||||||
|
#[serde(default = "get_arch")]
|
||||||
|
pub arch: InternedString,
|
||||||
|
#[serde(default = "get_platform")]
|
||||||
|
pub platform: InternedString,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub hostname: String,
|
pub hostname: String,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
@@ -112,7 +130,8 @@ pub struct ServerInfo {
|
|||||||
pub password_hash: String,
|
pub password_hash: String,
|
||||||
pub pubkey: String,
|
pub pubkey: String,
|
||||||
pub ca_fingerprint: String,
|
pub ca_fingerprint: String,
|
||||||
pub system_start_time: String,
|
#[serde(default)]
|
||||||
|
pub ntp_synced: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub zram: bool,
|
pub zram: bool,
|
||||||
}
|
}
|
||||||
@@ -152,6 +171,10 @@ pub struct ServerStatus {
|
|||||||
pub backup_progress: Option<BTreeMap<PackageId, BackupProgress>>,
|
pub backup_progress: Option<BTreeMap<PackageId, BackupProgress>>,
|
||||||
pub updated: bool,
|
pub updated: bool,
|
||||||
pub update_progress: Option<UpdateProgress>,
|
pub update_progress: Option<UpdateProgress>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub shutting_down: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub restarting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub async fn btrfs_check_repair(logicalname: impl AsRef<Path>) -> Result<Require
|
|||||||
Command::new("btrfs")
|
Command::new("btrfs")
|
||||||
.arg("check")
|
.arg("check")
|
||||||
.arg("--repair")
|
.arg("--repair")
|
||||||
|
.arg("--force")
|
||||||
.arg(logicalname.as_ref())
|
.arg(logicalname.as_ref())
|
||||||
.invoke(crate::ErrorKind::DiskManagement)
|
.invoke(crate::ErrorKind::DiskManagement)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
|||||||
argon2::hash_encoded(
|
argon2::hash_encoded(
|
||||||
password.as_bytes(),
|
password.as_bytes(),
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::default(),
|
&argon2::Config::rfc9106_low_mem(),
|
||||||
)
|
)
|
||||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
||||||
);
|
);
|
||||||
@@ -134,7 +134,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
|||||||
argon2::hash_encoded(
|
argon2::hash_encoded(
|
||||||
new_password.as_bytes(),
|
new_password.as_bytes(),
|
||||||
&rand::random::<[u8; 16]>()[..],
|
&rand::random::<[u8; 16]>()[..],
|
||||||
&argon2::Config::default(),
|
&argon2::Config::rfc9106_low_mem(),
|
||||||
)
|
)
|
||||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ use std::os::unix::ffi::OsStrExt;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use color_eyre::eyre::eyre;
|
|
||||||
use digest::generic_array::GenericArray;
|
use digest::generic_array::GenericArray;
|
||||||
use digest::{Digest, OutputSizeUser};
|
use digest::{Digest, OutputSizeUser};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
|
|
||||||
use super::{FileSystem, MountType};
|
use super::{FileSystem, MountType};
|
||||||
|
use crate::util::Invoke;
|
||||||
use crate::{Error, ResultExt};
|
use crate::{Error, ResultExt};
|
||||||
|
|
||||||
pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||||
@@ -17,7 +16,7 @@ pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
|||||||
key: &str,
|
key: &str,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
tokio::fs::create_dir_all(dst.as_ref()).await?;
|
tokio::fs::create_dir_all(dst.as_ref()).await?;
|
||||||
let mut ecryptfs = tokio::process::Command::new("mount")
|
tokio::process::Command::new("mount")
|
||||||
.arg("-t")
|
.arg("-t")
|
||||||
.arg("ecryptfs")
|
.arg("ecryptfs")
|
||||||
.arg(src.as_ref())
|
.arg(src.as_ref())
|
||||||
@@ -25,22 +24,9 @@ pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
|||||||
.arg("-o")
|
.arg("-o")
|
||||||
// for more information `man ecryptfs`
|
// for more information `man ecryptfs`
|
||||||
.arg(format!("key=passphrase:passphrase_passwd={},ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=y,no_sig_cache", key))
|
.arg(format!("key=passphrase:passphrase_passwd={},ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=y,no_sig_cache", key))
|
||||||
.stdin(std::process::Stdio::piped())
|
.input(Some(&mut std::io::Cursor::new(b"\n")))
|
||||||
.stderr(std::process::Stdio::piped())
|
.invoke(crate::ErrorKind::Filesystem).await?;
|
||||||
.spawn()?;
|
Ok(())
|
||||||
let mut stdin = ecryptfs.stdin.take().unwrap();
|
|
||||||
let mut stderr = ecryptfs.stderr.take().unwrap();
|
|
||||||
stdin.write_all(b"\n").await?;
|
|
||||||
stdin.flush().await?;
|
|
||||||
stdin.shutdown().await?;
|
|
||||||
drop(stdin);
|
|
||||||
let mut err = String::new();
|
|
||||||
stderr.read_to_string(&mut err).await?;
|
|
||||||
if !ecryptfs.wait().await?.success() {
|
|
||||||
Err(Error::new(eyre!("{}", err), crate::ErrorKind::Filesystem))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EcryptFS<EncryptedDir: AsRef<Path>, Key: AsRef<str>> {
|
pub struct EcryptFS<EncryptedDir: AsRef<Path>, Key: AsRef<str>> {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Stdio;
|
|
||||||
|
|
||||||
use async_compression::tokio::bufread::GzipDecoder;
|
use async_compression::tokio::bufread::GzipDecoder;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::{AsyncRead, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncRead, BufReader};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::disk::fsck::RequiresReboot;
|
use crate::disk::fsck::RequiresReboot;
|
||||||
@@ -23,7 +22,7 @@ pub async fn update_firmware() -> Result<RequiresReboot, Error> {
|
|||||||
if product_name.is_empty() {
|
if product_name.is_empty() {
|
||||||
return Ok(RequiresReboot(false));
|
return Ok(RequiresReboot(false));
|
||||||
}
|
}
|
||||||
let firmware_dir = Path::new("/usr/lib/embassy/firmware").join(&product_name);
|
let firmware_dir = Path::new("/usr/lib/startos/firmware").join(&product_name);
|
||||||
if tokio::fs::metadata(&firmware_dir).await.is_ok() {
|
if tokio::fs::metadata(&firmware_dir).await.is_ok() {
|
||||||
let current_firmware = String::from_utf8(
|
let current_firmware = String::from_utf8(
|
||||||
Command::new("dmidecode")
|
Command::new("dmidecode")
|
||||||
@@ -44,36 +43,25 @@ pub async fn update_firmware() -> Result<RequiresReboot, Error> {
|
|||||||
let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?;
|
let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?;
|
||||||
while let Some(entry) = firmware_read_dir.next_entry().await? {
|
while let Some(entry) = firmware_read_dir.next_entry().await? {
|
||||||
let filename = entry.file_name().to_string_lossy().into_owned();
|
let filename = entry.file_name().to_string_lossy().into_owned();
|
||||||
let rdr: Option<Box<dyn AsyncRead + Unpin>> = if filename.ends_with(".rom.gz") {
|
let rdr: Option<Box<dyn AsyncRead + Unpin + Send>> =
|
||||||
Some(Box::new(GzipDecoder::new(BufReader::new(
|
if filename.ends_with(".rom.gz") {
|
||||||
File::open(entry.path()).await?,
|
Some(Box::new(GzipDecoder::new(BufReader::new(
|
||||||
))))
|
File::open(entry.path()).await?,
|
||||||
} else if filename.ends_with(".rom") {
|
))))
|
||||||
Some(Box::new(File::open(entry.path()).await?))
|
} else if filename.ends_with(".rom") {
|
||||||
} else {
|
Some(Box::new(File::open(entry.path()).await?))
|
||||||
None
|
} else {
|
||||||
};
|
None
|
||||||
|
};
|
||||||
if let Some(mut rdr) = rdr {
|
if let Some(mut rdr) = rdr {
|
||||||
let mut flashrom = Command::new("flashrom")
|
Command::new("flashrom")
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg("internal")
|
.arg("internal")
|
||||||
.arg("-w-")
|
.arg("-w-")
|
||||||
.stdin(Stdio::piped())
|
.input(Some(&mut rdr))
|
||||||
.spawn()?;
|
.invoke(ErrorKind::Firmware)
|
||||||
let mut rom_dest = flashrom.stdin.take().or_not_found("stdin")?;
|
.await?;
|
||||||
tokio::io::copy(&mut rdr, &mut rom_dest).await?;
|
return Ok(RequiresReboot(true));
|
||||||
rom_dest.flush().await?;
|
|
||||||
rom_dest.shutdown().await?;
|
|
||||||
drop(rom_dest);
|
|
||||||
let o = flashrom.wait_with_output().await?;
|
|
||||||
if !o.status.success() {
|
|
||||||
return Err(Error::new(
|
|
||||||
eyre!("{}", std::str::from_utf8(&o.stderr)?),
|
|
||||||
ErrorKind::Firmware,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return Ok(RequiresReboot(true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fs::Permissions;
|
use std::fs::Permissions;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use helpers::NonDetachingJoinHandle;
|
use helpers::NonDetachingJoinHandle;
|
||||||
@@ -19,7 +19,6 @@ use crate::install::PKG_ARCHIVE_DIR;
|
|||||||
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::sound::BEP;
|
use crate::sound::BEP;
|
||||||
use crate::system::time;
|
|
||||||
use crate::util::cpupower::{
|
use crate::util::cpupower::{
|
||||||
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE,
|
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE,
|
||||||
};
|
};
|
||||||
@@ -255,6 +254,17 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::disk::mount::util::bind(&log_dir, "/var/log/journal", false).await?;
|
crate::disk::mount::util::bind(&log_dir, "/var/log/journal", false).await?;
|
||||||
|
match Command::new("chattr")
|
||||||
|
.arg("-R")
|
||||||
|
.arg("+C")
|
||||||
|
.arg("/var/log/journal")
|
||||||
|
.invoke(ErrorKind::Filesystem)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) if e.source.to_string().contains("Operation not supported") => Ok(()),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}?;
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.arg("restart")
|
.arg("restart")
|
||||||
.arg("systemd-journald")
|
.arg("systemd-journald")
|
||||||
@@ -263,6 +273,9 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
tracing::info!("Mounted Logs");
|
tracing::info!("Mounted Logs");
|
||||||
|
|
||||||
let tmp_dir = cfg.datadir().join("package-data/tmp");
|
let tmp_dir = cfg.datadir().join("package-data/tmp");
|
||||||
|
if should_rebuild && tokio::fs::metadata(&tmp_dir).await.is_ok() {
|
||||||
|
tokio::fs::remove_dir_all(&tmp_dir).await?;
|
||||||
|
}
|
||||||
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
||||||
tokio::fs::create_dir_all(&tmp_dir).await?;
|
tokio::fs::create_dir_all(&tmp_dir).await?;
|
||||||
}
|
}
|
||||||
@@ -275,9 +288,6 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
.datadir()
|
.datadir()
|
||||||
.join(format!("package-data/tmp/{CONTAINER_TOOL}"));
|
.join(format!("package-data/tmp/{CONTAINER_TOOL}"));
|
||||||
let tmp_docker_exists = tokio::fs::metadata(&tmp_docker).await.is_ok();
|
let tmp_docker_exists = tokio::fs::metadata(&tmp_docker).await.is_ok();
|
||||||
if should_rebuild && tmp_docker_exists {
|
|
||||||
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
|
||||||
}
|
|
||||||
if CONTAINER_TOOL == "docker" {
|
if CONTAINER_TOOL == "docker" {
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.arg("stop")
|
.arg("stop")
|
||||||
@@ -309,7 +319,7 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("Loading System Docker Images");
|
tracing::info!("Loading System Docker Images");
|
||||||
crate::install::load_images("/usr/lib/embassy/system-images").await?;
|
crate::install::load_images("/usr/lib/startos/system-images").await?;
|
||||||
tracing::info!("Loaded System Docker Images");
|
tracing::info!("Loaded System Docker Images");
|
||||||
|
|
||||||
tracing::info!("Loading Package Docker Images");
|
tracing::info!("Loading Package Docker Images");
|
||||||
@@ -361,15 +371,28 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut warn_time_not_synced = true;
|
let mut time_not_synced = true;
|
||||||
for _ in 0..60 {
|
let mut not_made_progress = 0u32;
|
||||||
|
for _ in 0..1800 {
|
||||||
if check_time_is_synchronized().await? {
|
if check_time_is_synchronized().await? {
|
||||||
warn_time_not_synced = false;
|
time_not_synced = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let t = SystemTime::now();
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
if t.elapsed()
|
||||||
|
.map(|t| t > Duration::from_secs_f64(1.1))
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
not_made_progress = 0;
|
||||||
|
} else {
|
||||||
|
not_made_progress += 1;
|
||||||
|
}
|
||||||
|
if not_made_progress > 30 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if warn_time_not_synced {
|
if time_not_synced {
|
||||||
tracing::warn!("Timed out waiting for system time to synchronize");
|
tracing::warn!("Timed out waiting for system time to synchronize");
|
||||||
} else {
|
} else {
|
||||||
tracing::info!("Syncronized system clock");
|
tracing::info!("Syncronized system clock");
|
||||||
@@ -383,9 +406,24 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
updated: false,
|
updated: false,
|
||||||
update_progress: None,
|
update_progress: None,
|
||||||
backup_progress: None,
|
backup_progress: None,
|
||||||
|
shutting_down: false,
|
||||||
|
restarting: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
server_info.system_start_time = time().await?;
|
server_info.ntp_synced = if time_not_synced {
|
||||||
|
let db = db.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while !check_time_is_synchronized().await.unwrap() {
|
||||||
|
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||||
|
}
|
||||||
|
db.mutate(|v| v.as_server_info_mut().as_ntp_synced_mut().ser(&true))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
db.mutate(|v| {
|
db.mutate(|v| {
|
||||||
v.as_server_info_mut().ser(&server_info)?;
|
v.as_server_info_mut().ser(&server_info)?;
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ where
|
|||||||
);
|
);
|
||||||
cleanup(ctx, id, &version).await?;
|
cleanup(ctx, id, &version).await?;
|
||||||
cleanup_folder(volume_dir, Arc::new(dependents_paths)).await;
|
cleanup_folder(volume_dir, Arc::new(dependents_paths)).await;
|
||||||
remove_tor_keys(secrets, id).await?;
|
remove_network_keys(secrets, id).await?;
|
||||||
|
|
||||||
ctx.db
|
ctx.db
|
||||||
.mutate(|d| {
|
.mutate(|d| {
|
||||||
@@ -188,12 +188,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn remove_tor_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
|
pub async fn remove_network_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||||
{
|
{
|
||||||
|
sqlx::query!("DELETE FROM network_keys WHERE package = $1", &*id)
|
||||||
|
.execute(&mut *secrets)
|
||||||
|
.await?;
|
||||||
sqlx::query!("DELETE FROM tor WHERE package = $1", &*id)
|
sqlx::query!("DELETE FROM tor WHERE package = $1", &*id)
|
||||||
.execute(secrets)
|
.execute(&mut *secrets)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
|
|||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -49,9 +48,9 @@ use crate::s9pk::manifest::{Manifest, PackageId};
|
|||||||
use crate::s9pk::reader::S9pkReader;
|
use crate::s9pk::reader::S9pkReader;
|
||||||
use crate::status::{MainStatus, Status};
|
use crate::status::{MainStatus, Status};
|
||||||
use crate::util::docker::CONTAINER_TOOL;
|
use crate::util::docker::CONTAINER_TOOL;
|
||||||
use crate::util::io::{copy_and_shutdown, response_to_reader};
|
use crate::util::io::response_to_reader;
|
||||||
use crate::util::serde::{display_serializable, Port};
|
use crate::util::serde::{display_serializable, Port};
|
||||||
use crate::util::{display_none, AsyncFileExt, Version};
|
use crate::util::{display_none, AsyncFileExt, Invoke, Version};
|
||||||
use crate::volume::{asset_dir, script_dir};
|
use crate::volume::{asset_dir, script_dir};
|
||||||
use crate::{Error, ErrorKind, ResultExt};
|
use crate::{Error, ErrorKind, ResultExt};
|
||||||
|
|
||||||
@@ -838,15 +837,15 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let icon_path = if let Some(marketplace_url) = &marketplace_url {
|
let icon_path = if let Some(manifest) = &manifest {
|
||||||
if let Some(manifest) = &manifest {
|
let dir = ctx
|
||||||
let dir = ctx
|
.datadir
|
||||||
.datadir
|
.join(PKG_PUBLIC_DIR)
|
||||||
.join(PKG_PUBLIC_DIR)
|
.join(&manifest.id)
|
||||||
.join(&manifest.id)
|
.join(manifest.version.as_str());
|
||||||
.join(manifest.version.as_str());
|
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
||||||
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
if tokio::fs::metadata(&icon_path).await.is_err() {
|
||||||
if tokio::fs::metadata(&icon_path).await.is_err() {
|
if let Some(marketplace_url) = &marketplace_url {
|
||||||
tokio::fs::create_dir_all(&dir).await?;
|
tokio::fs::create_dir_all(&dir).await?;
|
||||||
let icon = ctx
|
let icon = ctx
|
||||||
.client
|
.client
|
||||||
@@ -864,10 +863,12 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
|||||||
let mut dst = File::create(&icon_path).await?;
|
let mut dst = File::create(&icon_path).await?;
|
||||||
tokio::io::copy(&mut response_to_reader(icon), &mut dst).await?;
|
tokio::io::copy(&mut response_to_reader(icon), &mut dst).await?;
|
||||||
dst.sync_all().await?;
|
dst.sync_all().await?;
|
||||||
|
Some(icon_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
Some(icon_path)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(icon_path)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -951,32 +952,11 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
|||||||
tracing::info!("Install {}@{}: Unpacking Docker Images", pkg_id, version);
|
tracing::info!("Install {}@{}: Unpacking Docker Images", pkg_id, version);
|
||||||
progress
|
progress
|
||||||
.track_read_during(ctx.db.clone(), pkg_id, || async {
|
.track_read_during(ctx.db.clone(), pkg_id, || async {
|
||||||
let mut load = Command::new(CONTAINER_TOOL)
|
Command::new(CONTAINER_TOOL)
|
||||||
.arg("load")
|
.arg("load")
|
||||||
.stdin(Stdio::piped())
|
.input(Some(&mut rdr.docker_images().await?))
|
||||||
.stderr(Stdio::piped())
|
.invoke(ErrorKind::Docker)
|
||||||
.spawn()?;
|
.await
|
||||||
let load_in = load.stdin.take().ok_or_else(|| {
|
|
||||||
Error::new(
|
|
||||||
eyre!("Could not write to stdin of docker load"),
|
|
||||||
crate::ErrorKind::Docker,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let mut docker_rdr = rdr.docker_images().await?;
|
|
||||||
copy_and_shutdown(&mut docker_rdr, load_in).await?;
|
|
||||||
let res = load.wait_with_output().await?;
|
|
||||||
if !res.status.success() {
|
|
||||||
Err(Error::new(
|
|
||||||
eyre!(
|
|
||||||
"{}",
|
|
||||||
String::from_utf8(res.stderr)
|
|
||||||
.unwrap_or_else(|e| format!("Could not parse stderr: {}", e))
|
|
||||||
),
|
|
||||||
crate::ErrorKind::Docker,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!("Install {}@{}: Unpacked Docker Images", pkg_id, version,);
|
tracing::info!("Install {}@{}: Unpacked Docker Images", pkg_id, version,);
|
||||||
@@ -1273,57 +1253,36 @@ pub fn load_images<'a, P: AsRef<Path> + 'a + Send + Sync>(
|
|||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let ext = path.extension().and_then(|ext| ext.to_str());
|
let ext = path.extension().and_then(|ext| ext.to_str());
|
||||||
if ext == Some("tar") || ext == Some("s9pk") {
|
if ext == Some("tar") || ext == Some("s9pk") {
|
||||||
let mut load = Command::new(CONTAINER_TOOL)
|
if let Err(e) = async {
|
||||||
.arg("load")
|
match ext {
|
||||||
.stdin(Stdio::piped())
|
Some("tar") => {
|
||||||
.stderr(Stdio::piped())
|
Command::new(CONTAINER_TOOL)
|
||||||
.spawn()?;
|
.arg("load")
|
||||||
let load_in = load.stdin.take().ok_or_else(|| {
|
.input(Some(&mut File::open(&path).await?))
|
||||||
Error::new(
|
.invoke(ErrorKind::Docker)
|
||||||
eyre!("Could not write to stdin of docker load"),
|
.await
|
||||||
crate::ErrorKind::Docker,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
match ext {
|
|
||||||
Some("tar") => {
|
|
||||||
copy_and_shutdown(&mut File::open(&path).await?, load_in)
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
Some("s9pk") => match async {
|
|
||||||
let mut reader = S9pkReader::open(&path, true).await?;
|
|
||||||
copy_and_shutdown(&mut reader.docker_images().await?, load_in)
|
|
||||||
.await?;
|
|
||||||
Ok::<_, Error>(())
|
|
||||||
}
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!(
|
|
||||||
"Error loading docker images from s9pk: {e}"
|
|
||||||
);
|
|
||||||
tracing::debug!("{e:?}");
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
},
|
Some("s9pk") => {
|
||||||
_ => unreachable!(),
|
Command::new(CONTAINER_TOOL)
|
||||||
};
|
.arg("load")
|
||||||
|
.input(Some(
|
||||||
let res = load.wait_with_output().await?;
|
&mut S9pkReader::open(&path, true)
|
||||||
if !res.status.success() {
|
.await?
|
||||||
Err(Error::new(
|
.docker_images()
|
||||||
eyre!(
|
.await?,
|
||||||
"{}",
|
))
|
||||||
String::from_utf8(res.stderr).unwrap_or_else(|e| format!(
|
.invoke(ErrorKind::Docker)
|
||||||
"Could not parse stderr: {}",
|
.await
|
||||||
e
|
}
|
||||||
))
|
_ => unreachable!(),
|
||||||
),
|
}
|
||||||
crate::ErrorKind::Docker,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("Error loading docker images from s9pk: {e}");
|
||||||
|
tracing::debug!("{e:?}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,21 @@ pub const DEFAULT_MARKETPLACE: &str = "https://registry.start9.com";
|
|||||||
pub const BUFFER_SIZE: usize = 1024;
|
pub const BUFFER_SIZE: usize = 1024;
|
||||||
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
|
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
|
||||||
pub const TARGET: &str = current_platform::CURRENT_PLATFORM;
|
pub const TARGET: &str = current_platform::CURRENT_PLATFORM;
|
||||||
pub const OS_ARCH: &str = env!("OS_ARCH");
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref ARCH: &'static str = {
|
pub static ref ARCH: &'static str = {
|
||||||
let (arch, _) = TARGET.split_once("-").unwrap();
|
let (arch, _) = TARGET.split_once("-").unwrap();
|
||||||
arch
|
arch
|
||||||
};
|
};
|
||||||
|
pub static ref PLATFORM: String = {
|
||||||
|
if let Ok(platform) = std::fs::read_to_string("/usr/lib/startos/PLATFORM.txt") {
|
||||||
|
platform
|
||||||
|
} else {
|
||||||
|
ARCH.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub static ref SOURCE_DATE: SystemTime = {
|
||||||
|
std::fs::metadata(std::env::current_exe().unwrap()).unwrap().modified().unwrap()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod account;
|
pub mod account;
|
||||||
@@ -56,6 +65,8 @@ pub mod util;
|
|||||||
pub mod version;
|
pub mod version;
|
||||||
pub mod volume;
|
pub mod volume;
|
||||||
|
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub use error::{Error, ErrorKind, ResultExt};
|
pub use error::{Error, ErrorKind, ResultExt};
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
|
|||||||
@@ -136,7 +136,13 @@ pub struct LogEntry {
|
|||||||
}
|
}
|
||||||
impl std::fmt::Display for LogEntry {
|
impl std::fmt::Display for LogEntry {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "{} {}", self.timestamp, self.message)
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {}",
|
||||||
|
self.timestamp
|
||||||
|
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||||
|
self.message
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +151,7 @@ pub struct JournalctlEntry {
|
|||||||
#[serde(rename = "__REALTIME_TIMESTAMP")]
|
#[serde(rename = "__REALTIME_TIMESTAMP")]
|
||||||
pub timestamp: String,
|
pub timestamp: String,
|
||||||
#[serde(rename = "MESSAGE")]
|
#[serde(rename = "MESSAGE")]
|
||||||
#[serde(deserialize_with = "deserialize_string_or_utf8_array")]
|
#[serde(deserialize_with = "deserialize_log_message")]
|
||||||
pub message: String,
|
pub message: String,
|
||||||
#[serde(rename = "__CURSOR")]
|
#[serde(rename = "__CURSOR")]
|
||||||
pub cursor: String,
|
pub cursor: String,
|
||||||
@@ -164,7 +170,7 @@ impl JournalctlEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
fn deserialize_log_message<'de, D: serde::de::Deserializer<'de>>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> std::result::Result<String, D::Error> {
|
) -> std::result::Result<String, D::Error> {
|
||||||
struct Visitor;
|
struct Visitor;
|
||||||
@@ -177,13 +183,7 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
|||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
Ok(v.to_owned())
|
Ok(v.trim().to_owned())
|
||||||
}
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(v)
|
|
||||||
}
|
}
|
||||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
@@ -201,6 +201,7 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
|||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Result<Vec<u8>, _>>()?,
|
.collect::<Result<Vec<u8>, _>>()?,
|
||||||
)
|
)
|
||||||
|
.map(|s| s.trim().to_owned())
|
||||||
.map_err(serde::de::Error::custom)
|
.map_err(serde::de::Error::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,12 +375,12 @@ pub async fn journalctl(
|
|||||||
cmd.arg(format!("_COMM={}", SYSTEM_UNIT));
|
cmd.arg(format!("_COMM={}", SYSTEM_UNIT));
|
||||||
}
|
}
|
||||||
LogSource::Container(id) => {
|
LogSource::Container(id) => {
|
||||||
#[cfg(feature = "podman")]
|
#[cfg(not(feature = "docker"))]
|
||||||
cmd.arg(format!(
|
cmd.arg(format!(
|
||||||
"SYSLOG_IDENTIFIER={}",
|
"SYSLOG_IDENTIFIER={}",
|
||||||
DockerProcedure::container_name(&id, None)
|
DockerProcedure::container_name(&id, None)
|
||||||
));
|
));
|
||||||
#[cfg(not(feature = "podman"))]
|
#[cfg(feature = "docker")]
|
||||||
cmd.arg(format!(
|
cmd.arg(format!(
|
||||||
"CONTAINER_NAME={}",
|
"CONTAINER_NAME={}",
|
||||||
DockerProcedure::container_name(&id, None)
|
DockerProcedure::container_name(&id, None)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use models::ErrorKind;
|
use models::ErrorKind;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
|
use crate::procedure::docker::DockerProcedure;
|
||||||
|
use crate::procedure::PackageProcedure;
|
||||||
use crate::s9pk::manifest::Manifest;
|
use crate::s9pk::manifest::Manifest;
|
||||||
use crate::util::docker::stop_container;
|
use crate::util::docker::stop_container;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
@@ -16,11 +18,13 @@ impl ManagerSeed {
|
|||||||
pub async fn stop_container(&self) -> Result<(), Error> {
|
pub async fn stop_container(&self) -> Result<(), Error> {
|
||||||
match stop_container(
|
match stop_container(
|
||||||
&self.container_name,
|
&self.container_name,
|
||||||
self.manifest
|
match &self.manifest.main {
|
||||||
.containers
|
PackageProcedure::Docker(DockerProcedure {
|
||||||
.as_ref()
|
sigterm_timeout: Some(sigterm_timeout),
|
||||||
.and_then(|c| c.main.sigterm_timeout)
|
..
|
||||||
.map(|d| *d),
|
}) => Some(**sigterm_timeout),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ pub fn pbkdf2(password: impl AsRef<[u8]>, salt: impl AsRef<[u8]>) -> CipherKey<A
|
|||||||
salt.as_ref(),
|
salt.as_ref(),
|
||||||
1000,
|
1000,
|
||||||
aeskey.as_mut_slice(),
|
aeskey.as_mut_slice(),
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
aeskey
|
aeskey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::path::Path;
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use libc::time_t;
|
||||||
use openssl::asn1::{Asn1Integer, Asn1Time};
|
use openssl::asn1::{Asn1Integer, Asn1Time};
|
||||||
use openssl::bn::{BigNum, MsbOption};
|
use openssl::bn::{BigNum, MsbOption};
|
||||||
use openssl::ec::{EcGroup, EcKey};
|
use openssl::ec::{EcGroup, EcKey};
|
||||||
@@ -20,12 +21,20 @@ use tracing::instrument;
|
|||||||
use crate::account::AccountInfo;
|
use crate::account::AccountInfo;
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::hostname::Hostname;
|
use crate::hostname::Hostname;
|
||||||
|
use crate::init::check_time_is_synchronized;
|
||||||
use crate::net::dhcp::ips;
|
use crate::net::dhcp::ips;
|
||||||
use crate::net::keys::{Key, KeyInfo};
|
use crate::net::keys::{Key, KeyInfo};
|
||||||
use crate::{Error, ErrorKind, ResultExt};
|
use crate::{Error, ErrorKind, ResultExt, SOURCE_DATE};
|
||||||
|
|
||||||
static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.
|
static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.
|
||||||
|
|
||||||
|
fn unix_time(time: SystemTime) -> time_t {
|
||||||
|
time.duration_since(UNIX_EPOCH)
|
||||||
|
.map(|d| d.as_secs() as time_t)
|
||||||
|
.or_else(|_| UNIX_EPOCH.elapsed().map(|d| -(d.as_secs() as time_t)))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct CertPair {
|
pub struct CertPair {
|
||||||
pub ed25519: X509,
|
pub ed25519: X509,
|
||||||
@@ -55,9 +64,13 @@ impl CertPair {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if cert
|
if cert
|
||||||
.not_after()
|
.not_before()
|
||||||
.compare(Asn1Time::days_from_now(30)?.as_ref())?
|
.compare(Asn1Time::days_from_now(0)?.as_ref())?
|
||||||
== Ordering::Greater
|
== Ordering::Less
|
||||||
|
&& cert
|
||||||
|
.not_after()
|
||||||
|
.compare(Asn1Time::days_from_now(30)?.as_ref())?
|
||||||
|
== Ordering::Greater
|
||||||
&& ips.is_superset(&ip)
|
&& ips.is_superset(&ip)
|
||||||
{
|
{
|
||||||
return Ok(cert.clone());
|
return Ok(cert.clone());
|
||||||
@@ -80,6 +93,14 @@ impl CertPair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn root_ca_start_time() -> Result<SystemTime, Error> {
|
||||||
|
Ok(if check_time_is_synchronized().await? {
|
||||||
|
SystemTime::now()
|
||||||
|
} else {
|
||||||
|
*SOURCE_DATE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SslManager {
|
pub struct SslManager {
|
||||||
hostname: Hostname,
|
hostname: Hostname,
|
||||||
@@ -89,9 +110,13 @@ pub struct SslManager {
|
|||||||
cert_cache: RwLock<BTreeMap<Key, CertPair>>,
|
cert_cache: RwLock<BTreeMap<Key, CertPair>>,
|
||||||
}
|
}
|
||||||
impl SslManager {
|
impl SslManager {
|
||||||
pub fn new(account: &AccountInfo) -> Result<Self, Error> {
|
pub fn new(account: &AccountInfo, start_time: SystemTime) -> Result<Self, Error> {
|
||||||
let int_key = generate_key()?;
|
let int_key = generate_key()?;
|
||||||
let int_cert = make_int_cert((&account.root_ca_key, &account.root_ca_cert), &int_key)?;
|
let int_cert = make_int_cert(
|
||||||
|
(&account.root_ca_key, &account.root_ca_cert),
|
||||||
|
&int_key,
|
||||||
|
start_time,
|
||||||
|
)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
hostname: account.hostname.clone(),
|
hostname: account.hostname.clone(),
|
||||||
root_cert: account.root_ca_cert.clone(),
|
root_cert: account.root_ca_cert.clone(),
|
||||||
@@ -160,14 +185,20 @@ pub fn generate_key() -> Result<PKey<Private>, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub fn make_root_cert(root_key: &PKey<Private>, hostname: &Hostname) -> Result<X509, Error> {
|
pub fn make_root_cert(
|
||||||
|
root_key: &PKey<Private>,
|
||||||
|
hostname: &Hostname,
|
||||||
|
start_time: SystemTime,
|
||||||
|
) -> Result<X509, Error> {
|
||||||
let mut builder = X509Builder::new()?;
|
let mut builder = X509Builder::new()?;
|
||||||
builder.set_version(CERTIFICATE_VERSION)?;
|
builder.set_version(CERTIFICATE_VERSION)?;
|
||||||
|
|
||||||
let embargo = Asn1Time::days_from_now(0)?;
|
let unix_start_time = unix_time(start_time);
|
||||||
|
|
||||||
|
let embargo = Asn1Time::from_unix(unix_start_time - 86400)?;
|
||||||
builder.set_not_before(&embargo)?;
|
builder.set_not_before(&embargo)?;
|
||||||
|
|
||||||
let expiration = Asn1Time::days_from_now(3650)?;
|
let expiration = Asn1Time::from_unix(unix_start_time + (10 * 364 * 86400))?;
|
||||||
builder.set_not_after(&expiration)?;
|
builder.set_not_after(&expiration)?;
|
||||||
|
|
||||||
builder.set_serial_number(&*rand_serial()?)?;
|
builder.set_serial_number(&*rand_serial()?)?;
|
||||||
@@ -214,14 +245,17 @@ pub fn make_root_cert(root_key: &PKey<Private>, hostname: &Hostname) -> Result<X
|
|||||||
pub fn make_int_cert(
|
pub fn make_int_cert(
|
||||||
signer: (&PKey<Private>, &X509),
|
signer: (&PKey<Private>, &X509),
|
||||||
applicant: &PKey<Private>,
|
applicant: &PKey<Private>,
|
||||||
|
start_time: SystemTime,
|
||||||
) -> Result<X509, Error> {
|
) -> Result<X509, Error> {
|
||||||
let mut builder = X509Builder::new()?;
|
let mut builder = X509Builder::new()?;
|
||||||
builder.set_version(CERTIFICATE_VERSION)?;
|
builder.set_version(CERTIFICATE_VERSION)?;
|
||||||
|
|
||||||
let embargo = Asn1Time::days_from_now(0)?;
|
let unix_start_time = unix_time(start_time);
|
||||||
|
|
||||||
|
let embargo = Asn1Time::from_unix(unix_start_time - 86400)?;
|
||||||
builder.set_not_before(&embargo)?;
|
builder.set_not_before(&embargo)?;
|
||||||
|
|
||||||
let expiration = Asn1Time::days_from_now(3650)?;
|
let expiration = Asn1Time::from_unix(unix_start_time + (10 * 364 * 86400))?;
|
||||||
builder.set_not_after(&expiration)?;
|
builder.set_not_after(&expiration)?;
|
||||||
|
|
||||||
builder.set_serial_number(&*rand_serial()?)?;
|
builder.set_serial_number(&*rand_serial()?)?;
|
||||||
@@ -344,17 +378,10 @@ pub fn make_leaf_cert(
|
|||||||
let mut builder = X509Builder::new()?;
|
let mut builder = X509Builder::new()?;
|
||||||
builder.set_version(CERTIFICATE_VERSION)?;
|
builder.set_version(CERTIFICATE_VERSION)?;
|
||||||
|
|
||||||
let embargo = Asn1Time::from_unix(
|
let embargo = Asn1Time::from_unix(unix_time(SystemTime::now()) - 86400)?;
|
||||||
SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.map(|d| d.as_secs() as i64)
|
|
||||||
.or_else(|_| UNIX_EPOCH.elapsed().map(|d| -(d.as_secs() as i64)))
|
|
||||||
.unwrap_or_default()
|
|
||||||
- 86400,
|
|
||||||
)?;
|
|
||||||
builder.set_not_before(&embargo)?;
|
builder.set_not_before(&embargo)?;
|
||||||
|
|
||||||
// Google Apple and Mozilla reject certificate horizons longer than 397 days
|
// Google Apple and Mozilla reject certificate horizons longer than 398 days
|
||||||
// https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
|
// https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
|
||||||
let expiration = Asn1Time::days_from_now(397)?;
|
let expiration = Asn1Time::days_from_now(397)?;
|
||||||
builder.set_not_after(&expiration)?;
|
builder.set_not_after(&expiration)?;
|
||||||
|
|||||||
@@ -272,7 +272,14 @@ impl VHostServer {
|
|||||||
.await
|
.await
|
||||||
.with_kind(crate::ErrorKind::OpenSsl)?;
|
.with_kind(crate::ErrorKind::OpenSsl)?;
|
||||||
let mut tls_stream =
|
let mut tls_stream =
|
||||||
mid.into_stream(Arc::new(cfg)).await?;
|
match mid.into_stream(Arc::new(cfg)).await {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||||
|
tracing::trace!("{e:?}");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
tls_stream.get_mut().0.stop_buffering();
|
tls_stream.get_mut().0.stop_buffering();
|
||||||
tokio::io::copy_bidirectional(
|
tokio::io::copy_bidirectional(
|
||||||
&mut tls_stream,
|
&mut tls_stream,
|
||||||
@@ -287,7 +294,14 @@ impl VHostServer {
|
|||||||
cfg.alpn_protocols.push(proto.into());
|
cfg.alpn_protocols.push(proto.into());
|
||||||
}
|
}
|
||||||
let mut tls_stream =
|
let mut tls_stream =
|
||||||
mid.into_stream(Arc::new(cfg)).await?;
|
match mid.into_stream(Arc::new(cfg)).await {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||||
|
tracing::trace!("{e:?}");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
tls_stream.get_mut().0.stop_buffering();
|
tls_stream.get_mut().0.stop_buffering();
|
||||||
tokio::io::copy_bidirectional(
|
tokio::io::copy_bidirectional(
|
||||||
&mut tls_stream,
|
&mut tls_stream,
|
||||||
@@ -298,7 +312,14 @@ impl VHostServer {
|
|||||||
Err(AlpnInfo::Specified(alpn)) => {
|
Err(AlpnInfo::Specified(alpn)) => {
|
||||||
cfg.alpn_protocols = alpn;
|
cfg.alpn_protocols = alpn;
|
||||||
let mut tls_stream =
|
let mut tls_stream =
|
||||||
mid.into_stream(Arc::new(cfg)).await?;
|
match mid.into_stream(Arc::new(cfg)).await {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||||
|
tracing::trace!("{e:?}");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
tls_stream.get_mut().0.stop_buffering();
|
tls_stream.get_mut().0.stop_buffering();
|
||||||
tokio::io::copy_bidirectional(
|
tokio::io::copy_bidirectional(
|
||||||
&mut tls_stream,
|
&mut tls_stream,
|
||||||
@@ -308,10 +329,12 @@ impl VHostServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|e| match e.kind() {
|
|e| {
|
||||||
std::io::ErrorKind::UnexpectedEof => Ok(()),
|
use std::io::ErrorKind as E;
|
||||||
|
match e.kind() {
|
||||||
|
E::UnexpectedEof | E::BrokenPipe | E::ConnectionAborted | E::ConnectionReset | E::ConnectionRefused | E::TimedOut | E::Interrupted | E::NotConnected => Ok(()),
|
||||||
_ => Err(e),
|
_ => Err(e),
|
||||||
},
|
}},
|
||||||
|_| Ok(()),
|
|_| Ok(()),
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
@@ -327,8 +350,10 @@ impl VHostServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Error in VHostController on port {port}: {e}");
|
tracing::trace!(
|
||||||
tracing::debug!("{e:?}");
|
"VHostController: failed to accept connection on port {port}: {e}"
|
||||||
|
);
|
||||||
|
tracing::trace!("{e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ use std::os::unix::prelude::FileTypeExt;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_stream::stream;
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use color_eyre::Report;
|
|
||||||
use futures::future::{BoxFuture, Either as EitherFuture};
|
use futures::future::{BoxFuture, Either as EitherFuture};
|
||||||
use futures::{FutureExt, TryStreamExt};
|
use futures::{FutureExt, TryStreamExt};
|
||||||
use helpers::{NonDetachingJoinHandle, UnixRpcClient};
|
use helpers::{NonDetachingJoinHandle, UnixRpcClient};
|
||||||
@@ -396,7 +394,7 @@ impl DockerProcedure {
|
|||||||
|
|
||||||
cmd.arg("exec");
|
cmd.arg("exec");
|
||||||
|
|
||||||
cmd.args(self.docker_args_inject(pkg_id).await?);
|
cmd.args(self.docker_args_inject(pkg_id));
|
||||||
let input_buf = if let (Some(input), Some(format)) = (&input, &self.io_format) {
|
let input_buf = if let (Some(input), Some(format)) = (&input, &self.io_format) {
|
||||||
cmd.stdin(std::process::Stdio::piped());
|
cmd.stdin(std::process::Stdio::piped());
|
||||||
Some(format.to_vec(input)?)
|
Some(format.to_vec(input)?)
|
||||||
@@ -756,7 +754,7 @@ impl DockerProcedure {
|
|||||||
+ self.args.len(), // [ARG...]
|
+ self.args.len(), // [ARG...]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
async fn docker_args_inject(&self, pkg_id: &PackageId) -> Result<Vec<Cow<'_, OsStr>>, Error> {
|
fn docker_args_inject(&self, pkg_id: &PackageId) -> Vec<Cow<'_, OsStr>> {
|
||||||
let mut res = self.new_docker_args();
|
let mut res = self.new_docker_args();
|
||||||
if let Some(shm_size_mb) = self.shm_size_mb {
|
if let Some(shm_size_mb) = self.shm_size_mb {
|
||||||
res.push(OsStr::new("--shm-size").into());
|
res.push(OsStr::new("--shm-size").into());
|
||||||
@@ -769,7 +767,7 @@ impl DockerProcedure {
|
|||||||
|
|
||||||
res.extend(self.args.iter().map(|s| OsStr::new(s).into()));
|
res.extend(self.args.iter().map(|s| OsStr::new(s).into()));
|
||||||
|
|
||||||
Ok(res)
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,7 +811,7 @@ impl LongRunning {
|
|||||||
socket_path: &Path,
|
socket_path: &Path,
|
||||||
) -> Result<tokio::process::Command, Error> {
|
) -> Result<tokio::process::Command, Error> {
|
||||||
const INIT_EXEC: &str = "/start9/bin/embassy_container_init";
|
const INIT_EXEC: &str = "/start9/bin/embassy_container_init";
|
||||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
const BIND_LOCATION: &str = "/usr/lib/startos/container/";
|
||||||
tracing::trace!("setup_long_running_docker_cmd");
|
tracing::trace!("setup_long_running_docker_cmd");
|
||||||
|
|
||||||
remove_container(container_name, true).await?;
|
remove_container(container_name, true).await?;
|
||||||
@@ -892,23 +890,12 @@ async fn buf_reader_to_lines(
|
|||||||
reader: impl AsyncBufRead + Unpin,
|
reader: impl AsyncBufRead + Unpin,
|
||||||
limit: impl Into<Option<usize>>,
|
limit: impl Into<Option<usize>>,
|
||||||
) -> Result<Vec<String>, Error> {
|
) -> Result<Vec<String>, Error> {
|
||||||
let lines = stream! {
|
let mut lines = reader.lines();
|
||||||
let mut lines = reader.lines();
|
let mut answer = RingVec::new(limit.into().unwrap_or(1000));
|
||||||
while let Some(line) = lines.next_line().await? {
|
while let Some(line) = lines.next_line().await? {
|
||||||
yield Ok::<_, Report>(line);
|
answer.push(line);
|
||||||
}
|
}
|
||||||
};
|
let output: Vec<String> = answer.value.into_iter().collect();
|
||||||
let output: RingVec<String> = lines
|
|
||||||
.try_fold(
|
|
||||||
RingVec::new(limit.into().unwrap_or(1000)),
|
|
||||||
|mut acc, line| async move {
|
|
||||||
acc.push(line);
|
|
||||||
Ok(acc)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_kind(crate::ErrorKind::Unknown)?;
|
|
||||||
let output: Vec<String> = output.value.into_iter().collect();
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,4 +960,11 @@ mod tests {
|
|||||||
assert_eq!(CAPACITY_IN, ring.value.capacity());
|
assert_eq!(CAPACITY_IN, ring.value.capacity());
|
||||||
assert_eq!(CAPACITY_IN, ring.value.len());
|
assert_eq!(CAPACITY_IN, ring.value.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tests_buf_reader_to_lines() {
|
||||||
|
let mut reader = BufReader::new("hello\nworld\n".as_bytes());
|
||||||
|
let lines = futures::executor::block_on(buf_reader_to_lines(&mut reader, None)).unwrap();
|
||||||
|
assert_eq!(lines, vec!["hello", "world"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Stdio,
|
|
||||||
};
|
|
||||||
|
|
||||||
use color_eyre::eyre::eyre;
|
|
||||||
use embassy_container_init::ProcessGroupId;
|
use embassy_container_init::ProcessGroupId;
|
||||||
use helpers::UnixRpcClient;
|
use helpers::UnixRpcClient;
|
||||||
pub use js_engine::JsError;
|
pub use js_engine::JsError;
|
||||||
@@ -19,8 +15,8 @@ use tracing::instrument;
|
|||||||
use super::ProcedureName;
|
use super::ProcedureName;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::s9pk::manifest::PackageId;
|
use crate::s9pk::manifest::PackageId;
|
||||||
use crate::util::io::to_json_async_writer;
|
use crate::util::serde::IoFormat;
|
||||||
use crate::util::Version;
|
use crate::util::{Invoke, Version};
|
||||||
use crate::volume::Volumes;
|
use crate::volume::Volumes;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
@@ -85,45 +81,23 @@ impl JsProcedure {
|
|||||||
_gid: ProcessGroupId,
|
_gid: ProcessGroupId,
|
||||||
_rpc_client: Option<Arc<UnixRpcClient>>,
|
_rpc_client: Option<Arc<UnixRpcClient>>,
|
||||||
) -> Result<Result<O, (i32, String)>, Error> {
|
) -> Result<Result<O, (i32, String)>, Error> {
|
||||||
let runner_argument = ExecuteArgs {
|
Command::new("start-deno")
|
||||||
procedure: self.clone(),
|
|
||||||
directory: directory.clone(),
|
|
||||||
pkg_id: pkg_id.clone(),
|
|
||||||
pkg_version: pkg_version.clone(),
|
|
||||||
name,
|
|
||||||
volumes: volumes.clone(),
|
|
||||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
|
||||||
};
|
|
||||||
let mut runner = Command::new("start-deno")
|
|
||||||
.arg("execute")
|
.arg("execute")
|
||||||
.stdin(Stdio::piped())
|
.input(Some(&mut std::io::Cursor::new(IoFormat::Json.to_vec(
|
||||||
.stdout(Stdio::piped())
|
&ExecuteArgs {
|
||||||
.stderr(Stdio::piped())
|
procedure: self.clone(),
|
||||||
.kill_on_drop(true)
|
directory: directory.clone(),
|
||||||
.spawn()?;
|
pkg_id: pkg_id.clone(),
|
||||||
to_json_async_writer(
|
pkg_version: pkg_version.clone(),
|
||||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
name,
|
||||||
&runner_argument,
|
volumes: volumes.clone(),
|
||||||
)
|
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||||
.await?;
|
},
|
||||||
|
)?)))
|
||||||
let res = if let Some(timeout) = timeout {
|
.timeout(timeout)
|
||||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
.invoke(ErrorKind::Javascript)
|
||||||
.await
|
.await
|
||||||
.with_kind(ErrorKind::Timeout)??
|
.and_then(|res| IoFormat::Json.from_slice(&res))
|
||||||
} else {
|
|
||||||
runner.wait_with_output().await?
|
|
||||||
};
|
|
||||||
|
|
||||||
if res.status.success() {
|
|
||||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
|
||||||
.with_kind(ErrorKind::Deserialization)
|
|
||||||
} else {
|
|
||||||
Err(Error::new(
|
|
||||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
|
||||||
ErrorKind::Javascript,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
@@ -137,45 +111,23 @@ impl JsProcedure {
|
|||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
name: ProcedureName,
|
name: ProcedureName,
|
||||||
) -> Result<Result<O, (i32, String)>, Error> {
|
) -> Result<Result<O, (i32, String)>, Error> {
|
||||||
let runner_argument = ExecuteArgs {
|
Command::new("start-deno")
|
||||||
procedure: self.clone(),
|
|
||||||
directory: directory.clone(),
|
|
||||||
pkg_id: pkg_id.clone(),
|
|
||||||
pkg_version: pkg_version.clone(),
|
|
||||||
name,
|
|
||||||
volumes: volumes.clone(),
|
|
||||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
|
||||||
};
|
|
||||||
let mut runner = Command::new("start-deno")
|
|
||||||
.arg("sandbox")
|
.arg("sandbox")
|
||||||
.stdin(Stdio::piped())
|
.input(Some(&mut std::io::Cursor::new(IoFormat::Json.to_vec(
|
||||||
.stdout(Stdio::piped())
|
&ExecuteArgs {
|
||||||
.stderr(Stdio::piped())
|
procedure: self.clone(),
|
||||||
.kill_on_drop(true)
|
directory: directory.clone(),
|
||||||
.spawn()?;
|
pkg_id: pkg_id.clone(),
|
||||||
to_json_async_writer(
|
pkg_version: pkg_version.clone(),
|
||||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
name,
|
||||||
&runner_argument,
|
volumes: volumes.clone(),
|
||||||
)
|
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||||
.await?;
|
},
|
||||||
|
)?)))
|
||||||
let res = if let Some(timeout) = timeout {
|
.timeout(timeout)
|
||||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
.invoke(ErrorKind::Javascript)
|
||||||
.await
|
.await
|
||||||
.with_kind(ErrorKind::Timeout)??
|
.and_then(|res| IoFormat::Json.from_slice(&res))
|
||||||
} else {
|
|
||||||
runner.wait_with_output().await?
|
|
||||||
};
|
|
||||||
|
|
||||||
if res.status.success() {
|
|
||||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
|
||||||
.with_kind(ErrorKind::Deserialization)
|
|
||||||
} else {
|
|
||||||
Err(Error::new(
|
|
||||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
|
||||||
ErrorKind::Javascript,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
|
|||||||
@@ -172,7 +172,13 @@ impl<'de> Deserialize<'de> for NoOutput {
|
|||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let _ = Value::deserialize(deserializer)?;
|
let _ = Value::deserialize(deserializer);
|
||||||
Ok(NoOutput)
|
Ok(NoOutput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deser_no_output() {
|
||||||
|
serde_json::from_str::<NoOutput>("").unwrap();
|
||||||
|
serde_json::from_str::<Result<NoOutput, NoOutput>>("{\"Ok\": null}").unwrap();
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub fn with_query_params(ctx: RpcContext, mut url: Url) -> Url {
|
|||||||
"os.compat",
|
"os.compat",
|
||||||
&crate::version::Current::new().compat().to_string(),
|
&crate::version::Current::new().compat().to_string(),
|
||||||
)
|
)
|
||||||
.append_pair("os.arch", crate::OS_ARCH)
|
.append_pair("os.arch", &*crate::PLATFORM)
|
||||||
.append_pair("hardware.arch", &*crate::ARCH)
|
.append_pair("hardware.arch", &*crate::ARCH)
|
||||||
.append_pair("hardware.ram", &ctx.hardware.ram.to_string());
|
.append_pair("hardware.ram", &ctx.hardware.ram.to_string());
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use crate::disk::REPAIR_DISK_PATH;
|
|||||||
use crate::hostname::Hostname;
|
use crate::hostname::Hostname;
|
||||||
use crate::init::{init, InitResult};
|
use crate::init::{init, InitResult};
|
||||||
use crate::middleware::encrypt::EncryptedWire;
|
use crate::middleware::encrypt::EncryptedWire;
|
||||||
|
use crate::net::ssl::root_ca_start_time;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::io::{dir_copy, dir_size, Counter};
|
use crate::util::io::{dir_copy, dir_size, Counter};
|
||||||
use crate::{Error, ErrorKind, ResultExt};
|
use crate::{Error, ErrorKind, ResultExt};
|
||||||
@@ -378,7 +379,7 @@ async fn fresh_setup(
|
|||||||
ctx: &SetupContext,
|
ctx: &SetupContext,
|
||||||
embassy_password: &str,
|
embassy_password: &str,
|
||||||
) -> Result<(Hostname, OnionAddressV3, X509), Error> {
|
) -> Result<(Hostname, OnionAddressV3, X509), Error> {
|
||||||
let account = AccountInfo::new(embassy_password)?;
|
let account = AccountInfo::new(embassy_password, root_ca_start_time().await?)?;
|
||||||
let sqlite_pool = ctx.secret_store().await?;
|
let sqlite_pool = ctx.secret_store().await?;
|
||||||
account.save(&sqlite_pool).await?;
|
account.save(&sqlite_pool).await?;
|
||||||
sqlite_pool.close().await;
|
sqlite_pool.close().await;
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ use rpc_toolkit::command;
|
|||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::disk::main::export;
|
use crate::disk::main::export;
|
||||||
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::sound::SHUTDOWN;
|
use crate::sound::SHUTDOWN;
|
||||||
use crate::util::docker::CONTAINER_TOOL;
|
use crate::util::docker::CONTAINER_TOOL;
|
||||||
use crate::util::{display_none, Invoke};
|
use crate::util::{display_none, Invoke};
|
||||||
use crate::{Error, OS_ARCH};
|
use crate::PLATFORM;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Shutdown {
|
pub struct Shutdown {
|
||||||
@@ -60,7 +61,7 @@ impl Shutdown {
|
|||||||
tracing::debug!("{:?}", e);
|
tracing::debug!("{:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if OS_ARCH != "raspberrypi" || self.restart {
|
if &*PLATFORM != "raspberrypi" || self.restart {
|
||||||
if let Err(e) = SHUTDOWN.play().await {
|
if let Err(e) = SHUTDOWN.play().await {
|
||||||
tracing::error!("Error Playing Shutdown Song: {}", e);
|
tracing::error!("Error Playing Shutdown Song: {}", e);
|
||||||
tracing::debug!("{:?}", e);
|
tracing::debug!("{:?}", e);
|
||||||
@@ -68,7 +69,7 @@ impl Shutdown {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
drop(rt);
|
drop(rt);
|
||||||
if OS_ARCH == "raspberrypi" {
|
if &*PLATFORM == "raspberrypi" {
|
||||||
if !self.restart {
|
if !self.restart {
|
||||||
std::fs::write(STANDBY_MODE_PATH, "").unwrap();
|
std::fs::write(STANDBY_MODE_PATH, "").unwrap();
|
||||||
Command::new("sync").spawn().unwrap().wait().unwrap();
|
Command::new("sync").spawn().unwrap().wait().unwrap();
|
||||||
@@ -90,6 +91,14 @@ impl Shutdown {
|
|||||||
|
|
||||||
#[command(display(display_none))]
|
#[command(display(display_none))]
|
||||||
pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||||
|
ctx.db
|
||||||
|
.mutate(|db| {
|
||||||
|
db.as_server_info_mut()
|
||||||
|
.as_status_info_mut()
|
||||||
|
.as_shutting_down_mut()
|
||||||
|
.ser(&true)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
ctx.shutdown
|
ctx.shutdown
|
||||||
.send(Some(Shutdown {
|
.send(Some(Shutdown {
|
||||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||||
@@ -102,6 +111,14 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
|||||||
|
|
||||||
#[command(display(display_none))]
|
#[command(display(display_none))]
|
||||||
pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> {
|
pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||||
|
ctx.db
|
||||||
|
.mutate(|db| {
|
||||||
|
db.as_server_info_mut()
|
||||||
|
.as_status_info_mut()
|
||||||
|
.as_restarting_mut()
|
||||||
|
.ser(&true)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
ctx.shutdown
|
ctx.shutdown
|
||||||
.send(Some(Shutdown {
|
.send(Some(Shutdown {
|
||||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use clap::ArgMatches;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use rpc_toolkit::command;
|
use rpc_toolkit::command;
|
||||||
@@ -84,9 +85,65 @@ pub async fn zram(#[context] ctx: RpcContext, #[arg] enable: bool) -> Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub async fn time() -> Result<String, Error> {
|
pub struct TimeInfo {
|
||||||
Ok(Utc::now().to_rfc3339())
|
now: String,
|
||||||
|
uptime: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_time(arg: TimeInfo, matches: &ArgMatches) {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use prettytable::*;
|
||||||
|
|
||||||
|
if matches.is_present("format") {
|
||||||
|
return display_serializable(arg, matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
let days = arg.uptime / (24 * 60 * 60);
|
||||||
|
let days_s = arg.uptime % (24 * 60 * 60);
|
||||||
|
let hours = days_s / (60 * 60);
|
||||||
|
let hours_s = arg.uptime % (60 * 60);
|
||||||
|
let minutes = hours_s / 60;
|
||||||
|
let seconds = arg.uptime % 60;
|
||||||
|
let mut uptime_string = String::new();
|
||||||
|
if days > 0 {
|
||||||
|
write!(&mut uptime_string, "{days} days").unwrap();
|
||||||
|
}
|
||||||
|
if hours > 0 {
|
||||||
|
if !uptime_string.is_empty() {
|
||||||
|
uptime_string += ", ";
|
||||||
|
}
|
||||||
|
write!(&mut uptime_string, "{hours} hours").unwrap();
|
||||||
|
}
|
||||||
|
if minutes > 0 {
|
||||||
|
if !uptime_string.is_empty() {
|
||||||
|
uptime_string += ", ";
|
||||||
|
}
|
||||||
|
write!(&mut uptime_string, "{minutes} minutes").unwrap();
|
||||||
|
}
|
||||||
|
if !uptime_string.is_empty() {
|
||||||
|
uptime_string += ", ";
|
||||||
|
}
|
||||||
|
write!(&mut uptime_string, "{seconds} seconds").unwrap();
|
||||||
|
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![bc -> "NOW", &arg.now]);
|
||||||
|
table.add_row(row![bc -> "UPTIME", &uptime_string]);
|
||||||
|
table.print_tty(false).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command(display(display_time))]
|
||||||
|
pub async fn time(
|
||||||
|
#[context] ctx: RpcContext,
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[arg(long = "format")]
|
||||||
|
format: Option<IoFormat>,
|
||||||
|
) -> Result<TimeInfo, Error> {
|
||||||
|
Ok(TimeInfo {
|
||||||
|
now: Utc::now().to_rfc3339(),
|
||||||
|
uptime: ctx.start_time.elapsed().as_secs(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(
|
#[command(
|
||||||
@@ -303,60 +360,44 @@ impl<'de> Deserialize<'de> for GigaBytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct MetricsGeneral {
|
pub struct MetricsGeneral {
|
||||||
#[serde(rename = "Temperature")]
|
pub temperature: Option<Celsius>,
|
||||||
temperature: Option<Celsius>,
|
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct MetricsMemory {
|
pub struct MetricsMemory {
|
||||||
#[serde(rename = "Percentage Used")]
|
|
||||||
pub percentage_used: Percentage,
|
pub percentage_used: Percentage,
|
||||||
#[serde(rename = "Total")]
|
|
||||||
pub total: MebiBytes,
|
pub total: MebiBytes,
|
||||||
#[serde(rename = "Available")]
|
|
||||||
pub available: MebiBytes,
|
pub available: MebiBytes,
|
||||||
#[serde(rename = "Used")]
|
|
||||||
pub used: MebiBytes,
|
pub used: MebiBytes,
|
||||||
#[serde(rename = "Swap Total")]
|
pub zram_total: MebiBytes,
|
||||||
pub swap_total: MebiBytes,
|
pub zram_available: MebiBytes,
|
||||||
#[serde(rename = "Swap Free")]
|
pub zram_used: MebiBytes,
|
||||||
pub swap_free: MebiBytes,
|
|
||||||
#[serde(rename = "Swap Used")]
|
|
||||||
pub swap_used: MebiBytes,
|
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct MetricsCpu {
|
pub struct MetricsCpu {
|
||||||
#[serde(rename = "User Space")]
|
percentage_used: Percentage,
|
||||||
user_space: Percentage,
|
|
||||||
#[serde(rename = "Kernel Space")]
|
|
||||||
kernel_space: Percentage,
|
|
||||||
#[serde(rename = "I/O Wait")]
|
|
||||||
wait: Percentage,
|
|
||||||
#[serde(rename = "Idle")]
|
|
||||||
idle: Percentage,
|
idle: Percentage,
|
||||||
#[serde(rename = "Usage")]
|
user_space: Percentage,
|
||||||
usage: Percentage,
|
kernel_space: Percentage,
|
||||||
|
wait: Percentage,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct MetricsDisk {
|
pub struct MetricsDisk {
|
||||||
#[serde(rename = "Size")]
|
percentage_used: Percentage,
|
||||||
size: GigaBytes,
|
|
||||||
#[serde(rename = "Used")]
|
|
||||||
used: GigaBytes,
|
used: GigaBytes,
|
||||||
#[serde(rename = "Available")]
|
|
||||||
available: GigaBytes,
|
available: GigaBytes,
|
||||||
#[serde(rename = "Percentage Used")]
|
capacity: GigaBytes,
|
||||||
used_percentage: Percentage,
|
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Metrics {
|
pub struct Metrics {
|
||||||
#[serde(rename = "General")]
|
|
||||||
general: MetricsGeneral,
|
general: MetricsGeneral,
|
||||||
#[serde(rename = "Memory")]
|
|
||||||
memory: MetricsMemory,
|
memory: MetricsMemory,
|
||||||
#[serde(rename = "CPU")]
|
|
||||||
cpu: MetricsCpu,
|
cpu: MetricsCpu,
|
||||||
#[serde(rename = "Disk")]
|
|
||||||
disk: MetricsDisk,
|
disk: MetricsDisk,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,7 +723,7 @@ async fn get_cpu_info(last: &mut ProcStat) -> Result<MetricsCpu, Error> {
|
|||||||
kernel_space: Percentage((new.system() - last.system()) as f64 * 100.0 / total_diff as f64),
|
kernel_space: Percentage((new.system() - last.system()) as f64 * 100.0 / total_diff as f64),
|
||||||
idle: Percentage((new.idle - last.idle) as f64 * 100.0 / total_diff as f64),
|
idle: Percentage((new.idle - last.idle) as f64 * 100.0 / total_diff as f64),
|
||||||
wait: Percentage((new.iowait - last.iowait) as f64 * 100.0 / total_diff as f64),
|
wait: Percentage((new.iowait - last.iowait) as f64 * 100.0 / total_diff as f64),
|
||||||
usage: Percentage((new.used() - last.used()) as f64 * 100.0 / total_diff as f64),
|
percentage_used: Percentage((new.used() - last.used()) as f64 * 100.0 / total_diff as f64),
|
||||||
};
|
};
|
||||||
*last = new;
|
*last = new;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@@ -695,8 +736,8 @@ pub struct MemInfo {
|
|||||||
buffers: Option<u64>,
|
buffers: Option<u64>,
|
||||||
cached: Option<u64>,
|
cached: Option<u64>,
|
||||||
slab: Option<u64>,
|
slab: Option<u64>,
|
||||||
swap_total: Option<u64>,
|
zram_total: Option<u64>,
|
||||||
swap_free: Option<u64>,
|
zram_free: Option<u64>,
|
||||||
}
|
}
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
||||||
@@ -708,8 +749,8 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
|||||||
buffers: None,
|
buffers: None,
|
||||||
cached: None,
|
cached: None,
|
||||||
slab: None,
|
slab: None,
|
||||||
swap_total: None,
|
zram_total: None,
|
||||||
swap_free: None,
|
zram_free: None,
|
||||||
};
|
};
|
||||||
fn get_num_kb(l: &str) -> Result<u64, Error> {
|
fn get_num_kb(l: &str) -> Result<u64, Error> {
|
||||||
let e = Error::new(
|
let e = Error::new(
|
||||||
@@ -734,8 +775,8 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
|||||||
_ if entry.starts_with("Buffers") => mem_info.buffers = Some(get_num_kb(entry)?),
|
_ if entry.starts_with("Buffers") => mem_info.buffers = Some(get_num_kb(entry)?),
|
||||||
_ if entry.starts_with("Cached") => mem_info.cached = Some(get_num_kb(entry)?),
|
_ if entry.starts_with("Cached") => mem_info.cached = Some(get_num_kb(entry)?),
|
||||||
_ if entry.starts_with("Slab") => mem_info.slab = Some(get_num_kb(entry)?),
|
_ if entry.starts_with("Slab") => mem_info.slab = Some(get_num_kb(entry)?),
|
||||||
_ if entry.starts_with("SwapTotal") => mem_info.swap_total = Some(get_num_kb(entry)?),
|
_ if entry.starts_with("SwapTotal") => mem_info.zram_total = Some(get_num_kb(entry)?),
|
||||||
_ if entry.starts_with("SwapFree") => mem_info.swap_free = Some(get_num_kb(entry)?),
|
_ if entry.starts_with("SwapFree") => mem_info.zram_free = Some(get_num_kb(entry)?),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,24 +792,24 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
|||||||
let buffers = ensure_present(mem_info.buffers, "Buffers")?;
|
let buffers = ensure_present(mem_info.buffers, "Buffers")?;
|
||||||
let cached = ensure_present(mem_info.cached, "Cached")?;
|
let cached = ensure_present(mem_info.cached, "Cached")?;
|
||||||
let slab = ensure_present(mem_info.slab, "Slab")?;
|
let slab = ensure_present(mem_info.slab, "Slab")?;
|
||||||
let swap_total_k = ensure_present(mem_info.swap_total, "SwapTotal")?;
|
let zram_total_k = ensure_present(mem_info.zram_total, "SwapTotal")?;
|
||||||
let swap_free_k = ensure_present(mem_info.swap_free, "SwapFree")?;
|
let zram_free_k = ensure_present(mem_info.zram_free, "SwapFree")?;
|
||||||
|
|
||||||
let total = MebiBytes(mem_total as f64 / 1024.0);
|
let total = MebiBytes(mem_total as f64 / 1024.0);
|
||||||
let available = MebiBytes(mem_available as f64 / 1024.0);
|
let available = MebiBytes(mem_available as f64 / 1024.0);
|
||||||
let used = MebiBytes((mem_total - mem_free - buffers - cached - slab) as f64 / 1024.0);
|
let used = MebiBytes((mem_total - mem_free - buffers - cached - slab) as f64 / 1024.0);
|
||||||
let swap_total = MebiBytes(swap_total_k as f64 / 1024.0);
|
let zram_total = MebiBytes(zram_total_k as f64 / 1024.0);
|
||||||
let swap_free = MebiBytes(swap_free_k as f64 / 1024.0);
|
let zram_available = MebiBytes(zram_free_k as f64 / 1024.0);
|
||||||
let swap_used = MebiBytes((swap_total_k - swap_free_k) as f64 / 1024.0);
|
let zram_used = MebiBytes((zram_total_k - zram_free_k) as f64 / 1024.0);
|
||||||
let percentage_used = Percentage((total.0 - available.0) / total.0 * 100.0);
|
let percentage_used = Percentage((total.0 - available.0) / total.0 * 100.0);
|
||||||
Ok(MetricsMemory {
|
Ok(MetricsMemory {
|
||||||
percentage_used,
|
percentage_used,
|
||||||
total,
|
total,
|
||||||
available,
|
available,
|
||||||
used,
|
used,
|
||||||
swap_total,
|
zram_total,
|
||||||
swap_free,
|
zram_available,
|
||||||
swap_used,
|
zram_used,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,10 +833,10 @@ async fn get_disk_info() -> Result<MetricsDisk, Error> {
|
|||||||
let total_percentage = total_used as f64 / total_size as f64 * 100.0f64;
|
let total_percentage = total_used as f64 / total_size as f64 * 100.0f64;
|
||||||
|
|
||||||
Ok(MetricsDisk {
|
Ok(MetricsDisk {
|
||||||
size: GigaBytes(total_size as f64 / 1_000_000_000.0),
|
capacity: GigaBytes(total_size as f64 / 1_000_000_000.0),
|
||||||
used: GigaBytes(total_used as f64 / 1_000_000_000.0),
|
used: GigaBytes(total_used as f64 / 1_000_000_000.0),
|
||||||
available: GigaBytes(total_available as f64 / 1_000_000_000.0),
|
available: GigaBytes(total_available as f64 / 1_000_000_000.0),
|
||||||
used_percentage: Percentage(total_percentage as f64),
|
percentage_used: Percentage(total_percentage as f64),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use crate::sound::{
|
|||||||
};
|
};
|
||||||
use crate::update::latest_information::LatestInformation;
|
use crate::update::latest_information::LatestInformation;
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
|
use crate::{Error, ErrorKind, ResultExt, PLATFORM};
|
||||||
|
|
||||||
mod latest_information;
|
mod latest_information;
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ impl EosUrl {
|
|||||||
.host_str()
|
.host_str()
|
||||||
.ok_or_else(|| Error::new(eyre!("Could not get host of base"), ErrorKind::ParseUrl))?;
|
.ok_or_else(|| Error::new(eyre!("Could not get host of base"), ErrorKind::ParseUrl))?;
|
||||||
let version: &Version = &self.version;
|
let version: &Version = &self.version;
|
||||||
Ok(format!("{host}::{version}/{OS_ARCH}/")
|
Ok(format!("{host}::{version}/{}/", &*PLATFORM)
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| Error::new(eyre!("Could not parse path"), ErrorKind::ParseUrl))?)
|
.map_err(|_| Error::new(eyre!("Could not parse path"), ErrorKind::ParseUrl))?)
|
||||||
}
|
}
|
||||||
@@ -297,7 +297,7 @@ async fn sync_boot() -> Result<(), Error> {
|
|||||||
.await?
|
.await?
|
||||||
.wait()
|
.wait()
|
||||||
.await?;
|
.await?;
|
||||||
if OS_ARCH != "raspberrypi" {
|
if &*PLATFORM != "raspberrypi" {
|
||||||
let dev_mnt =
|
let dev_mnt =
|
||||||
MountGuard::mount(&Bind::new("/dev"), "/media/embassy/next/dev", ReadWrite).await?;
|
MountGuard::mount(&Bind::new("/dev"), "/media/embassy/next/dev", ReadWrite).await?;
|
||||||
let sys_mnt =
|
let sys_mnt =
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
|
||||||
use ed25519_dalek::{SecretKey, EXPANDED_SECRET_KEY_LENGTH};
|
use ed25519_dalek::{SecretKey, EXPANDED_SECRET_KEY_LENGTH};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ed25519_expand_key(key: &SecretKey) -> [u8; EXPANDED_SECRET_KEY_LENGTH] {
|
pub fn ed25519_expand_key(key: &SecretKey) -> [u8; EXPANDED_SECRET_KEY_LENGTH] {
|
||||||
let key = ExpandedSecretKey::from(key);
|
ed25519_dalek_v1::ExpandedSecretKey::from(
|
||||||
|
&ed25519_dalek_v1::SecretKey::from_bytes(key).unwrap(),
|
||||||
let mut bytes: [u8; 64] = [0u8; 64];
|
)
|
||||||
|
.to_bytes()
|
||||||
bytes[..32].copy_from_slice(key.scalar.as_bytes());
|
|
||||||
bytes[32..].copy_from_slice(&key.hash_prefix[..]);
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
|
|
||||||
#[cfg(not(feature = "podman"))]
|
#[cfg(feature = "docker")]
|
||||||
pub const CONTAINER_TOOL: &str = "docker";
|
pub const CONTAINER_TOOL: &str = "docker";
|
||||||
#[cfg(feature = "podman")]
|
#[cfg(not(feature = "docker"))]
|
||||||
pub const CONTAINER_TOOL: &str = "podman";
|
pub const CONTAINER_TOOL: &str = "podman";
|
||||||
|
|
||||||
#[cfg(not(feature = "podman"))]
|
#[cfg(feature = "docker")]
|
||||||
pub const CONTAINER_DATADIR: &str = "/var/lib/docker";
|
pub const CONTAINER_DATADIR: &str = "/var/lib/docker";
|
||||||
#[cfg(feature = "podman")]
|
#[cfg(not(feature = "docker"))]
|
||||||
pub const CONTAINER_DATADIR: &str = "/var/lib/containers";
|
pub const CONTAINER_DATADIR: &str = "/var/lib/containers";
|
||||||
|
|
||||||
pub struct DockerImageSha(String);
|
pub struct DockerImageSha(String);
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ use crate::shutdown::Shutdown;
|
|||||||
use crate::{Error, ErrorKind, ResultExt as _};
|
use crate::{Error, ErrorKind, ResultExt as _};
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod cpupower;
|
pub mod cpupower;
|
||||||
|
pub mod crypto;
|
||||||
pub mod docker;
|
pub mod docker;
|
||||||
pub mod http_reader;
|
pub mod http_reader;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod lshw;
|
pub mod lshw;
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
pub mod crypto;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, ::serde::Deserialize, ::serde::Serialize)]
|
#[derive(Clone, Copy, Debug, ::serde::Deserialize, ::serde::Serialize)]
|
||||||
pub enum Never {}
|
pub enum Never {}
|
||||||
@@ -50,30 +50,113 @@ impl std::fmt::Display for Never {
|
|||||||
impl std::error::Error for Never {}
|
impl std::error::Error for Never {}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Invoke {
|
pub trait Invoke<'a> {
|
||||||
|
type Extended<'ext>
|
||||||
|
where
|
||||||
|
Self: 'ext,
|
||||||
|
'ext: 'a;
|
||||||
|
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext>;
|
||||||
|
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||||
|
&'ext mut self,
|
||||||
|
input: Option<&'ext mut Input>,
|
||||||
|
) -> Self::Extended<'ext>;
|
||||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error>;
|
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error>;
|
||||||
async fn invoke_timeout(
|
|
||||||
&mut self,
|
|
||||||
error_kind: crate::ErrorKind,
|
|
||||||
timeout: Option<Duration>,
|
|
||||||
) -> Result<Vec<u8>, Error>;
|
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl Invoke for tokio::process::Command {
|
pub struct ExtendedCommand<'a> {
|
||||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
cmd: &'a mut tokio::process::Command,
|
||||||
self.invoke_timeout(error_kind, None).await
|
timeout: Option<Duration>,
|
||||||
|
input: Option<&'a mut (dyn tokio::io::AsyncRead + Unpin + Send)>,
|
||||||
|
}
|
||||||
|
impl<'a> std::ops::Deref for ExtendedCommand<'a> {
|
||||||
|
type Target = tokio::process::Command;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*self.cmd
|
||||||
}
|
}
|
||||||
async fn invoke_timeout(
|
}
|
||||||
&mut self,
|
impl<'a> std::ops::DerefMut for ExtendedCommand<'a> {
|
||||||
error_kind: crate::ErrorKind,
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
timeout: Option<Duration>,
|
self.cmd
|
||||||
) -> Result<Vec<u8>, Error> {
|
}
|
||||||
self.kill_on_drop(true);
|
}
|
||||||
self.stdout(Stdio::piped());
|
|
||||||
self.stderr(Stdio::piped());
|
#[async_trait::async_trait]
|
||||||
let res = match timeout {
|
impl<'a> Invoke<'a> for tokio::process::Command {
|
||||||
None => self.output().await?,
|
type Extended<'ext> = ExtendedCommand<'ext>
|
||||||
Some(t) => tokio::time::timeout(t, self.output())
|
where
|
||||||
|
Self: 'ext,
|
||||||
|
'ext: 'a;
|
||||||
|
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext> {
|
||||||
|
ExtendedCommand {
|
||||||
|
cmd: self,
|
||||||
|
timeout,
|
||||||
|
input: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||||
|
&'ext mut self,
|
||||||
|
input: Option<&'ext mut Input>,
|
||||||
|
) -> Self::Extended<'ext> {
|
||||||
|
ExtendedCommand {
|
||||||
|
cmd: self,
|
||||||
|
timeout: None,
|
||||||
|
input: if let Some(input) = input {
|
||||||
|
Some(&mut *input)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||||
|
ExtendedCommand {
|
||||||
|
cmd: self,
|
||||||
|
timeout: None,
|
||||||
|
input: None,
|
||||||
|
}
|
||||||
|
.invoke(error_kind)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
||||||
|
type Extended<'ext> = &'ext mut ExtendedCommand<'ext>
|
||||||
|
where
|
||||||
|
Self: 'ext,
|
||||||
|
'ext: 'a;
|
||||||
|
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext> {
|
||||||
|
self.timeout = timeout;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||||
|
&'ext mut self,
|
||||||
|
input: Option<&'ext mut Input>,
|
||||||
|
) -> Self::Extended<'ext> {
|
||||||
|
self.input = if let Some(input) = input {
|
||||||
|
Some(&mut *input)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||||
|
self.cmd.kill_on_drop(true);
|
||||||
|
if self.input.is_some() {
|
||||||
|
self.cmd.stdin(Stdio::piped());
|
||||||
|
}
|
||||||
|
self.cmd.stdout(Stdio::piped());
|
||||||
|
self.cmd.stderr(Stdio::piped());
|
||||||
|
let mut child = self.cmd.spawn()?;
|
||||||
|
if let (Some(mut stdin), Some(input)) = (child.stdin.take(), self.input.take()) {
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
tokio::io::copy(input, &mut stdin).await?;
|
||||||
|
stdin.flush().await?;
|
||||||
|
stdin.shutdown().await?;
|
||||||
|
drop(stdin);
|
||||||
|
}
|
||||||
|
let res = match self.timeout {
|
||||||
|
None => child.wait_with_output().await?,
|
||||||
|
Some(t) => tokio::time::timeout(t, child.wait_with_output())
|
||||||
.await
|
.await
|
||||||
.with_kind(ErrorKind::Timeout)??,
|
.with_kind(ErrorKind::Timeout)??,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -200,8 +200,7 @@ pub async fn init(db: &PatchDb, secrets: &PgPool) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const COMMIT_HASH: &str =
|
pub const COMMIT_HASH: &str = include_str!("../../../GIT_HASH.txt");
|
||||||
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"]);
|
|
||||||
|
|
||||||
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
||||||
pub fn git_info() -> Result<&'static str, Error> {
|
pub fn git_info() -> Result<&'static str, Error> {
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ impl Volumes {
|
|||||||
volume
|
volume
|
||||||
.validate(interfaces)
|
.validate(interfaces)
|
||||||
.with_ctx(|_| (crate::ErrorKind::ValidateS9pk, format!("Volume {}", id)))?;
|
.with_ctx(|_| (crate::ErrorKind::ValidateS9pk, format!("Volume {}", id)))?;
|
||||||
|
if let Volume::Backup { .. } = volume {
|
||||||
|
return Err(Error::new(
|
||||||
|
eyre!("Invalid volume type \"backup\""),
|
||||||
|
ErrorKind::ParseS9pk,
|
||||||
|
)); // Volume::Backup is for internal use and shouldn't be declared in manifest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -131,7 +137,6 @@ pub enum Volume {
|
|||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
Certificate { interface_id: InterfaceId },
|
Certificate { interface_id: InterfaceId },
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[serde(skip)]
|
|
||||||
Backup { readonly: bool },
|
Backup { readonly: bool },
|
||||||
}
|
}
|
||||||
impl Volume {
|
impl Volume {
|
||||||
|
|||||||
@@ -855,7 +855,7 @@ export const action = {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Created this test because of issue
|
* Created this test because of issue
|
||||||
* https://github.com/Start9Labs/embassy-os/issues/1737
|
* https://github.com/Start9Labs/start-os/issues/1737
|
||||||
* which that we couldn't create a dir that was deeply nested, and the parents where
|
* which that we couldn't create a dir that was deeply nested, and the parents where
|
||||||
* not created yet. Found this out during the migrations, where the parent would die.
|
* not created yet. Found this out during the migrations, where the parent would die.
|
||||||
* @param {*} effects
|
* @param {*} effects
|
||||||
@@ -931,7 +931,7 @@ export const action = {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Created this test because of issue
|
* Created this test because of issue
|
||||||
* https://github.com/Start9Labs/embassy-os/issues/2121
|
* https://github.com/Start9Labs/start-os/issues/2121
|
||||||
* That the empty in the create dies
|
* That the empty in the create dies
|
||||||
* @param {*} effects
|
* @param {*} effects
|
||||||
* @param {*} _input
|
* @param {*} _input
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR
|
|||||||
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres)
|
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres)
|
||||||
|
|
||||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres cargo sqlx migrate run
|
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres cargo sqlx migrate run
|
||||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres OS_ARCH=$(uname -m) cargo sqlx prepare -- --lib --profile=test
|
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres PLATFORM=$(uname -m) cargo sqlx prepare -- --lib --profile=test
|
||||||
)
|
)
|
||||||
|
|
||||||
docker stop tmp_postgres
|
docker stop tmp_postgres
|
||||||
|
|||||||
19
basename.sh
Executable file
19
basename.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
|
||||||
|
PLATFORM="$(if [ -f ./PLATFORM.txt ]; then cat ./PLATFORM.txt; else echo unknown; fi)"
|
||||||
|
VERSION="$(cat ./VERSION.txt)"
|
||||||
|
GIT_HASH="$(cat ./GIT_HASH.txt)"
|
||||||
|
if [[ "$GIT_HASH" =~ ^@ ]]; then
|
||||||
|
GIT_HASH=unknown
|
||||||
|
else
|
||||||
|
GIT_HASH="$(echo -n "$GIT_HASH" | head -c 7)"
|
||||||
|
fi
|
||||||
|
STARTOS_ENV="$(cat ./ENVIRONMENT.txt)"
|
||||||
|
VERSION_FULL="${VERSION}-${GIT_HASH}"
|
||||||
|
if [ -n "$STARTOS_ENV" ]; then
|
||||||
|
VERSION_FULL="$VERSION_FULL~${STARTOS_ENV}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "startos-${VERSION_FULL}_${PLATFORM}"
|
||||||
@@ -4,7 +4,7 @@ set -e
|
|||||||
shopt -s expand_aliases
|
shopt -s expand_aliases
|
||||||
|
|
||||||
if [ "$0" != "./build-cargo-dep.sh" ]; then
|
if [ "$0" != "./build-cargo-dep.sh" ]; then
|
||||||
>&2 echo "Must be run from embassy-os directory"
|
>&2 echo "Must be run from start-os directory"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
2
build/.gitignore
vendored
Normal file
2
build/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
lib/depends
|
||||||
|
lib/conflicts
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
openresolv
|
|
||||||
dhcpcd5
|
dhcpcd5
|
||||||
firewalld
|
firewalld
|
||||||
nginx
|
nginx
|
||||||
nginx-common
|
nginx-common
|
||||||
|
openresolv
|
||||||
@@ -6,21 +6,15 @@ bmon
|
|||||||
btrfs-progs
|
btrfs-progs
|
||||||
ca-certificates
|
ca-certificates
|
||||||
cifs-utils
|
cifs-utils
|
||||||
containerd.io
|
|
||||||
cryptsetup
|
cryptsetup
|
||||||
curl
|
curl
|
||||||
dmidecode
|
dmidecode
|
||||||
docker-ce
|
|
||||||
docker-ce-cli
|
|
||||||
docker-compose-plugin
|
|
||||||
dosfstools
|
dosfstools
|
||||||
e2fsprogs
|
e2fsprogs
|
||||||
ecryptfs-utils
|
ecryptfs-utils
|
||||||
exfatprogs
|
exfatprogs
|
||||||
flashrom
|
flashrom
|
||||||
gdb
|
|
||||||
grub-common
|
grub-common
|
||||||
heaptrack
|
|
||||||
htop
|
htop
|
||||||
httpdirfs
|
httpdirfs
|
||||||
iotop
|
iotop
|
||||||
5
build/dpkg-deps/docker.depends
Normal file
5
build/dpkg-deps/docker.depends
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
+ containerd.io
|
||||||
|
+ docker-ce
|
||||||
|
+ docker-ce-cli
|
||||||
|
+ docker-compose-plugin
|
||||||
|
- podman
|
||||||
43
build/dpkg-deps/generate.sh
Executable file
43
build/dpkg-deps/generate.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
|
||||||
|
IFS="-" read -ra FEATURES <<< "$ENVIRONMENT"
|
||||||
|
|
||||||
|
feature_file_checker='
|
||||||
|
/^#/ { next }
|
||||||
|
/^\+ [a-z0-9]+$/ { next }
|
||||||
|
/^- [a-z0-9]+$/ { next }
|
||||||
|
{ exit 1 }
|
||||||
|
'
|
||||||
|
|
||||||
|
for type in conflicts depends; do
|
||||||
|
pkgs=()
|
||||||
|
for feature in ${FEATURES[@]}; do
|
||||||
|
file="$feature.$type"
|
||||||
|
if [ -f $file ]; then
|
||||||
|
# TODO check for syntax errrors
|
||||||
|
cat $file | awk "$feature_file_checker"
|
||||||
|
for pkg in $(cat $file | awk '/^\+/ {print $2}'); do
|
||||||
|
pkgs+=($pkg)
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for pkg in $(cat $type); do
|
||||||
|
SKIP=
|
||||||
|
for feature in ${FEATURES[@]}; do
|
||||||
|
file="$feature.$type"
|
||||||
|
if [ -f $file ]; then
|
||||||
|
if grep "^- $pkg$" $file; then
|
||||||
|
SKIP=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z $SKIP ]; then
|
||||||
|
pkgs+=($pkg)
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
(IFS=$'\n'; echo "${pkgs[*]}") | sort -u > ../lib/$type
|
||||||
|
done
|
||||||
2
build/dpkg-deps/unstable.depends
Normal file
2
build/dpkg-deps/unstable.depends
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
+ gdb
|
||||||
|
+ heaptrack
|
||||||
@@ -2,23 +2,33 @@
|
|||||||
printf "\n"
|
printf "\n"
|
||||||
printf "Welcome to\n"
|
printf "Welcome to\n"
|
||||||
cat << "ASCII"
|
cat << "ASCII"
|
||||||
╭ ━ ━ ━ ╮ ╭ ╮ ╱ ╱ ╱ ╱ ╱ ╭ ╮ ╭ ━ ━ ━ ┳ ━ ━ ━ ╮
|
|
||||||
┃ ╭ ━ ╮ ┣ ╯ ╰ ╮ ╱ ╱ ╱ ╭ ╯ ╰ ┫ ╭ ━ ╮ ┃ ╭ ━ ╮ ┃
|
███████
|
||||||
┃ ╰ ━ ━ ╋ ╮ ╭ ╋ ━ ━ ┳ ┻ ╮ ╭ ┫ ┃ ╱ ┃ ┃ ╰ ━ ━ ╮
|
█ █ █
|
||||||
╰ ━ ━ ╮ ┃ ┃ ┃ ┃ ╭ ╮ ┃ ╭ ┫ ┃ ┃ ┃ ╱ ┃ ┣ ━ ━ ╮ ┃
|
█ █ █ █
|
||||||
┃ ╰ ━ ╯ ┃ ┃ ╰ ┫ ╭ ╮ ┃ ┃ ┃ ╰ ┫ ╰ ━ ╯ ┃ ╰ ━ ╯ ┃
|
█ █ █ █
|
||||||
╰ ━ ━ ━ ╯ ╰ ━ ┻ ╯ ╰ ┻ ╯ ╰ ━ ┻ ━ ━ ━ ┻ ━ ━ ━ ╯
|
█ █ █ █
|
||||||
|
█ █ █ █
|
||||||
|
█ █
|
||||||
|
███████
|
||||||
|
|
||||||
|
_____ __ ___ __ __
|
||||||
|
(_ | /\ |__) | / \(_
|
||||||
|
__) | / \| \ | \__/__)
|
||||||
ASCII
|
ASCII
|
||||||
|
printf " v$(cat /usr/lib/startos/VERSION.txt)\n\n"
|
||||||
printf " %s (%s %s)\n" "$(uname -o)" "$(uname -r)" "$(uname -m)"
|
printf " %s (%s %s)\n" "$(uname -o)" "$(uname -r)" "$(uname -m)"
|
||||||
printf " $(start-cli --version | sed 's/StartOS CLI /StartOS v/g') - $(start-cli git-info)"
|
printf " Git Hash: $(cat /usr/lib/startos/GIT_HASH.txt)"
|
||||||
if [ -n "$(cat /usr/lib/embassy/ENVIRONMENT.txt)" ]; then
|
if [ -n "$(cat /usr/lib/startos/ENVIRONMENT.txt)" ]; then
|
||||||
printf " ~ $(cat /usr/lib/embassy/ENVIRONMENT.txt)\n"
|
printf " ~ $(cat /usr/lib/startos/ENVIRONMENT.txt)\n"
|
||||||
else
|
else
|
||||||
printf "\n"
|
printf "\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "\n"
|
printf "\n"
|
||||||
printf " * Documentation: https://start9.com\n"
|
printf " * Documentation: https://docs.start9.com\n"
|
||||||
printf " * Management: https://%s.local\n" "$(hostname)"
|
printf " * Management: https://%s.local\n" "$(hostname)"
|
||||||
printf " * Support: https://t.me/start9_labs\n"
|
printf " * Support: https://start9.com/contact\n"
|
||||||
|
printf " * Source Code: https://github.com/Start9Labs/start-os\n"
|
||||||
|
printf " * License: MIT\n"
|
||||||
printf "\n"
|
printf "\n"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ user_pref("messaging-system.rsexperimentloader.enabled", false);
|
|||||||
user_pref("network.allow-experiments", false);
|
user_pref("network.allow-experiments", false);
|
||||||
user_pref("network.captive-portal-service.enabled", false);
|
user_pref("network.captive-portal-service.enabled", false);
|
||||||
user_pref("network.connectivity-service.enabled", false);
|
user_pref("network.connectivity-service.enabled", false);
|
||||||
user_pref("network.proxy.autoconfig_url", "file:///usr/lib/embassy/proxy.pac");
|
user_pref("network.proxy.autoconfig_url", "file:///usr/lib/startos/proxy.pac");
|
||||||
user_pref("network.proxy.socks_remote_dns", true);
|
user_pref("network.proxy.socks_remote_dns", true);
|
||||||
user_pref("network.proxy.type", 2);
|
user_pref("network.proxy.type", 2);
|
||||||
user_pref("signon.rememberSignons", false);
|
user_pref("signon.rememberSignons", false);
|
||||||
@@ -91,11 +91,11 @@ EOT
|
|||||||
while ! curl "http://localhost" > /dev/null; do
|
while ! curl "http://localhost" > /dev/null; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
while ! /usr/lib/embassy/scripts/check-monitor; do
|
while ! /usr/lib/startos/scripts/check-monitor; do
|
||||||
sleep 15
|
sleep 15
|
||||||
done
|
done
|
||||||
(
|
(
|
||||||
while /usr/lib/embassy/scripts/check-monitor; do
|
while /usr/lib/startos/scripts/check-monitor; do
|
||||||
sleep 15
|
sleep 15
|
||||||
done
|
done
|
||||||
killall firefox-esr
|
killall firefox-esr
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ fi
|
|||||||
>&2 echo ' sudo rm /usr/local/bin/apt'
|
>&2 echo ' sudo rm /usr/local/bin/apt'
|
||||||
>&2 echo
|
>&2 echo
|
||||||
>&2 echo 'Otherwise, what you probably want to do is run:'
|
>&2 echo 'Otherwise, what you probably want to do is run:'
|
||||||
>&2 echo ' sudo /usr/lib/embassy/scripts/chroot-and-upgrade'
|
>&2 echo ' sudo /usr/lib/startos/scripts/chroot-and-upgrade'
|
||||||
>&2 echo 'You can run apt in this context to add packages to your system.'
|
>&2 echo 'You can run apt in this context to add packages to your system.'
|
||||||
>&2 echo 'When you are done with your changes, type "exit" and the device will reboot into a system with the changes applied.'
|
>&2 echo 'When you are done with your changes, type "exit" and the device will reboot into a system with the changes applied.'
|
||||||
>&2 echo 'This is still NOT RECOMMENDED if you don'"'"'t know what you are doing, but at least isn'"'"'t guaranteed to break things.'
|
>&2 echo 'This is still NOT RECOMMENDED if you don'"'"'t know what you are doing, but at least isn'"'"'t guaranteed to break things.'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ while [ -n "$1" ]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#TO_INSTALL[@]} -ne 0 ]; then
|
if [ ${#TO_INSTALL[@]} -ne 0 ]; then
|
||||||
/usr/lib/embassy/scripts/chroot-and-upgrade << EOF
|
/usr/lib/startos/scripts/chroot-and-upgrade << EOF
|
||||||
apt-get update && apt-get install -y ${TO_INSTALL[@]}
|
apt-get update && apt-get install -y ${TO_INSTALL[@]}
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
@@ -60,12 +60,12 @@ sudo mount `partition_for ${OUTPUT_DEVICE} 2` $TMPDIR
|
|||||||
sudo mkdir $TMPDIR/boot
|
sudo mkdir $TMPDIR/boot
|
||||||
sudo mount `partition_for ${OUTPUT_DEVICE} 1` $TMPDIR/boot
|
sudo mount `partition_for ${OUTPUT_DEVICE} 1` $TMPDIR/boot
|
||||||
sudo unsquashfs -f -d $TMPDIR startos.raspberrypi.squashfs
|
sudo unsquashfs -f -d $TMPDIR startos.raspberrypi.squashfs
|
||||||
REAL_GIT_HASH=$(cat $TMPDIR/usr/lib/embassy/GIT_HASH.txt)
|
REAL_GIT_HASH=$(cat $TMPDIR/usr/lib/startos/GIT_HASH.txt)
|
||||||
REAL_VERSION=$(cat $TMPDIR/usr/lib/embassy/VERSION.txt)
|
REAL_VERSION=$(cat $TMPDIR/usr/lib/startos/VERSION.txt)
|
||||||
REAL_ENVIRONMENT=$(cat $TMPDIR/usr/lib/embassy/ENVIRONMENT.txt)
|
REAL_ENVIRONMENT=$(cat $TMPDIR/usr/lib/startos/ENVIRONMENT.txt)
|
||||||
sudo sed -i 's| boot=embassy| init=/usr/lib/embassy/scripts/init_resize\.sh|' $TMPDIR/boot/cmdline.txt
|
sudo sed -i 's| boot=embassy| init=/usr/lib/startos/scripts/init_resize\.sh|' $TMPDIR/boot/cmdline.txt
|
||||||
sudo cp ./build/raspberrypi/fstab $TMPDIR/etc/
|
sudo cp ./build/raspberrypi/fstab $TMPDIR/etc/
|
||||||
sudo cp ./build/raspberrypi/init_resize.sh $TMPDIR/usr/lib/embassy/scripts/init_resize.sh
|
sudo cp ./build/raspberrypi/init_resize.sh $TMPDIR/usr/lib/startos/scripts/init_resize.sh
|
||||||
sudo umount $TMPDIR/boot
|
sudo umount $TMPDIR/boot
|
||||||
sudo umount $TMPDIR
|
sudo umount $TMPDIR
|
||||||
sudo losetup -d $OUTPUT_DEVICE
|
sudo losetup -d $OUTPUT_DEVICE
|
||||||
|
|||||||
9
build/registry/cleanDanglingImages
Normal file
9
build/registry/cleanDanglingImages
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
for image in $(find /root/resources/eos/ -type f -name '*.squashfs' -mmin +240 -exec realpath {} \;); do
|
||||||
|
if ! mount | grep "^$image" > /dev/null; then
|
||||||
|
>&2 echo "Removing dangling image: $image"
|
||||||
|
rm $image
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
find /root/resources/eos -type d -empty -delete
|
||||||
45
build/registry/downloadIndexActionResult
Normal file
45
build/registry/downloadIndexActionResult
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RUN_ID=$1
|
||||||
|
|
||||||
|
if [ -z "$RUN_ID" ]; then
|
||||||
|
>&2 echo usage: $0 '<run-id>'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMP_DIR=/var/tmp/action-run-results/$RUN_ID
|
||||||
|
|
||||||
|
rm -rf $TMP_DIR
|
||||||
|
mkdir -p $TMP_DIR
|
||||||
|
|
||||||
|
cd $TMP_DIR
|
||||||
|
|
||||||
|
for arch in x86_64 x86_64-nonfree aarch64 aarch64-nonfree raspberrypi; do
|
||||||
|
gh run download -R Start9Labs/start-os $RUN_ID -n $arch.squashfs
|
||||||
|
done
|
||||||
|
|
||||||
|
VERSION=
|
||||||
|
HASH=
|
||||||
|
for file in $(ls *.squashfs); do
|
||||||
|
if [[ $file =~ ^startos-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?)-([a-f0-9]{7}(~[a-z-]+)?|unknown)_([a-z0-9_-]+).squashfs$ ]]; then
|
||||||
|
if [ -n "$VERSION" ] && [ "$VERSION" != "${BASH_REMATCH[1]}" ]; then
|
||||||
|
>&2 echo "VERSION MISMATCH: expected $VERSION got ${BASH_REMATCH[1]}"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [ -n "$HASH" ] && [ "$HASH" != "${BASH_REMATCH[3]}" ]; then
|
||||||
|
>&2 echo "HASH MISMATCH: expected $HASH got ${BASH_REMATCH[3]}"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
VERSION="${BASH_REMATCH[1]}"
|
||||||
|
HASH="${BASH_REMATCH[3]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p /root/resources/eos/$VERSION
|
||||||
|
rm -rf /root/resources/eos/$VERSION/$HASH
|
||||||
|
mv $TMP_DIR /root/resources/eos/$VERSION/$HASH
|
||||||
|
|
||||||
|
cd /root/resources/eos/$VERSION
|
||||||
|
setOsCommitHash $HASH
|
||||||
22
build/registry/resync.cgi
Normal file
22
build/registry/resync.cgi
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
declare -A params
|
||||||
|
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
|
||||||
|
params["$key"]=$value
|
||||||
|
done <<<"${QUERY_STRING}&"
|
||||||
|
|
||||||
|
index_key="${params['key']}"
|
||||||
|
if [ -z "$index_key" ] || [ "$index_key" != "$(cat /var/www/index_key.txt)" ]; then
|
||||||
|
echo "HTTP/1.1 401 UNAUTHORIZED"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "UNAUTHORIZED"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch /tmp/resync
|
||||||
|
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "OK: Upload successful"
|
||||||
@@ -6,12 +6,14 @@
|
|||||||
# Then we are going to make sure that each of these files is then put on the rsyncd server
|
# Then we are going to make sure that each of these files is then put on the rsyncd server
|
||||||
# so the embassies can pull them down
|
# so the embassies can pull them down
|
||||||
|
|
||||||
|
date >> /var/log/resyncRsyncRegistry.runlog
|
||||||
|
|
||||||
|
|
||||||
cat > /etc/rsyncd.conf << RD
|
cat > /etc/rsyncd.conf << RD
|
||||||
uid = root
|
uid = root
|
||||||
gid = root
|
gid = root
|
||||||
use chroot = yes
|
use chroot = yes
|
||||||
max connections = 50
|
max connections = 4
|
||||||
pid file = /var/run/rsyncd.pid
|
pid file = /var/run/rsyncd.pid
|
||||||
exclude = lost+found/
|
exclude = lost+found/
|
||||||
timeout = 900
|
timeout = 900
|
||||||
@@ -27,7 +29,7 @@ do
|
|||||||
filename=${dir##*/}
|
filename=${dir##*/}
|
||||||
version=$(echo $directory | sed -r 's/.*\///')
|
version=$(echo $directory | sed -r 's/.*\///')
|
||||||
version_dir="/srv/rsync/$version"
|
version_dir="/srv/rsync/$version"
|
||||||
type=$(echo "$filename" | sed -r "s/^.*?\.(\w+)\.squashfs$/\1/")
|
type=$(echo "$filename" | sed -r "s/^.*?\.([a-z0-9_-]+)\.squashfs$/\1/")
|
||||||
new_dir="$version_dir/$type"
|
new_dir="$version_dir/$type"
|
||||||
|
|
||||||
|
|
||||||
@@ -51,4 +53,4 @@ INSERTING
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "Created rsyncd.conf file, restarting service"
|
echo "Created rsyncd.conf file, restarting service"
|
||||||
systemctl restart rsync
|
systemctl restart rsync
|
||||||
39
build/registry/setOsCommitHash
Normal file
39
build/registry/setOsCommitHash
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Get the current directory
|
||||||
|
PWD=$(pwd)
|
||||||
|
HASH=$1
|
||||||
|
|
||||||
|
if [ -z "$HASH" ]; then
|
||||||
|
>&2 echo "usage: setOsCommitHash <hash>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define the expected pattern for the directory
|
||||||
|
pattern="/root/resources/eos/"
|
||||||
|
|
||||||
|
# Check if the current directory matches the pattern
|
||||||
|
if [[ $PWD =~ ^$pattern([0-9.]+)$ ]]; then
|
||||||
|
# Extract the version number from the directory path
|
||||||
|
version="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
>&2 echo "MUST BE IN OS VERSION DIRECTORY"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -d "$HASH" ]; then
|
||||||
|
>&2 echo "$HASH: No such directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in $(ls $HASH/startos-$version-${HASH}_*.squashfs); do
|
||||||
|
if [[ $file =~ ^$HASH/startos-$version-${HASH}_([a-z0-9_-]+).squashfs$ ]]; then
|
||||||
|
arch="${BASH_REMATCH[1]}"
|
||||||
|
echo "Found arch $arch"
|
||||||
|
umount /srv/rsync/$version/$arch
|
||||||
|
rm eos.$arch.squashfs
|
||||||
|
ln -s $file eos.$arch.squashfs
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
resyncRsyncRegistry
|
||||||
48
build/registry/upload.cgi
Normal file
48
build/registry/upload.cgi
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
declare -A params
|
||||||
|
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
|
||||||
|
params["$key"]=$value
|
||||||
|
done <<<"${QUERY_STRING}&"
|
||||||
|
|
||||||
|
index_key="${params['key']}"
|
||||||
|
if [ -z "$index_key" ] || [ "$index_key" != "$(cat /var/www/index_key.txt)" ]; then
|
||||||
|
echo "HTTP/1.1 401 UNAUTHORIZED"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "UNAUTHORIZED"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
git_hash="${params['gitHash']}"
|
||||||
|
version="${params['version']}"
|
||||||
|
platform="${params['platform']}"
|
||||||
|
shasum="${params['shasum']}"
|
||||||
|
if [ -z "$git_hash" ] || [ -z "$version" ] || [ -z "$platform" ] || [ -z "$shasum" ]; then
|
||||||
|
echo "HTTP/1.1 400 BAD REQUEST"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "BAD REQUEST: missing param"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_file=$(mktemp /var/tmp/tmp.XXXXXXXXXX.squashfs)
|
||||||
|
cat > $tmp_file
|
||||||
|
|
||||||
|
if ! sha256sum $tmp_file | grep "$shasum"; then
|
||||||
|
rm $tmp_file
|
||||||
|
echo "HTTP/1.1 400 BAD REQUEST"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "BAD REQUEST: shasum mismatch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /var/www/resources/eos/${version}/${git_hash}
|
||||||
|
mv $tmp_file /var/www/resources/eos/${version}/${git_hash}/startos-${version}-${git_hash}_${platform}.squashfs
|
||||||
|
rm /var/www/resources/eos/${version}/eos.${platform}.squashfs
|
||||||
|
ln -rs /var/www/resources/eos/${version}/${git_hash}/startos-${version}-${git_hash}_${platform}.squashfs /var/www/resources/eos/${version}/eos.${platform}.squashfs
|
||||||
|
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo
|
||||||
|
echo "OK: Upload successful"
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
GIT_HASH="$(git describe --always --abbrev=40 --dirty=-modified)"
|
if [ "$GIT_BRANCH_AS_HASH" != 1 ]; then
|
||||||
|
GIT_HASH="$(git describe --always --abbrev=40 --dirty=-modified)"
|
||||||
|
else
|
||||||
|
GIT_HASH="@$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
fi
|
||||||
|
|
||||||
if ! [ -f ./GIT_HASH.txt ] || [ "$(cat ./GIT_HASH.txt)" != "$GIT_HASH" ]; then
|
if ! [ -f ./GIT_HASH.txt ] || [ "$(cat ./GIT_HASH.txt)" != "$GIT_HASH" ]; then
|
||||||
echo -n "$GIT_HASH" > ./GIT_HASH.txt
|
echo -n "$GIT_HASH" > ./GIT_HASH.txt
|
||||||
|
|||||||
8
check-platform.sh
Executable file
8
check-platform.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if ! [ -f ./PLATFORM.txt ] || [ "$(cat ./PLATFORM.txt)" != "$PLATFORM" ] && [ -n "$PLATFORM" ]; then
|
||||||
|
>&2 echo "Updating PLATFORM.txt to \"$PLATFORM\""
|
||||||
|
echo -n "$PLATFORM" > ./PLATFORM.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n ./PLATFORM.txt
|
||||||
28
build/lib/scripts/postinst → debian/postinst
vendored
28
build/lib/scripts/postinst → debian/postinst
vendored
@@ -8,10 +8,10 @@ fi
|
|||||||
|
|
||||||
if [ -f /usr/sbin/grub-probe ]; then
|
if [ -f /usr/sbin/grub-probe ]; then
|
||||||
mv /usr/sbin/grub-probe /usr/sbin/grub-probe-default
|
mv /usr/sbin/grub-probe /usr/sbin/grub-probe-default
|
||||||
ln -s /usr/lib/embassy/scripts/grub-probe-eos /usr/sbin/grub-probe
|
ln -s /usr/lib/startos/scripts/grub-probe-eos /usr/sbin/grub-probe
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp /usr/lib/embassy/scripts/embassy-initramfs-module /etc/initramfs-tools/scripts/embassy
|
cp /usr/lib/startos/scripts/embassy-initramfs-module /etc/initramfs-tools/scripts/embassy
|
||||||
|
|
||||||
if ! grep overlay /etc/initramfs-tools/modules > /dev/null; then
|
if ! grep overlay /etc/initramfs-tools/modules > /dev/null; then
|
||||||
echo overlay >> /etc/initramfs-tools/modules
|
echo overlay >> /etc/initramfs-tools/modules
|
||||||
@@ -21,6 +21,7 @@ update-initramfs -u -k all
|
|||||||
|
|
||||||
if [ -f /etc/default/grub ]; then
|
if [ -f /etc/default/grub ]; then
|
||||||
sed -i '/\(^\|#\)GRUB_CMDLINE_LINUX=/c\GRUB_CMDLINE_LINUX="boot=embassy"' /etc/default/grub
|
sed -i '/\(^\|#\)GRUB_CMDLINE_LINUX=/c\GRUB_CMDLINE_LINUX="boot=embassy"' /etc/default/grub
|
||||||
|
sed -i '/\(^\|#\)GRUB_DISTRIBUTOR=/c\GRUB_DISTRIBUTOR="StartOS v$(cat /usr/lib/startos/VERSION.txt)"' /etc/default/grub
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# change timezone
|
# change timezone
|
||||||
@@ -46,6 +47,7 @@ dns=systemd-resolved
|
|||||||
[ifupdown]
|
[ifupdown]
|
||||||
managed=true
|
managed=true
|
||||||
EOF
|
EOF
|
||||||
|
$SYSTEMCTL enable startd.service
|
||||||
$SYSTEMCTL enable systemd-resolved.service
|
$SYSTEMCTL enable systemd-resolved.service
|
||||||
$SYSTEMCTL enable systemd-networkd-wait-online.service
|
$SYSTEMCTL enable systemd-networkd-wait-online.service
|
||||||
$SYSTEMCTL enable ssh.service
|
$SYSTEMCTL enable ssh.service
|
||||||
@@ -70,20 +72,20 @@ fi
|
|||||||
|
|
||||||
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
|
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
|
||||||
sed -i 's/Restart=on-failure/Restart=always/g' /lib/systemd/system/tor@default.service
|
sed -i 's/Restart=on-failure/Restart=always/g' /lib/systemd/system/tor@default.service
|
||||||
sed -i 's/ExecStart=\/usr\/bin\/dockerd/ExecStart=\/usr\/bin\/dockerd --exec-opt native.cgroupdriver=systemd/g' /lib/systemd/system/docker.service
|
|
||||||
sed -i '/\(^\|#\)entries-per-entry-group-max=/c\entries-per-entry-group-max=128' /etc/avahi/avahi-daemon.conf
|
sed -i '/\(^\|#\)entries-per-entry-group-max=/c\entries-per-entry-group-max=128' /etc/avahi/avahi-daemon.conf
|
||||||
sed -i '/\(^\|#\)Storage=/c\Storage=persistent' /etc/systemd/journald.conf
|
sed -i '/\(^\|#\)Storage=/c\Storage=persistent' /etc/systemd/journald.conf
|
||||||
sed -i '/\(^\|#\)Compress=/c\Compress=yes' /etc/systemd/journald.conf
|
sed -i '/\(^\|#\)Compress=/c\Compress=yes' /etc/systemd/journald.conf
|
||||||
sed -i '/\(^\|#\)SystemMaxUse=/c\SystemMaxUse=1G' /etc/systemd/journald.conf
|
sed -i '/\(^\|#\)SystemMaxUse=/c\SystemMaxUse=1G' /etc/systemd/journald.conf
|
||||||
sed -i '/\(^\|#\)ForwardToSyslog=/c\ForwardToSyslog=no' /etc/systemd/journald.conf
|
sed -i '/\(^\|#\)ForwardToSyslog=/c\ForwardToSyslog=no' /etc/systemd/journald.conf
|
||||||
sed -i '/^\s*#\?\s*issue_discards\s*=\s*/c\issue_discards = 1' /etc/lvm/lvm.conf
|
sed -i '/^\s*#\?\s*issue_discards\s*=\s*/c\issue_discards = 1' /etc/lvm/lvm.conf
|
||||||
mkdir -p /etc/docker
|
|
||||||
cat > /etc/docker/daemon.json << EOF
|
if cat /usr/lib/startos/ENVIRONMENT.txt | grep '(^|-)docker(-|$)'; then
|
||||||
{
|
sed -i 's/ExecStart=\/usr\/bin\/dockerd/ExecStart=\/usr\/bin\/dockerd --exec-opt native.cgroupdriver=systemd/g' /lib/systemd/system/docker.service
|
||||||
"storage-driver": "overlay2"
|
mkdir -p /etc/docker
|
||||||
}
|
echo '{ "storage-driver": "overlay2" }' > /etc/docker/daemon.json
|
||||||
EOF
|
else
|
||||||
podman network create -d bridge --subnet 172.18.0.1/24 --opt com.docker.network.bridge.name=br-start9 start9
|
podman network create -d bridge --subnet 172.18.0.1/24 --opt com.docker.network.bridge.name=br-start9 start9
|
||||||
|
fi
|
||||||
mkdir -p /etc/nginx/ssl
|
mkdir -p /etc/nginx/ssl
|
||||||
|
|
||||||
# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934
|
# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934
|
||||||
@@ -100,7 +102,7 @@ CookieAuthentication 1
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
rm -rf /var/lib/tor/*
|
rm -rf /var/lib/tor/*
|
||||||
ln -sf /usr/lib/embassy/scripts/tor-check.sh /usr/bin/tor-check
|
ln -sf /usr/lib/startos/scripts/tor-check.sh /usr/bin/tor-check
|
||||||
|
|
||||||
echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-embassy.conf
|
echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-embassy.conf
|
||||||
|
|
||||||
@@ -113,9 +115,9 @@ dpkg-reconfigure --frontend noninteractive locales
|
|||||||
|
|
||||||
groupadd embassy
|
groupadd embassy
|
||||||
|
|
||||||
ln -s /usr/lib/embassy/scripts/dhclient-exit-hook /etc/dhcp/dhclient-exit-hooks.d/embassy
|
ln -s /usr/lib/startos/scripts/dhclient-exit-hook /etc/dhcp/dhclient-exit-hooks.d/embassy
|
||||||
|
|
||||||
rm -f /etc/motd
|
rm -f /etc/motd
|
||||||
ln -sf /usr/lib/embassy/motd /etc/update-motd.d/00-embassy
|
ln -sf /usr/lib/startos/motd /etc/update-motd.d/00-embassy
|
||||||
chmod -x /etc/update-motd.d/*
|
chmod -x /etc/update-motd.d/*
|
||||||
chmod +x /etc/update-motd.d/00-embassy
|
chmod +x /etc/update-motd.d/00-embassy
|
||||||
48
dpkg-build.sh
Executable file
48
dpkg-build.sh
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
|
||||||
|
BASENAME=$(./basename.sh)
|
||||||
|
VERSION=$(cat ./VERSION.txt)
|
||||||
|
if [ "$PLATFORM" = "x86_64" ] || [ "$PLATFORM" = "x86_64-nonfree" ]; then
|
||||||
|
DEB_ARCH=amd64
|
||||||
|
elif [ "$PLATFORM" = "aarch64" ] || [ "$PLATFORM" = "aarch64-nonfree" ] || [ "$PLATFORM" = "raspberrypi" ]; then
|
||||||
|
DEB_ARCH=arm64
|
||||||
|
else
|
||||||
|
DEB_ARCH="$PLATFORM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf dpkg-workdir/$BASENAME
|
||||||
|
mkdir -p dpkg-workdir/$BASENAME
|
||||||
|
|
||||||
|
make install DESTDIR=dpkg-workdir/$BASENAME
|
||||||
|
|
||||||
|
DEPENDS=$(cat dpkg-workdir/$BASENAME/usr/lib/startos/depends | tr $'\n' ',' | sed 's/,,\+/,/g' | sed 's/,$//')
|
||||||
|
CONFLICTS=$(cat dpkg-workdir/$BASENAME/usr/lib/startos/conflicts | tr $'\n' ',' | sed 's/,,\+/,/g' | sed 's/,$//')
|
||||||
|
|
||||||
|
cp -r debian dpkg-workdir/$BASENAME/DEBIAN
|
||||||
|
cat > dpkg-workdir/$BASENAME/DEBIAN/control << EOF
|
||||||
|
Package: startos
|
||||||
|
Version: ${VERSION}
|
||||||
|
Section: unknown
|
||||||
|
Priority: required
|
||||||
|
Maintainer: Aiden McClelland <aiden@start9.com>
|
||||||
|
Homepage: https://start9.com
|
||||||
|
Architecture: ${DEB_ARCH}
|
||||||
|
Multi-Arch: foreign
|
||||||
|
Depends: ${DEPENDS}
|
||||||
|
Conflicts: ${CONFLICTS}
|
||||||
|
Description: StartOS Debian Package
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cd dpkg-workdir/$BASENAME
|
||||||
|
find . -type f -not -path "./DEBIAN/*" -exec md5sum {} \; | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sums
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
cd dpkg-workdir
|
||||||
|
dpkg-deb --root-owner-group -b $BASENAME
|
||||||
|
mkdir -p ../results
|
||||||
|
mv $BASENAME.deb ../results/$BASENAME.deb
|
||||||
|
rm -rf $BASENAME
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"useMocks": true,
|
"useMocks": true,
|
||||||
"enableWidgets": false,
|
"enableWidgets": false,
|
||||||
"packageArch": "aarch64",
|
|
||||||
"osArch": "raspberrypi",
|
|
||||||
"ui": {
|
"ui": {
|
||||||
"api": {
|
"api": {
|
||||||
"url": "rpc",
|
"url": "rpc",
|
||||||
|
|||||||
13319
frontend/package-lock.json
generated
13319
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,8 +47,9 @@
|
|||||||
"@maskito/angular": "^0.10.0",
|
"@maskito/angular": "^0.10.0",
|
||||||
"@maskito/core": "^0.10.0",
|
"@maskito/core": "^0.10.0",
|
||||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||||
"@start9labs/argon2": "^0.1.0",
|
"@start9labs/argon2": "^0.2.2",
|
||||||
"@start9labs/emver": "^0.1.5",
|
"@start9labs/emver": "^0.1.5",
|
||||||
|
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
|
||||||
"@taiga-ui/addon-charts": "3.28.0",
|
"@taiga-ui/addon-charts": "3.28.0",
|
||||||
"@taiga-ui/cdk": "3.28.0",
|
"@taiga-ui/cdk": "3.28.0",
|
||||||
"@taiga-ui/core": "3.28.0",
|
"@taiga-ui/core": "3.28.0",
|
||||||
@@ -76,7 +77,6 @@
|
|||||||
"patch-db-client": "file: ../../../patch-db/client",
|
"patch-db-client": "file: ../../../patch-db/client",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
|
|
||||||
"swiper": "^8.2.4",
|
"swiper": "^8.2.4",
|
||||||
"ts-matches": "^5.2.1",
|
"ts-matches": "^5.2.1",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
@@ -115,4 +115,4 @@
|
|||||||
"pre-commit": "lint-staged --concurrent false"
|
"pre-commit": "lint-staged --concurrent false"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
[type]="!unmasked1 ? 'password' : 'text'"
|
[type]="!unmasked1 ? 'password' : 'text'"
|
||||||
placeholder="Enter Password"
|
placeholder="Enter Password"
|
||||||
(ionChange)="validate()"
|
(ionChange)="validate()"
|
||||||
maxlength="64"
|
|
||||||
></ion-input>
|
></ion-input>
|
||||||
<ion-button fill="clear" color="light" (click)="unmasked1 = !unmasked1">
|
<ion-button fill="clear" color="light" (click)="unmasked1 = !unmasked1">
|
||||||
<ion-icon
|
<ion-icon
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
[type]="!unmasked2 ? 'password' : 'text'"
|
[type]="!unmasked2 ? 'password' : 'text'"
|
||||||
placeholder="Retype Password"
|
placeholder="Retype Password"
|
||||||
(ionChange)="checkVer()"
|
(ionChange)="checkVer()"
|
||||||
maxlength="64"
|
|
||||||
></ion-input>
|
></ion-input>
|
||||||
<ion-button
|
<ion-button
|
||||||
fill="clear"
|
fill="clear"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<p>
|
<p>
|
||||||
Download your server's Root CA and
|
Download your server's Root CA and
|
||||||
<a
|
<a
|
||||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
|
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-lan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
<span style="font-weight: bold">Note:</span>
|
<span style="font-weight: bold">Note:</span>
|
||||||
This address will only work from a Tor-enabled browser.
|
This address will only work from a Tor-enabled browser.
|
||||||
<a
|
<a
|
||||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
|
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-tor"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||||
|
|||||||
@@ -8,23 +8,21 @@
|
|||||||
<ion-card>
|
<ion-card>
|
||||||
<ion-row class="ion-align-items-center">
|
<ion-row class="ion-align-items-center">
|
||||||
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
||||||
<div class="inline" style="margin-bottom: 3rem">
|
<div class="inline mb-12">
|
||||||
<ion-icon
|
<ion-icon
|
||||||
name="checkmark-circle-outline"
|
name="checkmark-circle-outline"
|
||||||
color="success"
|
color="success"
|
||||||
></ion-icon>
|
></ion-icon>
|
||||||
<h1>Setup Complete!</h1>
|
<h1>Setup Complete!</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-container">
|
<ion-button
|
||||||
<ion-card id="exit" (click)="exitKiosk()">
|
shape="round"
|
||||||
<div class="container">
|
class="login-button mb-12"
|
||||||
<div class="inline">
|
(click)="exitKiosk()"
|
||||||
<p>Continue to login</p>
|
>
|
||||||
<ion-icon name="log-in-outline"></ion-icon>
|
Continue to Login
|
||||||
</div>
|
<ion-icon name="log-in-outline" slot="end"></ion-icon>
|
||||||
</div>
|
</ion-button>
|
||||||
</ion-card>
|
|
||||||
</div>
|
|
||||||
</ion-col>
|
</ion-col>
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
@@ -34,8 +32,8 @@
|
|||||||
<ion-card *ngIf="lanAddress">
|
<ion-card *ngIf="lanAddress">
|
||||||
<ion-row class="ion-align-items-center">
|
<ion-row class="ion-align-items-center">
|
||||||
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
||||||
<div style="margin-bottom: 4rem">
|
<div class="mb-12">
|
||||||
<div class="inline">
|
<div class="inline-container setup">
|
||||||
<ion-icon
|
<ion-icon
|
||||||
name="checkmark-circle-outline"
|
name="checkmark-circle-outline"
|
||||||
color="success"
|
color="success"
|
||||||
@@ -52,35 +50,40 @@
|
|||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<ion-card id="information" (click)="download()">
|
<ion-card id="information" (click)="download()">
|
||||||
<ion-card-content>
|
<ion-card-content>
|
||||||
<ion-card-title>
|
<ion-card-title>Download address info</ion-card-title>
|
||||||
Download permanent address info
|
|
||||||
</ion-card-title>
|
|
||||||
<p>
|
<p>
|
||||||
start.local was for setup purposes only. It will no
|
start.local was for setup purposes only. It will no
|
||||||
longer work.
|
longer work.
|
||||||
</p>
|
</p>
|
||||||
</ion-card-content>
|
</ion-card-content>
|
||||||
<ion-footer>
|
<ion-footer>
|
||||||
<div class="container">
|
<div class="inline-container">
|
||||||
<div class="inline">
|
<p class="action-text">Download</p>
|
||||||
<p>Download</p>
|
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||||
<ion-icon name="download-outline"></ion-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<ion-card
|
<ion-card
|
||||||
[disabled]="disableLogin"
|
|
||||||
id="launch"
|
id="launch"
|
||||||
|
[disabled]="disableLogin"
|
||||||
href="{{ lanAddress }}"
|
href="{{ lanAddress }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<div class="container">
|
<ion-card-content>
|
||||||
<div class="inline">
|
<ion-card-title>Trust your Root CA</ion-card-title>
|
||||||
<p>Login to StartOS</p>
|
<p>
|
||||||
<ion-icon name="open-outline"></ion-icon>
|
In the new tab, follow instructions to trust your
|
||||||
|
server's Root CA and log in.
|
||||||
|
</p>
|
||||||
|
</ion-card-content>
|
||||||
|
<ion-footer>
|
||||||
|
<div class="container">
|
||||||
|
<div class="inline-container">
|
||||||
|
<p class="action-text">Open</p>
|
||||||
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ion-footer>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
</div>
|
</div>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
|
|||||||
@@ -18,19 +18,24 @@ ion-content {
|
|||||||
|
|
||||||
ion-grid {
|
ion-grid {
|
||||||
max-width: 760px;
|
max-width: 760px;
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-center-wrapper {
|
.inline-container {
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
ion-card {
|
ion-card {
|
||||||
padding: 3rem;
|
padding: 2.4rem;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: var(--ion-color-success);
|
color: var(--ion-color-success);
|
||||||
@@ -44,14 +49,14 @@ ion-card {
|
|||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// download info card
|
||||||
ion-card {
|
ion-card {
|
||||||
max-width: 91%;
|
min-height: 260px;
|
||||||
min-width: 91%;
|
width: 80%;
|
||||||
background: #615F5F;
|
background: #615F5F;
|
||||||
color: var(--ion-text-color);
|
color: var(--ion-text-color);
|
||||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||||
border-radius: 44px;
|
border-radius: 44px;
|
||||||
margin: auto;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -70,14 +75,6 @@ ion-card {
|
|||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-card-content {
|
|
||||||
padding-bottom: 4rem;
|
|
||||||
|
|
||||||
p {
|
|
||||||
padding: 1rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-footer {
|
ion-footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
@@ -100,19 +97,24 @@ ion-card {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.login-button {
|
||||||
display: flex;
|
--background: var(--color-accent);
|
||||||
justify-content: center;
|
--padding-bottom: 2.5rem;
|
||||||
align-items: center;
|
--padding-top: 2.5rem;
|
||||||
}
|
--padding-start: 2.5rem;
|
||||||
|
--padding-end: 2.5rem;
|
||||||
#exit {
|
--border-radius: 44px;
|
||||||
background: var(--color-accent);
|
font-size: 1.4rem !important;
|
||||||
height: 100%;
|
font-weight: bold;
|
||||||
|
text-transform: none;
|
||||||
.container p {
|
letter-spacing: normal;
|
||||||
font-size: 1.4rem !important;
|
transition: all 350ms ease;
|
||||||
font-weight: bold;
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transition-property: transform;
|
||||||
|
transform: scale(1.05);
|
||||||
|
transition-delay: 40ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-icon {
|
ion-icon {
|
||||||
@@ -120,40 +122,62 @@ ion-card {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#launch {
|
.launch-button {
|
||||||
background: var(--alt-blue);
|
--background: var(--alt-blue);
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.container p {
|
|
||||||
font-size: 1.4rem !important;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-icon {
|
|
||||||
font-size: 1.7rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#information:after {
|
#information:after, #launch:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 80%;
|
top: 79%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--color-accent);
|
background: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#launch:after {
|
||||||
|
background: var(--alt-blue);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-container {
|
.mb-12 {
|
||||||
display: flex;
|
margin-bottom: 3rem;
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.emphasis-warn {
|
.pb-2 {
|
||||||
font-weight: 600;
|
padding-bottom: 0.5rem;
|
||||||
color: var(--ion-color-warning);
|
}
|
||||||
|
|
||||||
|
.pt-1 {
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.action-text {
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
letter-spacing: 0.03rem;
|
||||||
|
padding-bottom: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.setup {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-card {
|
||||||
|
ion-card {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: unset;
|
||||||
|
}
|
||||||
|
#information:after {
|
||||||
|
top: 84%;
|
||||||
|
}
|
||||||
|
#launch:after {
|
||||||
|
top: 85%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -45,18 +45,7 @@ export class SuccessPage {
|
|||||||
|
|
||||||
async ngAfterViewInit() {
|
async ngAfterViewInit() {
|
||||||
this.ngZone.runOutsideAngular(() => this.initMatrix())
|
this.ngZone.runOutsideAngular(() => this.initMatrix())
|
||||||
try {
|
setTimeout(() => this.complete(), 1000)
|
||||||
const ret = await this.api.complete()
|
|
||||||
if (!this.isKiosk) {
|
|
||||||
this.torAddress = ret['tor-address']
|
|
||||||
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
|
|
||||||
this.cert = ret['root-ca']
|
|
||||||
|
|
||||||
await this.api.exit()
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
await this.errCtrl.present(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
download() {
|
download() {
|
||||||
@@ -83,6 +72,21 @@ export class SuccessPage {
|
|||||||
this.api.exit()
|
this.api.exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async complete() {
|
||||||
|
try {
|
||||||
|
const ret = await this.api.complete()
|
||||||
|
if (!this.isKiosk) {
|
||||||
|
this.torAddress = ret['tor-address'].replace(/^https:/, 'http:')
|
||||||
|
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
|
||||||
|
this.cert = ret['root-ca']
|
||||||
|
|
||||||
|
await this.api.exit()
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
await this.errCtrl.present(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private initMatrix() {
|
private initMatrix() {
|
||||||
this.ctx = this.canvas.nativeElement.getContext('2d')!
|
this.ctx = this.canvas.nativeElement.getContext('2d')!
|
||||||
this.canvas.nativeElement.width = window.innerWidth
|
this.canvas.nativeElement.width = window.innerWidth
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ ion-toast {
|
|||||||
* {
|
* {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0.3rem;
|
padding-left: 0px 0.3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Directive, HostListener, Inject } from '@angular/core'
|
import { Directive, HostListener, Inject } from '@angular/core'
|
||||||
import { DOCUMENT } from '@angular/common'
|
import { DOCUMENT } from '@angular/common'
|
||||||
import { debounce } from '@start9labs/shared'
|
import { debounce } from '../../util/misc.util'
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[appEnter]',
|
selector: '[appEnter]',
|
||||||
@@ -27,6 +27,8 @@ export * from './directives/responsive-col/responsive-col.module'
|
|||||||
export * from './directives/responsive-col/responsive-col-viewport.directive'
|
export * from './directives/responsive-col/responsive-col-viewport.directive'
|
||||||
export * from './directives/safe-links/safe-links.directive'
|
export * from './directives/safe-links/safe-links.directive'
|
||||||
export * from './directives/safe-links/safe-links.module'
|
export * from './directives/safe-links/safe-links.module'
|
||||||
|
export * from './directives/enter/enter.directive'
|
||||||
|
export * from './directives/enter/enter.module'
|
||||||
|
|
||||||
export * from './mocks/get-setup-status'
|
export * from './mocks/get-setup-status'
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ export function getErrorMessage(
|
|||||||
} else if (e.code === 0) {
|
} else if (e.code === 0) {
|
||||||
message =
|
message =
|
||||||
'Request Error. Your browser blocked the request. This is usually caused by a corrupt browser cache or an overly aggressive ad blocker. Please clear your browser cache and/or adjust your ad blocker and try again'
|
'Request Error. Your browser blocked the request. This is usually caused by a corrupt browser cache or an overly aggressive ad blocker. Please clear your browser cache and/or adjust your ad blocker and try again'
|
||||||
|
link = 'https://docs.start9.com/0.3.5.x/support/common-issues#request-error'
|
||||||
} else if (!e.message) {
|
} else if (!e.message) {
|
||||||
message = 'Unknown Error'
|
message = 'Unknown Error'
|
||||||
link = 'https://docs.start9.com/latest/support/faq'
|
|
||||||
} else {
|
} else {
|
||||||
message = e.message
|
message = e.message
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
export type WorkspaceConfig = {
|
export type WorkspaceConfig = {
|
||||||
packageArch: 'aarch64' | 'x86_64'
|
|
||||||
osArch: 'aarch64' | 'x86_64' | 'raspberrypi'
|
|
||||||
gitHash: string
|
gitHash: string
|
||||||
useMocks: boolean
|
useMocks: boolean
|
||||||
enableWidgets: boolean
|
enableWidgets: boolean
|
||||||
|
|||||||
@@ -26,10 +26,7 @@
|
|||||||
type="overlay"
|
type="overlay"
|
||||||
side="end"
|
side="end"
|
||||||
class="right-menu container"
|
class="right-menu container"
|
||||||
[class.container_offline]="
|
[class.container_offline]="offline$ | async"
|
||||||
(authService.isVerified$ | async) &&
|
|
||||||
!(connection.connected$ | async)
|
|
||||||
"
|
|
||||||
[class.right-menu_hidden]="!drawer.open"
|
[class.right-menu_hidden]="!drawer.open"
|
||||||
[style.--side-width.px]="drawer.width"
|
[style.--side-width.px]="drawer.width"
|
||||||
>
|
>
|
||||||
@@ -47,10 +44,7 @@
|
|||||||
[responsiveColViewport]="viewport"
|
[responsiveColViewport]="viewport"
|
||||||
id="main-content"
|
id="main-content"
|
||||||
class="container"
|
class="container"
|
||||||
[class.container_offline]="
|
[class.container_offline]="offline$ | async"
|
||||||
(authService.isVerified$ | async) &&
|
|
||||||
!(connection.connected$ | async)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<ion-content
|
<ion-content
|
||||||
#viewport="viewport"
|
#viewport="viewport"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, inject, OnDestroy } from '@angular/core'
|
import { Component, inject, OnDestroy } from '@angular/core'
|
||||||
import { merge } from 'rxjs'
|
import { combineLatest, map, merge, startWith } from 'rxjs'
|
||||||
import { AuthService } from './services/auth.service'
|
import { AuthService } from './services/auth.service'
|
||||||
import { SplitPaneTracker } from './services/split-pane.service'
|
import { SplitPaneTracker } from './services/split-pane.service'
|
||||||
import { PatchDataService } from './services/patch-data.service'
|
import { PatchDataService } from './services/patch-data.service'
|
||||||
@@ -25,6 +25,19 @@ export class AppComponent implements OnDestroy {
|
|||||||
readonly sidebarOpen$ = this.splitPane.sidebarOpen$
|
readonly sidebarOpen$ = this.splitPane.sidebarOpen$
|
||||||
readonly widgetDrawer$ = this.clientStorageService.widgetDrawer$
|
readonly widgetDrawer$ = this.clientStorageService.widgetDrawer$
|
||||||
readonly theme$ = inject(THEME)
|
readonly theme$ = inject(THEME)
|
||||||
|
readonly offline$ = combineLatest([
|
||||||
|
this.authService.isVerified$,
|
||||||
|
this.connection.connected$,
|
||||||
|
this.patch
|
||||||
|
.watch$('server-info', 'status-info')
|
||||||
|
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||||
|
]).pipe(
|
||||||
|
map(
|
||||||
|
([verified, connected, status]) =>
|
||||||
|
verified &&
|
||||||
|
(!connected || status.restarting || status['shutting-down']),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
|||||||
import { IonicModule } from '@ionic/angular'
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||||
import {
|
import {
|
||||||
MarkdownModule,
|
|
||||||
DarkThemeModule,
|
DarkThemeModule,
|
||||||
|
EnterModule,
|
||||||
|
LightThemeModule,
|
||||||
|
MarkdownModule,
|
||||||
ResponsiveColModule,
|
ResponsiveColModule,
|
||||||
SharedPipesModule,
|
SharedPipesModule,
|
||||||
LightThemeModule,
|
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
|
|
||||||
import { AppComponent } from './app.component'
|
import { AppComponent } from './app.component'
|
||||||
@@ -24,7 +25,6 @@ import { OSWelcomePageModule } from './common/os-welcome/os-welcome.module'
|
|||||||
import { PreloaderModule } from './app/preloader/preloader.module'
|
import { PreloaderModule } from './app/preloader/preloader.module'
|
||||||
import { FooterModule } from './app/footer/footer.module'
|
import { FooterModule } from './app/footer/footer.module'
|
||||||
import { MenuModule } from './app/menu/menu.module'
|
import { MenuModule } from './app/menu/menu.module'
|
||||||
import { EnterModule } from './app/enter/enter.module'
|
|
||||||
import { APP_PROVIDERS } from './app.providers'
|
import { APP_PROVIDERS } from './app.providers'
|
||||||
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
||||||
import { ToastContainerModule } from './common/toast-container/toast-container.module'
|
import { ToastContainerModule } from './common/toast-container/toast-container.module'
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||||
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { combineLatest, map, Observable, startWith } from 'rxjs'
|
import { combineLatest, map, Observable, startWith } from 'rxjs'
|
||||||
import { ConnectionService } from 'src/app/services/connection.service'
|
import { ConnectionService } from 'src/app/services/connection.service'
|
||||||
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'connection-bar',
|
selector: 'connection-bar',
|
||||||
@@ -19,8 +21,11 @@ export class ConnectionBarComponent {
|
|||||||
}> = combineLatest([
|
}> = combineLatest([
|
||||||
this.connectionService.networkConnected$,
|
this.connectionService.networkConnected$,
|
||||||
this.websocket$.pipe(startWith(false)),
|
this.websocket$.pipe(startWith(false)),
|
||||||
|
this.patch
|
||||||
|
.watch$('server-info', 'status-info')
|
||||||
|
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([network, websocket]) => {
|
map(([network, websocket, status]) => {
|
||||||
if (!network)
|
if (!network)
|
||||||
return {
|
return {
|
||||||
message: 'No Internet',
|
message: 'No Internet',
|
||||||
@@ -35,6 +40,20 @@ export class ConnectionBarComponent {
|
|||||||
icon: 'cloud-offline-outline',
|
icon: 'cloud-offline-outline',
|
||||||
dots: true,
|
dots: true,
|
||||||
}
|
}
|
||||||
|
if (status['shutting-down'])
|
||||||
|
return {
|
||||||
|
message: 'Shutting Down',
|
||||||
|
color: 'dark',
|
||||||
|
icon: 'power',
|
||||||
|
dots: true,
|
||||||
|
}
|
||||||
|
if (status.restarting)
|
||||||
|
return {
|
||||||
|
message: 'Restarting',
|
||||||
|
color: 'dark',
|
||||||
|
icon: 'power',
|
||||||
|
dots: true,
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: 'Connected',
|
message: 'Connected',
|
||||||
@@ -45,5 +64,8 @@ export class ConnectionBarComponent {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(private readonly connectionService: ConnectionService) {}
|
constructor(
|
||||||
|
private readonly connectionService: ConnectionService,
|
||||||
|
private readonly patch: PatchDB<DataModel>,
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,17 @@
|
|||||||
<ion-label class="label montserrat" routerLinkActive="label_selected">
|
<ion-label class="label montserrat" routerLinkActive="label_selected">
|
||||||
{{ page.title }}
|
{{ page.title }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
|
<ion-icon
|
||||||
|
*ngIf="page.url === '/system' && (warning$ | async)"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
name="warning"
|
||||||
|
></ion-icon>
|
||||||
<ion-icon
|
<ion-icon
|
||||||
*ngIf="page.url === '/system' && (showEOSUpdate$ | async)"
|
*ngIf="page.url === '/system' && (showEOSUpdate$ | async)"
|
||||||
color="success"
|
color="success"
|
||||||
size="small"
|
size="small"
|
||||||
name="rocket-outline"
|
name="rocket"
|
||||||
></ion-icon>
|
></ion-icon>
|
||||||
<ion-badge
|
<ion-badge
|
||||||
*ngIf="page.url === '/updates' && (updateCount$ | async) as updateCount"
|
*ngIf="page.url === '/updates' && (updateCount$ | async) as updateCount"
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import {
|
|||||||
filter,
|
filter,
|
||||||
first,
|
first,
|
||||||
map,
|
map,
|
||||||
|
merge,
|
||||||
Observable,
|
Observable,
|
||||||
|
of,
|
||||||
pairwise,
|
pairwise,
|
||||||
startWith,
|
startWith,
|
||||||
switchMap,
|
switchMap,
|
||||||
@@ -22,6 +24,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
|||||||
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
||||||
import { Emver, THEME } from '@start9labs/shared'
|
import { Emver, THEME } from '@start9labs/shared'
|
||||||
import { ConnectionService } from 'src/app/services/connection.service'
|
import { ConnectionService } from 'src/app/services/connection.service'
|
||||||
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menu',
|
selector: 'app-menu',
|
||||||
@@ -112,6 +115,11 @@ export class MenuComponent {
|
|||||||
|
|
||||||
readonly theme$ = inject(THEME)
|
readonly theme$ = inject(THEME)
|
||||||
|
|
||||||
|
readonly warning$ = merge(
|
||||||
|
of(this.config.isTorHttp()),
|
||||||
|
this.patch.watch$('server-info', 'ntp-synced').pipe(map(synced => !synced)),
|
||||||
|
)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly patch: PatchDB<DataModel>,
|
private readonly patch: PatchDB<DataModel>,
|
||||||
private readonly eosService: EOSService,
|
private readonly eosService: EOSService,
|
||||||
@@ -120,5 +128,6 @@ export class MenuComponent {
|
|||||||
private readonly splitPane: SplitPaneTracker,
|
private readonly splitPane: SplitPaneTracker,
|
||||||
private readonly emver: Emver,
|
private readonly emver: Emver,
|
||||||
private readonly connectionService: ConnectionService,
|
private readonly connectionService: ConnectionService,
|
||||||
|
private readonly config: ConfigService,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +1,102 @@
|
|||||||
<ion-grid class="grid-wiz">
|
<div class="center-container">
|
||||||
<img width="60px" height="60px" src="/assets/img/icon.png" alt="StartOS" />
|
<ng-container *ngIf="!caTrusted; else trusted">
|
||||||
<ion-row>
|
<ion-card id="untrusted" class="text-center">
|
||||||
<ion-col class="ion-text-center">
|
|
||||||
<ion-icon name="lock-closed-outline" class="wiz-icon"></ion-icon>
|
<ion-icon name="lock-closed-outline" class="wiz-icon"></ion-icon>
|
||||||
</ion-col>
|
<h1>Trust Your Root CA</h1>
|
||||||
</ion-row>
|
|
||||||
<ion-row>
|
|
||||||
<ion-col class="ion-text-center">
|
|
||||||
<h2><b>Trust your Root Certificate Authority (CA)</b></h2>
|
|
||||||
<p>
|
<p>
|
||||||
Download and trust your server's Root CA to establish secure, encrypted
|
Download and trust your server's Root Certificate Authority to establish
|
||||||
(
|
a secure (HTTPS) connection. You will need to repeat this on every
|
||||||
<b>HTTPS</b>
|
device you use to connect to your server.
|
||||||
) connections with your server
|
|
||||||
</p>
|
</p>
|
||||||
</ion-col>
|
<ol>
|
||||||
</ion-row>
|
<li>
|
||||||
<ion-row>
|
<b>Bookmark this page</b>
|
||||||
<ion-col sizeXs="12" sizeLg="4">
|
- Save this page so you can access it later. You can also find the
|
||||||
<div class="wiz-card">
|
address in the
|
||||||
<ion-row class="ion-justify-content-between">
|
<code>StartOS-info.html</code>
|
||||||
<b class="wiz-step">1</b>
|
file downloaded at the end of initial setup.
|
||||||
<tui-tooltip
|
</li>
|
||||||
content="Your server uses its Root CA to generate SSL/TLS certificates for itself and its installed services. These certificates are used to encrypt network traffic with your client devices."
|
<li>
|
||||||
direction="right"
|
<b>Download your server's Root CA</b>
|
||||||
></tui-tooltip>
|
- Your server uses its Root CA to generate SSL/TLS certificates for
|
||||||
</ion-row>
|
itself and installed services. These certificates are then used to
|
||||||
<div class="ion-text-center">
|
encrypt network traffic with your client devices.
|
||||||
<h2>Download Root CA</h2>
|
<br />
|
||||||
<p>Download your server's Root CA</p>
|
<ion-button
|
||||||
</div>
|
strong
|
||||||
<ion-button class="wiz-card-button" shape="round" (click)="download()">
|
size="small"
|
||||||
<ion-icon slot="start" name="download-outline"></ion-icon>
|
shape="round"
|
||||||
Download
|
color="tertiary"
|
||||||
</ion-button>
|
(click)="download()"
|
||||||
</div>
|
>
|
||||||
</ion-col>
|
Download
|
||||||
<ion-col sizeXs="12" sizeLg="4">
|
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||||
<div class="wiz-card" [class.disabled]="!downloadClicked">
|
</ion-button>
|
||||||
<ion-row class="ion-justify-content-between">
|
</li>
|
||||||
<b class="wiz-step">2</b>
|
<li>
|
||||||
<tui-tooltip
|
<b>Trust your server's Root CA</b>
|
||||||
content="By trusting your server's Root CA, your device can verify the authenticity of its encrypted communications with your server and installed services. You will need to trust the Root CA on every device used to connect to your server."
|
- Follow instructions for your OS. By trusting your server's Root CA,
|
||||||
direction="right"
|
your device can verify the authenticity of encrypted communications
|
||||||
></tui-tooltip>
|
with your server.
|
||||||
</ion-row>
|
<br />
|
||||||
<div class="ion-text-center">
|
<ion-button
|
||||||
<h2>Trust Root CA</h2>
|
strong
|
||||||
<p>Follow instructions for your OS</p>
|
size="small"
|
||||||
</div>
|
shape="round"
|
||||||
<ion-button
|
color="primary"
|
||||||
class="wiz-card-button"
|
href="https://docs.start9.com/0.3.5.x/user-manual/trust-ca#establishing-trust"
|
||||||
shape="round"
|
target="_blank"
|
||||||
(click)="instructions()"
|
noreferrer
|
||||||
[disabled]="!downloadClicked"
|
>
|
||||||
>
|
View Instructions
|
||||||
View Docs
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
</ion-button>
|
||||||
</ion-button>
|
</li>
|
||||||
</div>
|
<li>
|
||||||
</ion-col>
|
<b>Test</b>
|
||||||
<ion-col sizeXs="12" sizeLg="4">
|
- Refresh the page. If refreshing the page does not work, you may need
|
||||||
<div class="wiz-card" [class.disabled]="!polling && !caTrusted">
|
to quit and re-open your browser, then revisit this page.
|
||||||
<b class="wiz-step">3</b>
|
<br />
|
||||||
<div class="ion-text-center">
|
<ion-button
|
||||||
<h2>Go To Login</h2>
|
strong
|
||||||
<p *ngIf="instructionsClicked; else space" class="inline-center">
|
size="small"
|
||||||
<ion-spinner
|
shape="round"
|
||||||
class="wiz-spinner"
|
class="refresh"
|
||||||
*ngIf="!caTrusted; else trusted"
|
(click)="refresh()"
|
||||||
></ion-spinner>
|
>
|
||||||
<ng-template #trusted>
|
Refresh
|
||||||
<ion-icon name="ribbon-outline" color="success"></ion-icon>
|
<ion-icon slot="end" name="refresh"></ion-icon>
|
||||||
</ng-template>
|
</ion-button>
|
||||||
{{ caTrusted ? 'Root CA trusted!' : 'Waiting for trust...' }}
|
</li>
|
||||||
</p>
|
</ol>
|
||||||
<ng-template #space>
|
|
||||||
<!-- to keep alignment -->
|
|
||||||
<p><br /></p>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
<ion-button
|
|
||||||
class="wiz-card-button"
|
|
||||||
shape="round"
|
|
||||||
(click)="launchHttps()"
|
|
||||||
[disabled]="!caTrusted"
|
|
||||||
>
|
|
||||||
Open
|
|
||||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
</div>
|
|
||||||
</ion-col>
|
|
||||||
</ion-row>
|
|
||||||
<ion-row>
|
|
||||||
<ion-col class="ion-text-center">
|
|
||||||
<ion-button fill="clear" (click)="launchHttps()" [disabled]="caTrusted">
|
<ion-button fill="clear" (click)="launchHttps()" [disabled]="caTrusted">
|
||||||
Skip
|
Skip
|
||||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-col>
|
<span class="skip_detail">(not recommended)</span>
|
||||||
</ion-row>
|
</ion-card>
|
||||||
</ion-grid>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #trusted>
|
||||||
|
<ion-card id="trusted" class="text-center">
|
||||||
|
<ion-icon
|
||||||
|
name="shield-checkmark-outline"
|
||||||
|
class="wiz-icon"
|
||||||
|
color="success"
|
||||||
|
></ion-icon>
|
||||||
|
<h1>Root CA Trusted!</h1>
|
||||||
|
<p>
|
||||||
|
You have successfully trusted your server's Root CA and may now log in
|
||||||
|
securely.
|
||||||
|
</p>
|
||||||
|
<ion-button strong (click)="launchHttps()" color="tertiary" shape="round">
|
||||||
|
Go to login
|
||||||
|
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-card>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
id="install-cert"
|
id="install-cert"
|
||||||
href="/eos/local.crt"
|
href="/eos/local.crt"
|
||||||
|
|||||||
@@ -1,44 +1,83 @@
|
|||||||
.grid-wiz {
|
#trusted {
|
||||||
--ion-grid-padding: 36px;
|
max-width: 40%;
|
||||||
height: 100%
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiz-icon {
|
#untrusted {
|
||||||
font-size: 84px;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiz-card {
|
.center-container {
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-card {
|
||||||
|
color: var(--ion-color-dark);
|
||||||
background: #414141;
|
background: #414141;
|
||||||
margin: 24px;
|
box-shadow: 0 4px 4px rgba(17, 17, 17, 0.144);
|
||||||
padding: 16px;
|
border-radius: 35px;
|
||||||
height: 280px;
|
padding: 1.5rem;
|
||||||
border-radius: 16px;
|
width: 100%;
|
||||||
display: grid;
|
|
||||||
|
|
||||||
& h2 {
|
h1 {
|
||||||
font-weight: 600;
|
font-weight: bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 21px;
|
||||||
|
line-height: 25px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiz-card-button {
|
.text-center {
|
||||||
justify-self: center;
|
text-align: center;
|
||||||
white-space: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiz-spinner {
|
ol {
|
||||||
width: 14px;
|
font-size: 17px;
|
||||||
height: 14px;
|
line-height: 25px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-button {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.refresh {
|
||||||
filter: saturate(0.2) contrast(0.5)
|
--background: var(--ion-color-success-shade);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiz-step {
|
.wiz-icon {
|
||||||
margin-top: 4px;
|
font-size: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-center {
|
.skip_detail {
|
||||||
display: inline-flex;
|
display: block;
|
||||||
align-items: center;
|
font-size: 0.8rem;
|
||||||
|
margin-top: -13px;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
#trusted, #untrusted {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 701px) and (max-width: 1200px) {
|
||||||
|
#trusted, #untrusted {
|
||||||
|
max-width: 75%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ConfigService } from 'src/app/services/config.service'
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
import { pauseFor, RELATIVE_URL } from '@start9labs/shared'
|
import { RELATIVE_URL } from '@start9labs/shared'
|
||||||
import { DOCUMENT } from '@angular/common'
|
import { DOCUMENT } from '@angular/common'
|
||||||
import { WINDOW } from '@ng-web-apis/common'
|
import { WINDOW } from '@ng-web-apis/common'
|
||||||
|
|
||||||
@@ -11,9 +11,6 @@ import { WINDOW } from '@ng-web-apis/common'
|
|||||||
styleUrls: ['./ca-wizard.component.scss'],
|
styleUrls: ['./ca-wizard.component.scss'],
|
||||||
})
|
})
|
||||||
export class CAWizardComponent {
|
export class CAWizardComponent {
|
||||||
downloadClicked = false
|
|
||||||
instructionsClicked = false
|
|
||||||
polling = false
|
|
||||||
caTrusted = false
|
caTrusted = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -25,51 +22,27 @@ export class CAWizardComponent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (!this.config.isSecure()) {
|
await this.testHttps().catch(e =>
|
||||||
await this.testHttps().catch(e =>
|
console.warn('Failed Https connection attempt'),
|
||||||
console.warn('Failed Https connection attempt'),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
download() {
|
download() {
|
||||||
this.downloadClicked = true
|
|
||||||
this.document.getElementById('install-cert')?.click()
|
this.document.getElementById('install-cert')?.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
instructions() {
|
refresh() {
|
||||||
this.windowRef.open(
|
this.document.location.reload()
|
||||||
'https://docs.start9.com/0.3.5.x/getting-started/trust-ca/#trust-root-ca',
|
|
||||||
'_blank',
|
|
||||||
'noreferrer',
|
|
||||||
)
|
|
||||||
this.instructionsClicked = true
|
|
||||||
this.startDaemon()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async startDaemon(): Promise<void> {
|
|
||||||
this.polling = true
|
|
||||||
while (this.polling) {
|
|
||||||
try {
|
|
||||||
await this.testHttps()
|
|
||||||
this.polling = false
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed Https connection attempt')
|
|
||||||
await pauseFor(2000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
launchHttps() {
|
launchHttps() {
|
||||||
const host = this.config.getHost()
|
const host = this.config.getHost()
|
||||||
this.windowRef.open(`https://${host}`, '_blank', 'noreferrer')
|
this.windowRef.open(`https://${host}`, '_self')
|
||||||
}
|
}
|
||||||
|
|
||||||
private async testHttps() {
|
private async testHttps() {
|
||||||
const url = `https://${this.document.location.host}${this.relativeUrl}`
|
const url = `https://${this.document.location.host}${this.relativeUrl}`
|
||||||
await this.api.echo({ message: 'ping' }, url).then(() => {
|
await this.api.echo({ message: 'ping' }, url).then(() => {
|
||||||
this.downloadClicked = true
|
|
||||||
this.instructionsClicked = true
|
|
||||||
this.caTrusted = true
|
this.caTrusted = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,17 @@
|
|||||||
<ion-icon slot="start" name="warning-outline"></ion-icon>
|
<ion-icon slot="start" name="warning-outline"></ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2 style="font-weight: bold">Http detected</h2>
|
<h2 style="font-weight: bold">Http detected</h2>
|
||||||
<p style="font-weight: 600">Tor is faster over https.</p>
|
<p style="font-weight: 600">
|
||||||
|
Tor is faster over https. Your Root CA must be trusted.
|
||||||
|
<a
|
||||||
|
href="https://docs.start9.com/0.3.5.x/user-manual/trust-ca"
|
||||||
|
target="_blank"
|
||||||
|
noreferrer
|
||||||
|
style="color: black"
|
||||||
|
>
|
||||||
|
View instructions
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-button slot="end" color="light" (click)="launchHttps()">
|
<ion-button slot="end" color="light" (click)="launchHttps()">
|
||||||
Open Https
|
Open Https
|
||||||
@@ -48,7 +58,6 @@
|
|||||||
[type]="unmasked ? 'text' : 'password'"
|
[type]="unmasked ? 'text' : 'password'"
|
||||||
[(ngModel)]="password"
|
[(ngModel)]="password"
|
||||||
(ionChange)="error = ''"
|
(ionChange)="error = ''"
|
||||||
maxlength="64"
|
|
||||||
></ion-input>
|
></ion-input>
|
||||||
<ion-button
|
<ion-button
|
||||||
slot="end"
|
slot="end"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class LoginPage {
|
|||||||
|
|
||||||
launchHttps() {
|
launchHttps() {
|
||||||
const host = this.config.getHost()
|
const host = this.config.getHost()
|
||||||
this.windowRef.open(`https://${host}`, '_blank', 'noreferrer')
|
this.windowRef.open(`https://${host}`, '_self')
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.metric-note {
|
ion-note {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ export class AppShowStatusComponent {
|
|||||||
PrimaryStatus.Running,
|
PrimaryStatus.Running,
|
||||||
PrimaryStatus.Starting,
|
PrimaryStatus.Starting,
|
||||||
PrimaryStatus.Restarting,
|
PrimaryStatus.Restarting,
|
||||||
].includes(this.status.primary)
|
].includes(this.status.primary as PrimaryStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
get isStopped(): boolean {
|
get isStopped(): boolean {
|
||||||
|
|||||||
@@ -6,18 +6,25 @@
|
|||||||
[style.font-style]="style"
|
[style.font-style]="style"
|
||||||
[style.font-weight]="weight"
|
[style.font-weight]="weight"
|
||||||
>
|
>
|
||||||
<span *ngIf="!installProgress">
|
{{ (connected$ | async) ? rendering.display : 'Unknown' }}
|
||||||
{{ (connected$ | async) ? rendering.display : 'Unknown' }}
|
|
||||||
<span *ngIf="rendering.showDots" class="loading-dots"></span>
|
<span
|
||||||
|
*ngIf="
|
||||||
|
rendering.display === PR[PS.Stopping].display &&
|
||||||
|
(sigtermTimeout | durationToSeconds) > 30
|
||||||
|
"
|
||||||
|
>
|
||||||
|
this may take a while
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span *ngIf="installProgress">
|
<span *ngIf="installProgress">
|
||||||
<ion-text
|
<ion-text
|
||||||
*ngIf="installProgress | installProgressDisplay as progress"
|
*ngIf="installProgress | installProgressDisplay as progress"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
Installing
|
|
||||||
<span class="loading-dots"></span>
|
|
||||||
{{ progress }}
|
{{ progress }}
|
||||||
</ion-text>
|
</ion-text>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span *ngIf="rendering.showDots" class="loading-dots"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<h2>
|
<h2>
|
||||||
For a secure local connection and faster Tor experience,
|
For a secure local connection and faster Tor experience,
|
||||||
<a
|
<a
|
||||||
href="https://docs.start9.com/0.3.5.x/getting-started/connecting-lan"
|
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-lan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<logs
|
<logs
|
||||||
[fetchLogs]="fetchLogs()"
|
[fetchLogs]="fetchLogs()"
|
||||||
[followLogs]="followLogs()"
|
[followLogs]="followLogs()"
|
||||||
context="eos"
|
context="start-os"
|
||||||
defaultBack="system"
|
defaultBack="system"
|
||||||
pageTitle="OS Logs"
|
pageTitle="OS Logs"
|
||||||
class="ion-page"
|
class="ion-page"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { Metrics } from 'src/app/services/api/api.types'
|
import { Metrics } from 'src/app/services/api/api.types'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { TimeInfo, TimeService } from 'src/app/services/time-service'
|
import { TimeService } from 'src/app/services/time-service'
|
||||||
import {
|
import {
|
||||||
catchError,
|
catchError,
|
||||||
combineLatest,
|
combineLatest,
|
||||||
@@ -29,9 +29,24 @@ export class ServerMetricsPage {
|
|||||||
private readonly connectionService: ConnectionService,
|
private readonly connectionService: ConnectionService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private getServerData$(): Observable<[TimeInfo, Metrics]> {
|
private getServerData$(): Observable<
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: number
|
||||||
|
synced: boolean
|
||||||
|
},
|
||||||
|
{
|
||||||
|
days: number
|
||||||
|
hours: number
|
||||||
|
minutes: number
|
||||||
|
seconds: number
|
||||||
|
},
|
||||||
|
Metrics,
|
||||||
|
]
|
||||||
|
> {
|
||||||
return combineLatest([
|
return combineLatest([
|
||||||
this.timeService.getTimeInfo$(),
|
this.timeService.now$,
|
||||||
|
this.timeService.uptime$,
|
||||||
this.getMetrics$(),
|
this.getMetrics$(),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user