From 1f0a0377235a0670cd817b3433fb628423a67b4c Mon Sep 17 00:00:00 2001 From: Antoine Pelletier Date: Wed, 23 Sep 2026 10:21:51 +0200 Subject: [PATCH] feat: add new VM --- README.md | 11 ++++++----- db/migrations/20260923101500_add_cs_477_vm.sql | 11 +++++++++++ frontend/src/views/VmsView.vue | 5 +++-- 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 db/migrations/20260923101500_add_cs_477_vm.sql diff --git a/README.md b/README.md index 06a1d28..705a928 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A page to start and stop the VMs of the server, one at a time. **Rust** backend (axum + sqlx + aide) driving **libvirt**, serving a **Vue 3** frontend (TypeScript + vue-query + shadcn-vue), on **PostgreSQL** with **dbmate** migrations. -The two VMs (`win11` and `arch-hyprland`) share the same hardware and may never run +The VMs (`win11`, `arch-hyprland` and `cs-477`) share the same hardware and may never run together: starting one while the other is up asks first, shuts that one down, waits for it to be really off, and only then boots the wanted one. The state is read from libvirt on every request, so a VM shut down from inside the guest shows up as @@ -21,7 +21,7 @@ is the only thing that changes between the two. # 1. Start the development database cd dev-db && docker compose up -d && cd .. psql -h localhost -U postgres -c 'CREATE DATABASE app_template' -dbmate up # creates the schema and inserts the two VMs +dbmate up # creates the schema and inserts the VMs # 2. Configure the app cp config.example.yml config.yml # then set vm.uri, see below @@ -70,7 +70,7 @@ build dependency, and the uri alone decides local or remote. It needs `virsh` on │ │ └── hypervisor/ libvirt (virsh) and mock implementations │ └── utils/ configuration ├── db/ -│ ├── migrations/ dbmate migrations (the two VMs are inserted by one) +│ ├── migrations/ dbmate migrations (the VMs are inserted by them) │ └── schema.sql dump regenerated by dbmate, do not edit by hand ├── dev-db/ postgres + adminer for development └── frontend/ @@ -103,8 +103,9 @@ documentation sits right next to them (`fn *_docs`). ### The `vms` table It only holds what libvirt does not know: how to present a domain in the UI -(`display_name`, `icon`, `position`). The live state is never stored. Adding a third -VM is one insert, no redeploy — but the one-at-a-time rule then applies to all three. +(`display_name`, `icon`, `position`). The live state is never stored. Adding a VM is +one insert (see the migrations), no redeploy — but the one-at-a-time rule then +applies to it as well. ### Routes diff --git a/db/migrations/20260923101500_add_cs_477_vm.sql b/db/migrations/20260923101500_add_cs_477_vm.sql new file mode 100644 index 0000000..64d1ecd --- /dev/null +++ b/db/migrations/20260923101500_add_cs_477_vm.sql @@ -0,0 +1,11 @@ +-- migrate:up +-- Third VM: an Ubuntu server guest. Like the others it is configuration, not +-- demo data, so a fresh deployment shows it without manual seeding. +-- +-- `domain` must match the libvirt domain name exactly (`virsh list --all`). +INSERT INTO vms ("domain", "display_name", "icon", "position") VALUES + ('cs-477', 'CS-477 Advanced OS', 'server', 3) +ON CONFLICT ("domain") DO NOTHING; + +-- migrate:down +DELETE FROM vms WHERE "domain" = 'cs-477'; diff --git a/frontend/src/views/VmsView.vue b/frontend/src/views/VmsView.vue index 5d25710..ba7d838 100644 --- a/frontend/src/views/VmsView.vue +++ b/frontend/src/views/VmsView.vue @@ -3,14 +3,14 @@ * The page: one card per VM, its live state, and the buttons to act on it. * * The state is polled, so a VM shut down from inside the guest shows up as - * stopped here on its own. The two VMs share the same hardware and may never run + * stopped here on its own. The VMs share the same hardware and may never run * together: starting one while the other is up asks the user first, then runs * the sequence "stop the other, wait for it to be really off, start the wanted * one". The backend refuses the start anyway (409), this is only the nice path. */ import { computed, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' -import { AppWindow, Monitor, Play, Power, Terminal, TriangleAlert, Zap } from '@lucide/vue' +import { AppWindow, Monitor, Play, Power, Server, Terminal, TriangleAlert, Zap } from '@lucide/vue' import { HttpStatus } from 'http-status-ts' import { toast } from 'vue-sonner' @@ -82,6 +82,7 @@ const STATE_DOT: Record = { const ICONS: Record = { windows: AppWindow, linux: Terminal, + server: Server, } function iconFor(vm: VmStatus) {