fix
This commit is contained in:
+5
-1
@@ -2,7 +2,7 @@ FROM python:3.12-slim
|
||||
|
||||
# ffmpeg/ffprobe — движок записи и перекодирования
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ffmpeg \
|
||||
&& apt-get install -y --no-install-recommends ffmpeg openssl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
@@ -12,6 +12,10 @@ RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY backend/app ./app
|
||||
COPY frontend ./frontend
|
||||
COPY backend/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 8090
|
||||
# entrypoint генерирует self-signed TLS-сертификат при отсутствии, затем запускает CMD/команду
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8090"]
|
||||
|
||||
@@ -280,3 +280,18 @@ async def delete_camera(cam_id: str, request: Request) -> dict:
|
||||
await runtime.supervisor.remove_camera(cam_id)
|
||||
await go2rtc.remove_camera_streams(runtime.config, cam_id)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/clear")
|
||||
async def clear_cameras(request: Request) -> dict:
|
||||
"""Удаляет ВСЕ камеры: чистит cameras.yaml, останавливает запись и потоки go2rtc.
|
||||
|
||||
Архив (nvr.db + файлы .mp4) НЕ трогает — очищается только список камер."""
|
||||
require_role(request, "admin")
|
||||
ids = [c.id for c in runtime.config.cameras]
|
||||
runtime.config.cameras = []
|
||||
save_config(runtime.config)
|
||||
for cam_id in ids:
|
||||
await runtime.supervisor.remove_camera(cam_id)
|
||||
await go2rtc.remove_camera_streams(runtime.config, cam_id)
|
||||
return {"ok": True, "removed": len(ids)}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
# Гарантирует наличие self-signed TLS-сертификата перед стартом uvicorn.
|
||||
# Если cert.pem/key.pem отсутствуют (свежий деплой) — генерирует их;
|
||||
# иначе использует существующие. Затем запускает переданную команду (CMD).
|
||||
set -e
|
||||
|
||||
CERT_DIR=/app/certs
|
||||
if [ ! -f "$CERT_DIR/cert.pem" ] || [ ! -f "$CERT_DIR/key.pem" ]; then
|
||||
echo "[entrypoint] TLS cert not found in $CERT_DIR — generating self-signed (CN=${NVR_TLS_CN:-localhost})"
|
||||
mkdir -p "$CERT_DIR"
|
||||
openssl req -x509 -newkey rsa:2048 -nodes \
|
||||
-keyout "$CERT_DIR/key.pem" -out "$CERT_DIR/cert.pem" \
|
||||
-days 3650 -subj "/CN=${NVR_TLS_CN:-localhost}"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
+1
-1
@@ -12,7 +12,7 @@ services:
|
||||
ports:
|
||||
- "443:8090" # дашборд на https://<сервер>
|
||||
volumes:
|
||||
- ./certs:/app/certs:ro # self-signed TLS-сертификат
|
||||
- ./certs:/app/certs # self-signed TLS-сертификат (rw: entrypoint генерирует при отсутствии)
|
||||
- ./VERSION:/app/VERSION:ro # номер версии (показывается в UI)
|
||||
- ./cameras.yaml:/app/cameras.yaml # rw: редактирование камер из UI
|
||||
- ./go2rtc.yaml:/app/go2rtc.yaml # rw: бэкенд регенерирует live-потоки
|
||||
|
||||
@@ -1100,6 +1100,8 @@ const CamSettings = {
|
||||
if (be) be.onclick = () => this.bulk ? this.saveBulk() : this.enterBulk();
|
||||
const bc = document.getElementById("bulk-cancel");
|
||||
if (bc) bc.onclick = () => this.exitBulk();
|
||||
const cc = document.getElementById("clear-cams");
|
||||
if (cc) cc.onclick = () => this.clearAll();
|
||||
await this.load();
|
||||
connectStatusWS(() => { if (!this.editing) this.load(); });
|
||||
},
|
||||
@@ -1108,10 +1110,12 @@ const CamSettings = {
|
||||
const be = document.getElementById("bulk-edit");
|
||||
const bc = document.getElementById("bulk-cancel");
|
||||
const add = document.getElementById("add-cam");
|
||||
const cc = document.getElementById("clear-cams");
|
||||
if (be) be.textContent = this.bulk ? "Сохранить" : "Изменить";
|
||||
if (be) be.classList.toggle("primary", this.bulk);
|
||||
if (bc) bc.hidden = !this.bulk;
|
||||
if (add) add.hidden = this.bulk; // во время массового редактирования не добавляем
|
||||
if (cc) cc.hidden = this.bulk;
|
||||
},
|
||||
|
||||
enterBulk() {
|
||||
@@ -1127,6 +1131,23 @@ const CamSettings = {
|
||||
await this.load();
|
||||
},
|
||||
|
||||
// удалить ВСЕ камеры (архив не трогается) — POST /api/cameras/clear
|
||||
async clearAll() {
|
||||
if (this.bulk) return;
|
||||
const n = this.cams.length;
|
||||
if (!n) { alert("Камер нет"); return; }
|
||||
if (!confirm(`Удалить ВСЕ камеры (${n})?\nЗаписи архива НЕ затрагиваются. Действие необратимо.`)) return;
|
||||
const r = await fetch("/api/cameras/clear", { method: "POST" });
|
||||
if (!r.ok) {
|
||||
const d = await r.json().catch(() => ({}));
|
||||
alert("Не удалось очистить: " + (d.detail || r.status));
|
||||
return;
|
||||
}
|
||||
const d = await r.json().catch(() => ({}));
|
||||
await this.load();
|
||||
alert(`Удалено камер: ${d.removed != null ? d.removed : n}`);
|
||||
},
|
||||
|
||||
// ── строка камеры в режиме массового редактирования ──
|
||||
bulkRowHtml(slotNo, cam) {
|
||||
const v = (x) => this.esc(x);
|
||||
|
||||
Reference in New Issue
Block a user