Compare commits
62 Commits
5d4b8eb77f
...
testing
| Author | SHA1 | Date | |
|---|---|---|---|
| d25945ac17 | |||
| 5b222292fa | |||
| 7318586e3a | |||
| 91f46d9307 | |||
| 0d2384456f | |||
| 185988d411 | |||
| f38ab53719 | |||
| f77e395635 | |||
| 5ad0584e9e | |||
| fc92aee0fe | |||
| 1f508ad264 | |||
| a3fc5e8f17 | |||
| 74ebd6339c | |||
| ac1a03ace7 | |||
| e3c0370b0e | |||
| c61a45e5d7 | |||
| bf33639749 | |||
| f186ac502a | |||
| d0e08c0f76 | |||
| a23dd9dc22 | |||
| a34232b489 | |||
| f6063fe153 | |||
| 39104ecf92 | |||
| 92e0f077a4 | |||
| 9376a87caa | |||
| 9973b8054e | |||
| faf8e9816a | |||
| f35b917362 | |||
| 873fed9fd2 | |||
| 3680768414 | |||
| 514d13d8ff | |||
| 91d90be2a9 | |||
| 8f8a8b4be4 | |||
| 35fbe3ee12 | |||
| 26441e5130 | |||
| d4af5bbdb1 | |||
| 55c51c7205 | |||
| 297df7bb2e | |||
| d3bdf02a6a | |||
| 8752e4ff16 | |||
| 5b63d890ca | |||
| aeea4d23c5 | |||
| 74e5211bee | |||
| e77174e595 | |||
| bad0e9ee84 | |||
| 49fd4aeb5a | |||
| 3c5a746413 | |||
| 568d5aa165 | |||
| f83efd001e | |||
| 6982bf70c9 | |||
| c8d496039a | |||
| 8c0e3acf20 | |||
| 44a02b36af | |||
| 1eacc23288 | |||
| 354b3a8aa8 | |||
| adbbda4a9d | |||
| 0f070e1124 | |||
| cba5cea39d | |||
| 19ae7369e3 | |||
| e3142e3032 | |||
| f989dd8f3a | |||
| 50f624e9e4 |
+1
-2
@@ -1,5 +1,4 @@
|
||||
config/
|
||||
web/ux/
|
||||
/config/
|
||||
test*
|
||||
.DS_Store
|
||||
.env
|
||||
@@ -1,5 +1,4 @@
|
||||
/config/
|
||||
web/ux/
|
||||
test*
|
||||
example*
|
||||
.DS_Store
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# 🚀 Booting into the NixOS Live Environment
|
||||
|
||||
Before Numbus can work its magic, your **target machine** needs to be running a "Live" version of NixOS. This runs entirely from your USB stick and provides the necessary building blocks Numbus needs to partition your disks and install the permanent system.
|
||||
|
||||
## 🛠️ Step 1: BIOS/UEFI Tweaks
|
||||
Modern computers have security features that might prevent a "non-standard" OS (realistically speaking, non-Windows OSes) like NixOS from booting. You may need to enter your BIOS/UEFI settings (usually by tapping `F2` or `DEL` right after power-on).
|
||||
|
||||
First, **find** your BIOS/UEFI bios key. If you're lucky, the boot screen tells you **which key to press**. Else, you can find the bios key of your device by **searching online** "your-device-brand bios key". Make you sure you press the bios key **very rapidly** and **many times** during the boot process. In last resort, if you can't find the right key, you can try **mashing all** function keys (plus `enter`, `escape` and `delete` in some cases).
|
||||
|
||||
**Next, check these settings:**
|
||||
|
||||
* **Secure Boot**: ⚠️ **Disable this.** Most Linux Live USBs will not boot with Secure Boot enabled.
|
||||
* **SATA Mode**: Ensure this is set to **AHCI**. If it is set to "RAID" or "Intel Optane," NixOS might not see your hard drives.
|
||||
* **Fast Boot**: Disable this to ensure the hardware is fully initialized at boot.
|
||||
* **Boot Mode**: Ensure it is set to **UEFI** (Numbus is designed for modern UEFI systems).
|
||||
|
||||
## ⌨️ Step 2: Accessing the Boot Menu
|
||||
Instead of changing the permanent boot order, it is easier to use the **Temporary Boot Menu**. Plug in your NixOS USB stick, turn on the machine, and repeatedly tap the Boot Menu key.
|
||||
|
||||
**Common Boot Menu Keys:**
|
||||
* **F12**: Dell, Lenovo, Toshiba.
|
||||
* **F11**: MSI, ASRock.
|
||||
* **F10**: HP.
|
||||
* **F8**: ASUS.
|
||||
* **ESC**: Sony, some HPs.
|
||||
|
||||
Same with the bios key, you can search the right key for your brand online.
|
||||
|
||||
*Once the menu appears, select your USB stick (it often says "UEFI: [USB Stick Name]").*
|
||||
|
||||
---
|
||||
|
||||
## 🍦 Step 3: Inside the Live Environment
|
||||
When the NixOS desktop or terminal appears, you might see a friendly "Install NixOS" window.
|
||||
|
||||
> **IMPORTANT :** Do **NOT** click install.
|
||||
> Numbus handles the installation entirely through the script on your **source** machine. If you install NixOS manually via the GUI, Numbus will not be able to configure your disks and secrets correctly.
|
||||
|
||||
Now we need to allow your **source machine** (the one running the script) to talk to this **target machine**.
|
||||
|
||||
1. **Set a temporary password**:
|
||||
Open a terminal in the Live Environment and type:
|
||||
```bash
|
||||
passwd
|
||||
```
|
||||
*(Enter something simple, like `nixos`. You won't need this after the installation is done.)*
|
||||
|
||||
2. **Get the IP Address**:
|
||||
Type:
|
||||
```bash
|
||||
ip a
|
||||
```
|
||||
Look for your local IP (usually starts with `192.168...` or `10.0...`). **Write this down.**
|
||||
|
||||
## 🩺 Troubleshooting: "My USB isn't working!"
|
||||
|
||||
| Issue | Solution |
|
||||
| :--- | :--- |
|
||||
| **USB doesn't show up in Boot Menu** | Try a different USB port (preferably USB 2.0 if available). Ensure the ISO was flashed in "ISO mode" (standard in BalenaEtcher). |
|
||||
| **Black screen after selecting NixOS** | Your GPU might be too new. Try the "NixOS (Nomodeset)" option in the initial boot list. |
|
||||
| **NixOS boots, but no Internet** | If using Wi-Fi, click the network icon in the taskbar to connect. If Ethernet isn't working, check that the cable is fully seated. |
|
||||
| **"Access Denied" or "Security Policy"** | Secure Boot is likely still enabled in your BIOS. |
|
||||
|||
|
||||
|
||||
### 🏁 Ready to go?
|
||||
Once you have the **IP Address** and have set the **Password**, return to your **Source Machine** and run the Numbus deployment script.
|
||||
|
||||
**[Choose your Numbus Device →](device_choice.md)**
|
||||
@@ -0,0 +1,16 @@
|
||||
# Creating a NixOS USB stick
|
||||
|
||||
### 📀 Flashing process
|
||||
|
||||
To deploy Numbus, you need a USB drive with NixOS software on it: a **NixOS Live USB stick**.
|
||||
|
||||
1. First download a NixOS iso file at the [official NixOS website](https://nixos.org/download/). Choose the the **graphical** image under the Linux distibution option. **Intel/AMD** and **ARM** refer to the architecture or the device you plan to install NixOS on. **If you don't know** if your device is AMD or ARM, it is **most likely** AMD.
|
||||
2. Now choose an **empty** USB drive. <br> **EVERYTHING ON THE USB DRIVE WILL BE WIPED**. <br> Make sure you **backup** anything important.
|
||||
3. Download [Balena Etcher](https://etcher.balena.io/#download-etcher) for your OS. Once in the software, select the NixOS image you downloaded and **flash the USB stick**.
|
||||
4. **Congrats !** You can now proceed to the [installation process](installation.md).
|
||||
|
||||
### 🚀 Next Steps
|
||||
|
||||
**[Live environment Guide →](booting_live_environment.md)**
|
||||
|
||||
**[Choose your Numbus Device →](device_choice.md)**
|
||||
@@ -0,0 +1,98 @@
|
||||
# 🧭 Choosing Your Numbus Device
|
||||
|
||||
The Numbus ecosystem is built upon **NixOS**, providing a suite of configurations designed to be **secure**, highly **reliable**, **private**, and **automated**. While they share a common DNA, each device is optimized for a **specific role** in your digital life.
|
||||
|
||||
**Auto updates** are enabled for **all the devices** by default in order to **minimize maintenance efforts** and keep your systems secure.
|
||||
|
||||
**[Install Numbus Server →](server/index.md)**
|
||||
|
||||
**[Install Numbus Backup Server →](backup/index.md)**
|
||||
|
||||
**[Install Numbus Workstation →](workstation/index.md)**
|
||||
|
||||
**[Install Numbus TV →](tv/index.md)**
|
||||
|
||||
**[Install Numbus Game Console →](console/index.md)**
|
||||
|
||||
## 🏗️ Numbus Server: The Infrastructure
|
||||
|
||||
The **Numbus Server** is the core of the ecosystem. It is designed to host your private **cloud** and **smart home** services with professional-grade reliability.
|
||||
|
||||
### Who is it for?
|
||||
**Individuals**, **families**, and small **enterprises** who want to own their data without the complexity of **traditional server management**.
|
||||
|
||||
### Key Capabilities:
|
||||
* **Web Service Orchestration:** Pre-configured for a vast array of services including:
|
||||
* **Privacy & Ads:** Pi-Hole, AdGuard Home.
|
||||
* **Cloud & Productivity:** Nextcloud, Passbolt, N8N, Immich.
|
||||
* **Monitoring & Tools:** Uptime Kuma, Dashy, Netbird.
|
||||
* **Smart Home:** Home-Assistant, Frigate.
|
||||
* **Entertainment:** Jellyfin, Crafty.
|
||||
* **Automated Infrastructure:**
|
||||
* **SSL/TLS Management:** Traefik handles automatic ACME certificate generation for your domain.
|
||||
* **System Health Alerts:** Automated email notifications (via SMTP) for systemd service failures or disk health issues.
|
||||
* **Monitoring:** Access a sleek **Cockpit** web interface for real-time monitoring and health checks.
|
||||
* **Security & Reliability:**
|
||||
* **Hardening:** Includes ClamAV antivirus and rootless Podman containers.
|
||||
* **Virtualization:** Full QEMU/KVM capabilities for running VMs and specialized workloads.
|
||||
* **Data Protection:** Flexible RAID configurations (utilizing technologies like SnapRAID and Disko) to ensure data integrity.
|
||||
* **GitOps Workflow:** Designed for a "Configuration-as-Code" approach. Secrets are encrypted with `sops-nix` and the entire setup can be managed via a Git repository, allowing for **easy updates** and rollbacks.
|
||||
|
||||
**[Install Numbus Server →](server/index.md)**
|
||||
|
||||
## 🛡️ Numbus Backup Server: The Guardian
|
||||
|
||||
The **Backup Server** is a specialized node focused on data preservation and network watchdog duties.
|
||||
|
||||
### Use Case:
|
||||
**Automated**, **high-efficiency** protection. It works silently to pull backups from your other Numbus devices and **alerts** you the moment critical network devices **go offline**.
|
||||
|
||||
### Key Features:
|
||||
* **Data Receiver:** Efficiently handles backup data pushed from Numbus clients based on custom triggers.
|
||||
* **Buddy Backup:** Offers collaborative backup solutions to ensure data redundancy across trusted nodes.
|
||||
* **Infrastructure Watchdog:** Integrates into the Numbus mesh network (powered by **Netbird**) to monitor and report on the health of your entire infrastructure.
|
||||
|
||||
**[Install Numbus Backup Server →](backup/index.md)**
|
||||
|
||||
## 💻 Numbus Workstation: The Computer
|
||||
|
||||
A **privacy-respecting** workstation built for creation and work, **removing** the corporate telemetry and "**bloat**" found in traditional operating systems.
|
||||
|
||||
### Use Case:
|
||||
A **daily driver** for developers, creators, and enthusiasts who value a **sleek interface** backed by an immutable, reproducible system.
|
||||
|
||||
### Key Features:
|
||||
* **Interface Choices:** GNOME, KDE Plasma, or XFCE for older hardware.
|
||||
* **Seamless Installation:** Automatic detection and configuration of **proprietary drivers** (including NVIDIA).
|
||||
* **User-Friendly Management:** Includes a dedicated **Driver Manager** for hardware control and a curated **App Store** to install applications without ever touching the terminal.
|
||||
* **Software Freedom:** Combines the reliability of NixOS with the ease of modern app management.
|
||||
|
||||
**[Install Numbus Workstation →](workstation/index.md)**
|
||||
|
||||
## 📺 Numbus TV: The Experience
|
||||
|
||||
The **Numbus TV** configuration reclaims the living room from "Smart" TVs that serve as surveillance and advertising billboards.
|
||||
|
||||
### Use Case:
|
||||
A premium **cinematic** portal for your big screen, offering a clean, **ad-free** interface.
|
||||
|
||||
### Key Features:
|
||||
* **KDE Plasma Bigscreen:** Optimized for remote control navigation.
|
||||
* **Web App Integration:** Turn your favorite streaming sites into native-feeling applications.
|
||||
|
||||
**[Install Numbus TV →](tv/index.md)**
|
||||
|
||||
## 🎮 Numbus Game Console: The Entertainment
|
||||
|
||||
A dedicated **gaming environment** built to deliver a console-like experience on **standard PC** hardware.
|
||||
|
||||
### Use Case:
|
||||
Transforming **any PC** into a dedicated gaming station for the living room.
|
||||
|
||||
### Key Features:
|
||||
* **Steam Bigscreen Base:** The system starts **directly** into bigscreen mode, requiring **no mouse or keyboard** and allowing for **controller only** interaction.
|
||||
* **Automated Maintenance:** The console handles **auto updates** of both your **games** and the underlying **nix packages** automatically.
|
||||
* **Proton Integration:** High compatibility with Windows-based games via Steam.
|
||||
* **Privacy-First Gaming:** No forced accounts or telemetry reporting your play habits to manufacturers.
|
||||
|
||||
**[Install Numbus Game Console →](console/index.md)**
|
||||
@@ -0,0 +1,114 @@
|
||||
# ⚙️ Running NixOS on another OS
|
||||
|
||||
To deploy a Numbus system to your target machine, your **source machine** (the one you are typing on) needs to run NixOS. This is because the deployment script builds the entire system configuration locally before pushing it to the target.
|
||||
|
||||
If you aren't running NixOS yet, don't worry! You don't need to overwrite your current OS. You can run NixOS in a **Virtual Machine**, from a **Live USB**, or use a **Remote Bridge**.
|
||||
|
||||
## ⚠️ The Architecture Challenge
|
||||
|
||||
Before choosing a method, **you must** check your CPU architecture on **both source and target machine** :
|
||||
|
||||
* On Linux, launch a terminal and paste `uname -m`.
|
||||
* On Windows, launch a terminal and paste `echo $env:PROCESSOR_ARCHITECTURE`.
|
||||
* On Mac, click on the Apple logo and on `About this Mac`. If your processor is M series, your CPU architecture is ARM . If you have an Intel processor, your CPU architecture is AMD.
|
||||
|
||||
**Now, two possibilities arise :**
|
||||
|
||||
* The architectures **match**. Proceed to the [methods step](#-choose-your-current-operating-system).
|
||||
* They **don't**. You will have to use a [Bridge system](#the-ssh-bridge-system-) or use [Emulation]().
|
||||
|
||||
## 🎯 Choose your current Operating System:
|
||||
|
||||
**Before anything**, if you **don't want** to install software on your daily driver computer or mess around with **Live NixOS** bootable USB drives, follow [Bridge system](#the-ssh-bridge-system-) instead.
|
||||
|
||||
| [🐧 Linux](#-linux) | [🪟 Windows](#-windows) | [🍎 MacOS](#-macos) |
|
||||
| :---: | :---: | :---: |
|
||||
|
||||
## 🐧 Linux
|
||||
|
||||
### Option 1 : 📦 Libvirt / KVM (Recommended)
|
||||
Using a Virtual Machine (VM) is the most efficient method. It allows you to keep your browser open for instructions on your main OS while running the deployment script inside the NixOS guest.
|
||||
|
||||
1. **Install Virtualization Tools**:
|
||||
* **Debian / Ubuntu / Mint**:
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
|
||||
sudo systemctl enable --now libvirtd
|
||||
```
|
||||
* **Fedora / RHEL / Nobara**:
|
||||
```bash
|
||||
sudo dnf update
|
||||
sudo dnf install @virtualization
|
||||
sudo systemctl enable --now libvirtd
|
||||
```
|
||||
* **Arch / Manjaro / EndeavourOS**:
|
||||
```bash
|
||||
sudo pacman -Syu
|
||||
sudo pacman -S qemu-desktop virt-manager libvirt dnsmasq
|
||||
sudo systemctl enable --now libvirtd
|
||||
```
|
||||
|
||||
2. **Download the ISO**: Grab the [NixOS Graphical ISO](https://nixos.org/download). Ensure the ISO architecture matches your CPU.
|
||||
3. **Boot & Go**: No need to install the OS. Run it as a "Live" environment, open a terminal, and clone the Numbus repo.
|
||||
|
||||
### Option 2 : 🍦 Live USB (Advanced users)
|
||||
If you are already on a Linux machine, the simplest way to get a native NixOS environment is to boot from a USB. This option offers best performance.
|
||||
|
||||
1. [Follow this guide to create one](creating_a_nixos_live_usb.md).
|
||||
2. Boot your computer from the USB stick.
|
||||
3. **Do NOT install**: Close the installation window. You are now in a **Live NixOS environment**. Your existing files and OS remain untouched. 🛡️
|
||||
|
||||
## 🪟 Windows
|
||||
|
||||
### Option 1 : 💻 VirtualBox (Recommended)
|
||||
1. Download and install [VirtualBox](https://www.virtualbox.org/wiki/Downloads).
|
||||
2. Grab the [NixOS Graphical ISO](https://nixos.org/download) with the correct architecture.
|
||||
3. Create a **new VM** using the NixOS Graphical ISO. Feel free to add more RAM or CPU cores or leave defaults. **2 vCPUs** and **4GB of RAM** is plenty enough.
|
||||
4. Start the VM. **Close the NixOS installer** window inside the VM.
|
||||
5. **Great job !** You can now continue the **Numbus** installation by following the [installation instructions](#-next-step).
|
||||
|
||||
### Option 2 : 🐳 WSL2 (Advanced users)
|
||||
For those who prefer the terminal:
|
||||
1. **Install WSL** on Windows if not already installed.
|
||||
2. Get the **NixOS distribution**. This provides a NixOS environment directly within your Windows terminal.
|
||||
3. **Great job !** You can now continue the **Numbus** installation by following the [installation instructions](#-next-step).
|
||||
|
||||
## 🍎 MacOS
|
||||
|
||||
### 🐎 UTM
|
||||
UTM is the standard for running Linux on modern Macs.
|
||||
|
||||
1. Download and install [UTM](https://mac.getutm.app/).
|
||||
2. Grab the [NixOS Graphical ISO](https://nixos.org/download) with the correct architecture.
|
||||
3. Create a **new VM** using the NixOS Graphical ISO. Select **Virtualize** if both PC's architectures match and **Emulate** if they don't (refer to this [warning](#️-the-architecture-challenge)). Feel free to add more RAM or CPU cores or leave defaults. **2 vCPUs** and **4GB of RAM** is plenty enough.
|
||||
4. Start the VM. **Close the NixOS installer** window inside the VM.
|
||||
5. **Great job !** You can now continue the **Numbus** installation by following the [installation instructions](#-next-step).
|
||||
|
||||
## The SSH "Bridge system" 🚀
|
||||
|
||||
This method **avoids** architecture mismatches, **eliminates performance problems** and allows you **not to mess** with your daily driver machine.
|
||||
|
||||
This involves using a third machine that **matches the server's architecture** as a "middle-man".
|
||||
|
||||
1. Your server is **AMD** : You're in luck ! **Any old computer** from the last decade lying around will do the trick, even low power ones.
|
||||
2. Your server is **ARM** : This might be **more challenging** to find an ARM device. You can resort to [Emulation](#-choose-your-current-operating-system). For that you have to use a VM.
|
||||
|
||||
**Scenario:** You have an ARM MacBook or laptop, but you are deploying to an x86_64 Server.
|
||||
|
||||
1. Find an old Intel or AMD laptop or PC.
|
||||
2. Boot that old PC with a **NixOS Live USB**. [Follow the guide to create one](creating_a_nixos_live_usb.md).
|
||||
3. On the NixOS machine, close the installation prompt. Launch a terminal and set a password using the `passwd` command. Then get the IP using the `ip a` command.
|
||||
4. On your **MacBook or ARM laptop**, open your terminal and SSH into the old PC:
|
||||
```bash
|
||||
ssh nixos@<obtained-ip-address>
|
||||
```
|
||||
5. **Result**: you are now using your comfortable laptop keyboard and screen, but the actual NixOS build processes are running natively (and fast!) on the bridge machine.
|
||||
|
||||
### 🚀 Next Steps
|
||||
|
||||
**[NixOS bootable USB Guide →](creating_live_usb)**
|
||||
|
||||
**[Live environment Guide →](booting_live_environment.md)**
|
||||
|
||||
**[Choose your Numbus Device →](device_choice.md)**
|
||||
@@ -1,19 +0,0 @@
|
||||
# Numbus Server documentation
|
||||
|
||||
## ✏️ Filling the configuration file
|
||||
|
||||
You can deploy the numbus-server without using a configuration file, **but I would strongly advise it** as it diminishes the risk of **typos** when providing credentials. It also creates a file that you can **keep**, **reuse**, **consult** whenever you are **in doubt** about a certain setting.
|
||||
|
||||
> 🚀 Let's fill this configuration file !
|
||||
|
||||
The configuration file is divided into **multiple categories**. Some of them are **optional**, some are **mandatory**. Here is the list of categories :
|
||||
|
||||
| Category | Available variables |
|
||||
| -------- | ------------------ |
|
||||
| [Live target settings](./live_target.md) | 2 |
|
||||
| [Server settings](/.server.md) | 7 |
|
||||
| [Mail settings](./mail.md) | 4 |
|
||||
| [Traefik settings](./automatic_ssl_certs.md) | 1 |
|
||||
| [Network settings](./network.md) | 3 |
|
||||
| [Services selection](./services/index.md) | 5 |
|
||||
| [Script settings](./script.md) | 1 |
|
||||
@@ -1,31 +0,0 @@
|
||||
# Requirements
|
||||
|
||||
### To deploy
|
||||
|
||||
To deploy a numbus-backup-server, you will need :
|
||||
|
||||
* A **live NixOS** bootable USB disk.
|
||||
|
||||
You will make the **target** machine **boot** into the NixOS live environment using this **USB stick**. Download the [NixOS iso](https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso) image.
|
||||
|
||||
*On Linux* : Flash it using [Impression (flatpak)](https://flathub.org/en/apps/io.gitlab.adhami3310.Impression) or [BalenaEtcher (AppImage)](https://etcher.balena.io/#download-etcher).
|
||||
|
||||
*On MacOS* : Flash it using [BalenaEtcher](https://etcher.balena.io/#download-etcher).
|
||||
|
||||
*On Windows* : Flash it using [Rufus](https://rufus.ie/en/#download) or [BalenaEtcher](https://etcher.balena.io/#download-etcher).
|
||||
|
||||
* **Source** Machine:
|
||||
|
||||
Can be any machine with **Nix installed**, e.g. a **NixOS** machine.
|
||||
|
||||
* **Target** Machine:
|
||||
|
||||
Can be **any computer** (desktop, SFF, tiny/mini/micro, even a laptop). It could be a dedicated server that you bought or just some computer that you decided to repurpose into a backup server.
|
||||
|
||||
* **Network connection** between the source and the target machine.
|
||||
|
||||
---
|
||||
|
||||
### Next step
|
||||
|
||||
[Configuration](./configuration/index.md)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Numbus Server
|
||||
|
||||
### ✔️ Check the requirements
|
||||
|
||||
- A source machine with NixOS installed either bare-metal or in a VM, or in the NixOS Live environment.
|
||||
- A target machine booted in the NixOS Live environment.
|
||||
|
||||
### 🛠️ Proceed to the installation
|
||||
|
||||
@@ -1,81 +1,127 @@
|
||||
{
|
||||
description = "Numbus - Simplified NixOS deployments";
|
||||
description = "Numbus - The Open Ecosystem";
|
||||
|
||||
inputs = {
|
||||
# Nix unstable packages
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
# Nix stable packages
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||
# Numbus
|
||||
numbus.url = "https://gittea.dev/numbus/numbus";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
|
||||
|
||||
# Nix unstable packages
|
||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
# Disko
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
# Sops-nix
|
||||
sops-nix.url = "github:Mic92/sops-nix";
|
||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
# Power savings
|
||||
autoaspm.url = "git+https://git.notthebe.ee/notthebee/AutoASPM";
|
||||
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
# Flatpaks
|
||||
nix-flatpak.url = "github:gmodena/nix-flatpak";
|
||||
flatpkgs.url = "github:gmodena/nix-flatpak";
|
||||
flatpkgs.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, ... }@inputs:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
# Helper for defining systems
|
||||
mkNumbus = { deviceModule, extraModules ? [], nixpkgsRef, deviceType }: nixpkgsRef.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs deviceType; };
|
||||
modules = [
|
||||
inputs.disko.nixosModules.disko
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
self.nixosModules.common
|
||||
deviceModule
|
||||
] ++ extraModules;
|
||||
outputs = { self, nixpkgs, nixpkgs-unstable, flatpkgs, disko, sops-nix, autoaspm, ... }@inputs:
|
||||
{
|
||||
nixosModules = {
|
||||
numbus-backup = {
|
||||
imports = [
|
||||
./modules/backup/default.nix
|
||||
sops-nix.nixosModules.sops-nix
|
||||
autoaspm.nixosModules.autoaspm
|
||||
];
|
||||
};
|
||||
in {
|
||||
nixosModules = {
|
||||
common = ./modules/common/default.nix;
|
||||
server = ./modules/server/default.nix;
|
||||
backup = ./modules/backup/default.nix;
|
||||
console = ./modules/console/default.nix;
|
||||
computer = ./modules/computer/default.nix;
|
||||
tv = ./modules/tv/default.nix;
|
||||
numbus-common = {
|
||||
imports = [
|
||||
./modules/common/default.nix
|
||||
disko.nixosModules.disko
|
||||
];
|
||||
};
|
||||
|
||||
nixosConfigurations = {
|
||||
numbus-server = mkNumbus {
|
||||
deviceModule = self.nixosModules.server;
|
||||
nixpkgsRef = inputs.nixpkgs-stable;
|
||||
deviceType = "server";
|
||||
};
|
||||
|
||||
numbus-backup = mkNumbus {
|
||||
deviceModule = self.nixosModules.backup;
|
||||
nixpkgsRef = inputs.nixpkgs-stable;
|
||||
deviceType = "backup";
|
||||
};
|
||||
|
||||
numbus-computer = mkNumbus {
|
||||
deviceModule = self.nixosModules.computer;
|
||||
extraModules = [ inputs.nix-flatpak.nixosModules.nix-flatpak ];
|
||||
nixpkgsRef = inputs.nixpkgs;
|
||||
deviceType = "computer";
|
||||
};
|
||||
|
||||
numbus-console = mkNumbus {
|
||||
deviceModule = self.nixosModules.console;
|
||||
nixpkgsRef = inputs.nixpkgs;
|
||||
deviceType = "console";
|
||||
};
|
||||
|
||||
numbus-tv = mkNumbus {
|
||||
deviceModule = self.nixosModules.tv;
|
||||
nixpkgsRef = inputs.nixpkgs;
|
||||
extraModules = [ inputs.nix-flatpak.nixosModules.nix-flatpak ];
|
||||
deviceType = "tv";
|
||||
};
|
||||
numbus-console = {
|
||||
imports = [
|
||||
./modules/console/default.nix
|
||||
flatpkgs.nixosModules.nix-flatpak
|
||||
];
|
||||
};
|
||||
numbus = {
|
||||
imports = [
|
||||
./modules/backup/default.nix
|
||||
./modules/common/default.nix
|
||||
./modules/console/default.nix
|
||||
./modules/server/default.nix
|
||||
./modules/tv/default.nix
|
||||
./modules/workstation/default.nix
|
||||
disko.nixosModules.disko
|
||||
sops-nix.nixosModules.sops-nix
|
||||
autoaspm.nixosModules.autoaspm
|
||||
flatpkgs.nixosModules.nix-flatpak
|
||||
];
|
||||
};
|
||||
numbus-server = {
|
||||
imports = [
|
||||
./modules/server/default.nix
|
||||
sops-nix.nixosModules.sops-nix
|
||||
autoaspm.nixosModules.autoaspm
|
||||
];
|
||||
};
|
||||
numbus-tv = {
|
||||
imports = [
|
||||
./modules/tv/default.nix
|
||||
sops-nix.nixosModules.sops-nix
|
||||
autoaspm.nixosModules.autoaspm
|
||||
flatpkgs.nixosModules.nix-flatpak
|
||||
];
|
||||
};
|
||||
numbus-workstation = {
|
||||
imports = [
|
||||
./modules/workstation/default.nix
|
||||
sops-nix.nixosModules.sops-nix
|
||||
autoaspm.nixosModules.autoaspm
|
||||
flatpkgs.nixosModules.nix-flatpak
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
nixosConfigurations = {
|
||||
numbus-backup = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModules.numbus-server
|
||||
self.nixosModules.numbus-common
|
||||
];
|
||||
};
|
||||
numbus-console = nixpkgs-unstable.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModules.numbus-console
|
||||
self.nixosModules.numbus-common
|
||||
];
|
||||
};
|
||||
numbus-server = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModules.numbus-server
|
||||
self.nixosModules.numbus-common
|
||||
];
|
||||
};
|
||||
numbus-tv = nixpkgs-unstable.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModules.numbus-tv
|
||||
self.nixosModules.numbus-common
|
||||
];
|
||||
};
|
||||
numbus-workstation = nixpkgs-unstable.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModules.numbus-workstation
|
||||
self.nixosModules.numbus-common
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{ system.autoUpgrade = {
|
||||
enable = true;
|
||||
allowReboot = false;
|
||||
flake = "path:/etc/nixos/";
|
||||
flags = [
|
||||
"--recreate-lock-file"
|
||||
"--print-build-logs"
|
||||
];
|
||||
dates = "03:00";
|
||||
randomizedDelaySec = "45min";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
system.autoUpgrade = {
|
||||
enable = true;
|
||||
allowReboot = false;
|
||||
flake = "path:/etc/nixos/";
|
||||
flags = [
|
||||
"--recreate-lock-file"
|
||||
"--print-build-logs"
|
||||
];
|
||||
dates = "03:00";
|
||||
randomizedDelaySec = "45min";
|
||||
};
|
||||
}
|
||||
+242
-151
@@ -5,15 +5,16 @@
|
||||
|
||||
# --- UTILITY FUNCTIONS --->
|
||||
echod() {
|
||||
MESSAGE=${1}
|
||||
local MESSAGE="${1}"
|
||||
|
||||
if [[ ${DEBUG} -eq 1 ]]; then
|
||||
echo -e ${MESSAGE}
|
||||
echo -e "${MESSAGE}"
|
||||
fi
|
||||
}
|
||||
|
||||
ssh_to_host() {
|
||||
local COMMAND="${1}"
|
||||
|
||||
ssh -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" "${COMMAND}"
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@ get_valid_input() {
|
||||
fi
|
||||
|
||||
while true; do
|
||||
local INPUT=$(gum input --header "${HEADER}" --prompt "${PROMPT}" --placeholder "${PLACEHOLDER}")
|
||||
local INPUT=$(gum input --header "${HEADER}" --prompt "${PROMPT}" --placeholder "${PLACEHOLDER}" --password="${SENSITIVE}")
|
||||
|
||||
# Handle empty input
|
||||
if [[ -z "${INPUT}" ]]; then
|
||||
@@ -59,90 +60,215 @@ get_valid_input() {
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
format_numbus_json() {
|
||||
local KEY="${1}"
|
||||
local TYPE="${2}"
|
||||
local VALUE="${3}"
|
||||
local TMP_FILE="${INSTALL_DIR}/tmp/numbus-generated.json"
|
||||
local FINAL_FILE="${TMP_EXTRA_PATH}/etc/nixos/numbus-generated.json"
|
||||
local JQ_VALUE
|
||||
|
||||
case "$TYPE" in
|
||||
bool) JQ_VALUE="$VALUE" ;;
|
||||
num) JQ_VALUE="$VALUE" ;;
|
||||
string) JQ_VALUE=$(jq -Rn --arg v "$VALUE" '$v') ;;
|
||||
json) JQ_VALUE="$VALUE" ;;
|
||||
*) echo "unknown type: $TYPE" >&2; return 1 ;;
|
||||
esac
|
||||
|
||||
jq --arg k "$KEY" --argjson v "$JQ_VALUE" '
|
||||
def setpathdot($k; $v):
|
||||
setpath(($k | split(".")); $v);
|
||||
|
||||
. as $root
|
||||
| setpathdot($k; $v)
|
||||
' "${FINAL_FILE}" > "${TMP_FILE}" && mv "${TMP_FILE}" "${FINAL_FILE}"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ ${DEBUG} -eq 1 ]]; then
|
||||
echo -e "\n ✅ Exiting..."
|
||||
echo -e "\n ✅ Debug mode is enabled. Clean up manually files located at ${INSTALL_DIR} or reboot."
|
||||
else
|
||||
echo -e "\n ✅ Cleaning up..."
|
||||
rm -${DIR_RM_FLAGS} "${INSTALL_DIR}"
|
||||
fi
|
||||
|
||||
if [[ -n "${BRIDGE_PID:-}" ]] && ps -p ${BRIDGE_PID} >> "${STDOUT}" 2>> "${STDERR}"; then
|
||||
kill ${BRIDGE_PID}
|
||||
fi
|
||||
|
||||
echo -e "\n 🌟 Thanks for using Numbus, consider supporting the project !"
|
||||
}
|
||||
# --- UTILITY FUNCTIONS ---<
|
||||
|
||||
|
||||
|
||||
# --- GLOBAL FUNCTIONS --->
|
||||
cleanup() {
|
||||
echo -e "\n ✅ Cleaning up..."
|
||||
launch_gui() {
|
||||
echo -e "\n 🚀 Launching Numbus Configurator..."
|
||||
echo -e " ➡️ You will now proceed to the configuration of your device through your browser"
|
||||
|
||||
rm -${DIR_RM_FLAGS} "/run/user/$(id -u)/numbus-installer"
|
||||
python3 "${BRIDGE_SCRIPT}" >> "${STDOUT}" 2>> "${STDERR}" &
|
||||
export BRIDGE_PID=$!
|
||||
|
||||
if [[ -n "${BRIDGE_PID:-}" ]] && ps -p ${BRIDGE_PID} > /dev/null; then
|
||||
kill ${BRIDGE_PID}
|
||||
fi
|
||||
local START_URL="http://localhost:${WEBSERVER_PORT}/pages/index.html"
|
||||
|
||||
xdg-open "${START_URL}" >> "${STDOUT}" 2>> "${STDERR}" || open "${START_URL}" >> "${STDOUT}" 2>> "${STDERR}" || true
|
||||
|
||||
sleep 10
|
||||
|
||||
echo -e "\n ⚠️ If it doesn't automatically, open your browser at: $(gum style "${START_URL}")"
|
||||
}
|
||||
|
||||
hierarchy_preparation() {
|
||||
echod "\n 🔄 Preparing the folder hierarchy for the final configuration..."
|
||||
echod "\n 🔄 Preparing the folder hierarchy for the final configuration...\n"
|
||||
|
||||
# Extra files folders
|
||||
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/
|
||||
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/var/lib/sops-nix/
|
||||
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/disks
|
||||
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/system
|
||||
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/podman
|
||||
mkdir -${MKDIR_FLAGS} "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/"
|
||||
mkdir -${MKDIR_FLAGS} "${TMP_EXTRA_PATH}/var/lib/sops-nix/"
|
||||
mkdir -${MKDIR_FLAGS} "${TMP_EXTRA_PATH}/etc/nixos/secrets/disks"
|
||||
mkdir -${MKDIR_FLAGS} "${TMP_EXTRA_PATH}/etc/nixos/secrets/system"
|
||||
mkdir -${MKDIR_FLAGS} "${TMP_EXTRA_PATH}/etc/nixos/secrets/podman"
|
||||
|
||||
echod "\n ✅ Folder hierarchy ready"
|
||||
}
|
||||
|
||||
hardware_detection() {
|
||||
local TMPFILE="/tmp/nixos-installation-hw-detection.json"
|
||||
setup_ssh() {
|
||||
edit_var() {
|
||||
echo -e "${1}"
|
||||
echo -e " Please check the credentials provided in the configuration."
|
||||
echo -e "\n ➡️ Here are the current settings :
|
||||
Target IP address : $(gum style --italic "\"${LIVE_TARGET_IP}\"")
|
||||
Target password : $(gum style --italic "\"${LIVE_TARGET_PASSWORD}\"")"
|
||||
gum confirm "Are these correct ?" || {
|
||||
get_valid_input "LIVE_TARGET_IP" " ➡️ Provide the IP address of your machine in a NixOS live environment :" "192.168.1.100" "${IP_REGEX}";
|
||||
get_valid_input "LIVE_TARGET_PASSWORD" " ➡️ Provide the password of your machine in a NixOS live environment :" "password" "" "true" "true";
|
||||
return 0;
|
||||
}
|
||||
gum confirm "Retry connection ?" || {
|
||||
echo -e "\n ❌ Host unreachable or connection refused.";
|
||||
exit 226;
|
||||
}
|
||||
}
|
||||
|
||||
ssh_to_host "nix-shell -p jq pciutils usbutils smartmontools iproute2 --run 'bash -s'" << SSHEND
|
||||
set -euo pipefail
|
||||
echod "\n ➡️ Generating new SSH key for numbus-admin..."
|
||||
|
||||
# --- Initialize Global JSON Output ---
|
||||
HW_REPORT=\$(jq -n '{}')
|
||||
chmod 700 "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/"
|
||||
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-${DEVICE_TYPE}" -f "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" -N "" -q
|
||||
|
||||
# --- Helper: Add JSON array to the main report ---
|
||||
append_to_report() {
|
||||
local key="\$1"
|
||||
local json_array="\$2"
|
||||
HW_REPORT=\$(echo "\$HW_REPORT" | jq --argjson arr "\$json_array" --arg k "\$key" '.[\$k] = \$arr')
|
||||
echod "\n ➡️ Copying SSH key to target host '${TARGET_USER}@${LIVE_TARGET_IP}'..."
|
||||
|
||||
while true; do
|
||||
if sshpass -p "${LIVE_TARGET_PASSWORD}" ssh-copy-id -o StrictHostKeyChecking=no -o ConnectTimeout=10 -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" >> "${STDOUT}" 2>> "${STDERR}"; then
|
||||
echod "\n ✅ SSH key copied successfully"
|
||||
return 0
|
||||
else
|
||||
local EXIT_CODE=$?
|
||||
if [[ ${EXIT_CODE} -eq 5 ]]; then
|
||||
edit_var "\n ❌ Invalid password for ${TARGET_USER}@${LIVE_TARGET_IP}."
|
||||
elif ! ping -c 2 "${LIVE_TARGET_IP}" >> "${STDOUT}" 2>> "${STDERR}"; then
|
||||
edit_var "\n ❌ The IP address you specified cannot be reached."
|
||||
elif ssh-keygen -F "${LIVE_TARGET_IP}" >> "${STDOUT}" 2>> "${STDERR}"; then
|
||||
echo -e "\n ⚠️ The SSH fingerprint for the selected IP address $(gum style --italic "\"${LIVE_TARGET_IP}\"") is not the same as the one in $(gum style --italic "\".ssh/known_hosts\"").
|
||||
|
||||
This could occur for multiple reasons :
|
||||
- You ran this script multiple times
|
||||
- Your live machine uses an IP address that was used by another devices you SSHed in
|
||||
- You are under a Man-In-The-Middle attack
|
||||
- Other
|
||||
|
||||
The script $(gum style --bold "cannot continue") without the correct fingerprint installed.
|
||||
If you are unsure, it is always better to check manually.\n"
|
||||
|
||||
gum confirm "Remove the old fingerprint and accept the new one ?" || {
|
||||
echo -e "\n ❌ SSH fingerprints don't match.";
|
||||
exit 22;
|
||||
}
|
||||
ssh-keygen -R "${LIVE_TARGET_IP}" >> "${STDOUT}" 2>> "${STDERR}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
hardware_detection() {
|
||||
local TMPFILE="/run/user/1000/numbus-installer/hw_detection.json"
|
||||
|
||||
ssh_to_host "nix-shell -p jq pciutils usbutils smartmontools iproute2 --run 'bash -s'" << SSHEND >> "${STDOUT}" 2>> "${STDERR}"
|
||||
set -euo pipefail
|
||||
|
||||
HW_REPORT=\$(jq -n '{}')
|
||||
|
||||
append_to_report() {
|
||||
local key="\${1}"
|
||||
local json_array="\${2}"
|
||||
HW_REPORT=\$(echo "\${HW_REPORT}" | jq --argjson arr "\${json_array}" --arg k "\${key}" '.[\${k}] = \${arr}')
|
||||
}
|
||||
|
||||
# --- 1. Detect Graphics ---
|
||||
detect_graphics() {
|
||||
local gpus="[]"
|
||||
|
||||
# Iterate over all VGA and 3D controllers
|
||||
while read -r pci_addr; do
|
||||
local brand="Unknown"
|
||||
|
||||
while read -r line; do
|
||||
[[ -z "\${line}" ]] && continue
|
||||
local brand="unknown"
|
||||
local renderer="none"
|
||||
local product="unknown"
|
||||
local integrated="false"
|
||||
local renderer=""
|
||||
local pci_addr="none"
|
||||
|
||||
# Determine Brand
|
||||
local vendor_info
|
||||
vendor_info=\$(lspci -vms "\$pci_addr" | grep -i "^Vendor:" || true)
|
||||
if echo "\$vendor_info" | grep -iq "intel"; then
|
||||
brand="Intel"
|
||||
integrated="true" # General heuristic for Intel
|
||||
elif echo "\$vendor_info" | grep -iq "amd"; then
|
||||
brand="AMD"
|
||||
# If boot_vga is 1, it's likely the primary/integrated APU
|
||||
if [[ -f "/sys/bus/pci/devices/0000:\$pci_addr/boot_vga" ]] && grep -q "1" "/sys/bus/pci/devices/0000:\$pci_addr/boot_vga"; then
|
||||
integrated="true"
|
||||
# Extract PCI address
|
||||
pci_addr="\$(echo "\${line}" | cut -d' ' -f1)"
|
||||
|
||||
# Brand
|
||||
for b in Intel AMD NVIDIA; do
|
||||
if echo "\${line}" | grep -iq "\${b}"; then
|
||||
brand="\${b}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Renderer
|
||||
if [[ -d "/dev/dri/by-path" ]]; then
|
||||
local render_node=\$(ls /dev/dri/by-path | grep "\${pci_addr}" | grep "render" | head -n1 || true)
|
||||
if [[ -n "\${render_node}" ]]; then
|
||||
renderer=\$(basename "\$(readlink -f "/dev/dri/by-path/\${render_node}")")
|
||||
fi
|
||||
elif echo "\$vendor_info" | grep -iq "nvidia"; then
|
||||
brand="NVIDIA"
|
||||
integrated="false" # NVIDIA is almost always discrete in these contexts
|
||||
fi
|
||||
|
||||
# Find Renderer (e.g., renderD128)
|
||||
if [[ -d "/sys/bus/pci/devices/0000:\$pci_addr/drm" ]]; then
|
||||
renderer=\$(find "/sys/bus/pci/devices/0000:\$pci_addr/drm" -maxdepth 1 -name "renderD*" -exec basename {} \; | head -n 1)
|
||||
[[ -n "\$renderer" ]] && renderer="/dev/dri/\$renderer"
|
||||
# Product name
|
||||
product="\${line#*:}"
|
||||
product="\${product#*: }"
|
||||
|
||||
# Form factor
|
||||
if [[ "\${brand}" == "NVIDIA" ]]; then
|
||||
integrated="false"
|
||||
fi
|
||||
if [[ "\${brand}" == "Intel" ]]; then
|
||||
if echo "\${line}" | grep -Ei "HD Graphics|Xe"; then
|
||||
integrated="true"
|
||||
else
|
||||
integrated="false"
|
||||
fi
|
||||
fi
|
||||
if [[ "\${brand}" == "AMD" ]]; then
|
||||
if echo "\$line" | grep -i "Mobile"; then
|
||||
integrated="true"
|
||||
else
|
||||
integrated="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Append to array
|
||||
local gpu_obj
|
||||
gpu_obj=\$(jq -n --arg b "\$brand" --arg r "\${renderer:-N/A}" --argjson i "\$integrated" \
|
||||
'{brand: \$b, renderer: \$r, integrated: \$i}')
|
||||
gpus=\$(echo "\$gpus" | jq --argjson obj "\$gpu_obj" '. += [\$obj]')
|
||||
|
||||
done < <(lspci -D | grep -iE 'VGA|3D' | awk '{print \$1}')
|
||||
local obj=\$(jq -n \
|
||||
--arg b "\${brand}" \
|
||||
--arg r "\${renderer}" \
|
||||
--arg p "\${product}" \
|
||||
--argjson i "\${integrated}" \
|
||||
'{brand: \$b, renderer: \$r, product: \$p, integrated: \$i}')
|
||||
|
||||
gpus=\$(echo "\$gpus" | jq --argjson obj "\$obj" '. += [\$obj]')
|
||||
|
||||
done < <(lspci | grep -Ei "VGA|3D")
|
||||
|
||||
append_to_report "graphics" "\$gpus"
|
||||
}
|
||||
@@ -152,7 +278,7 @@ detect_corals() {
|
||||
local corals="[]"
|
||||
|
||||
# Check PCIe Coral (Google ID 1ac1:089a)
|
||||
if lspci -nn 2>/dev/null | grep -iq "1ac1:089a"; then
|
||||
if lspci -nn | grep -iq "1ac1:089a"; then
|
||||
local pcie_count
|
||||
pcie_count=\$(lspci -nn | grep -ic "1ac1:089a")
|
||||
for ((i=1; i<=pcie_count; i++)); do
|
||||
@@ -162,7 +288,7 @@ detect_corals() {
|
||||
fi
|
||||
|
||||
# Check USB Coral (Google ID 18d1:9302)
|
||||
if lsusb 2>/dev/null | grep -iq "18d1:9302"; then
|
||||
if lsusb | grep -iq "18d1:9302"; then
|
||||
local usb_count
|
||||
usb_count=\$(lsusb | grep -ic "18d1:9302")
|
||||
for ((i=1; i<=usb_count; i++)); do
|
||||
@@ -197,7 +323,7 @@ detect_zigbee() {
|
||||
detect_network() {
|
||||
local networks="[]"
|
||||
local default_iface
|
||||
default_iface=\$(ip -4 route show default 2>/dev/null | awk '{print \$5}' | head -n1)
|
||||
default_iface=\$(ip -4 route show default | awk '{print \$5}' | head -n1)
|
||||
|
||||
for iface_path in /sys/class/net/*; do
|
||||
[[ -e "\$iface_path" ]] || continue
|
||||
@@ -247,59 +373,51 @@ detect_tpm() {
|
||||
# --- 6. Detect Disks ---
|
||||
detect_disks() {
|
||||
local disks="[]"
|
||||
|
||||
# lsblk to loop through block devices (ignoring loops, rams, and cdroms)
|
||||
while read -r disk; do
|
||||
local dev_path="/dev/\$disk"
|
||||
|
||||
# Disk Type Mapping
|
||||
local disk_type="Other"
|
||||
local transport
|
||||
transport=\$(lsblk -d -n -o TRAN "\$dev_path" 2>/dev/null || echo "")
|
||||
local rotational
|
||||
rotational=\$(lsblk -d -n -o ROTA "\$dev_path" 2>/dev/null || echo "1")
|
||||
|
||||
if [[ "\$disk" == nvme* ]]; then
|
||||
disk_type="NVMe"
|
||||
elif [[ "\$transport" == "usb" ]]; then
|
||||
disk_type="USB"
|
||||
while read -r disk_name; do
|
||||
# Disk Type Mapping
|
||||
local disk_type="unknown"
|
||||
local disk_transport=\$(lsblk -d -n -o TRAN "/dev/\$disk_name" || echo "unknown")
|
||||
local rotational=\$(lsblk -d -n -o ROTA "/dev/\$disk_name" || echo "1")
|
||||
|
||||
if [[ "\$disk_name" == nvme* ]]; then
|
||||
disk_type="NVMe"
|
||||
elif [[ "\$rotational" == "1" ]]; then
|
||||
disk_type="HDD"
|
||||
disk_type="HDD"
|
||||
elif [[ "\$rotational" == "0" ]]; then
|
||||
disk_type="SSD"
|
||||
disk_type="SSD"
|
||||
fi
|
||||
|
||||
# Size in GB
|
||||
local size_bytes
|
||||
size_bytes=\$(lsblk -d -n -b -o SIZE "\$dev_path" 2>/dev/null || echo "0")
|
||||
local size_gb
|
||||
size_gb=\$(awk "BEGIN {printf \"%.2f\", \$size_bytes/1073741824}")
|
||||
local disk_size=\$(lsblk -d -n -o SIZE "/dev/\$disk_name" || echo "unknown")
|
||||
|
||||
# ID via by-id
|
||||
local disk_id="N/A"
|
||||
local disk_id="unknown"
|
||||
if [[ -d "/dev/disk/by-id" ]]; then
|
||||
local id_match
|
||||
id_match=\$(find /dev/disk/by-id/ -type l -not -name "wwn-*" -not -name "nvme-eui*" -printf "%p %l\n" | grep -m1 "/\$disk\$" | awk '{print \$1}')
|
||||
[[ -n "\$id_match" ]] && disk_id="\$id_match"
|
||||
local id_match=\$(find /dev/disk/by-id/ -type l -not -name "wwn-*" -not -name "nvme-eui*" -printf "%p %l\n" | grep -m1 "/\$disk_name\$" | awk '{print \$1}')
|
||||
[[ -n "\$id_match" ]] && disk_id="\$id_match"
|
||||
fi
|
||||
|
||||
# Health Assessment (Requires smartctl)
|
||||
local health="N/A"
|
||||
if echo "${LIVE_TARGET_PASSWORD}" | sudo -S smartctl -H "\$dev_path" >/dev/null 2>&1; then
|
||||
if smartctl -H "\$dev_path" 2>/dev/null | grep -iq "PASSED\|OK"; then
|
||||
health="PASSED"
|
||||
else
|
||||
health="FAILED"
|
||||
fi
|
||||
# Health
|
||||
local health="unknown"
|
||||
if echo "${LIVE_TARGET_PASSWORD}" | sudo -S smartctl -H /dev/\$disk_name 2>/dev/null | grep -i "PASSED"; then
|
||||
disk_health="Passed"
|
||||
elif echo "${LIVE_TARGET_PASSWORD}" | sudo -S smartctl -H /dev/\$disk_name 2>/dev/null | grep -i "FAILED"; then
|
||||
disk_health="Failed"
|
||||
fi
|
||||
|
||||
local obj=\$(jq -n --arg n "\$disk" --arg dp "\$dev_path" --arg t "\$disk_type" \
|
||||
--arg h "\$health" --arg id "\$disk_id" --arg s "\$size_gb" \
|
||||
'{name: \$n, device_path: \$dp, type: \$t, health: \$h, id: \$id, size_gb: \$s}')
|
||||
local obj=\$(jq -n \
|
||||
--arg n "\${disk_name}" \
|
||||
--arg t "\${disk_type}" \
|
||||
--arg tr "\${disk_transport}" \
|
||||
--arg h "\${disk_health}" \
|
||||
--arg s "\${disk_size}" \
|
||||
--arg i "\${disk_id}" \
|
||||
'{name: \$n, type: \$t, transport: \$tr, health: \$h, size: \$s, id: \$i}')
|
||||
|
||||
disks=\$(echo "\$disks" | jq --argjson obj "\$obj" '. += [\$obj]')
|
||||
|
||||
done < <(lsblk -d -n -o NAME -e 7,11,252) # Exclude loop(7), sr(11), zram(252)
|
||||
done < <(lsblk -d -n -o NAME -e 7,11,252)
|
||||
|
||||
append_to_report "disks" "\$disks"
|
||||
}
|
||||
@@ -316,12 +434,14 @@ detect_disks
|
||||
echo "\$HW_REPORT" | jq '.' > "$TMPFILE"
|
||||
SSHEND
|
||||
|
||||
scp -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${HW_DATA_FILE}" &> /dev/null
|
||||
scp -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${HW_DATA_FILE}" >> "${STDOUT}" 2>> "${STDERR}"
|
||||
[[ ${DEBUG} -eq 1 ]] && scp -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${REMOTE_STDOUT}" "${INSTALL_DIR}/web/logs/hw_std.log" >> "${STDOUT}" 2>> "${STDERR}"
|
||||
[[ ${DEBUG} -eq 1 ]] && scp -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${REMOTE_STDERR}" "${INSTALL_DIR}/web/logs/hw_err.log" >> "${STDOUT}" 2>> "${STDERR}"
|
||||
|
||||
if ssh_to_host "sudo nixos-generate-config --no-filesystems --show-hardware-config" > ${TMP_EXTRA_PATH}/etc/nixos/hardware-configuration.nix; then
|
||||
echo -e "\n✅ Hardware configuration generated"
|
||||
if ssh_to_host "echo \"${LIVE_TARGET_PASSWORD}\" | sudo -S nixos-generate-config --no-filesystems --show-hardware-config &> /dev/null" > ${TMP_EXTRA_PATH}/etc/nixos/hardware-configuration.nix; then
|
||||
echo -e "\n ✅ Hardware configuration generated"
|
||||
else
|
||||
echo -e "\n❌ Failed to generate hardware configuration"
|
||||
echo -e "\n ❌ Failed to generate hardware configuration"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -329,47 +449,8 @@ SSHEND
|
||||
|
||||
|
||||
|
||||
# --- MAIN WEB FUNCTIONS --->
|
||||
launch_gui() {
|
||||
echo -e "\n 🚀 Launching Numbus Configurator..."
|
||||
echo -e " ➡️ You will now proceed to the configuration of your device through your browser"
|
||||
|
||||
python3 "${BRIDGE_SCRIPT}" > /dev/null 2>&1 &
|
||||
export BRIDGE_PID=$!
|
||||
|
||||
local START_URL="http://localhost:${WEBSERVER_PORT}/pages/index.html"
|
||||
|
||||
xdg-open "${START_URL}" 2>/dev/null || open "${START_URL}" 2>/dev/null || true
|
||||
|
||||
sleep 5
|
||||
|
||||
echo -e "\n ⚠️ If it doesn't automatically, open your browser at: $(gum style --foreground 212 "${START_URL}")"
|
||||
}
|
||||
# --- MAIN WEB FUNCTIONS ---<
|
||||
|
||||
|
||||
|
||||
# --- MAIN SCRIPT FUNCTIONS --->
|
||||
setup_ssh() {
|
||||
echod "\n ✅ Generating new SSH key for numbus-admin..."
|
||||
|
||||
chmod 700 "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/"
|
||||
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-${DEVICE_TYPE}" -f "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" -N "" -q
|
||||
|
||||
if [[ ${DEBUG} -eq 1 ]]; then
|
||||
echo -e "\n ➡️ Copying SSH key to target host '${TARGET_USER}@${LIVE_TARGET_IP}'..."
|
||||
fi
|
||||
|
||||
if sshpass -p "${LIVE_TARGET_PASSWORD}" ssh-copy-id -o StrictHostKeyChecking=no -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}"; then
|
||||
if [[ ${DEBUG} -eq 1 ]]; then
|
||||
echo -e "\n ✅ SSH key copied successfully"
|
||||
fi
|
||||
else
|
||||
echo -e "\n ❌ Failed to copy SSH key. Please check the host IP and password."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
server_config_generation() {
|
||||
echod "\n 📝 Generating structured settings.json..."
|
||||
|
||||
@@ -473,7 +554,7 @@ EOF
|
||||
done
|
||||
|
||||
local SSH_KEYS_FORMATTED=""
|
||||
if [[ "$(declare -p AUTHORIZED_SSH_PUBLIC_KEY 2>/dev/null)" =~ "declare -a" ]]; then
|
||||
if [[ "$(declare -p AUTHORIZED_SSH_PUBLIC_KEY)" =~ "declare -a" ]]; then
|
||||
for key in "${AUTHORIZED_SSH_PUBLIC_KEY[@]}"; do
|
||||
SSH_KEYS_FORMATTED+=" $key"$'\n'
|
||||
done
|
||||
@@ -526,7 +607,7 @@ cloudflare_dns_setup() {
|
||||
local SUBDOMAIN="${1}"
|
||||
|
||||
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
|
||||
⚠️ $(gum style --foreground 212 'WARNING:') One or more existing type A DNS records found for \`${SUBDOMAIN}\`.
|
||||
⚠️ $(gum style 'WARNING:') One or more existing type A DNS records found for \`${SUBDOMAIN}\`.
|
||||
This script can clear those DNS records for you and create the correct ones needed for the server.
|
||||
If you are unsure that these records are actually in use, please select \"no\"."
|
||||
gum confirm "Select \"yes\" to clear ALL EXISTING type A DNS records for this subdomain and automatically create the correct ones." \
|
||||
@@ -539,7 +620,7 @@ cloudflare_dns_setup() {
|
||||
for id in ${RECORD_IDS}; do
|
||||
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${id}" \
|
||||
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" > /dev/null 2>&1
|
||||
-H "Content-Type: application/json" >> "${STDOUT}" 2>> "${STDERR}"
|
||||
done
|
||||
|
||||
create_records "${SUBDOMAIN}"
|
||||
@@ -639,7 +720,7 @@ postrun_action() {
|
||||
FOUND="false"
|
||||
i="0"
|
||||
while [[ "${FOUND}" == "false" ]]; do
|
||||
if ping -c1 -W1 $HOME_SERVER_IP >/dev/null 2>&1; then
|
||||
if ping -c1 -W1 $HOME_SERVER_IP >> "${STDOUT}" 2>> "${STDERR}"; then
|
||||
FOUND="true"
|
||||
echo -e "\n✅ Ping ${HOME_SERVER_IP} successful ! Continuing..."
|
||||
else
|
||||
@@ -698,7 +779,7 @@ EOF
|
||||
fi
|
||||
|
||||
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
|
||||
⚠️ $(gum style --foreground 212 'WARNING:') You will now set the password of the numbus-admin user.
|
||||
⚠️ $(gum style 'WARNING:') You will now set the password of the numbus-admin user.
|
||||
You will almost never user it. Consider using a very strong password : you can write it down
|
||||
securely on a hidden sheet of paper or add it to your password manager (locally with Passbolt
|
||||
with any other online password manager provider)."
|
||||
@@ -712,6 +793,8 @@ securely on a hidden sheet of paper or add it to your password manager (locally
|
||||
|
||||
|
||||
# --- DEFAULT VARIABLES --->
|
||||
INSTALL_DIR="/run/user/$(id -u)/numbus-installer"
|
||||
|
||||
WEBSERVER_PORT=${WEBSERVER_PORT:-8088}
|
||||
|
||||
LIVE_DATA_FILE="web/config/live.yaml"
|
||||
@@ -722,7 +805,7 @@ BRIDGE_SCRIPT="web/logic/interactive.py"
|
||||
|
||||
TARGET_USER="nixos"
|
||||
|
||||
TMP_EXTRA_PATH="extra"
|
||||
TMP_EXTRA_PATH="${INSTALL_DIR}/extra"
|
||||
|
||||
if [[ ${DEBUG-0} -eq 1 ]]; then
|
||||
FILES_CP_FLAGS="vau"
|
||||
@@ -730,6 +813,10 @@ if [[ ${DEBUG-0} -eq 1 ]]; then
|
||||
DIR_RM_FLAGS="rvf"
|
||||
MKDIR_FLAGS="pv"
|
||||
MV_FLAGS="vu"
|
||||
STDOUT="${INSTALL_DIR}/web/logs/std.log"
|
||||
STDERR="${INSTALL_DIR}/web/logs/err.log"
|
||||
REMOTE_STDOUT="/run/user/1000/numbus-installer/std.log"
|
||||
REMOTE_STDERR="/run/user/1000/numbus-installer/err.log"
|
||||
else
|
||||
DEBUG=0
|
||||
FILES_CP_FLAGS="au"
|
||||
@@ -737,9 +824,12 @@ else
|
||||
DIR_RM_FLAGS="rf"
|
||||
MKDIR_FLAGS="p"
|
||||
MV_FLAGS="u"
|
||||
STDOUT="/dev/null"
|
||||
STDERR="/dev/null"
|
||||
fi
|
||||
|
||||
IP_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
|
||||
IP_OCTET='(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'
|
||||
IP_REGEX="^${IP_OCTET}\\.${IP_OCTET}\\.${IP_OCTET}\\.${IP_OCTET}$"
|
||||
SUBNET_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$'
|
||||
DOMAIN_REGEX='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
|
||||
EMAIL_REGEX='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
@@ -747,10 +837,11 @@ PORT_REGEX='^[0-9]{1,5}$'
|
||||
SSH_KEY_REGEX='^ssh-[a-z0-9]+ [A-Za-z0-9+/]+.*'
|
||||
PHONE_REGEX='^\+[1-9][0-9]{7,14}$'
|
||||
|
||||
GUM_INPUT_PADDING="1 1"
|
||||
GUM_INPUT_HEADER_FOREGROUND="212"
|
||||
GUM_INPUT_CURSOR_FOREGROUND="212"
|
||||
GUM_INPUT_TIMEOUT="3600"
|
||||
export FOREGROUND="212"
|
||||
export GUM_INPUT_PADDING="1 1"
|
||||
export GUM_INPUT_HEADER_FOREGROUND="212"
|
||||
export GUM_INPUT_CURSOR_FOREGROUND="212"
|
||||
export GUM_INPUT_TIMEOUT="3600s"
|
||||
# --- DEFAULTS VARIABLES ---<
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
0: successful.
|
||||
1: error.
|
||||
225: Bad SSH credentials.
|
||||
226: Host unreachable or connection refused.
|
||||
22: SSH fingerprint in `known_hosts` for the IP is different than the current one.
|
||||
@@ -1,20 +0,0 @@
|
||||
{ modulesPath, config, pkgs, inputs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
];
|
||||
|
||||
# System
|
||||
system.stateVersion = "25.11";
|
||||
|
||||
# Secrets management
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ];
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
# Secrets
|
||||
sops.secrets."authorizedSshPublicKeys" = { owner = "numbus-admin"; path = "/home/numbus-admin/.ssh/authorized_keys"; mode = "0600"; };
|
||||
sops.secrets."smtpPassword" = { owner = "numbus-admin"; mode = "0600"; };
|
||||
sops.secrets."cloudflareDnsApiToken" = { owner = "numbus-admin"; mode = "0600"; };
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
# Core Nixpkgs
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
# Numbus server configuration
|
||||
numbus.url = "git+https://gittea.dev/numbus/numbus-backup-server";
|
||||
numbus.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Disk-partitioning helper
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Secrets handling
|
||||
sops-nix.url = "github:Mic92/sops-nix";
|
||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Power savings
|
||||
autoaspm.url = "git+https://git.notthebe.ee/notthebee/AutoASPM";
|
||||
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, numbus, disko, sops-nix, autoaspm, ... }@inputs: let
|
||||
# System definition
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
numbus-server = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
# Numbus server configuration
|
||||
numbus.nixosModules.numbus
|
||||
# Disk-partitioning helper
|
||||
disko.nixosModules.disko
|
||||
# Secrets handling
|
||||
sops-nix.nixosModules.sops
|
||||
# Power savings
|
||||
autoaspm.nixosModules.autoaspm
|
||||
# Core host configuration
|
||||
./configuration.nix
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
authorizedSshPublicKeys: |
|
||||
$SSH_KEYS_FORMATTED
|
||||
smtpPassword: "$SMTP_SERVER_PASSWORD"
|
||||
cloudflareDnsApiToken: "$CLOUDFLARE_DNS_API_TOKEN"
|
||||
@@ -1,18 +0,0 @@
|
||||
{ modulesPath, config, pkgs, inputs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
];
|
||||
|
||||
# System
|
||||
system.stateVersion = "25.11";
|
||||
|
||||
# Secrets management
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ];
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
# Secrets
|
||||
sops.secrets."authorizedSshPublicKeys" = { owner = "numbus-admin"; path = "/home/numbus-admin/.ssh/authorized_keys"; mode = "0600"; };
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
# Core Nixpkgs
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
# Numbus server configuration
|
||||
numbus.url = "git+https://gittea.dev/numbus/numbus-computer";
|
||||
numbus.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Disk-partitioning helper
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Secrets handling
|
||||
sops-nix.url = "github:Mic92/sops-nix";
|
||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Power savings
|
||||
autoaspm.url = "git+https://git.notthebe.ee/notthebee/AutoASPM";
|
||||
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, numbus, disko, sops-nix, autoaspm, ... }@inputs: let
|
||||
# System definition
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
numbus-server = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
# Numbus server configuration
|
||||
numbus.nixosModules.numbus
|
||||
# Disk-partitioning helper
|
||||
disko.nixosModules.disko
|
||||
# Secrets handling
|
||||
sops-nix.nixosModules.sops
|
||||
# Power savings
|
||||
autoaspm.nixosModules.autoaspm
|
||||
# Core host configuration
|
||||
./configuration.nix
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# .sops.yaml
|
||||
|
||||
keys:
|
||||
- &primary $SOPS_PUBLIC_KEY
|
||||
creation_rules:
|
||||
- path_regex: secrets/secrets.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *primary
|
||||
@@ -0,0 +1,37 @@
|
||||
# Do NOT edit this file manually.
|
||||
# Please use the dedicated script : https://gittea.dev/numbus/numbus.
|
||||
# This could compromise system stability and is not supported by Numbus.
|
||||
|
||||
{
|
||||
inputs = {
|
||||
numbus.url = "git+https://gittea.dev/numbus/numbus";
|
||||
};
|
||||
|
||||
outputs = inputs@{ numbus, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = numbus.inputs.nixpkgs;
|
||||
|
||||
mainModules = [
|
||||
numbus.nixosModules.numbus-common
|
||||
./numbus-generated.nix
|
||||
./hardware-configuration.nix
|
||||
./custom-configuration/default.nix
|
||||
];
|
||||
|
||||
mkHost = module: pkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = mainModules ++ [ module ];
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
numbus-backup = mkHost numbus.nixosModules.numbus-backup;
|
||||
numbus-console = mkHost numbus.nixosModules.numbus-console;
|
||||
numbus-server = mkHost numbus.nixosModules.numbus-server;
|
||||
numbus-tv = mkHost numbus.nixosModules.numbus-tv;
|
||||
numbus-workstation = mkHost numbus.nixosModules.numbus-workstation;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ config, ... }:
|
||||
|
||||
let
|
||||
numbus-generated = builtins.fromJSON (builtins.readFile ./numbus-generated.json);
|
||||
in
|
||||
|
||||
{
|
||||
config = numbus-generated.numbus;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# .sops.yaml
|
||||
|
||||
keys:
|
||||
- &primary $SOPS_PUBLIC_KEY
|
||||
creation_rules:
|
||||
- path_regex: secrets/(disks|podman|system)/.*\.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *primary
|
||||
@@ -1,8 +0,0 @@
|
||||
# This file is reserved for ADVANCED USERS ONLY.
|
||||
# Editing could compromise system stability and is not supported by numbus.
|
||||
# Do NOT set options already managed by numbus. i.e. config.numbus.* and other options (networking, storage, etc.)
|
||||
# Please use the dedicated script for those options : https://gittea.dev/numbus/numbus.
|
||||
|
||||
{ config }:
|
||||
|
||||
{}
|
||||
@@ -1,53 +0,0 @@
|
||||
# Do NOT edit this file manually.
|
||||
# Please use the dedicated script : https://gittea.dev/numbus/numbus.
|
||||
# This could compromise system stability and is not supported by numbus.
|
||||
|
||||
{
|
||||
inputs = {
|
||||
# Core Nixpkgs
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
# Numbus server configuration
|
||||
numbus-server.url = "git+https://git.numbus.eu/numbus/numbus-server";
|
||||
numbus-server.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Disk-partitioning helper
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Secrets handling
|
||||
sops-nix.url = "github:Mic92/sops-nix";
|
||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Power savings
|
||||
autoaspm.url = "git+https://git.notthebe.ee/notthebee/AutoASPM";
|
||||
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, numbus-server, disko, sops-nix, autoaspm, ... }@inputs: let
|
||||
# System definition
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
numbus-server = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
# Numbus server configuration
|
||||
numbus-server.nixosModules.numbus-server
|
||||
# Disk-partitioning helper
|
||||
disko.nixosModules.disko
|
||||
# Secrets handling
|
||||
sops-nix.nixosModules.sops
|
||||
# Power savings
|
||||
autoaspm.nixosModules.autoaspm
|
||||
# Core host configuration
|
||||
./hardware-configuration.nix
|
||||
./numbus-generated.nix
|
||||
./custom-configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# Do NOT edit this file manually.
|
||||
# Please use the dedicated script : https://gittea.dev/numbus/numbus.
|
||||
# This could compromise system stability and is not supported by numbus.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
@@ -1,2 +0,0 @@
|
||||
disks:
|
||||
boot: "whoever blabber senior liqueur riverboat filing wronged seventeen grandma moonshine"
|
||||
@@ -1,3 +0,0 @@
|
||||
disks:
|
||||
content-1: "nemeses starship tackle anime cardinal scarcity marmalade divorcee gondola kindle"
|
||||
content-2: "chihuahua smolder cardiac mooned ovary suing bless nuptials driver slighting"
|
||||
@@ -1,2 +0,0 @@
|
||||
disks:
|
||||
parity-1: "armed despise atrophy province strategy lustrous provoking jurist ramble phoenix"
|
||||
@@ -1,3 +0,0 @@
|
||||
# CLOUDFLARE
|
||||
traefik:
|
||||
cloudflareDnsApiToken: ""
|
||||
@@ -1,3 +0,0 @@
|
||||
# SMTP
|
||||
mail:
|
||||
smtpPassword: "$SMTP_SERVER_PASSWORD"
|
||||
@@ -1,3 +0,0 @@
|
||||
# SSH
|
||||
ssh:
|
||||
authorizedPublicKeys: |
|
||||
@@ -1,18 +0,0 @@
|
||||
{ modulesPath, config, pkgs, inputs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
];
|
||||
|
||||
# System
|
||||
system.stateVersion = "25.11";
|
||||
|
||||
# Secrets management
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ];
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
# Secrets
|
||||
sops.secrets."authorizedSshPublicKeys" = { owner = "numbus-admin"; path = "/home/numbus-admin/.ssh/authorized_keys"; mode = "0600"; };
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
# Core Nixpkgs
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
# Numbus server configuration
|
||||
numbus.url = "git+https://gittea.dev/numbus/numbus-tv";
|
||||
numbus.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Disk-partitioning helper
|
||||
disko.url = "github:nix-community/disko";
|
||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Secrets handling
|
||||
sops-nix.url = "github:Mic92/sops-nix";
|
||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
# Power savings
|
||||
autoaspm.url = "git+https://git.notthebe.ee/notthebee/AutoASPM";
|
||||
autoaspm.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, numbus, disko, sops-nix, autoaspm, ... }@inputs: let
|
||||
# System definition
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
numbus-server = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
# Numbus server configuration
|
||||
numbus.nixosModules.numbus
|
||||
# Disk-partitioning helper
|
||||
disko.nixosModules.disko
|
||||
# Secrets handling
|
||||
sops-nix.nixosModules.sops
|
||||
# Power savings
|
||||
autoaspm.nixosModules.autoaspm
|
||||
# Core host configuration
|
||||
./configuration.nix
|
||||
./hardware-configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# .sops.yaml
|
||||
|
||||
keys:
|
||||
- &primary $SOPS_PUBLIC_KEY
|
||||
creation_rules:
|
||||
- path_regex: secrets/secrets.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *primary
|
||||
@@ -1,2 +0,0 @@
|
||||
authorizedSshPublicKeys: |
|
||||
$SSH_KEYS_FORMATTED
|
||||
+565
-60
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html :lang="lang" x-data="numbusConfiguration()">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -16,19 +16,280 @@
|
||||
<link rel="icon" href="/media/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body class="p-4 bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
|
||||
<body x-data="setupNavigation()" class="p-4 bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function setupNavigation() {
|
||||
function numbusConfiguration() {
|
||||
const langConst = localStorage.getItem('wizard_language') || 'en';
|
||||
const deploymentMode = localStorage.getItem('deployment_mode') || '';
|
||||
return {
|
||||
lang: langConst,
|
||||
get i18n() { return this.translations[this.lang] },
|
||||
step: 1,
|
||||
discoveryLoading: false,
|
||||
formData: {
|
||||
1: {
|
||||
gitUrl: '',
|
||||
gitUsername: '',
|
||||
gitPassword: '',
|
||||
},
|
||||
2: {
|
||||
bootDisks: '',
|
||||
dataDisks: '',
|
||||
parityDisks: '',
|
||||
HddSpindown: '',
|
||||
},
|
||||
3: {
|
||||
lanSubnet: '',
|
||||
routerIpAddress: '',
|
||||
serverIpAddress: '',
|
||||
additionalDnsServerAddress: '',
|
||||
},
|
||||
4: {
|
||||
remoteAccessPlatform: 'netbird-cloud',
|
||||
netbirdToken: '',
|
||||
},
|
||||
5: {
|
||||
smtpHost: '',
|
||||
smtpPort: '',
|
||||
smtpUsername: '',
|
||||
smtpPassword: '',
|
||||
},
|
||||
6: {
|
||||
selectedDnsService: 'pi-hole',
|
||||
selectedWebServices: [],
|
||||
selectedSystemServices: [],
|
||||
},
|
||||
7: {
|
||||
users: [
|
||||
{ username: 'admin', name: '', email: '', groups: ['admin'], isStatic: true }
|
||||
],
|
||||
groups: {
|
||||
admin: ['all'],
|
||||
developer: ['gitea', 'vscodium', 'it-tools', 'odoo'],
|
||||
family: ['frigate', 'home-assistant', 'homepage', 'immich', 'jellyfin', 'n8n', 'nextcloud', 'ntfy', 'passbolt'],
|
||||
office: ['passbolt', 'nextcloud']
|
||||
},
|
||||
},
|
||||
8: {
|
||||
use_sso: true,
|
||||
public_sharing: true,
|
||||
ssh_keys: ''
|
||||
},
|
||||
9: {
|
||||
backupServers: [
|
||||
{ backupType: '', backupHost: '', backupSecret: '', backupTime: '', backupFrequency: '', },
|
||||
],
|
||||
},
|
||||
},
|
||||
translations: {
|
||||
en: {
|
||||
// Page
|
||||
step2_title: "Step 2 : Configuration",
|
||||
back: "Back",
|
||||
continue: "Continue",
|
||||
|
||||
//Card 1 : Repository configuration
|
||||
card1_title: "Repository",
|
||||
card1_subtitle: "Add a Git repository to import/export your configuration. This is what makes Numbus systems so easy to restore, duplicate, reproduce.",
|
||||
card1_option1_title: "Repository URL",
|
||||
card1_option1_desc1: "Provide the URL of a repository containing your configuration. <br> Don't have one ? Click on the back button and select the <span class=\"text-sky-400 font-semibold\">\"non-interactive\"</span> option.",
|
||||
card1_option1_desc2: "Provide the URL of a clean, uninitialized repository. Numbus will automatically export your configuration to this repository and handle the version tagging for you. <br> Don't have one ? See <a target=\"_blank\" class=\"text-sky-400 hover:text-sky-500 transition-colors underline font-semibold\" href=\"https://gittea.dev/\">Gitea</a> or <a target=\"_blank\" class=\"text-sky-400 hover:text-sky-500 transition-colors underline font-semibold\" href=\"https://gitlab.com/\">GitLab</a>.",
|
||||
card1_option1_placeholder: "https://gittea.dev/numbus/numbus",
|
||||
card1_option2_title: "Branch (optional)",
|
||||
card1_option2_desc: "The name of the branch that is going to be used. Defaults to \"main\".",
|
||||
card1_option2_placeholder: "main",
|
||||
card1_option3_title: "Username",
|
||||
card1_option3_desc: "",
|
||||
card1_option3_placeholder: "yours@example.com",
|
||||
card1_option4_title: "Password",
|
||||
card1_option4_desc: "A static IP address that is available in your LAN. <br> Use <span class=\"text-sky-400 font-semibold\">\"ip a\"</span> on Linux/MacOS and <span class=\"text-sky-400 font-semibold\">\"ifconfig\"</span> on Windows to find your subnet (192.168.1.0/24 for example).",
|
||||
card1_option4_placeholder: "supersecretpassword",
|
||||
|
||||
// Card 3 : Networking
|
||||
card3_title: "Networking",
|
||||
card3_subtitle: "Configure your server's network settings to ensure client reachability.",
|
||||
card3_option1_title: "LAN subnet",
|
||||
card3_option1_desc: "The subnet in which your device is located. Use <span class=\"text-sky-400 font-semibold\">\"ip a\"</span> on Linux/MacOS and <span class=\"text-sky-400 font-semibold\">\"ifconfig\"</span> on Windows to find your subnet (192.168.1.0/24 for example).",
|
||||
card3_option1_placeholder: "192.168.1.0/24",
|
||||
card3_option2_title: "Router IP address",
|
||||
card3_option2_desc: "The IP address of your LAN router. Usually <i>192.168.1.1</i> or <i>192.168.1.254</i>. <br> Find your it using <span class=\"text-sky-400 font-semibold\">\"ip a\"</span> on Linux/MacOS and <span class=\"text-sky-400 font-semibold\">\"ifconfig\"</span> on Windows to find your subnet (192.168.1.0/24 for example).",
|
||||
card3_option2_placeholder: "192.168.1.1",
|
||||
card3_option3_title: "Server IP address",
|
||||
card3_option3_desc: "A static IP address that is available in your LAN. <br> Use <span class=\"text-sky-400 font-semibold\">\"ip a\"</span> on Linux/MacOS and <span class=\"text-sky-400 font-semibold\">\"ifconfig\"</span> on Windows to find your subnet (192.168.1.0/24 for example).",
|
||||
card3_option3_placeholder: "192.168.1.5",
|
||||
card3_option4_title: "Additional DNS server address",
|
||||
card3_option4_desc: "The DNS server your server will use for domain resolution when the local DNS server is down. <br> Quad 9 servers are recommended for privacy and security reasons.",
|
||||
card3_option4_placeholder: "9.9.9.9",
|
||||
|
||||
// Card 2 : Users & Groups
|
||||
card2_title: "Users & Groups",
|
||||
card2_subtitle: "Set your regional preferences to ensure <b>correct</b> time and language display.",
|
||||
card2_option1_title: "Locale",
|
||||
card2_option1_desc: "Provide the locale of this device. The locale is composed of the language and the country. Find a list of locales <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">here</a>.",
|
||||
card2_option1_placeholder: "en_US",
|
||||
card2_option2_title: "Time zone",
|
||||
card2_option2_desc: "Provide the time zone in which the device is located. Find your time zone at <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">here</a>.",
|
||||
card2_option2_placeholder: "America/Los_Angeles",
|
||||
|
||||
// Card 2
|
||||
card2_title: "Device Type",
|
||||
card2_subtitle: "Select the <b>device type</b> for your new Numbus machine that matches <b>your needs</b>.",
|
||||
card2_option1_title: "Server",
|
||||
card2_option1_subtitle: "Your own Cloud at Home.",
|
||||
card2_option1_desc: "A versatile home cloud solution for hosting containers, media, and automated services.",
|
||||
card2_option2_title: "Backup Server",
|
||||
card2_option2_subtitle: "Backup all Numbus devices and monitor your servers.",
|
||||
card2_option2_desc: "An all-in-one backup solution for all your Numbus devices, with monitoring tools.",
|
||||
card2_option3_title: "Computer",
|
||||
card2_option3_subtitle: "A workstation powered by leading open-source software.",
|
||||
card2_option3_desc: "A polished workstation powered by leading open-source software.",
|
||||
card2_option4_title: "TV",
|
||||
card2_option4_subtitle: "Your TV, your way. No spying on you.",
|
||||
card2_option4_desc: "A computer -and all its accompanying advantages- but with a slick, familiar TV interface.",
|
||||
card2_option5_title: "Game Console",
|
||||
card2_option5_subtitle: "Play games and nothing else.",
|
||||
card2_option5_desc: "A true console-like experience using Steam Bigscreen.",
|
||||
card2_option6_title: "And more ?",
|
||||
card2_option6_subtitle: "Consider <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">contributing !</a> 🌟",
|
||||
},
|
||||
fr: {
|
||||
// Page
|
||||
step2_title: "Étape 2 : Configuration",
|
||||
back: "Retour",
|
||||
continue: "Continuer",
|
||||
|
||||
//Card 1 : Repository configuration
|
||||
card1_title: "Dépôt Git",
|
||||
card1_subtitle1: "Ajouter un dépôt afin d'importer votre configuration. Le paramètre qui rend les systèmes Numbus si faciles à reconstruire, cloner, reproduire.",
|
||||
card1_subtitle2: "Ajouter un dépôt afin d'exporter votre configuration. Le paramètre qui rend les systèmes Numbus si faciles à reconstruire, cloner, reproduire.",
|
||||
card1_option1_title: "URL du dépôt",
|
||||
card1_option1_desc1: "Fournissez l'URL du dépôt contenant la configuration Numbus que vous souhaitez reproduire. <br> Vous n'en avez pas ? Cliquez sur le bouton retour et sélectionnez l'option <span class=\"text-sky-400 font-semibold\">\"interactive\"</span>.",
|
||||
card1_option1_desc2: "Fournissez l'URL d'un nouveau dépôt non initialisé. Numbus s'occupe automatiquement de l'export de votre configuration et de l'étiquettage des versions. <br> Vous n'en avez pas ? Voyez <a target=\"_blank\" class=\"text-sky-400 hover:text-sky-500 transition-colors underline font-semibold\" href=\"https://gittea.dev/\">Gitea</a> ou <a target=\"_blank\" class=\"text-sky-400 hover:text-sky-500 transition-colors underline font-semibold\" href=\"https://gitlab.com/\">GitLab</a>.",
|
||||
card1_option1_placeholder: "https://gittea.dev/yourusername/yourrepo",
|
||||
card1_option2_title: "Branche (optionel)",
|
||||
card1_option2_desc: "",
|
||||
card1_option2_placeholder: "main",
|
||||
card1_option3_title: "Nom d'utilisateur",
|
||||
card1_option3_desc: "",
|
||||
card1_option3_placeholder: "yours@example.com",
|
||||
card1_option4_title: "Mot de passe",
|
||||
card1_option4_desc: "",
|
||||
card1_option4_placeholder: "motdepassesecret",
|
||||
|
||||
// Card 1
|
||||
card7_title: "Utilisateurs & Groupes",
|
||||
card7_subtitle: "Définissez l'adminisatrateur du serveur ainsi que les utilisateurs des services hébergés sur celui-ci.",
|
||||
card7_option1_title: "Admin",
|
||||
card7_option3_title: "Nom",
|
||||
card7_option3_desc: "Le nom de l'utilisateur.",
|
||||
card7_option3_placeholder: "George",
|
||||
card7_option4_title: "Groupe(s)",
|
||||
card7_option4_desc: "Assignez l'utilisateur à un groupe afin qu'ils puisse accéder aux services que vous avez configuré. Utilisez les groupes pré-définis ou définissez en de nouveaux.",
|
||||
|
||||
// Card 2
|
||||
card2_title: "Type d'appareil",
|
||||
card2_subtitle: "Sélectionnez le <b>type d'appareil</b> pour votre nouvelle machine Numbus qui correspond à <b>vos besoins</b>.",
|
||||
card2_option1_title: "Serveur",
|
||||
card2_option1_subtitle: "Votre propre Cloud à la maison.",
|
||||
card2_option1_desc: "Une solution cloud domestique polyvalente pour l'hébergement de conteneurs, de médias et de services automatisés.",
|
||||
card2_option2_title: "Serveur de Sauvegarde",
|
||||
card2_option2_subtitle: "Sauvegardez vos appareils Numbus et surveillez vos serveurs.",
|
||||
card2_option2_desc: "Une solution de sauvegarde tout-en-un pour tous vos appareils Numbus, avec des outils de surveillance.",
|
||||
card2_option3_title: "Ordinateur",
|
||||
card2_option3_subtitle: "Une station de travail propulsée par l'open-source.",
|
||||
card2_option3_desc: "Une station de travail polie propulsée par les meilleurs logiciels open-source.",
|
||||
card2_option4_title: "TV",
|
||||
card2_option4_subtitle: "Votre télé, à votre façon. Sans espionnage.",
|
||||
card2_option4_desc: "Un ordinateur -et tous ses avantages- mais avec une interface TV fluide et familière.",
|
||||
card2_option5_title: "Console de Jeu",
|
||||
card2_option5_subtitle: "Du gaming et rien d'autre.",
|
||||
card2_option5_desc: "Une authentique expérience de console grâce à l'interface Steam Bigscreen.",
|
||||
card2_option6_title: "Mais encore ?",
|
||||
card2_option6_subtitle: "Pensez à <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">contribuer !</a> 🌟",
|
||||
}
|
||||
},
|
||||
isStepValid() {
|
||||
const currentStepData = this.formData[this.step];
|
||||
if (!currentStepData) return true;
|
||||
if (this.step === 3 && currentStepData.deploymentMode === 'interactive') return true;
|
||||
return Object.values(currentStepData).every(value => !!value);
|
||||
},
|
||||
goToPrevStep() {
|
||||
this.step--;
|
||||
},
|
||||
goToNextStep() {
|
||||
this.step++;
|
||||
}
|
||||
},
|
||||
async startDiscovery() {
|
||||
if (this.discoveryLoading) return;
|
||||
this.discoveryLoading = true;
|
||||
|
||||
const data = JSON.parse(JSON.stringify(this.formData));
|
||||
console.log("Discovery started with:", data);
|
||||
|
||||
const payload = {
|
||||
internationalization: {
|
||||
locale: data[1].locale,
|
||||
time_zone: data[1].timeZone
|
||||
},
|
||||
device: {
|
||||
type: data[2].deviceType
|
||||
},
|
||||
deployment: {
|
||||
mode: data[3].deploymentMode,
|
||||
git_url: data[3].gitUrl || '',
|
||||
git_username: data[3].gitUsername || '',
|
||||
git_password: data[3].gitPassword || ''
|
||||
},
|
||||
live_target: {
|
||||
ip: data[4].liveIp,
|
||||
password: data[4].livePassword
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/discovery', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/yaml' },
|
||||
body: jsyaml.dump(payload, { quotingType: '"', forceQuotes: true })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log("Discovery signal sent successfully.");
|
||||
this.pollHardwareResults();
|
||||
} else {
|
||||
console.error("Server returned an error:", response.statusText);
|
||||
this.discoveryLoading = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Network error during discovery:", error);
|
||||
this.discoveryLoading = false;
|
||||
}
|
||||
},
|
||||
pollHardwareResults() {
|
||||
// Poll every 2 seconds for the hardware detection results file
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch('/config/hardware.yaml', { cache: 'no-store' });
|
||||
if (response.ok) {
|
||||
clearInterval(interval);
|
||||
window.location.href = 'configuration.html';
|
||||
}
|
||||
} catch (err) {
|
||||
// Errors are expected until the file is actually created
|
||||
}
|
||||
}, 2000);
|
||||
},
|
||||
tooltipText() {
|
||||
return this.deploymentMode === 'interactive'
|
||||
? this.i18n.card1_option1_desc1
|
||||
: this.i18n.card1_option1_desc2;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -36,17 +297,17 @@
|
||||
<nav class="bg-[#1e293b] border border-slate-700 rounded-2xl relative">
|
||||
<div class="mx-auto relative flex h-16 items-center justify-between">
|
||||
<div class="absolute inset-y-0 left-0 flex items-center">
|
||||
<a href="https://numbus.eu"><img class="w-auto h-10 pr-5 pl-8" src="/media/logo.png" aria-label="The numbus logo"></a>
|
||||
<a class="font-bold text-2xl tracking-tight bg-gradient-to-r from-sky-400 to-fuchsia-500 bg-clip-text text-transparent uppercase" href="https://numbus.eu">NUMBUS</a>
|
||||
<a target="_blank" href="https://numbus.eu"><img class="w-auto h-10 pr-5 pl-8" src="/media/logo.png" aria-label="The numbus logo"></a>
|
||||
<a class="font-bold text-2xl tracking-tight bg-gradient-to-r from-sky-400 to-fuchsia-500 bg-clip-text text-transparent uppercase" target="_blank" href="https://numbus.eu">NUMBUS</a>
|
||||
</div>
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<h1 class="sm:text-2xl sm:pr-20 lg:pr-0 text-xl text-white font-bold flex items-center">Step 1 - Preparation</h1>
|
||||
<h1 class="sm:text-2xl sm:pr-20 lg:pr-0 text-xl text-slate-200 font-bold flex items-center" x-text="i18n.step2_title"></h1>
|
||||
</div>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center my-auto">
|
||||
<button class="h-auto mdi mdi-menu text-slate-100 text-2xl sm:hidden pr-8" aria-label="Menu with links"></button>
|
||||
<button class="h-auto mdi mdi-brightness-2 text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-5" aria-label="Change theme"></button>
|
||||
<a href="https://gittea.dev/numbus" class="h-auto mdi mdi-source-repository text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-5" aria-label="See the source code on Gitea"></a>
|
||||
<a href="https://gittea.dev/numbus" class="h-auto mdi mdi-text-box-search text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-8" aria-label="See the full documentation"></a>
|
||||
<a target="_blank" href="https://gittea.dev/numbus" class="h-auto mdi mdi-source-repository text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-5" aria-label="See the source code on Gitea"></a>
|
||||
<a target="_blank" href="https://gittea.dev/numbus" class="h-auto mdi mdi-text-box-search text-slate-100 text-2xl hidden sm:block sm:text-3xl pr-8" aria-label="See the full documentation"></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -56,89 +317,333 @@
|
||||
<!-- Main content -->
|
||||
|
||||
<div class="bg-[#1e293b] border border-slate-700 rounded-3xl h-[calc(100vh-10rem)] max-w-[60vw] mx-auto relative">
|
||||
<!-- Device Type -->
|
||||
<div x-show="step === 1">
|
||||
<h1 class="p-5 pt-6 text-4xl font-bold text-sky-400">Device Type</h1>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[98%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-300">Select the <strong>device type</strong> for your new Numbus machine that matches <strong>your needs</strong>.</p>
|
||||
<div class="pl-5 pr-5 pt-10 pb-10 grid grid-cols-2 gap-4">
|
||||
<button @click="" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-server-light.svg" alt="Numbus Server icon">
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Numbus Server</h1>
|
||||
<p class="text-sm transition-colors">Your own Cloud at Home.</p>
|
||||
|
||||
|
||||
|
||||
<!-- Step 1: Repository Configuration -->
|
||||
<div x-show="step === 1" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400" x-text="i18n.card1_title"></h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 pb-0 text-xl text-slate-200" x-html="i18n.card1_subtitle"></p>
|
||||
<div class="p-5 pb-0 grid grid-cols-2 gap-4">
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option1_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="tooltipText()"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].gitUrl" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option1_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option2_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option2_desc"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-backup-server-light.svg" alt="Numbus Server icon">
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Numbus Backup Server</h1>
|
||||
<p class="text-sm transition-colors">Backup all Numbus devices and monitor your servers.</p>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].gitBranch" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option2_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option3_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option3_desc"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-computer-light.svg" alt="Numbus Server icon">
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Numbus Computer</h1>
|
||||
<p class="text-sm transition-colors">Backup all Numbus devices and monitor your servers.</p>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].gitUsername" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option3_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option4_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option4_desc"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-tv-light.svg" alt="Numbus Server icon">
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Numbus TV</h1>
|
||||
<p class="text-sm transition-colors">Your TV, your way. No spying on you.</p>
|
||||
</div>
|
||||
</button>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].gitPassword" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option4_placeholder">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Step 1: Repository Configuration -->
|
||||
|
||||
|
||||
|
||||
<!-- Step 3: Networking -->
|
||||
<div x-show="step === 3" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400" x-text="i18n.card3_title"></h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 pb-0 text-xl text-slate-200" x-html="i18n.card3_subtitle"></p>
|
||||
<div class="p-5 pb-0 grid grid-cols-2 gap-4">
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card3_option1_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card3_option1_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[3].lanSubnet" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card3_option1_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card3_option2_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card3_option2_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[3].routerIpAddress" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card3_option2_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card3_option3_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card3_option3_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[3].serverIpAddress" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card3_option3_placeholder">
|
||||
</div>
|
||||
<div class="p-6 pt-3 pb-3 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option4_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card3_option4_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[3].additionalDnsServerAddress" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card3_option4_placeholder">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Step 3: Networking -->
|
||||
|
||||
|
||||
|
||||
<!-- Step 2: Language & Region -->
|
||||
<div x-show="step === 2" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400" x-text="i18n.card2_title"></h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 pb-0 text-xl text-slate-200" x-html="i18n.card2_subtitle"></p>
|
||||
<div class="m-5 p-5 gap-4 grid grid-cols-3 bg-slate-900 rounded-2xl border border-slate-600">
|
||||
<div>
|
||||
<p x-text="i18n.card2_option1_title"></p>
|
||||
<span>ADMINISTRATEUR</span>
|
||||
</div>
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option3_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option3_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].locale" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option3_placeholder">
|
||||
</div>
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option4_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option4_desc"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].timeZone" class="w-full bg-slate-700 border border-slate-900 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option4_placeholder">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Step 1: Language & Region -->
|
||||
|
||||
|
||||
|
||||
<!-- Deployment Mode -->
|
||||
<div x-show="step === 2">
|
||||
<h1 class="p-5 pt-6 text-4xl font-bold text-sky-400">Deployment Mode</h1>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[98%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-300">Select your <strong>preferred</strong> deployment mode. Non-interactive <strong>requires</strong> a ready-to-go configuration hosted on a <strong>git platform</strong>.</p>
|
||||
<div x-show="step === 3" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Deployment Mode</h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-300">Select your <b>preferred</b> deployment mode. Non-interactive <b>requires</b> a ready-to-go configuration hosted on a <b>git platform</b>.</p>
|
||||
<div class="pl-5 pr-5 pt-10 grid grid-cols-2 gap-4">
|
||||
<button class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<button @click="formData[3].deploymentMode = 'interactive'" :class="formData[3].deploymentMode === 'interactive' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<i class="mdi mdi-gesture-tap text-5xl flex-shrink-0"></i>
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Interactive</h1>
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-2xl mb-1">Interactive</span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">If this is your first time setting up the Numbus device you chose, follow this option.</p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">We will guide you through the setup process.</p>
|
||||
</div>
|
||||
</button>
|
||||
<button class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left bg-slate-900 border-slate-700 hover:bg-fuchsia-600/20 hover:border-fuchsia-500 focus:bg-fuchsia-600/20 focus:border-fuchsia-500">
|
||||
<button @click="formData[3].deploymentMode = 'non-interactive'" :class="formData[3].deploymentMode === 'non-interactive' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<i class="mdi mdi-cog-clockwise text-5xl flex-shrink-0"></i>
|
||||
<div>
|
||||
<h1 class="font-bold text-2xl mb-1">Non-interactive</h1>
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-2xl mb-1">Non-interactive</span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">This option is used for mass devices deployments. It requires a first run in interactive mode.</p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">You already have a ready-to-go configuration.</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Deployment Mode -->
|
||||
|
||||
|
||||
|
||||
<!-- Live Setup -->
|
||||
<div x-show="step === 3">
|
||||
<h1 class="p-5 pt-6 text-4xl font-bold text-sky-400">Live Setup</h1>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[98%] mx-auto"></div>
|
||||
<p class="p-5 pb-10 text-xl text-slate-300">Provide the <strong>necessary information</strong> to connect to the device. It needs to be in a <strong>NixOS live environment</strong>.</p>
|
||||
<div x-show="step === 4" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Live Setup</h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 pb-10 text-xl text-slate-300">Provide the <b>necessary information</b> to connect to the device. It needs to be in a <b>NixOS live environment</b>.</p>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="p-6 space-y-2">
|
||||
<label class="text-sm font-bold text-slate-400 uppercase tracking-widest">Live Target IP Address</label>
|
||||
<input class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" placeholder="192.168.1.100">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase">Live Target IP Address</span>
|
||||
<input x-model="formData[4].liveIp" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" placeholder="192.168.1.100">
|
||||
</div>
|
||||
<div class="p-6 space-y-2">
|
||||
<label class="text-sm font-bold text-slate-400 uppercase tracking-widest">Temporary Password</label>
|
||||
<input class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="password" placeholder="••••••••">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase">Temporary Password</span>
|
||||
<input x-model="formData[4].livePassword" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="password" placeholder="••••••••">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Live Setup -->
|
||||
|
||||
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="bg-[#1e293b] border-t bottom-0 w-full rounded-3xl absolute border-slate-700 p-6 flex items-center justify-between">
|
||||
<div x-show="step === 1" class="px-8 py-3"></div>
|
||||
<button @click="goToPrevStep()" x-show="step > 1" class="px-8 py-3 text-slate-400 hover:text-white font-bold transition-all">Back</button>
|
||||
<button @click="goToNextStep()" class="px-10 py-3 bg-fuchsia-600 hover:bg-fuchsia-500 rounded-xl font-bold transition-all shadow-lg shadow-fuchsia-600/20">Continue</button>
|
||||
<button @click="goToPrevStep()" x-show="step > 1" x-cloak class="px-8 py-3 text-slate-400 font-bold rounded-xl hover:text-white transition-colors duration-200" x-text="i18n.back"></button>
|
||||
|
||||
<button @click="goToNextStep()"
|
||||
x-show="step != 100"
|
||||
:disabled="!isStepValid()"
|
||||
class="px-10 py-3 text-white font-bold rounded-xl scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100" x-text="i18n.continue"></button>
|
||||
|
||||
<button
|
||||
@click="startDeployment()"
|
||||
x-show="step === 4"
|
||||
x-cloak
|
||||
:disabled="!isStepValid() || discoveryLoading"
|
||||
class="px-10 py-3 text-white font-bold rounded-xl flex items-center gap-3 scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">
|
||||
<span x-text="discoveryLoading ? 'Discovering...' : 'Start Discovery'"></span>
|
||||
<svg x-show="discoveryLoading" aria-hidden="true" class="w-5 h-5 text-white animate-spin" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
||||
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
+54
-11
@@ -16,6 +16,7 @@
|
||||
</head>
|
||||
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
@keyframes pulse-slow {
|
||||
0%, 100% { transform: scale(1); filter: drop-shadow(0 0 20px rgba(14, 165, 233, 0.2)); }
|
||||
50% { transform: scale(1.03); filter: drop-shadow(0 0 40px rgba(192, 38, 211, 0.4)); }
|
||||
@@ -24,24 +25,66 @@
|
||||
.text-shadow-glow { text-shadow: 0 0 15px rgba(56, 189, 248, 0.4); }
|
||||
</style>
|
||||
|
||||
<body class="bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
|
||||
<script>
|
||||
function numbusIndex() {
|
||||
return {
|
||||
step: 1,
|
||||
formData: {
|
||||
1: { language: '' },
|
||||
},
|
||||
isStepValid() {
|
||||
const currentStepData = this.formData[this.step];
|
||||
if (!currentStepData) return true;
|
||||
return Object.values(currentStepData).every(value => !!value);
|
||||
},
|
||||
redirect() {
|
||||
localStorage.setItem('wizard_language', this.formData[1].language);
|
||||
window.location.href = 'preparation.html';
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<body x-data="numbusIndex()" class="bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
|
||||
<!-- Welcome screen -->
|
||||
<div class="justify-center flex flex-col items-center sm:p-6">
|
||||
<img class="auto drop-shadow-2xl animate-pulse-slow mt-4 w-32 h-32 sm:mt-0 sm:w-48 sm:h-48 2xl:w-64 2xl:h-64" src="/media/logo.png" alt="Numbus Logo">
|
||||
<h1 class="text-center font-extrabold tracking-tight p-4 pb-2 pt-2 text-3xl sm:p-2 sm:pt-4 sm:pb-7 sm:text-4xl md:text-5xl 2xl:text-6xl 2xl:pt-8 2xl:pb-10">Welcome to <span class="text-sky-400 animate-pulse-slow text-shadow-glow">NUMBUS</span></h1>
|
||||
<p class="text-center text-sm pl-4 pr-4 p-2 sm:text-lg sm:pl-10 sm:pr-10 md:text-xl xl:text-2xl 2xl:p-4">Transform your device into a <strong>reliable and private</strong> appliance <br class="invisible md:visible"> using the power of open-source software.</p>
|
||||
<p class="text-center text-sm pl-4 pr-4 pt-2 sm:text-lg sm:pl-10 sm:pr-10 md:text-xl xl:text-2xl 2xl:p-4">You will be <strong>guided</strong> through the configuration process.</p>
|
||||
<img class="auto drop-shadow-2xl animate-pulse-slow mt-4 w-32 h-32 sm:mt-0 sm:w-48 sm:h-48 2xl:w-64 2xl:h-64 2xl:mt-10" src="/media/logo.png" alt="Numbus Logo">
|
||||
<h1 class="text-center font-extrabold tracking-tight p-4 pb-2 pt-2 text-3xl sm:p-2 sm:pb-5 sm:pt-4 sm:text-4xl md:text-5xl 2xl:text-6xl 2xl:pt-8 2xl:pb-10">Welcome to <span class="text-sky-400 animate-pulse-slow text-shadow-glow">NUMBUS</span></h1>
|
||||
<p class="text-center text-base pl-4 pr-4 p-1 sm:text-lg sm:pl-5 sm:pr-5 md:text-xl xl:p-2 xl:text-2xl 2xl:p-4"><strong>Transform</strong> your device into a <strong>reliable and private</strong> appliance <br class="hidden lg:block">using the <strong>power</strong> of open-source software.</p>
|
||||
<p class="text-center text-base pl-4 pr-4 p-1 sm:text-lg sm:pl-10 sm:pr-10 md:text-xl xl:p-2 xl:text-2xl 2xl:p-4">You will be <strong>guided</strong> through the configuration process.</p>
|
||||
|
||||
<div class="bg-amber-500/20 border border-amber-500/30 rounded-xl flex text-left items-center mx-auto gap-5 mt-5 mb-5 ml-4 mr-4 p-2 sm:p-4 sm:ml-20 sm:mr-20 sm:mt-10 max-w-3xl">
|
||||
<span class="bg-amber-500 rounded-full shrink-0 p-1 px-2">
|
||||
<div class="bg-amber-500/20 border border-amber-500/30 rounded-xl flex text-left items-center mx-auto gap-5 mt-3 mb-4 ml-4 mr-4 p-2 sm:p-4 sm:ml-20 sm:mr-20 sm:mt-6 sm:mb-8 2xl:mb-10 max-w-3xl">
|
||||
<span class="bg-amber-500 rounded-full shrink-0 p-1 px-2 ml-2">
|
||||
<i class="mdi mdi-shield-lock text-slate-900 text-md sm:text-lg md:text-2xl 2xl:text-4xl"></i>
|
||||
</span>
|
||||
<p class="text-amber-200/90 italic text-xs sm:text-md md:text-lg xl:text-xl"><strong>Privacy First:</strong> No data entered here ever leaves your device. <br> This configurator runs entirely locally in your browser and is fully private.
|
||||
<p class="text-amber-200/90 italic text-sm sm:text-base md:text-lg xl:text-xl"><strong>Privacy First:</strong> No data entered here ever leaves your device. <br> This configurator runs entirely locally in your browser and is fully private.
|
||||
</div>
|
||||
|
||||
<span class="animate-pulse-slow drop-shadow-2xl pt-6 pb-4 pl-4 pr-4 sm:p-12 2xl:p-14">
|
||||
<a class="text-shadow-glow px-10 py-4 bg-fuchsia-600 hover:bg-fuchsia-500 rounded-full text-sm sm:text-xl font-bold transition-all transform hover:scale-105 shadow-lg shadow-fuchsia-600/20" href="/pages/preparation.html">Get Started</a>
|
||||
<div class="relative max-w-xl group" x-data="{ focused: false }">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none sm:pl-4 lg:pl-5">
|
||||
<i class="mdi mdi-translate text-white text-lg transition-colors group-focus-within:text-fuchsia-400 sm:text-xl xl:text-2xl"></i>
|
||||
</div>
|
||||
<select
|
||||
x-model="formData[1].language"
|
||||
@change="if (formData[1].language) $el.blur()"
|
||||
class="block text-sm sm:text-base lg:text-lg 2xl:text-xl text-slate-200 w-full pl-10 pr-8 py-2 sm:pl-12 sm:pr-10 sm:py-4 lg:pl-15 lg:pr-13 2xl:pl-20 2xl:pr-20 bg-slate-900 border border-slate-700 rounded-2xl appearance-none transition-all cursor-pointer focus:outline-none focus:ring-2 focus:ring-fuchsia-500 focus:border-fuchsia-500">
|
||||
<option value="">Select a language</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
</select>
|
||||
<div class="absolute inset-y-0 right-0 pr-2 flex items-center pointer-events-none sm:pr-3 lg:pr-4">
|
||||
<i class="mdi mdi-chevron-down text-slate-500 group-hover:text-slate-300 transition-colors"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="animate-pulse-slow drop-shadow-2xl p-4 sm:p-6 xl:p-8 2xl:p-10">
|
||||
<button
|
||||
x-cloak
|
||||
@click="redirect()"
|
||||
:disabled="!isStepValid()"
|
||||
class="text-shadow-glow px-5 py-2 bg-fuchsia-600 rounded-full text-sm font-bold transition-all transform shadow-lg shadow-fuchsia-600/20 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 hover:scale-105 hover:bg-fuchsia-500 sm:px-7 sm:py-3 xl:px-10 xl:py-4 sm:text-xl">Get Started</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
+177
-110
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html :lang="lang" x-data="numbusPreparation()">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -16,7 +16,7 @@
|
||||
<link rel="icon" href="/media/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body x-data="numbusPreparation()" class="p-4 bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
<body class="p-4 bg-[#0f172a] text-slate-100 min-h-screen font-sans selection:bg-fuchsia-500/30">
|
||||
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
@@ -24,14 +24,93 @@
|
||||
|
||||
<script>
|
||||
function numbusPreparation() {
|
||||
const langConst = localStorage.getItem('wizard_language') || 'en';
|
||||
return {
|
||||
lang: langConst,
|
||||
get i18n() { return this.translations[this.lang] },
|
||||
step: 1,
|
||||
discoveryLoading: false,
|
||||
formData: {
|
||||
1: { language: 'French', country: 'France', timeZone: 'Europe/Paris' },
|
||||
2: { deviceType: '' },
|
||||
3: { deploymentMode: '', gitUrl: '', gitUsername: '', gitPassword: '' },
|
||||
4: { liveIp: '', livePassword: '' },
|
||||
1: { language: langConst, locale: '', timeZone: '', },
|
||||
2: { deviceType: '', },
|
||||
3: { deploymentMode: '', },
|
||||
4: { liveIp: '', livePassword: '', },
|
||||
},
|
||||
translations: {
|
||||
en: {
|
||||
// Page
|
||||
step1_title: "Step 1 : Preparation",
|
||||
back: "Back",
|
||||
continue: "Continue",
|
||||
|
||||
// Card 1 : Language
|
||||
card1_title: "Language",
|
||||
card1_subtitle: "Set your regional preferences to ensure <b>correct</b> time and language display.",
|
||||
card1_option1_title: "Locale",
|
||||
card1_option1_desc: "Provide the locale of this device. The locale is composed of the language and the country. Find a list of locales <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">here</a>.",
|
||||
card1_option1_placeholder: "en_US",
|
||||
card1_option2_title: "Time zone",
|
||||
card1_option2_desc: "Provide the time zone in which the device is located. Find your time zone at <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">here</a>.",
|
||||
card1_option2_placeholder: "America/Los_Angeles",
|
||||
|
||||
// Card 2 : Device Type
|
||||
card2_title: "Device Type",
|
||||
card2_subtitle: "Select the <b>device type</b> for your new Numbus machine that matches <b>your needs</b>.",
|
||||
card2_option1_title: "Server",
|
||||
card2_option1_subtitle: "Your own Cloud at Home.",
|
||||
card2_option1_desc: "A versatile home cloud solution for hosting containers, media, and automated services.",
|
||||
card2_option2_title: "Backup Server",
|
||||
card2_option2_subtitle: "Backup all Numbus devices and monitor your servers.",
|
||||
card2_option2_desc: "An all-in-one backup solution for all your Numbus devices, with monitoring tools.",
|
||||
card2_option3_title: "Computer",
|
||||
card2_option3_subtitle: "A workstation powered by leading open-source software.",
|
||||
card2_option3_desc: "A polished workstation powered by leading open-source software.",
|
||||
card2_option4_title: "TV",
|
||||
card2_option4_subtitle: "Your TV, your way. No spying on you.",
|
||||
card2_option4_desc: "A computer -and all its accompanying advantages- but with a slick, familiar TV interface.",
|
||||
card2_option5_title: "Game Console",
|
||||
card2_option5_subtitle: "Play games and nothing else.",
|
||||
card2_option5_desc: "A true console-like experience using Steam Bigscreen.",
|
||||
card2_option6_title: "And more ?",
|
||||
card2_option6_subtitle: "Consider <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">contributing !</a> 🌟",
|
||||
},
|
||||
fr: {
|
||||
// Page
|
||||
step1_title: "Étape 1 : Préparation",
|
||||
back: "Retour",
|
||||
continue: "Continuer",
|
||||
|
||||
// Card 1 : Language
|
||||
card1_title: "Langage",
|
||||
card1_subtitle: "Définissez vos préférences régionales pour assurer un affichage <b>correct</b> de l'heure et de la langue.",
|
||||
card1_option1_title: "Paramètre régional",
|
||||
card1_option1_desc: "Fournissez la locale de cet appareil. La locale est composée de la langue et du pays. Trouvez une liste des locales <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">ici</a>.",
|
||||
card1_option1_placeholder: "fr_FR",
|
||||
card1_option2_title: "Fuseau horaire",
|
||||
card1_option2_desc: "Fournissez le fuseau horaire dans lequel se trouve l'appareil. Trouvez votre fuseau horaire <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">ici</a>.",
|
||||
card1_option2_placeholder: "Europe/Paris",
|
||||
|
||||
// Card 2 : Type d'appareil
|
||||
card2_title: "Type d'appareil",
|
||||
card2_subtitle: "Sélectionnez le <b>type d'appareil</b> pour votre nouvelle machine Numbus qui correspond à <b>vos besoins</b>.",
|
||||
card2_option1_title: "Serveur",
|
||||
card2_option1_subtitle: "Votre propre Cloud à la maison.",
|
||||
card2_option1_desc: "Une solution cloud domestique polyvalente pour l'hébergement de conteneurs, de médias et de services automatisés.",
|
||||
card2_option2_title: "Serveur de Sauvegarde",
|
||||
card2_option2_subtitle: "Sauvegardez vos appareils Numbus et surveillez vos serveurs.",
|
||||
card2_option2_desc: "Une solution de sauvegarde tout-en-un pour tous vos appareils Numbus, avec des outils de surveillance.",
|
||||
card2_option3_title: "Ordinateur",
|
||||
card2_option3_subtitle: "Une station de travail propulsée par l'open-source.",
|
||||
card2_option3_desc: "Une station de travail polie propulsée par les meilleurs logiciels open-source.",
|
||||
card2_option4_title: "TV",
|
||||
card2_option4_subtitle: "Votre télé, à votre façon. Sans espionnage.",
|
||||
card2_option4_desc: "Un ordinateur -et tous ses avantages- mais avec une interface TV fluide et familière.",
|
||||
card2_option5_title: "Console de Jeu",
|
||||
card2_option5_subtitle: "Du gaming et rien d'autre.",
|
||||
card2_option5_desc: "Une authentique expérience de console grâce à l'interface Steam Bigscreen.",
|
||||
card2_option6_title: "Mais encore ?",
|
||||
card2_option6_subtitle: "Pensez à <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_UTC_offsets\" class=\"text-sky-400 underline font-semibold\">contribuer !</a> 🌟",
|
||||
}
|
||||
},
|
||||
isStepValid() {
|
||||
const currentStepData = this.formData[this.step];
|
||||
@@ -53,9 +132,8 @@
|
||||
console.log("Discovery started with:", data);
|
||||
|
||||
const payload = {
|
||||
internationalisation: {
|
||||
language: data[1].language,
|
||||
country: data[1].country,
|
||||
internationalization: {
|
||||
locale: data[1].locale,
|
||||
time_zone: data[1].timeZone
|
||||
},
|
||||
device: {
|
||||
@@ -77,7 +155,7 @@
|
||||
const response = await fetch('/discovery', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/yaml' },
|
||||
body: jsyaml.dump(payload)
|
||||
body: jsyaml.dump(payload, { quotingType: '"', forceQuotes: true })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@@ -99,6 +177,7 @@
|
||||
const response = await fetch('/config/hardware.yaml', { cache: 'no-store' });
|
||||
if (response.ok) {
|
||||
clearInterval(interval);
|
||||
localStorage.setItem('deployment_mode', this.formData[3].deploymentMode);
|
||||
window.location.href = 'configuration.html';
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -117,7 +196,7 @@
|
||||
<a class="font-bold text-2xl tracking-tight bg-gradient-to-r from-sky-400 to-fuchsia-500 bg-clip-text text-transparent uppercase" target="_blank" href="https://numbus.eu">NUMBUS</a>
|
||||
</div>
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<h1 class="sm:text-2xl sm:pr-20 lg:pr-0 text-xl text-slate-200 font-bold flex items-center">Step 1 - Preparation</h1>
|
||||
<h1 class="sm:text-2xl sm:pr-20 lg:pr-0 text-xl text-slate-200 font-bold flex items-center" x-text="i18n.step1_title"></h1>
|
||||
</div>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center my-auto">
|
||||
<button class="h-auto mdi mdi-menu text-slate-100 text-2xl sm:hidden pr-8" aria-label="Menu with links"></button>
|
||||
@@ -135,89 +214,46 @@
|
||||
<div class="bg-[#1e293b] border border-slate-700 rounded-3xl h-[calc(100vh-10rem)] max-w-[60vw] mx-auto relative">
|
||||
|
||||
<!-- Step 1: Language & Region -->
|
||||
<div x-show="step === 1" x-cloak class="pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Language</h2>
|
||||
<div x-show="step === 1" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400" x-text="i18n.card1_title"></h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-200">Set your regional preferences to ensure <b>correct</b> time and language display.</p>
|
||||
<div class="pl-5 pr-5 pt-10 pb-10 grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-lg font-semibold text-slate-300">System Language</label>
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic">Select the primary language for the operating system and management interfaces.</p>
|
||||
</div>
|
||||
</button>
|
||||
<p class="p-5 pb-0 text-xl text-slate-200" x-html="i18n.card1_subtitle"></p>
|
||||
<div class="p-5 grid grid-cols-2 gap-4">
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option1_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option1_desc"></p>
|
||||
</div>
|
||||
<select x-model="formData[1].language" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none transition-all appearance-none cursor-pointer">
|
||||
<option value="FR">French</option>
|
||||
<option value="EN">English</option>
|
||||
<option value="DE">German</option>
|
||||
<option value="IT">Italian</option>
|
||||
<option value="ES">Spanish</option>
|
||||
</select>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].locale" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option1_placeholder">
|
||||
</div>
|
||||
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-lg font-semibold text-slate-300">Country</label>
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic">Select the country where you are located to help define the device's locale.</p>
|
||||
</div>
|
||||
</button>
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.card1_option2_title"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.card1_option2_desc"></p>
|
||||
</div>
|
||||
<select x-model="formData[1].country" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none appearance-none cursor-pointer">
|
||||
<option value="fr_FR">France</option>
|
||||
<option value="de_DE">Germany</option>
|
||||
<option value="it_IT">Italy</option>
|
||||
<option value="en_GB">United Kingdom</option>
|
||||
<option value="en_US">United States</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2 relative" x-data="{ infoBubbleOpen: false }">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-lg font-semibold text-slate-300">Time zone</label>
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic">Select the time zone in which the device is located. <br> Don't know your time zone ? Find it at <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_UTC_offsets" class="text-sky-400 underline font-semibold">Wikipedia</a>.</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<select x-model="formData[1].timeZone" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none appearance-none cursor-pointer">
|
||||
<option value="Europe/Paris">Europe/Paris</option>
|
||||
<option value="Europe/Berlin">Europe/Berlin</option>
|
||||
<option value="Europe/London">Europe/London</option>
|
||||
<option value="America/New_York">America/New_York</option>
|
||||
<option value="America/Los_Angeles">America/Los_Angeles</option>
|
||||
<option value="UTC">UTC</option>
|
||||
</select>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<input x-model="formData[1].timeZone" class="w-full bg-slate-900 border border-slate-700 rounded-xl p-4 focus:ring-2 focus:ring-fuchsia-500 outline-none placeholder:text-slate-500/40" type="text" :placeholder="i18n.card1_option2_placeholder">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,16 +262,16 @@
|
||||
|
||||
|
||||
<!-- Device Type -->
|
||||
<div x-show="step === 2" x-cloak class="pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Device Type</h2>
|
||||
<div x-show="step === 2" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400" x-text="i18n.card2_title"></h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-300">Select the <b>device type</b> for your new Numbus machine that matches <b>your needs</b>.</p>
|
||||
<div class="pl-5 pr-5 pt-10 pb-10 grid grid-cols-2 gap-4">
|
||||
<p class="p-5 pb-0 text-xl text-slate-300" x-html="i18n.card2_subtitle"></p>
|
||||
<div class="p-5 grid grid-cols-2 gap-4">
|
||||
<button @click="formData[2].deviceType = 'server'" :class="formData[2].deviceType === 'server' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-server-light.svg" alt="Numbus Server icon">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center gap-2 flex">
|
||||
<span class="font-bold text-2xl">Numbus Server</span>
|
||||
<span class="font-bold text-xl" x-text="i18n.card2_option1_title"></span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
@@ -247,18 +283,18 @@
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">A versatile home cloud solution for hosting containers, media, and automated services.</p>
|
||||
<p class="text-slate-300 text-lg italic" x-text="i18n.card2_option1_desc"></p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">Your own Cloud at Home.</p>
|
||||
<p class="text-sm transition-colors" x-text="i18n.card2_option1_subtitle"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="formData[2].deviceType = 'backup-server'" :class="formData[2].deviceType === 'backup-server' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-backup-server-light.svg" alt="Numbus Backup Server icon">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-2xl mb-1">Numbus Backup Server</span>
|
||||
<span class="font-bold text-xl mb-1" x-text="i18n.card2_option2_title"></span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
@@ -270,18 +306,18 @@
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">An all-in-one backup solution for all your Numbus devices, with monitoring tools.</p>
|
||||
<p class="text-slate-300 text-lg italic" x-text="i18n.card2_option2_desc"></p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">Backup all Numbus devices and monitor your servers.</p>
|
||||
<p class="text-sm transition-colors" x-text="i18n.card2_option2_subtitle"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="formData[2].deviceType = 'computer'" :class="formData[2].deviceType === 'computer' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-computer-light.svg" alt="Numbus Computer icon">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-2xl mb-1">Numbus Computer</span>
|
||||
<span class="font-bold text-xl mb-1" x-text="i18n.card2_option3_title"></span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
@@ -293,18 +329,18 @@
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">A polished workstation powered by leading open-source software.</p>
|
||||
<p class="text-slate-300 text-lg italic" x-text="i18n.card2_option3_desc"></p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">A workstation powered by leading open-source software.</p>
|
||||
<p class="text-sm transition-colors" x-text="i18n.card2_option3_subtitle"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="formData[2].deviceType = 'tv'" :class="formData[2].deviceType === 'tv' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-tv-light.svg" alt="Numbus TV icon">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-2xl mb-1">Numbus TV</span>
|
||||
<span class="font-bold text-xl mb-1" x-text="i18n.card2_option4_title"></span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
@@ -316,13 +352,44 @@
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic">A computer -and all its accompanying advantages- but with a slick, familiar TV interface.</p>
|
||||
<p class="text-slate-300 text-lg italic" x-text="i18n.card2_option4_desc"></p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors">Your TV, your way. No spying on you.</p>
|
||||
<p class="text-sm transition-colors" x-text="i18n.card2_option4_subtitle"></p>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="formData[2].deviceType = 'console'" :class="formData[2].deviceType === 'console' ? 'bg-fuchsia-600/20 border-fuchsia-500' : 'bg-slate-900 border-slate-700'" class="flex items-center gap-4 p-6 border rounded-2xl transition-all text-left hover:bg-fuchsia-600/20 hover:border-fuchsia-500">
|
||||
<img class="w-12 h-12 flex-shrink-0" src="/media/light/numbus-console-light.svg" alt="Numbus Console icon">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2">
|
||||
<span class="font-bold text-xl mb-1" x-text="i18n.card2_option5_title"></span>
|
||||
<span class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<i class="mdi mdi-information text-lg text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl z-50 text-sm leading-relaxed">
|
||||
<p class="text-slate-300 text-lg italic" x-text="i18n.card2_option5_desc"></p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm transition-colors" x-text="i18n.card2_option5_subtitle"></p>
|
||||
</div>
|
||||
</button>
|
||||
<div class="flex items-center gap-4 p-6 border border-slate-700 rounded-2xl bg-slate-900/60">
|
||||
<div x-data="{ infoBubbleOpen: false }">
|
||||
<div class="items-center flex gap-2 mx-auto">
|
||||
<span class="font-bold text-2xl mb-1" x-text="i18n.card2_option6_title"></span>
|
||||
</div>
|
||||
<p class="text-lg transition-colors mx-auto" x-html="i18n.card2_option6_subtitle"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Device Type -->
|
||||
@@ -330,7 +397,7 @@
|
||||
|
||||
|
||||
<!-- Deployment Mode -->
|
||||
<div x-show="step === 3" x-cloak class="pl-3 pr-3">
|
||||
<div x-show="step === 3" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Deployment Mode</h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 text-xl text-slate-300">Select your <b>preferred</b> deployment mode. Non-interactive <b>requires</b> a ready-to-go configuration hosted on a <b>git platform</b>.</p>
|
||||
@@ -388,7 +455,7 @@
|
||||
|
||||
|
||||
<!-- Live Setup -->
|
||||
<div x-show="step === 4" x-cloak class="pl-3 pr-3">
|
||||
<div x-show="step === 4" x-cloak class="h-full overflow-y-auto pb-32 pl-3 pr-3">
|
||||
<h2 class="p-5 pt-6 text-4xl font-bold text-sky-400">Live Setup</h2>
|
||||
<div class="p-0.5 bg-slate-700 rounded-3xl w-[97%] mx-auto"></div>
|
||||
<p class="p-5 pb-10 text-xl text-slate-300">Provide the <b>necessary information</b> to connect to the device. It needs to be in a <b>NixOS live environment</b>.</p>
|
||||
@@ -410,12 +477,12 @@
|
||||
<!-- Footer -->
|
||||
<div class="bg-[#1e293b] border-t bottom-0 w-full rounded-3xl absolute border-slate-700 p-6 flex items-center justify-between">
|
||||
<div x-show="step === 1" class="px-8 py-3"></div>
|
||||
<button @click="goToPrevStep()" x-show="step > 1" x-cloak class="px-8 py-3 text-slate-400 font-bold rounded-xl hover:text-white transition-colors duration-200">Back</button>
|
||||
<button @click="goToPrevStep()" x-show="step > 1" x-cloak class="px-8 py-3 text-slate-400 font-bold rounded-xl hover:text-white transition-colors duration-200" x-text="i18n.back"></button>
|
||||
|
||||
<button @click="goToNextStep()"
|
||||
x-show="step != 4"
|
||||
:disabled="!isStepValid()"
|
||||
class="px-10 py-3 text-white font-bold rounded-xl scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">Continue</button>
|
||||
class="px-10 py-3 text-white font-bold rounded-xl scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100" x-text="i18n.continue"></button>
|
||||
|
||||
<button
|
||||
@click="startDiscovery()"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Numbus installation process
|
||||
|
||||
### Initialization
|
||||
|
||||
For **testing** purposes :
|
||||
|
||||
``` bash
|
||||
export DEBUG=1 BRANCH="testing" && curl -sSL https://gittea.dev/numbus/numbus/raw/branch/${BRANCH:-production}/script/start.sh | bash
|
||||
```
|
||||
|
||||
For **production** :
|
||||
|
||||
``` bash
|
||||
curl -sSL https://gittea.dev/numbus/numbus/raw/branch/${BRANCH:-production}/script/start.sh | bash
|
||||
```
|
||||
|
||||
### Preparation
|
||||
|
||||
1. Language
|
||||
2. Device Type
|
||||
3. Deployment Mode
|
||||
4. Live Setup
|
||||
|
||||
### Configuration
|
||||
|
||||
1. Repository configuration
|
||||
|
||||
If the repository contains configuration, it will **auto-fill** the next steps. The user can **skip** to the installation step or **change** the settings he wants.
|
||||
|
||||
If the repository doesn't contain configuration, the script will **automatically export** the final configuration.
|
||||
|
||||
2. Disks partitioning
|
||||
|
||||
#### Server device
|
||||
|
||||
3. Networking
|
||||
4. Remote access
|
||||
5. Mail alerts
|
||||
6. Services
|
||||
7. Users & Groups
|
||||
8. Security
|
||||
9. Backup
|
||||
|
||||
#### Backup Server device
|
||||
|
||||
3. Networking
|
||||
4. Remote access
|
||||
5. Mail alerts
|
||||
6. Services
|
||||
|
||||
#### TV device
|
||||
|
||||
3.
|
||||
4.
|
||||
5.
|
||||
|
||||
#### Workstation device
|
||||
|
||||
3.
|
||||
4.
|
||||
5.
|
||||
|
||||
#### Game Console device
|
||||
|
||||
3.
|
||||
4.
|
||||
5.
|
||||
|
||||
### Review
|
||||
|
||||
### Installation
|
||||
|
||||
|
||||
|
||||
### Post-installation
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Buttons
|
||||
|
||||
1. Button with :
|
||||
- an offset ring on focus
|
||||
- a small shadow
|
||||
- a nice transition
|
||||
- a disabled feature
|
||||
|
||||
```html
|
||||
px-10 py-3 bg-fuchsia-600 rounded-xl font-bold shadow-lg shadow-fuchsia-600/30 transition duration-200 hover:bg-fuchsia-500 focus:outline-none focus:ring-2 focus:ring-fuchsia-500 focus:ring-offset-2 focus:ring-offset-slate-700 disabled:opacity-50 disabled:cursor-not-allowed
|
||||
```
|
||||
|
||||
2. Button with :
|
||||
- a grow transformation on hover
|
||||
- a gradient
|
||||
- a small shadow
|
||||
- a nice transition
|
||||
- a disabled feature
|
||||
|
||||
```html
|
||||
px-10 py-3 text-white font-bold rounded-xl scale-100 transition duration-200 ease-in bg-gradient-to-r from-fuchsia-500 via-fuchsia-600 to-fuchsia-700 hover:scale-105 hover:bg-gradient-to-br shadow-lg shadow-fuchsia-500/30 dark:shadow-lg dark:shadow-fuchsia-800/80 disabled:opacity-50 disabled:cursor-not-allowed
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
# Color palette
|
||||
|
||||
|Usage|Name|Tailwind code|Matching border|
|
||||
|-|-|-|-|
|
||||
|Body background|Slate|slate-900|N/A|
|
||||
|Cards Background|Slate|slate-800|slate-700|
|
||||
|Accent|Fuchsia|fuchsia-600|fuchsia-600
|
||||
|Warning|Amber|amber-500|amber-400|
|
||||
|Success|Emerald|emerald-500|emerald-400|
|
||||
|Failure|Rose|rose-600|rose-500|
|
||||
@@ -0,0 +1,8 @@
|
||||
# Guidelines
|
||||
|
||||
|Type|Importance|Classes|
|
||||
|-|-|-|
|
||||
|Title|1|text-4xl font-bold text-sky-400|
|
||||
|Title|2|text-4xl font-bold text-sky-400|
|
||||
|Title|3|text-4xl font-bold text-sky-400|
|
||||
|Title|4|text-4xl font-bold text-sky-400|
|
||||
@@ -0,0 +1,43 @@
|
||||
# Taglines, subtitles and descriptions
|
||||
|
||||
## Global
|
||||
|
||||
**Tagline :** Reclaim Your Digital Independence.
|
||||
|
||||
## Devices
|
||||
|
||||
### Numbus Server
|
||||
The Infrastructure
|
||||
|
||||
**Tagline :** Reclaim Your Cloud.
|
||||
|
||||
**Subtitle :** Professional-grade hosting, strictly kept under your roof.
|
||||
|
||||
**Detailed Description :** The Numbus Server is the heartbeat of your digital sovereignty. Built on an immutable NixOS foundation, it provides a rock-solid environment to host the services you rely on—from Nextcloud and Passbolt to Home Assistant and Frigate. Whether you are a small business looking to internalize your data or a power user securing your smart home, the Numbus Server delivers containerized efficiency and VM flexibility without the "Big Tech" tax. It’s not just a server; it’s your private corner of the internet, accessible from anywhere but controlled only by you.
|
||||
|
||||
### Numbus Backup Server
|
||||
The Guardian
|
||||
|
||||
**Tagline :** Your Digital Safety Net.
|
||||
|
||||
**Subtitle :** Automated, high-efficiency protection for your entire ecosystem.
|
||||
|
||||
**Detailed Description :** Data loss and downtime aren't just frustrating; they're disastrous. The Numbus Backup Server is your ecosystem's ultimate guardian. It works silently in the background, automatically pulling high-efficiency backups from your Numbus devices the moment they start charging. But it does more than just store files—it actively acts as a watchdog. By constantly monitoring your Numbus Server and other critical network devices, it immediately alerts you the second something goes offline. It is automated peace of mind for your entire digital infrastructure.
|
||||
|
||||
### Numbus Computer
|
||||
The Workstation
|
||||
|
||||
**Tagline :** The No-Bullshit Workstation.
|
||||
|
||||
**Subtitle :** A modern, privacy-respecting machine built for work, creation, and play — without the corporate bloat.
|
||||
|
||||
**Description :** Experience computing the way it was meant to be: entirely under your control. The Numbus Computer is a powerful workstation that strips away unwanted telemetry, forced updates, and bundled adware. Under the hood, it harnesses the unparalleled stability of NixOS, but we've hidden the complexity behind a sleek, intuitive interface. Our default apps make managing drivers, software, and system options a breeze. Whether you are coding, browsing, or gaming via Proton, the Numbus Computer delivers a snappy, secure, and truly "libre" desktop experience that respects your privacy while delivering the raw power of the hardware you paid for.
|
||||
|
||||
### Numbus TV
|
||||
The Experience
|
||||
|
||||
**Tagline :** Entertainment, Liberated.
|
||||
|
||||
**Subtitle :** A premium cinematic experience free from trackers and forced subscriptions.
|
||||
|
||||
**Detailed Description :** The Numbus TV reclaims your living room. Unlike "Smart" TVs that have essentially become surveillance machines and clutter your screen with unwanted ads, the Numbus TV offers a clean, classy interface powered by KDE Bigscreen. It’s a full-power PC in a TV’s body, allowing you to stream, browse, and play on your own terms. It doesn't force you into a specific ecosystem; it simply provides a beautiful, privacy-hardened portal to your favorite media. No spying, no "walled gardens"—just the big screen experience, perfected.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Tooltips
|
||||
|
||||
## For an element on the left
|
||||
|
||||
``` html
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.YOUR_TEXT"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute left-full top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.YOUR_TEXT"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<!-- Your element -->
|
||||
<div>
|
||||
```
|
||||
|
||||
## For an element on the right
|
||||
|
||||
``` html
|
||||
<div class="p-6 space-y-2 items-center" x-data="{ infoBubbleOpen: false }">
|
||||
<button class="relative" @mouseenter="infoBubbleOpen = true" @mouseleave="infoBubbleOpen = false">
|
||||
<span class="text-base font-bold text-center text-slate-300 uppercase" x-text="i18n.YOUR_TEXT"></span>
|
||||
<div
|
||||
x-show="infoBubbleOpen"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-cloak class="absolute right-6 top-1/2 -translate-y-1/2 ml-3 w-80 p-5 bg-slate-800 rounded-xl border border-slate-600 shadow-2xl shadow-slate-900 z-50">
|
||||
<p class="text-slate-200 text-lg italic" x-html="i18n.YOUR_TEXT"></p>
|
||||
</div>
|
||||
<i class="mdi mdi-information h-auto text-fuchsia-600 cursor-help transition-colors duration-300 hover:text-fuchsia-500"></i>
|
||||
</button>
|
||||
<!-- Your element -->
|
||||
<div>
|
||||
```
|
||||
Reference in New Issue
Block a user