Загрузить файлы в «/»
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
|||||||
|
FROM node:20-slim AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
EXPOSE 5173
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Proxmox VPS Panel
|
||||||
|
|
||||||
|
Минимальная self-hosted панель для мульти-тенантного управления VPS (VM и LXC) на вашем
|
||||||
|
Proxmox VE через API. Backend — FastAPI (Python), frontend — React (Vite), хранилище — Postgres.
|
||||||
|
|
||||||
|
Клиенты регистрируются, выбирают шаблон и разворачивают себе VM или LXC-контейнер одной кнопкой;
|
||||||
|
дальше могут запускать/останавливать/перезагружать/удалять сервер и открывать веб-консоль.
|
||||||
|
Первый зарегистрированный пользователь автоматически становится администратором и управляет
|
||||||
|
шаблонами и списком пользователей.
|
||||||
|
|
||||||
|
## Возможности MVP
|
||||||
|
|
||||||
|
- Регистрация/логин (JWT), роли admin / client
|
||||||
|
- Шаблоны VPS: админ создаёт тарифы, привязанные к VM-шаблону (клонирование) или CT-шаблону LXC
|
||||||
|
- Клиент создаёт инстанс из шаблона — панель сама берёт свободный VMID и вызывает Proxmox API
|
||||||
|
- Действия: start / stop / shutdown / reboot / delete
|
||||||
|
- Веб-консоль через noVNC (websocket-прокси на backend)
|
||||||
|
- Всё через docker-compose, разворачивается как Custom App в TrueNAS SCALE
|
||||||
|
|
||||||
|
## Чего нет в MVP (сознательно, чтобы не раздувать первую версию)
|
||||||
|
|
||||||
|
- Поддержки нескольких Proxmox-нод/кластеров одновременно (сейчас один `PVE_NODE` в конфиге)
|
||||||
|
- Биллинга и лимитов по ресурсам на пользователя
|
||||||
|
- Очереди задач (Celery/Redis) — создание VPS идёт в фоне через `BackgroundTasks` FastAPI,
|
||||||
|
для нескольких одновременных заявок этого достаточно, для десятков в секунду — уже нет
|
||||||
|
- Управления сетью/IP-пулами (LXC получает адрес по DHCP, для VM выдаётся из клонированного шаблона)
|
||||||
|
|
||||||
|
## 1. Подготовка Proxmox
|
||||||
|
|
||||||
|
### API-токен
|
||||||
|
|
||||||
|
Datacenter → Permissions → API Tokens → добавить токен для пользователя (например `root@pam`,
|
||||||
|
в проде лучше завести отдельного пользователя с ролью `PVEVMAdmin`/кастомной ролью и без
|
||||||
|
"Privilege Separation", если хотите, чтобы токен имел те же права, что и юзер).
|
||||||
|
|
||||||
|
Права, которые нужны панели: создание/клонирование/удаление VM и CT, управление питанием,
|
||||||
|
`VM.Console` для консоли, `Sys.Audit` для чтения статусов задач.
|
||||||
|
|
||||||
|
### Шаблон для VM
|
||||||
|
|
||||||
|
Создайте одну VM с cloud-init образом (Ubuntu/Debian cloud image), настройте её и переведите
|
||||||
|
в шаблон (`qm template <vmid>`). Полученный VMID указывается в панели как `source_vmid` шаблона.
|
||||||
|
|
||||||
|
### Шаблон для LXC
|
||||||
|
|
||||||
|
Скачайте официальный CT-шаблон через Proxmox (Node → local (storage) → CT Templates → Download),
|
||||||
|
например `debian-12-standard_12.7-1_amd64.tar.zst`. В панели укажите его как `source_template`
|
||||||
|
в формате `local:vztmpl/имя_файла.tar.zst`.
|
||||||
|
|
||||||
|
## 2. Настройка backend
|
||||||
|
|
||||||
|
Скопируйте `backend/.env.example` в `backend/.env` и заполните:
|
||||||
|
|
||||||
|
```
|
||||||
|
PVE_HOST=https://<ip-вашего-proxmox>:8006
|
||||||
|
PVE_NODE=pve
|
||||||
|
PVE_TOKEN_NAME=root@pam!panel
|
||||||
|
PVE_TOKEN_VALUE=<значение токена>
|
||||||
|
SECRET_KEY=<случайная строка>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend будет на `http://<host>:5173`, backend API — на `http://<host>:8000`.
|
||||||
|
Первый, кто зарегистрируется на `/register`, станет администратором — заходите первым сами.
|
||||||
|
|
||||||
|
## 4. Установка на TrueNAS SCALE
|
||||||
|
|
||||||
|
TrueNAS SCALE умеет запускать произвольные Docker-приложения ("Apps → Discover Apps → Custom App"
|
||||||
|
либо через "Launch Docker Compose", если версия TrueNAS это поддерживает). Проще всего:
|
||||||
|
|
||||||
|
1. Скопируйте папку проекта на TrueNAS (например, в датасет `/mnt/tank/apps/vps-panel`).
|
||||||
|
2. Заполните `backend/.env`, как описано выше.
|
||||||
|
3. Из этой директории выполните `docker compose up -d --build` через shell TrueNAS
|
||||||
|
(System Settings → Shell, либо через SSH), либо оформите как Custom App, указав тот же
|
||||||
|
`docker-compose.yml` в интерфейсе TrueNAS Apps.
|
||||||
|
4. Откройте порт 5173 наружу через reverse-proxy (например, встроенный в TrueNAS, либо
|
||||||
|
отдельный Nginx Proxy Manager), если панель должна быть доступна клиентам извне.
|
||||||
|
|
||||||
|
## Консоль VNC — важное примечание
|
||||||
|
|
||||||
|
Веб-консоль реализована как websocket-прокси: backend получает от Proxmox тикет
|
||||||
|
(`vncproxy`) и порт, затем проксирует бинарный поток на `/console/ws`, а фронтенд рисует
|
||||||
|
экран через noVNC (`@novnc/novnc`, подключается с CDN).
|
||||||
|
|
||||||
|
Proxmox исторически ожидает на `vncwebsocket` либо cookie-тикет (`PVEAuthCookie`) из обычной
|
||||||
|
браузерной сессии, либо (в более новых версиях) заголовок `Authorization: PVEAPIToken=...` —
|
||||||
|
в коде (`backend/app/routers/console.py`) используется второй вариант. Если на вашей версии
|
||||||
|
Proxmox консоль не подключается, скорее всего понадобится либо обновить PVE, либо переключить
|
||||||
|
аутентификацию панели на логин/пароль с получением `PVEAuthCookie` вместо API-токена именно
|
||||||
|
для этого запроса — сама бизнес-логика (создание/старт/стоп VPS) от этого не зависит и будет
|
||||||
|
работать в любом случае.
|
||||||
|
|
||||||
|
## Безопасность перед продакшеном
|
||||||
|
|
||||||
|
- Смените `SECRET_KEY` и пароль Postgres по умолчанию
|
||||||
|
- Ограничьте `allow_origins` в `backend/app/main.py` конкретным доменом фронтенда
|
||||||
|
- Закройте порт 8000 (backend) наружу, оставьте доступ только через nginx фронтенда
|
||||||
|
- Используйте отдельного Proxmox-пользователя с минимально необходимыми правами для токена
|
||||||
|
панели, а не `root@pam`
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/
|
||||||
|
app/
|
||||||
|
main.py — точка входа FastAPI
|
||||||
|
config.py — переменные окружения
|
||||||
|
models.py — таблицы: User, Template, Instance
|
||||||
|
proxmox_client.py — вся логика вызовов Proxmox API (proxmoxer)
|
||||||
|
routers/
|
||||||
|
auth.py — регистрация / логин
|
||||||
|
templates.py — CRUD шаблонов (admin)
|
||||||
|
instances.py — создание/список/действия/удаление VPS
|
||||||
|
admin.py — управление пользователями
|
||||||
|
console.py — websocket-прокси для VNC-консоли
|
||||||
|
frontend/
|
||||||
|
src/
|
||||||
|
pages/ — Login, Register, Dashboard, InstanceDetail, AdminTemplates, AdminUsers
|
||||||
|
components/ — InstanceCard, ConsoleViewer
|
||||||
|
api.js — обёртка над fetch к backend
|
||||||
|
docker-compose.yml
|
||||||
|
```
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
# Общие
|
||||||
|
database_url: str = "sqlite:///./panel.db"
|
||||||
|
secret_key: str = "change-me-please-super-secret"
|
||||||
|
access_token_expire_minutes: int = 60 * 12
|
||||||
|
|
||||||
|
# Proxmox
|
||||||
|
pve_host: str = "https://proxmox.local:8006"
|
||||||
|
pve_token_name: str = "root@pam!panel"
|
||||||
|
pve_token_value: str = ""
|
||||||
|
pve_verify_ssl: bool = False
|
||||||
|
pve_node: str = "pve"
|
||||||
|
|
||||||
|
# Пул VMID, из которого будут выделяться номера новым машинам
|
||||||
|
vmid_range_start: int = 9000
|
||||||
|
vmid_range_end: int = 9999
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: panel
|
||||||
|
POSTGRES_PASSWORD: panel
|
||||||
|
POSTGRES_DB: panel
|
||||||
|
volumes:
|
||||||
|
- panel_db:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./backend/.env
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://panel:panel@db:5432/panel
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
ports:
|
||||||
|
- "8000:8000" # можно убрать наружу, если доступ только через frontend/nginx
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
panel_db:
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Panel — VPS на вашем Proxmox</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
server {
|
||||||
|
listen 5173;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8000/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "proxmox-vps-panel-frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview --host --port 5173"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router-dom": "^6.24.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.3.1",
|
||||||
|
"vite": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
fastapi==0.111.0
|
||||||
|
uvicorn[standard]==0.30.1
|
||||||
|
sqlalchemy==2.0.30
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
|
python-jose[cryptography]==3.3.0
|
||||||
|
passlib[bcrypt]==1.7.4
|
||||||
|
bcrypt==4.0.1
|
||||||
|
pydantic==2.7.4
|
||||||
|
pydantic-settings==2.3.4
|
||||||
|
proxmoxer==2.0.1
|
||||||
|
requests==2.32.3
|
||||||
|
python-multipart==0.0.9
|
||||||
|
websockets==12.0
|
||||||
|
httpx==0.27.0
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
host: true,
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
"/api": {
|
||||||
|
target: "http://backend:8000",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user