fix: correct relative imports in proxmox subpackage and CSP headers

- Fix all `from ..` → `from ...` in app/api/proxmox/*.py after the
  proxmox.py split (PR #6) placed modules one level deeper, causing
  ModuleNotFoundError on startup
- Broaden Content-Security-Policy to allow CDN sources used in templates:
  cdnjs.cloudflare.com (Font Awesome), cdn.jsdelivr.net (Bootstrap,
  Chart.js, xterm), fonts.googleapis.com/gstatic.com (Google Fonts),
  esm.sh (noVNC) — the overly-strict CSP was blocking all icons and
  charts from loading in the browser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
markmorado
2026-03-08 01:56:08 +01:00
parent a2b53a1b84
commit 8c5a896683
7 changed files with 65 additions and 65 deletions

View File

@@ -10,14 +10,14 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
templates = Jinja2Templates(directory="app/templates") templates = Jinja2Templates(directory="app/templates")

View File

@@ -10,14 +10,14 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client, from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots, get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance, save_vm_instance, get_vm_instance, soft_delete_vm_instance,

View File

@@ -10,14 +10,14 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client, from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots, get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance, save_vm_instance, get_vm_instance, soft_delete_vm_instance,
@@ -31,7 +31,7 @@ router = APIRouter()
@router.get("/vms", response_class=HTMLResponse, include_in_schema=False) @router.get("/vms", response_class=HTMLResponse, include_in_schema=False)
def vms_page(request: Request, db: Session = Depends(get_db)): def vms_page(request: Request, db: Session = Depends(get_db)):
"""Страница управления Proxmox серверами, VM и LXC""" """Страница управления Proxmox серверами, VM и LXC"""
from ..i18n import t from ...i18n import t
lang = request.cookies.get("language", "en") lang = request.cookies.get("language", "en")
proxmox_servers = db.query(ProxmoxServer).all() proxmox_servers = db.query(ProxmoxServer).all()
@@ -430,7 +430,7 @@ def update_proxmox_server(
current_user: User = Depends(PermissionChecker("proxmox.servers.edit")) current_user: User = Depends(PermissionChecker("proxmox.servers.edit"))
): ):
"""Обновить Proxmox сервер""" """Обновить Proxmox сервер"""
from ..proxmox_client import clear_server_cache from ...proxmox_client import clear_server_cache
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first() server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
if not server: if not server:
@@ -460,8 +460,8 @@ def delete_proxmox_server(
current_user: User = Depends(PermissionChecker("proxmox.servers.delete")) current_user: User = Depends(PermissionChecker("proxmox.servers.delete"))
): ):
"""Удалить Proxmox сервер и связанные OS Templates""" """Удалить Proxmox сервер и связанные OS Templates"""
from ..proxmox_client import clear_server_cache from ...proxmox_client import clear_server_cache
from ..models import OSTemplate, OSTemplateGroup, VMInstance from ...models import OSTemplate, OSTemplateGroup, VMInstance
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first() server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
if not server: if not server:

View File

@@ -10,14 +10,14 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client, from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots, get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance, save_vm_instance, get_vm_instance, soft_delete_vm_instance,

View File

@@ -10,16 +10,16 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
from ..models import TaskQueue from ...models import TaskQueue
from ..services.task_queue_service import TaskQueueService, process_task_queue from ...services.task_queue_service import TaskQueueService, process_task_queue
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client, from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots, get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance, save_vm_instance, get_vm_instance, soft_delete_vm_instance,

View File

@@ -10,14 +10,14 @@ import asyncio
import httpx import httpx
import websockets import websockets
from ..db import get_db from ...db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService from ...logging_service import LoggingService
from ..template_helpers import add_i18n_context from ...template_helpers import add_i18n_context
from ..ipam_service import IPAMService from ...ipam_service import IPAMService
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client, from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots, get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance, save_vm_instance, get_vm_instance, soft_delete_vm_instance,
@@ -25,8 +25,8 @@ from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
router = APIRouter() router = APIRouter()
from ..models import TaskQueue from ...models import TaskQueue
from ..services.task_queue_service import TaskQueueService, process_task_queue from ...services.task_queue_service import TaskQueueService, process_task_queue
from pydantic import BaseModel from pydantic import BaseModel
from typing import List as TypingList, Optional from typing import List as TypingList, Optional
@@ -54,7 +54,7 @@ def sync_vms_now(
Useful when you create VMs directly in Proxmox and want them to appear in the panel immediately. Useful when you create VMs directly in Proxmox and want them to appear in the panel immediately.
""" """
try: try:
from ..workers.monitoring_worker import MonitoringWorker from ...workers.monitoring_worker import MonitoringWorker
worker = MonitoringWorker() worker = MonitoringWorker()
worker.sync_vm_cache() worker.sync_vm_cache()
@@ -86,7 +86,7 @@ def get_all_virtual_machines(
Для пользователей с ролью 'user' возвращаются только их собственные инстансы. Для пользователей с ролью 'user' возвращаются только их собственные инстансы.
""" """
from ..models import VMInstance from ...models import VMInstance
# Get servers for name lookup # Get servers for name lookup
servers = db.query(ProxmoxServer).all() servers = db.query(ProxmoxServer).all()
@@ -403,7 +403,7 @@ async def delete_vm(
current_user: User = Depends(PermissionChecker("vms.delete")) current_user: User = Depends(PermissionChecker("vms.delete"))
): ):
"""Удалить VM""" """Удалить VM"""
from ..i18n import t from ...i18n import t
lang = request.cookies.get("language", "en") lang = request.cookies.get("language", "en")
# VPS-style user isolation: check VM ownership for limited users # VPS-style user isolation: check VM ownership for limited users
@@ -784,7 +784,7 @@ async def delete_container(
current_user: User = Depends(PermissionChecker("vms.delete")) current_user: User = Depends(PermissionChecker("vms.delete"))
): ):
"""Удалить LXC контейнер""" """Удалить LXC контейнер"""
from ..i18n import t from ...i18n import t
lang = request.cookies.get("language", "en") lang = request.cookies.get("language", "en")
# VPS-style user isolation: check container ownership for limited users # VPS-style user isolation: check container ownership for limited users
@@ -2116,7 +2116,7 @@ async def create_lxc_container_smart(
# Update IPAM allocation with VMID and server info # Update IPAM allocation with VMID and server info
if ipam_allocation_id: if ipam_allocation_id:
try: try:
from ..models import IPAMAllocation from ...models import IPAMAllocation
allocation = db.query(IPAMAllocation).filter(IPAMAllocation.id == ipam_allocation_id).first() allocation = db.query(IPAMAllocation).filter(IPAMAllocation.id == ipam_allocation_id).first()
if allocation: if allocation:
allocation.resource_id = vmid allocation.resource_id = vmid

View File

@@ -178,11 +178,11 @@ def create_app() -> FastAPI:
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Content-Security-Policy"] = ( response.headers["Content-Security-Policy"] = (
"default-src 'self'; " "default-src 'self'; "
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://esm.sh; "
"style-src 'self' 'unsafe-inline'; " "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://fonts.googleapis.com; "
"img-src 'self' data: blob:; " "img-src 'self' data: blob:; "
"connect-src 'self' wss: ws:; " "connect-src 'self' wss: ws: https://esm.sh; "
"font-src 'self' data:; " "font-src 'self' data: https://cdnjs.cloudflare.com https://fonts.gstatic.com; "
"frame-ancestors 'self';" "frame-ancestors 'self';"
) )
if not settings.DEBUG: if not settings.DEBUG: