From 0f239d50156889294875724c5275643d26532e02 Mon Sep 17 00:00:00 2001 From: darkorion Date: Tue, 13 Oct 2026 13:27:20 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B8=D0=B7=20Nextcloud?= =?UTF-8?q?,=20=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=BA=20=D1=83=20=D1=81?= =?UTF-8?q?=D0=BE=D1=82=D1=80=D1=83=D0=B4=D0=BD=D0=B8=D0=BA=D0=B0,=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D0=BE=D1=87=D0=B8=D1=81=D1=82=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=84=D0=BE=D1=82=D0=BE,=20=D0=BC=D0=BE=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3,=20=D0=B8=D1=82=D0=BE?= =?UTF-8?q?=D0=B3=20=D0=BC=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=B0,=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BB=D1=8C=D1=82=D1=80=20=D0=BF=D0=BE=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC;=20=D0=B4=D0=BE=D0=BF.=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D0=B2=D0=B8=D1=81=D1=8B=20=D0=B2=20docker-compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели) - автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов во внутренней сети Docker; значения из интерфейса важнее - локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API - .env.example, docs/DEPLOY.md (раздел 7а), README --- .env.example | 42 +++++ README.md | 6 + app/assistant.py | 49 ++++-- app/assistant_kb.txt | 10 +- app/autoconf.py | 71 ++++++++ app/backup.py | 4 +- app/demo.py | 3 + app/docs_index.py | 160 ++++++++++++++++++ app/logic.py | 24 ++- app/main.py | 25 ++- app/media.py | 10 +- app/models.py | 24 ++- app/nextcloud.py | 316 +++++++++++++++++++++++++++++++++++ app/photo_cleanup.py | 63 +++++++ app/push.py | 7 +- app/routers/assistant.py | 2 +- app/routers/catalog.py | 3 + app/routers/docs.py | 191 +++++++++++++++++++++ app/routers/extras.py | 30 +++- app/routers/portal.py | 2 +- app/routers/tasks.py | 4 +- app/schemas.py | 13 +- app/settings_store.py | 21 ++- app/static/admin.js | 2 +- app/static/app.js | 85 ++++++++-- app/static/docs.js | 155 +++++++++++++++++ app/static/index.html | 3 + app/static/monitor.js | 44 +++++ app/static/portal.js | 4 +- app/static/service.js | 1 + app/static/setup.js | 71 ++++++-- app/static/style.css | 26 +++ app/sysinfo.py | 109 ++++++++++++ docker-compose.yml | 119 ++++++++++++- docs/API.md | 35 ++++ docs/DEPLOY.md | 30 ++++ docs/user-guide.pdf | Bin 13108834 -> 13117906 bytes requirements.txt | 1 + tests/test_flow.py | 18 +- tests/test_scope_autoconf.py | 56 +++++++ tests/test_scope_docs.py | 191 +++++++++++++++++++++ tests/test_scope_tweaks.py | 115 +++++++++++++ tests/test_security.py | 7 +- tools/guide/build_pdf.py | 73 ++++++-- 44 files changed, 2131 insertions(+), 94 deletions(-) create mode 100644 app/autoconf.py create mode 100644 app/docs_index.py create mode 100644 app/nextcloud.py create mode 100644 app/photo_cleanup.py create mode 100644 app/routers/docs.py create mode 100644 app/static/docs.js create mode 100644 app/static/monitor.js create mode 100644 app/sysinfo.py create mode 100644 tests/test_scope_autoconf.py create mode 100644 tests/test_scope_docs.py create mode 100644 tests/test_scope_tweaks.py diff --git a/.env.example b/.env.example index 1e94d56..87a95bd 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,45 @@ APP_PORT=8080 # там виден адрес самого прокси (на том же хосте — обычно шлюз сети Docker, например 172.18.0.1). Впишите его и выполните # docker compose up -d — после этого в той строке появится ваш собственный адрес. FORWARDED_ALLOW_IPS=127.0.0.1 + +# ---------- Дополнительные сервисы (необязательно) ---------- +# Включаются профилями docker-compose.yml: docs — Nextcloud, office — ONLYOFFICE, ai — локальная модель Ollama. +# Портал сам найдёт запущенные сервисы во внутренней сети Docker и заполнит их адреса в настройках. +# Пример: COMPOSE_PROFILES=docs,office,ai +COMPOSE_PROFILES= + +# --- docs: Nextcloud (документация оборудования) --- +# Администратор Nextcloud создаётся при первом запуске; без пароля Nextcloud покажет мастер установки в браузере. +NEXTCLOUD_ADMIN_USER=ncadmin +NEXTCLOUD_ADMIN_PASSWORD= +# Порт веб-интерфейса Nextcloud на хосте (сюда загружают документы) и его доменное имя за прокси (если есть). +NEXTCLOUD_PORT=8081 +NEXTCLOUD_DOMAIN= +# https — если Nextcloud открывают через прокси с HTTPS. +NEXTCLOUD_PROTOCOL= +# Пароль базы Nextcloud (пусто — как DB_PASSWORD). +NEXTCLOUD_DB_PASSWORD= +# Под какой учётной записью портал читает документацию. Пусто — администратор Nextcloud выше. +# Надёжнее завести отдельного пользователя только с чтением папки документации и пароль приложения для него. +NC_USER= +NC_PASSWORD= +# Папка с документацией внутри учётной записи, например Документация/Оборудование. +NC_ROOT= +# Свой Nextcloud вне этого docker-compose: укажите его адрес (тогда профиль docs не нужен). +NC_URL= + +# --- office: ONLYOFFICE Document Server (просмотр Word/Excel/PowerPoint в портале) --- +# Общий секрет портала и ONLYOFFICE. Обязательно задайте свой: openssl rand -hex 24 +ONLYOFFICE_JWT= +# Свой ONLYOFFICE вне этого docker-compose: его адрес и адрес портала, по которому ONLYOFFICE его видит. +ONLYOFFICE_URL= +PORTAL_INTERNAL_URL= + +# --- ai: локальная модель Ollama для AI-помощника --- +# Модель скачивается при первом запуске (qwen2.5:7b — около 5 ГБ на диске и 6–8 ГБ памяти; поменьше — qwen2.5:3b). +OLLAMA_MODEL=qwen2.5:7b +# Внешний провайдер вместо Ollama (необязательно): openai | anthropic | gigachat, адрес, модель, ключ. +ASSISTANT_PROVIDER= +ASSISTANT_BASE_URL= +ASSISTANT_MODEL= +ASSISTANT_API_KEY= diff --git a/README.md b/README.md index 7c37d33..8a9be7b 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,12 @@ python -m venv .venv && .venv\Scripts\pip install -r requirements.txt Пошаговая установка — [docs/DEPLOY.md](docs/DEPLOY.md). +## Дополнительные сервисы +Рядом с порталом `docker-compose.yml` может поднять **Nextcloud** (документация оборудования), **ONLYOFFICE** (просмотр +Word/Excel/PowerPoint в портале) и **Ollama** (локальная модель для AI-помощника) — профилями `docs`, `office`, `ai` +(`COMPOSE_PROFILES=docs,office,ai` в `.env`). Портал сам находит их во внутренней сети Docker и заполняет адреса в настройках. +Подробно — [docs/DEPLOY.md](docs/DEPLOY.md), раздел 7а. + ## Установка без Coolify (Docker Compose) Пошаговая инструкция для любого Linux-сервера с Docker: **[docs/DEPLOY.md](docs/DEPLOY.md)** (шаблон настроек — `.env.example`). Коротко: `cp .env.example .env` → заполнить → `docker compose up -d --build` → обратный прокси с HTTPS на `APP_PORT`. diff --git a/app/assistant.py b/app/assistant.py index 97e03d1..f8e76e3 100644 --- a/app/assistant.py +++ b/app/assistant.py @@ -16,10 +16,12 @@ import re import time from pathlib import Path +from urllib.parse import quote + import httpx from sqlalchemy.orm import Session -from . import config, settings_store as ss +from . import autoconf, config, settings_store as ss from .models import User log = logging.getLogger("assistant") @@ -59,7 +61,7 @@ def public_cfg(db: Session) -> dict: def ready(db: Session) -> bool: c = cfg(db) - local = c["provider"] == "openai" and any(h in c["base_url"] for h in ("localhost", "127.0.0.1")) + local = c["provider"] == "openai" and autoconf.is_local(c["base_url"]) # своя модель в сети (Ollama и т. п.) — без ключа return c["enabled"] and bool(c["api_key"] or local) and bool(c["model"]) @@ -78,11 +80,18 @@ TOOLS = { "equipment_history": ("История машины. args: {\"id\": id машины}", "GET", "/equipment/{id}/history?limit=30", "id"), "find_people": ("Сотрудники: ФИО, должность, на смене ли", "GET", "/users", None), "warehouses": ("Склады и сколько на них позиций", "GET", "/warehouses", None), + # опция «Работа с документацией» (включается в настройках сервера) + "search_docs": ("Поиск по документации машин: файлы и фрагменты текста. args: {\"q\": \"2-5 ключевых слов\"}", "GET", "/docs/search?q={q}", "q"), + "equipment_docs": ("Папки и файлы документации машины. args: {\"id\": id машины, \"sub\": \"вложенная папка или пусто\"}", + "GET", "/equipment/{id}/docs?sub={sub}", "id"), } +DOC_TOOLS = {"search_docs", "equipment_docs"} +TEXT_ARGS = ("q", "sub") KEEP = {"id", "title", "name", "full_name", "status", "state", "day", "due_at", "is_overdue", "equipment_name", "equipment_id", "unit_name", "assignee_name", "executor_name", "item_name", "qty", "unit", "what", "answer", "occurred_at", "inventory_no", "location_id", "remaining_hours", "remaining_days", "percent", "on_shift", "position", "type_ru", "at", "text", "open_incidents", "due", "soon", - "positions", "can_execute", "can_review", "last_comment", "description", "total", "is_crane", "labor_hours", "kind", "open_faults"} + "positions", "can_execute", "can_review", "last_comment", "description", "total", "is_crane", "labor_hours", "kind", "open_faults", + "path", "snippet", "items", "folder", "sub", "is_dir", "note"} def _slim(x, depth=0): @@ -101,11 +110,18 @@ def _slim(x, depth=0): return x -async def run_tool(app, token: str, name: str, args: dict) -> str: - if name not in TOOLS: +async def run_tool(app, token: str, name: str, args: dict, docs: bool = False) -> str: + if name not in TOOLS or (name in DOC_TOOLS and not docs): return json.dumps({"error": f"нет инструмента {name}"}, ensure_ascii=False) _, method, path, arg = TOOLS[name] - if arg: + for t in TEXT_ARGS: # текстовые параметры — в строку запроса, экранированными + if "{" + t + "}" in path: + v = args.get(t) + v = str(v).strip()[:200] if isinstance(v, (str, int, float)) else "" + if t == arg and len(v) < 2: + return json.dumps({"error": f"нужен параметр {t}"}, ensure_ascii=False) + path = path.replace("{" + t + "}", quote(v, safe="")) + if arg and arg not in TEXT_ARGS: v = args.get(arg) if not isinstance(v, int) and not (isinstance(v, str) and v.isdigit()): return json.dumps({"error": f"нужен числовой параметр {arg}"}, ensure_ascii=False) @@ -129,9 +145,21 @@ def who(u: User) -> str: f"роли: {', '.join(roles) or 'нет особых ролей'}.") -def system_prompt(u: User, scope: str) -> str: - tools = "\n".join(f"- {k}: {v[0]}" for k, v in TOOLS.items()) +DOCS_RULES = """ +ДОКУМЕНТАЦИЯ (это тоже тема портала, не OFFTOPIC). К машинам привязаны папки с документацией (паспорта, руководства, +схемы). Если спрашивают, где найти +сведения по машине (схема, параметры, порядок обслуживания, коды ошибок), найди файл через search_docs или equipment_docs +и подскажи, какой файл открыть и что в нём искать. Давай ссылку вида [имя файла](#/docs/ID?p=путь) — ID машины и путь +файла из результата, пробелы в пути замени на %20. Фрагменты текста — только подсказка, где искать: не выдавай их за полный ответ и не +придумывай содержимое, которого нет во фрагменте. Если ничего не нашлось — так и скажи и предложи открыть папку +документации машины [Документация](#/docs/ID). +""" + + +def system_prompt(u: User, scope: str, docs: bool = False) -> str: + tools = "\n".join(f"- {k}: {v[0]}" for k, v in TOOLS.items() if docs or k not in DOC_TOOLS) extra = f"\nДополнительно от администратора: {scope.strip()}\n" if scope and scope.strip() else "" + extra += DOCS_RULES if docs else "" return f"""Ты — помощник внутри «Портала технической службы» (ремонтная служба предприятия). Отвечай по-русски, кратко, по шагам. ПРАВИЛА: @@ -226,7 +254,8 @@ async def chat(app, db: Session, u: User, token: str, history: list[dict], cfg_o while msgs and msgs[0]["role"] != "user": # некоторые провайдеры требуют начинать с пользователя msgs.pop(0) rate_check(u.id) - system = system_prompt(u, c["scope"]) + docs = ss.get(db, "docs_ai") == "1" + system = system_prompt(u, c["scope"], docs) used = [] for _ in range(MAX_STEPS + 1): text = (await complete(c, system, msgs)).strip() @@ -240,7 +269,7 @@ async def chat(app, db: Session, u: User, token: str, history: list[dict], cfg_o args = json.loads(raw) except ValueError: args = {} - result = await run_tool(app, token, name, args if isinstance(args, dict) else {}) + result = await run_tool(app, token, name, args if isinstance(args, dict) else {}, docs) used.append(name) msgs += [{"role": "assistant", "content": text}, {"role": "user", "content": f"РЕЗУЛЬТАТ {name}: {result}\nОтветь пользователю."}] return {"reply": "Не получилось собрать ответ. Попробуйте переформулировать вопрос.", "offtopic": False, "tools": used} diff --git a/app/assistant_kb.txt b/app/assistant_kb.txt index de55e03..641059b 100644 --- a/app/assistant_kb.txt +++ b/app/assistant_kb.txt @@ -26,7 +26,8 @@ - Сотрудники — #/people — все: поиск, фильтр по отделу; карточка #/user/: ФИО, фото, должность, отдел, график, «что умею», задания, инструмент (инструмент коллеги видят только склад и инженеры). Своё фото, телефон и навыки — в своей карточке (нажать своё имя вверху меню). - Отчёты — #/reports — инженер+: «Выполненные наряды», «Текущее состояние» узлов, «Выполнение работ» (% по машинам, отклонения), «По сотрудникам» #/reports/people (сколько и как качественно выполнено за период: в срок, просрочки, возвраты с проверки, отклонения, трудоёмкость; списком и календарём по дням); PDF и CSV. - Администрирование — #/admin — модератор, админ: пользователи #/users, должности #/positions, графики #/schedules (конструктор), отделы #/departments, места #/locations. -- Только администратор портала: настройки сервера #/server (фото, публичная форма, «Краны — отдельная служба», ключи API, AI-помощник) и обратная связь #/feedback. Только администратор: резервные копии #/backups, мастер первичной настройки #/setup, демонстрационный режим #/demo. +- Документация машины (если подключён Nextcloud): блок «📁 Документация» в карточке машины, кнопка «Открыть» — #/docs/. PDF, картинки и текст открываются в портале, Word/Excel/PowerPoint — после перевода в PDF (ONLYOFFICE), любой файл можно скачать. Привязывают папку администратор или модератор: «Привязать папку» в том же блоке, выбор в проводнике. Видно документацию только тех машин, которые видит сотрудник. +- Только администратор портала: настройки сервера #/server (фото, публичная форма, «Краны — отдельная служба», ключи API, документация из Nextcloud, AI-помощник и его опция «Работа с документацией») и обратная связь #/feedback. Только администратор: резервные копии #/backups, мастер первичной настройки #/setup, демонстрационный режим #/demo. - Демонстрационный режим — личный: включается только у того, кто нажал «Войти в демо», остальные работают с рабочими данными. Пока он включён, вверху жёлтая полоса «ДЕМО-РЕЖИМ (только у вас)» с кнопкой «Выйти из демо». Администратор на странице #/demo может включить или выключить демо любому сотруднику. - Обратная связь по порталу — ссылка «Предложить улучшение портала или сообщить об ошибке» внизу каждой страницы (в мобильной версии и приложении — внизу бокового меню): идея, ошибка или другое. Ответы видят только администраторы портала — #/feedback. - Администратор портала — право администратора на настройки сервера и обратную связь. У admin оно есть всегда; другим администраторам его включает admin в карточке пользователя («Администратор портала»). @@ -41,7 +42,12 @@ - Администратор: всё, включая настройки сервера, копии, демо-режим. ## Частые вопросы -- Не вижу наряд: наряд на должность виден только сотрудникам этой должности на смене; проверьте график. +- Не вижу наряд: наряд на должность виден только сотрудникам этой должности, которые сейчас на смене по своему графику; проверьте график в своей карточке. +- График работы задаётся сотруднику (#/users → «Изменить» → «График работы»), не должности. Одну должность могут занимать сменщики: наряды и уведомления должности получает только тот, кто на смене. Без графика — всегда на смене. +- Фото к пункту чек-листа или к заданию можно отметить «хранить более года» (в конструкторе чек-листа рядом с режимом фото, в форме задания) — такие фото не удаляются автоочисткой. Автоочистку (удалять фото старше N дней) включает администратор портала в #/server. +- Мониторинг сервера — #/monitor (администратор портала): загрузка процессора, память, место на дисках. +- Фильтр «Место» над списком оборудования #/equipment и кранов: показывает машины места и вложенных мест. +- Мастер первичной настройки: «Завершить и проверить» открывает проверку готовности #/setup/done — что мешает работе и где поправить. - Не могу сдать наряд: не заполнены обязательные пункты или нет обязательного фото. - Нет связи: в мобильной версии и приложении ответы сохраняются на телефоне и уйдут сами. - Не вижу машину или кран: оборудование закреплено за другим отделом, или краны вынесены в раздел «Краны». diff --git a/app/autoconf.py b/app/autoconf.py new file mode 100644 index 0000000..dda1801 --- /dev/null +++ b/app/autoconf.py @@ -0,0 +1,71 @@ +"""Автонастройка из окружения и внутренней сети Docker. + +Пока администратор сам не заполнил поле в настройках, значение берётся: +1) из переменной окружения (docker-compose.yml передаёт их из .env), иначе +2) из внутренней сети Docker: если рядом запущен сервис с известным именем (nextcloud, onlyoffice, ollama — профили + docker-compose.yml), подставляется его внутренний адрес. +Значение, сохранённое в интерфейсе, всегда важнее. Имена сервисов проверяются через DNS Docker с кэшем на минуту.""" +import ipaddress +import os +import socket +import time +from urllib.parse import urlsplit + +CACHE_SEC = 60 +_seen: dict[str, tuple[float, bool]] = {} + +# настройка -> переменная окружения +ENV = { + "nc_url": "NC_URL", "nc_user": "NC_USER", "nc_password": "NC_PASSWORD", "nc_root": "NC_ROOT", + "onlyoffice_url": "ONLYOFFICE_URL", "onlyoffice_jwt": "ONLYOFFICE_JWT", "portal_internal_url": "PORTAL_INTERNAL_URL", + "assistant_provider": "ASSISTANT_PROVIDER", "assistant_base_url": "ASSISTANT_BASE_URL", + "assistant_model": "ASSISTANT_MODEL", "assistant_api_key": "ASSISTANT_API_KEY", +} +# настройка -> (имя сервиса в сети Docker, адрес, который подставить) +SERVICES = { + "nc_url": ("nextcloud", "http://nextcloud"), + "onlyoffice_url": ("onlyoffice", "http://onlyoffice"), + "assistant_base_url": ("ollama", "http://ollama:11434/v1"), +} + + +def resolves(host: str) -> bool: + now = time.monotonic() + hit = _seen.get(host) + if hit and now - hit[0] < CACHE_SEC: + return hit[1] + try: + socket.getaddrinfo(host, None) + ok = True + except OSError: + ok = False + _seen[host] = (now, ok) + return ok + + +def default(key: str) -> str | None: + """Значение по умолчанию из окружения или сети Docker; None — автонастройки для ключа нет.""" + env = ENV.get(key) + if env and os.getenv(env): + return os.getenv(env, "").strip() + svc = SERVICES.get(key) + if svc and resolves(svc[0]): + return svc[1] + if key == "assistant_model" and resolves("ollama"): + return os.getenv("OLLAMA_MODEL", "qwen2.5:7b") + if key == "portal_internal_url" and resolves("onlyoffice"): + return "http://app:8000" # по этому адресу ONLYOFFICE забирает документы у портала + return None + + +def is_local(url: str) -> bool: + """Адрес модели внутри своей сети (ключ API не нужен): localhost, имя сервиса Docker без точки, частный IP.""" + host = (urlsplit(url or "").hostname or "").lower() + if not host: + return False + if host in ("localhost", "127.0.0.1", "::1") or "." not in host: + return True + try: + return ipaddress.ip_address(host).is_private + except ValueError: + return False diff --git a/app/backup.py b/app/backup.py index 25cdb39..f203597 100644 --- a/app/backup.py +++ b/app/backup.py @@ -22,11 +22,11 @@ from .db import Base log = logging.getLogger("backup") FORMAT = 1 NAME_RE = re.compile(r"backup_\d{8}_\d{6}(_[a-z0-9-]{1,20})?\.zip") -SKIP_TABLES = {"refresh_tokens"} # сессии не переносим: после восстановления все входят заново +SKIP_TABLES = {"refresh_tokens", "doc_index"} # индекс документации строится заново # сессии не переносим: после восстановления все входят заново ENV_PUBLIC = ("APP_ENV", "APP_TZ", "APP_PORT", "FORWARDED_ALLOW_IPS", "DATA_DIR", "PHOTO_DIR", "ACCESS_TTL_MIN", "REFRESH_TTL_DAYS", "LOGIN_MAX_FAILS", "LOGIN_WINDOW_SEC", "SCHEDULER_INTERVAL", "ADMIN_USERNAME") ENV_SECRET = ("SECRET_KEY", "DATABASE_URL", "DB_PASSWORD", "ADMIN_PASSWORD") -SECRET_SETTINGS = {"assistant_api_key"} +SECRET_SETTINGS = {"assistant_api_key", "nc_password", "onlyoffice_jwt"} DEFAULTS = {"backup_auto_hours": "24", "backup_keep": "14", "backup_media": "0"} diff --git a/app/demo.py b/app/demo.py index d75150f..4201228 100644 --- a/app/demo.py +++ b/app/demo.py @@ -335,6 +335,7 @@ def seed(db: Session, admin: dict, photo_dir: str) -> None: # --- портал: склады, инструмент, заявки, инциденты, суточные задания, карта --- demo_portal.seed(db, users=users, eq=eq, pos=pos, sch_store=sch_d, pw=pw, now=now, rnd=rnd) db.commit() + logic.schedules_to_users(db) # графики демо-должностей — сотрудникам def demo_accounts(db: Session) -> list[dict]: @@ -443,3 +444,5 @@ def activate_on_startup() -> None: e = _engine_for(mode.State.db_file) ensure_schema(e) _db.set_demo_engine(e) + with sessionmaker(bind=e)() as db: # демо-база прежней версии: графики должностей — сотрудникам + logic.schedules_to_users(db) diff --git a/app/docs_index.py b/app/docs_index.py new file mode 100644 index 0000000..5cc8caf --- /dev/null +++ b/app/docs_index.py @@ -0,0 +1,160 @@ +"""Индекс документации для опции AI-помощника «Работа с документацией». + +Раз в сутки (и по кнопке) обходим папки, привязанные к машинам, и для новых или изменившихся файлов (по etag) +сохраняем имя и начало текста: PDF — через pypdf, docx/xlsx/pptx/odt — из XML внутри архива, текстовые файлы как есть. +Поиск — по словам в имени и тексте, только по машинам, которые видит пользователь. Помощник получает короткие +фрагменты и путь к файлу, а не документы целиком.""" +import io +import logging +import re +import threading +import zipfile +from datetime import timedelta + +from sqlalchemy import delete, or_, select +from sqlalchemy.orm import Session + +from . import config, nextcloud as nc, scope, settings_store as ss +from .models import DocIndex, Equipment, User + +log = logging.getLogger("docs_index") +MAX_TEXT = 20_000 # символов текста на файл +MAX_INDEX_MB = 60 # файлы крупнее — только по имени +MAX_PDF_PAGES = 40 +EVERY = timedelta(hours=24) +_lock = threading.Lock() +state = {"running": False, "done": 0, "total": 0, "error": ""} + + +def enabled(db: Session) -> bool: + return ss.get(db, "docs_ai") == "1" + + +def norm(s: str) -> str: + return re.sub(r"\s+", " ", s.lower().replace("ё", "е")).strip() + + +def _xml_text(z: zipfile.ZipFile, names: list[str]) -> str: + parts = [] + for n in names: + raw = z.read(n).decode("utf-8", "ignore") + parts.append(re.sub(r"<[^>]+>", " ", re.sub(r"", "\n", raw))) + return " ".join(parts) + + +def extract(name: str, data: bytes) -> str: + """Текст документа (может быть пустым — например, скан без текстового слоя).""" + ext = nc.ext_of(name) + if ext in nc.TEXT: + return data[: MAX_TEXT * 4].decode("utf-8", "ignore") + if ext == "pdf": + try: + from pypdf import PdfReader + except ImportError: + return "" + reader = PdfReader(io.BytesIO(data)) + out = [] + for page in reader.pages[:MAX_PDF_PAGES]: + out.append(page.extract_text() or "") + if sum(map(len, out)) > MAX_TEXT: + break + return "\n".join(out) + if ext in ("docx", "docm", "dotx", "xlsx", "xlsm", "pptx", "odt", "ods", "odp"): + with zipfile.ZipFile(io.BytesIO(data)) as z: + files = z.namelist() + pick = ([n for n in files if n == "word/document.xml"] or [n for n in files if n == "xl/sharedStrings.xml"] + or sorted(n for n in files if re.fullmatch(r"ppt/slides/slide\d+\.xml", n)) or [n for n in files if n == "content.xml"]) + return _xml_text(z, pick) + return "" + + +def index_equipment(db: Session, c: nc.Conf, eq: Equipment) -> int: + """Обновить индекс одной машины; вернуть число прочитанных заново файлов.""" + files = nc.walk(c, eq.docs_path) + have = {r.path: r for r in db.scalars(select(DocIndex).where(DocIndex.equipment_id == eq.id))} + seen, fresh = set(), 0 + base = len(nc.clean(eq.docs_path)) + for f in files: + rel = f["path"][base:].strip("/") + seen.add(rel) + row = have.get(rel) + if row and row.etag == f["etag"]: + continue + text, note = "", "" + if f["size"] > MAX_INDEX_MB * 1024 * 1024: + note = "большой файл — только имя" + elif nc.ext_of(f["name"]) in nc.TEXT | {"pdf"} | nc.OFFICE: + try: + text = extract(f["name"], nc.fetch(c, f).read_bytes()) + note = "" if text.strip() else "текст не найден (скан?)" + except Exception as e: # повреждённый или защищённый файл не должен останавливать индекс + note = f"не прочитан: {type(e).__name__}"[:200] + row = row or DocIndex(equipment_id=eq.id, path=rel) + row.name, row.etag, row.size = f["name"][:300], f["etag"][:200], f["size"] + row.text, row.note, row.indexed_at = norm(rel + " " + text)[:MAX_TEXT], note, config.now() # путь тоже ищется + db.add(row) + fresh += 1 + state["done"] += 1 + gone = [p for p in have if p not in seen] + if gone: + db.execute(delete(DocIndex).where(DocIndex.equipment_id == eq.id, DocIndex.path.in_(gone))) + db.commit() + return fresh + + +def rebuild(db: Session) -> dict: + """Обойти все привязанные папки. Ошибка одной машины не мешает остальным.""" + if not _lock.acquire(blocking=False): + return {"running": True} + try: + state.update(running=True, done=0, total=0, error="") + c = nc.conf(db) + eqs = db.scalars(select(Equipment).where(Equipment.docs_path != "")).all() + db.execute(delete(DocIndex).where(DocIndex.equipment_id.not_in([e.id for e in eqs] or [0]))) + errors = [] + for eq in eqs: + try: + index_equipment(db, c, eq) + except nc.DocsError as e: + db.rollback() + errors.append(f"{eq.name}: {e}") + ss.set_(db, "docs_index_at", config.now().isoformat()) + db.commit() + state["error"] = "; ".join(errors)[:500] + return {"files": db.query(DocIndex).count(), "errors": errors} + finally: + state["running"] = False + _lock.release() + + +def due(db: Session) -> bool: + if not enabled(db) or not nc.conf(db).ready: + return False + at = ss.get(db, "docs_index_at") + try: + from datetime import datetime + return not at or config.now() - datetime.fromisoformat(at) >= EVERY + except ValueError: + return True + + +def search(db: Session, u: User, q: str, limit: int = 10) -> list[dict]: + words = [w for w in re.findall(r"\w+", norm(q)) if len(w) >= 3][:8] + if not words: + return [] + cond = or_(*[DocIndex.text.contains(w) for w in words]) + stmt = select(DocIndex, Equipment).join(Equipment, Equipment.id == DocIndex.equipment_id).where(cond) + vis = scope.equipment_filter(db, u) + if vis is not None: + stmt = stmt.where(vis) + scored = [] + for row, eq in db.execute(stmt.limit(500)): + name = norm(row.path) + score = sum(5 * (w in name) + min(row.text.count(w), 20) for w in words) + body = row.text[len(name) + 1:] + hit = min((i for i in (body.find(w) for w in words) if i >= 0), default=-1) + snippet = body[max(0, hit - 120): hit + 220] if hit >= 0 else "" + scored.append((score, {"equipment_id": eq.id, "equipment_name": eq.name, "path": row.path, "name": row.name, + "snippet": snippet, "note": row.note})) + scored.sort(key=lambda x: -x[0]) + return [x for _, x in scored[:limit]] diff --git a/app/logic.py b/app/logic.py index acebf06..a2b0c0b 100644 --- a/app/logic.py +++ b/app/logic.py @@ -67,8 +67,26 @@ def shift_active(s: ShiftSchedule | None, dt: datetime) -> bool: def user_on_shift(u: User, dt: datetime | None = None) -> bool: + """На смене ли сотрудник: занимает должность и сейчас по СВОЕМУ графику работает (без графика — всегда).""" dt = dt or config.now() - return bool(u.position) and shift_active(u.position.schedule, dt) + return bool(u.position) and shift_active(u.schedule, dt) + + +def schedules_to_users(db) -> int: + """Однократный перенос: раньше график задавался должности — теперь он у каждого сотрудника. + Сотрудникам без своего графика ставится график их должности; повторно не выполняется (отметка в настройках).""" + from . import settings_store as ss + from .models import Position + if ss.get(db, "schedule_per_user") == "1": + return 0 + n = 0 + for u, sid in db.execute(select(User, Position.schedule_id).join(Position, Position.id == User.position_id) + .where(User.schedule_id.is_(None), Position.schedule_id.is_not(None))): + u.schedule_id = sid + n += 1 + ss.set_(db, "schedule_per_user", "1") + db.commit() + return n # ---------- расписание запуска нарядов ---------- @@ -120,7 +138,7 @@ def create_order(db: Session, *, title, checklist_id, equipment_id=None, locatio for it in cl.items: o.items.append(OrderItem(position=it.position, text=it.text, answer_type=it.answer_type, options=it.options, unit=it.unit, min_value=it.min_value, - max_value=it.max_value, required=it.required, photo=it.photo)) + max_value=it.max_value, required=it.required, photo=it.photo, photo_keep=it.photo_keep)) db.add(o) return o @@ -141,7 +159,7 @@ def _regular_items(db: Session, o: Order, cl: Checklist) -> None: pos += 1 o.items.append(OrderItem(position=pos, text=it.text, answer_type=it.answer_type, options=it.options, unit=it.unit, min_value=it.min_value, max_value=it.max_value, - required=it.required, photo=it.photo)) + required=it.required, photo=it.photo, photo_keep=it.photo_keep)) for u, ev in units: pos += 1 o.items.append(OrderItem(position=pos, text=service.describe(u, ev) + " — обслужено?", answer_type="status", diff --git a/app/main.py b/app/main.py index 83bc869..dcee711 100644 --- a/app/main.py +++ b/app/main.py @@ -9,13 +9,13 @@ from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from sqlalchemy import select -from . import backup, config, demo, logic, mode, push +from . import backup, config, demo, docs_index, logic, mode, photo_cleanup, push from .db import SessionLocal, real_engine from .migrate import ensure_schema from .models import User from .routers import catalog, orders, extras, reports, service as service_router, demo as demo_router from .routers import inventory, incidents, tasks, portal, external, history, backups, assistant as assistant_router -from .routers import reliability as reliability_router, feedback as feedback_router +from .routers import reliability as reliability_router, feedback as feedback_router, docs as docs_router from .security import hash_password log = logging.getLogger("scheduler") @@ -49,6 +49,14 @@ def bootstrap(): db.commit() log.warning("Создан администратор '%s' — смените пароль!", config.ADMIN_USERNAME) push.ensure_vapid(db) + moved = logic.schedules_to_users(db) + if moved: + log.warning("Графики работы перенесены с должностей на сотрудников: %s", moved) + + +def _reindex_docs(): + with SessionLocal() as db: + docs_index.rebuild(db) async def scheduler_loop(): @@ -67,6 +75,18 @@ async def scheduler_loop(): backup.auto_backup(db) except Exception: log.exception("Ошибка автоматического резервного копирования") + try: + with SessionLocal() as db: + if photo_cleanup.due(db): # старые фото — раз в сутки + photo_cleanup.run(db) + except Exception: + log.exception("Ошибка очистки старых фото") + try: + with SessionLocal() as db: + if docs_index.due(db): # индекс документации для AI — раз в сутки, в фоне + asyncio.get_running_loop().run_in_executor(None, _reindex_docs) + except Exception: + log.exception("Ошибка индекса документации") await asyncio.sleep(config.SCHEDULER_INTERVAL) @@ -123,6 +143,7 @@ api.include_router(backups.router) api.include_router(assistant_router.router) api.include_router(reliability_router.router) api.include_router(feedback_router.router) +api.include_router(docs_router.router) @api.get("/health", tags=["service"]) diff --git a/app/media.py b/app/media.py index c7c69fb..7ef9ee9 100644 --- a/app/media.py +++ b/app/media.py @@ -13,7 +13,7 @@ from . import settings_store as ss MAGIC = [(b"\xff\xd8\xff", "image/jpeg", ".jpg"), (b"\x89PNG\r\n\x1a\n", "image/png", ".png")] CT = {".jpg": "image/jpeg", ".png": "image/png", ".webp": "image/webp"} -NAME_RE = re.compile(r"[0-9a-f]{32}\.(jpg|png|webp)") +NAME_RE = re.compile(r"(keep/)?[0-9a-f]{32}\.(jpg|png|webp)") # keep/ — хранить долго (очистка не трогает) KINDS = ("equipment", "users", "map", "tasks") @@ -47,9 +47,11 @@ async def read_image(db: Session, request: Request, max_mb: int | None = None) - return data, kind[0], kind[1] -def save(db: Session, kind: str, data: bytes, ext: str) -> str: - name = ss.new_name() + ext - (root(db, kind) / name).write_bytes(data) +def save(db: Session, kind: str, data: bytes, ext: str, keep: bool = False) -> str: + name = ("keep/" if keep else "") + ss.new_name() + ext + f = root(db, kind) / name + f.parent.mkdir(parents=True, exist_ok=True) + f.write_bytes(data) return name diff --git a/app/models.py b/app/models.py index 8d6a0ec..c982f88 100644 --- a/app/models.py +++ b/app/models.py @@ -27,8 +27,8 @@ class Position(Base): id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(String(100), unique=True) kind: Mapped[str] = mapped_column(String(12), default="technician") + # устарело: график теперь у сотрудника (User.schedule_id); значение перенесено сотрудникам при обновлении и больше не используется schedule_id: Mapped[int | None] = mapped_column(ForeignKey("schedules.id"), nullable=True) - schedule: Mapped[ShiftSchedule | None] = relationship(lazy="joined") class Department(Base): @@ -55,6 +55,9 @@ class User(Base): is_active: Mapped[bool] = mapped_column(Boolean, default=True) position_id: Mapped[int | None] = mapped_column(ForeignKey("positions.id"), nullable=True) position: Mapped[Position | None] = relationship(lazy="joined") + # график работы сотрудника: должность выполняет тот, кто её занимает и сейчас на смене. Пусто — всегда на смене + schedule_id: Mapped[int | None] = mapped_column(ForeignKey("schedules.id"), nullable=True) + schedule: Mapped[ShiftSchedule | None] = relationship(lazy="joined") department_id: Mapped[int | None] = mapped_column(ForeignKey("departments.id"), nullable=True) department: Mapped[Department | None] = relationship(lazy="joined", foreign_keys=[department_id]) # карточка сотрудника @@ -89,6 +92,8 @@ class Equipment(Base): department_id: Mapped[int | None] = mapped_column(ForeignKey("departments.id"), nullable=True) # кран (грузоподъёмное оборудование): при включённой карте кранов показывается на ней, а не на карте завода is_crane: Mapped[bool] = mapped_column(Boolean, default=False) + # папка документации в Nextcloud (путь от корня документации в настройках сервера); пусто — не привязана + docs_path: Mapped[str] = mapped_column(String(500), default="") class ServiceUnit(Base): @@ -160,6 +165,7 @@ class ChecklistItem(Base): max_value: Mapped[float | None] = mapped_column(Float, nullable=True) required: Mapped[bool] = mapped_column(Boolean, default=True) photo: Mapped[str] = mapped_column(String(10), default="none") # none|optional|required + photo_keep: Mapped[bool] = mapped_column(Boolean, default=False) # фото хранить более года (каталог keep/, очистка не трогает) class OrderTemplate(Base): @@ -232,6 +238,7 @@ class OrderItem(Base): answered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) out_of_range: Mapped[bool] = mapped_column(Boolean, default=False) photo: Mapped[str] = mapped_column(String(10), default="none") + photo_keep: Mapped[bool] = mapped_column(Boolean, default=False) service_unit_id: Mapped[int | None] = mapped_column(ForeignKey("service_units.id"), nullable=True) # «узел обслужен» is_meter: Mapped[bool] = mapped_column(Boolean, default=False) # пункт «наработка, м/ч» — обновляет счётчик машины photos: Mapped[list["Photo"]] = relationship( @@ -276,6 +283,20 @@ class Feedback(Base): answered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) +class DocIndex(Base): + """Индекс документации для AI-помощника: имя файла и фрагмент текста (в нижнем регистре). Строится заново из Nextcloud.""" + __tablename__ = "doc_index" + id: Mapped[int] = mapped_column(primary_key=True) + equipment_id: Mapped[int] = mapped_column(ForeignKey("equipment.id", ondelete="CASCADE"), index=True) + path: Mapped[str] = mapped_column(String(1000)) # путь файла от папки машины + name: Mapped[str] = mapped_column(String(300)) + etag: Mapped[str] = mapped_column(String(200), default="") + size: Mapped[int] = mapped_column(Integer, default=0) + text: Mapped[str] = mapped_column(Text, default="") # извлечённый текст, нижний регистр, обрезан + note: Mapped[str] = mapped_column(String(200), default="") # почему текста нет (большой файл, ошибка чтения) + indexed_at: Mapped[datetime] = mapped_column(DateTime, default=config.now) + + class Setting(Base): __tablename__ = "settings" key: Mapped[str] = mapped_column(String(64), primary_key=True) @@ -464,6 +485,7 @@ class DailyTask(Base): updated_at: Mapped[datetime] = mapped_column(DateTime, default=config.now) finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) labor_hours: Mapped[float | None] = mapped_column(Float, nullable=True) # трудоёмкость (норма), ч — для отчёта по сотрудникам + photo_keep: Mapped[bool] = mapped_column(Boolean, default=False) # фото к заданию хранить более года class TaskEvent(Base): diff --git a/app/nextcloud.py b/app/nextcloud.py new file mode 100644 index 0000000..f3d2fa8 --- /dev/null +++ b/app/nextcloud.py @@ -0,0 +1,316 @@ +"""Документация оборудования из Nextcloud. + +Портал ходит в Nextcloud сам, по WebDAV, от служебной учётной записи (пароль приложения, только чтение). Сотрудники +Nextcloud не видят: список папок и файлы отдаёт портал, проверяя права на машину. Документы Office для просмотра +переводятся в PDF через ONLYOFFICE Document Server (ConvertService): сервер документов скачивает исходник по короткой +подписанной ссылке портала (/api/v1/docs/raw/...), результат кэшируется в DATA_DIR/docs_cache.""" +import base64 +import hashlib +import hmac +import logging +import os +import time +import xml.etree.ElementTree as ET +from dataclasses import dataclass +from email.utils import parsedate_to_datetime +from pathlib import Path +from urllib.parse import quote, unquote, urlsplit + +import httpx +import jwt +from sqlalchemy.orm import Session + +from . import config, settings_store as ss + +log = logging.getLogger("nextcloud") +TIMEOUT = httpx.Timeout(30, read=120) +CACHE_DIR = Path(config.DATA_DIR) / "docs_cache" +CACHE_MAX_MB = int(os.getenv("DOCS_CACHE_MB", "2048")) +MAX_FILE_MB = int(os.getenv("DOCS_MAX_FILE_MB", "300")) # больше — не открываем через портал +RAW_TTL = 600 # ссылка для ONLYOFFICE живёт 10 минут +DAV = "{DAV:}" +IMAGES = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg", "gif": "image/gif", "webp": "image/webp", "bmp": "image/bmp"} +TEXT = {"txt", "csv", "log", "md", "ini", "cfg"} +OFFICE = {"doc", "docx", "docm", "dotx", "rtf", "odt", "xls", "xlsx", "xlsm", "ods", "ppt", "pptx", "odp"} + + +class DocsError(Exception): + pass + + +@dataclass(frozen=True) +class Conf: + url: str + user: str + password: str + root: str + oo_url: str + oo_jwt: str + internal_url: str + + @property + def ready(self) -> bool: + return bool(self.url and self.user and self.password) + + +def conf(db: Session) -> Conf: + g = lambda k: (ss.get(db, k) or "").strip() + return Conf(g("nc_url").rstrip("/"), g("nc_user"), ss.get(db, "nc_password") or "", clean(g("nc_root")), + g("onlyoffice_url").rstrip("/"), ss.get(db, "onlyoffice_jwt") or "", g("portal_internal_url").rstrip("/")) + + +def clean(path: str | None, allow_empty: bool = True) -> str: + """Относительный путь без «..», обратных слешей и управляющих символов. Пустой — корень.""" + parts = [x for x in (path or "").replace("\\", "/").strip().split("/") if x] + for x in parts: + if x in (".", "..") or any(ord(ch) < 32 for ch in x) or len(x) > 255: + raise DocsError("Недопустимый путь") + if not parts and not allow_empty: + raise DocsError("Не указан путь") + return "/".join(parts) + + +def join(*parts: str) -> str: + return "/".join(x for x in (clean(p) for p in parts) if x) + + +def ext_of(name: str) -> str: + return name.rsplit(".", 1)[-1].lower() if "." in name else "" + + +def kind(name: str) -> str: + ext = ext_of(name) + if ext == "pdf": + return "pdf" + if ext in IMAGES: + return "image" + if ext in TEXT: + return "text" + if ext in OFFICE: + return "office" + return "file" + + +# ---------- WebDAV ---------- +def _base(c: Conf) -> str: + return f"{c.url}/remote.php/dav/files/{quote(c.user, safe='')}" + + +def _href(c: Conf, rel: str) -> str: + full = join(c.root, rel) + return _base(c) + ("/" + quote(full) if full else "") + + +def _oo_client(timeout) -> httpx.Client: + return httpx.Client(timeout=timeout) + + +def _ready(c: Conf): + if not c.ready: + raise DocsError("Nextcloud не настроен: укажите адрес, учётную запись и пароль приложения в настройках сервера") + + +def _client(c: Conf) -> httpx.Client: + return httpx.Client(auth=(c.user, c.password), timeout=TIMEOUT, follow_redirects=False) + + +PROPS = """ +""" + + +def _propfind(c: Conf, rel: str, depth: int) -> list[dict]: + _ready(c) + try: + with _client(c) as h: + r = h.request("PROPFIND", _href(c, rel), headers={"Depth": str(depth), "Content-Type": "application/xml"}, content=PROPS) + except httpx.HTTPError as e: + raise DocsError(f"Nextcloud недоступен: {type(e).__name__}") + if r.status_code == 401: + raise DocsError("Nextcloud отклонил учётную запись: проверьте логин и пароль приложения") + if r.status_code == 404: + raise DocsError("Папка или файл не найдены в Nextcloud") + if r.status_code != 207: + raise DocsError(f"Nextcloud ответил {r.status_code}") + return parse_multistatus(r.content, urlsplit(_href(c, "")).path) + + +def parse_multistatus(body: bytes, prefix: str) -> list[dict]: + """Ответ PROPFIND → [{path (от корня документации), name, is_dir, size, mtime, etag, mime, kind}].""" + prefix = unquote(prefix).rstrip("/") + out = [] + try: + root = ET.fromstring(body) + except ET.ParseError: + raise DocsError("Nextcloud ответил непонятно — проверьте адрес") + for resp in root.iter(DAV + "response"): + href = unquote(urlsplit(resp.findtext(DAV + "href") or "").path).rstrip("/") + if href != prefix and not href.startswith(prefix + "/"): + continue + rel = href[len(prefix):].strip("/") + prop = next((p.find(DAV + "prop") for p in resp.iter(DAV + "propstat") if "200" in (p.findtext(DAV + "status") or "")), None) + if prop is None: + continue + is_dir = prop.find(DAV + "resourcetype/" + DAV + "collection") is not None + lm = prop.findtext(DAV + "getlastmodified") + try: + mtime = parsedate_to_datetime(lm).astimezone(config.TZ).isoformat() if lm else None + except (TypeError, ValueError): + mtime = None + name = rel.rsplit("/", 1)[-1] + out.append({"path": rel, "name": name, "is_dir": is_dir, "size": int(prop.findtext(DAV + "getcontentlength") or 0), + "mtime": mtime, "etag": (prop.findtext(DAV + "getetag") or "").strip('"'), + "mime": prop.findtext(DAV + "getcontenttype") or "", "kind": "dir" if is_dir else kind(name)}) + return out + + +def listdir(c: Conf, rel: str) -> list[dict]: + """Содержимое папки (без самой папки и скрытых файлов): сначала папки, потом файлы, по имени.""" + rel = clean(rel) + items = [x for x in _propfind(c, rel, 1) if x["path"] != rel and not x["name"].startswith(".")] + return sorted(items, key=lambda x: (not x["is_dir"], x["name"].lower())) + + +def stat(c: Conf, rel: str) -> dict: + rel = clean(rel, allow_empty=False) + items = [x for x in _propfind(c, rel, 0) if x["path"] == rel] + if not items: + raise DocsError("Файл не найден в Nextcloud") + return items[0] + + +def walk(c: Conf, rel: str, max_depth: int = 6, limit: int = 3000) -> list[dict]: + """Все файлы в папке и вложенных (для индекса).""" + out, stack = [], [(clean(rel), 0)] + while stack and len(out) < limit: + p, d = stack.pop() + for x in listdir(c, p): + if x["is_dir"]: + if d < max_depth: + stack.append((x["path"], d + 1)) + else: + out.append(x) + return out[:limit] + + +# ---------- кэш файлов ---------- +def cache_path(rel: str, etag: str, suffix: str) -> Path: + return CACHE_DIR / (hashlib.sha1(f"{rel}|{etag}".encode()).hexdigest() + suffix) + + +def _prune(): + files = sorted((p for p in CACHE_DIR.glob("*") if p.is_file()), key=lambda p: p.stat().st_mtime) + total = sum(p.stat().st_size for p in files) + while files and total > CACHE_MAX_MB * 1024 * 1024: + p = files.pop(0) + total -= p.stat().st_size + p.unlink(missing_ok=True) + + +def original_path(info: dict) -> Path: + ext = ext_of(info["name"]) + return cache_path(info["path"], info["etag"], "." + ext if ext else "") + + +def fetch(c: Conf, info: dict) -> Path: + """Скачать файл в кэш (если ещё нет этой версии) и вернуть путь к нему.""" + if info["size"] > MAX_FILE_MB * 1024 * 1024: + raise DocsError(f"Файл больше {MAX_FILE_MB} МБ — его нельзя открыть через портал") + _ready(c) + dest = original_path(info) + if dest.exists(): + os.utime(dest) + return dest + CACHE_DIR.mkdir(parents=True, exist_ok=True) + tmp = dest.with_name(dest.name + f".{os.getpid()}.part") + try: + with _client(c) as h, h.stream("GET", _href(c, info["path"])) as r: + if r.status_code != 200: + raise DocsError(f"Nextcloud не отдал файл ({r.status_code})") + with open(tmp, "wb") as f: + for chunk in r.iter_bytes(1 << 16): + f.write(chunk) + tmp.replace(dest) + except httpx.HTTPError as e: + raise DocsError(f"Nextcloud недоступен: {type(e).__name__}") + finally: + tmp.unlink(missing_ok=True) + _prune() + return dest + + +# ---------- ONLYOFFICE: Office → PDF ---------- +def _sign(msg: str) -> str: + return hmac.new(config.SECRET_KEY.encode(), msg.encode(), hashlib.sha256).hexdigest()[:32] + + +def raw_token(info: dict, now: float | None = None) -> str: + """Короткая подписанная ссылка на исходник в кэше — по ней ONLYOFFICE скачивает документ без входа в портал.""" + exp = int((now or time.time()) + RAW_TTL) + p = base64.urlsafe_b64encode(f"{info['path']}\n{info['etag']}\n{info['name']}".encode()).decode().rstrip("=") + return f"{p}.{exp}.{_sign(f'raw|{p}|{exp}')}" + + +def check_raw_token(token: str) -> Path: + try: + p, exp, sig = token.split(".") + if not hmac.compare_digest(sig, _sign(f"raw|{p}|{exp}")) or int(exp) < time.time(): + raise ValueError + path, etag, name = base64.urlsafe_b64decode(p + "=" * (-len(p) % 4)).decode().split("\n") + except (ValueError, UnicodeDecodeError): + raise DocsError("Ссылка недействительна") + f = original_path({"path": path, "etag": etag, "name": name}) + if not f.exists(): + raise DocsError("Файл не найден") + return f + + +def to_pdf(c: Conf, info: dict) -> Path: + """Документ Office → PDF через ONLYOFFICE ConvertService (результат кэшируется по версии файла).""" + dest = cache_path(info["path"], info["etag"], ".view.pdf") + if dest.exists(): + os.utime(dest) + return dest + if not (c.oo_url and c.internal_url): + raise DocsError("Просмотр документов Office не настроен: укажите адрес ONLYOFFICE и внутренний адрес портала") + fetch(c, info) # исходник должен лежать в кэше: его отдаст /docs/raw + body = {"async": False, "filetype": ext_of(info["name"]), "outputtype": "pdf", "title": info["name"][:200], + "key": hashlib.sha1(f"{info['path']}|{info['etag']}".encode()).hexdigest(), + "url": f"{c.internal_url}/api/v1/docs/raw/{raw_token(info)}"} + headers = {"Accept": "application/json"} + if c.oo_jwt: # новые версии читают token в теле, старые — заголовок Authorization + headers["Authorization"] = "Bearer " + jwt.encode({"payload": body}, c.oo_jwt, algorithm="HS256") + body = {**body, "token": jwt.encode(body, c.oo_jwt, algorithm="HS256")} + try: + with _oo_client(httpx.Timeout(30, read=180)) as h: + r = h.post(f"{c.oo_url}/ConvertService.ashx", json=body, headers=headers) + data = r.json() if r.status_code == 200 else {} + if data.get("error") or not data.get("endConvert") or not data.get("fileUrl"): + code = data.get("error", r.status_code) + hint = ": проверьте секрет JWT" if code == -8 else ": ONLYOFFICE не смог скачать файл с портала — проверьте внутренний адрес портала" if code == -4 else "" + raise DocsError(f"ONLYOFFICE не смог преобразовать документ (код {code}){hint}") + out = h.get(data["fileUrl"]) + if out.status_code != 200: + raise DocsError(f"ONLYOFFICE не отдал результат ({out.status_code})") + except httpx.HTTPError as e: + raise DocsError(f"ONLYOFFICE недоступен: {type(e).__name__}") + except ValueError: + raise DocsError("ONLYOFFICE ответил непонятно — проверьте адрес Document Server") + CACHE_DIR.mkdir(parents=True, exist_ok=True) + dest.write_bytes(out.content) + _prune() + return dest + + +def check(c: Conf) -> dict: + """Проверка подключения: Nextcloud (корень документации) и ONLYOFFICE (healthcheck).""" + items = listdir(c, "") + out = {"nextcloud": f"в корне документации {sum(x['is_dir'] for x in items)} папок и {sum(not x['is_dir'] for x in items)} файлов", + "onlyoffice": None} + if c.oo_url: + try: + with _oo_client(10) as h: + r = h.get(f"{c.oo_url}/healthcheck") + out["onlyoffice"] = "работает" if r.status_code == 200 and r.text.strip().lower() == "true" else f"ответ {r.status_code}" + except httpx.HTTPError as e: + out["onlyoffice"] = f"недоступен: {type(e).__name__}" + return out diff --git a/app/photo_cleanup.py b/app/photo_cleanup.py new file mode 100644 index 0000000..7927990 --- /dev/null +++ b/app/photo_cleanup.py @@ -0,0 +1,63 @@ +"""Самоочистка сервера от старых фото нарядов и заданий. + +Фото, которые нужно хранить дольше (у пункта чек-листа или задания отмечено «хранить более года»), с самого начала +кладутся в отдельный каталог keep/ и очисткой не трогаются. Остальные удаляются, когда старше заданного числа дней +(настройка photo_keep_days, 0 — не удалять). Проверка — раз в сутки из планировщика, вручную — из настроек сервера.""" +import logging +from datetime import datetime, timedelta + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from . import config, media, settings_store as ss +from .models import Photo, TaskPhoto + +log = logging.getLogger("photo_cleanup") +KEEP = "keep/" +EVERY = timedelta(hours=24) + + +def days(db: Session) -> int: + try: + return max(0, int(ss.get(db, "photo_keep_days") or 0)) + except ValueError: + return 0 + + +def _old(db: Session, model, cut: datetime): + return db.scalars(select(model).where(model.created_at < cut, model.path.not_like(KEEP + "%"))).all() + + +def run(db: Session, dry: bool = False, now: datetime | None = None) -> dict: + """Удалить старые фото (dry — только посчитать). Возвращает число фото и освобождённые байты.""" + n = days(db) + if n <= 0: + return {"enabled": False, "days": 0, "photos": 0, "bytes": 0} + cut = (now or config.now()) - timedelta(days=n) + root = ss.photo_root(db) + order_ph, task_ph = _old(db, Photo, cut), _old(db, TaskPhoto, cut) + out = {"enabled": True, "days": n, "before": config.iso(cut), "photos": len(order_ph) + len(task_ph), + "bytes": sum(p.size or 0 for p in order_ph + task_ph)} + if dry: + return out + for p in order_ph: + (root / p.path).unlink(missing_ok=True) + db.delete(p) + for p in task_ph: + media.remove(db, "tasks", p.path) + db.delete(p) + ss.set_(db, "photo_cleanup_at", config.now().isoformat()) + db.commit() + if out["photos"]: + log.warning("Удалено старых фото: %s (%.1f МБ), старше %s дней", out["photos"], out["bytes"] / 1048576, n) + return out + + +def due(db: Session) -> bool: + if days(db) <= 0: + return False + at = ss.get(db, "photo_cleanup_at") + try: + return not at or config.now() - datetime.fromisoformat(at) >= EVERY + except ValueError: + return True diff --git a/app/push.py b/app/push.py index c70daf3..9f89883 100644 --- a/app/push.py +++ b/app/push.py @@ -122,13 +122,14 @@ def notify(user_ids, title: str, body: str, url: str = "/"): def users_for_assignment(db: Session, user_id, position_id) -> list[int]: - """Кого уведомлять о назначении: конкретного человека или тех, кто сейчас на смене в должности.""" + """Кого уведомлять о назначении: конкретного человека или тех, кто занимает должность и сейчас на смене + по своему графику (сменщики, которые дома, уведомлений не получают).""" if user_id: return [user_id] if position_id: - pos = db.get(Position, position_id) us = db.scalars(select(User).where(User.position_id == position_id, User.is_active.is_(True))).all() - return [u.id for u in us if pos and logic.shift_active(pos.schedule, logic.config.now())] + now = logic.config.now() + return [u.id for u in us if logic.user_on_shift(u, now)] return [] diff --git a/app/routers/assistant.py b/app/routers/assistant.py index 1dfd6ae..f385192 100644 --- a/app/routers/assistant.py +++ b/app/routers/assistant.py @@ -62,7 +62,7 @@ def put_config(data: ConfigIn, db: Session = Depends(get_db), _: User = Depends( ss.set_(db, "assistant_" + k, ("1" if v else "0") if isinstance(v, bool) else str(v).strip()) commit(db) c = A.cfg(db) - if c["enabled"] and (not c["model"] or not (c["api_key"] or "localhost" in c["base_url"] or "127.0.0.1" in c["base_url"])): + if c["enabled"] and (not c["model"] or not (c["api_key"] or A.autoconf.is_local(c["base_url"]))): ss.set_(db, "assistant_enabled", "0") commit(db) raise HTTPException(422, "Для включения укажите модель и ключ API (без ключа — только локальная модель)") diff --git a/app/routers/catalog.py b/app/routers/catalog.py index f2b3f84..459869a 100644 --- a/app/routers/catalog.py +++ b/app/routers/catalog.py @@ -190,6 +190,7 @@ def me(db: Session = Depends(get_db), u: User = Depends(current_user)): out.crane_map = scope.crane_map_enabled(db) from ..security import is_engineer, is_manager out.reliability = ss.get(db, "reliability") != "0" and (is_manager(u) or is_engineer(u)) + out.docs = bool(ss.get(db, "nc_url") and ss.get(db, "nc_user") and ss.get(db, "nc_password")) # документация из Nextcloud подключена return out @@ -357,6 +358,8 @@ def check_user_refs(db: Session, fields: dict): raise HTTPException(422, "Отдел не найден") if fields.get("position_id") and not db.get(Position, fields["position_id"]): raise HTTPException(422, "Должность не найдена") + if fields.get("schedule_id") and not db.get(ShiftSchedule, fields["schedule_id"]): + raise HTTPException(422, "График не найден") def guard_user_edit(me: User, target: User | None, fields: dict): diff --git a/app/routers/docs.py b/app/routers/docs.py new file mode 100644 index 0000000..6cd3d6d --- /dev/null +++ b/app/routers/docs.py @@ -0,0 +1,191 @@ +"""Документация оборудования (Nextcloud): настройки и проверка (администратор портала), проводник для привязки папок +(администратор и модератор), просмотр документов машины (кто видит машину), индекс и поиск для AI-помощника.""" +import asyncio +from typing import Optional + +from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi.responses import FileResponse +from pydantic import BaseModel, Field +from sqlalchemy.orm import Session + +from .. import docs_index as DI, nextcloud as nc, scope, settings_store as ss +from ..db import SessionLocal, get_db +from ..models import DocIndex, Equipment, User +from ..security import current_user, require_manager, require_portal_admin +from .catalog import commit, get_or_404 + +router = APIRouter(tags=["docs"]) +SECRETS = ("nc_password", "onlyoffice_jwt") +NOSNIFF = {"X-Content-Type-Options": "nosniff", "Cache-Control": "private, max-age=300"} + + +class DocsConfigIn(BaseModel): + nc_url: Optional[str] = Field(default=None, max_length=300) + nc_user: Optional[str] = Field(default=None, max_length=200) + nc_password: Optional[str] = Field(default=None, max_length=500) # пустая строка — удалить; null — не менять + nc_root: Optional[str] = Field(default=None, max_length=500) + onlyoffice_url: Optional[str] = Field(default=None, max_length=300) + onlyoffice_jwt: Optional[str] = Field(default=None, max_length=500) + portal_internal_url: Optional[str] = Field(default=None, max_length=300) + docs_ai: Optional[bool] = None + + +class LinkIn(BaseModel): + path: str = Field(default="", max_length=500) # пусто — отвязать + + +def public_cfg(db: Session) -> dict: + out = {k: ss.get(db, k) for k in ("nc_url", "nc_user", "nc_root", "onlyoffice_url", "portal_internal_url", "docs_index_at")} + out.update({k + "_set": bool(ss.get(db, k)) for k in SECRETS}) + out["docs_ai"] = DI.enabled(db) + out["index"] = {**DI.state, "files": db.query(DocIndex).count(), "linked": db.query(Equipment).filter(Equipment.docs_path != "").count()} + return out + + +def _fail(e: nc.DocsError): + msg = str(e) + code = 502 if any(w in msg for w in ("недоступен", "ответил", "отклонил", "не отдал", "не смог")) else 404 if "не найден" in msg else 422 + raise HTTPException(code, msg) + + +@router.get("/docs/config") +def get_config(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + return public_cfg(db) + + +@router.put("/docs/config") +def put_config(data: DocsConfigIn, db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + d = data.model_dump(exclude_unset=True) + for k in ("nc_url", "onlyoffice_url", "portal_internal_url"): + if d.get(k) and not d[k].strip().lower().startswith(("https://", "http://")): + raise HTTPException(422, "Адрес должен начинаться с http:// или https://") + try: + if d.get("nc_root") is not None: + d["nc_root"] = nc.clean(d["nc_root"]) + except nc.DocsError as e: + raise HTTPException(422, str(e)) + for k, v in d.items(): + if v is None: + continue + if isinstance(v, bool): + v = "1" if v else "0" + ss.set_(db, k, v.strip().rstrip("/") if k.endswith("url") else v.strip()) + commit(db) + return public_cfg(db) + + +@router.post("/docs/test") +def test(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + try: + return nc.check(nc.conf(db)) + except nc.DocsError as e: + _fail(e) + + +@router.get("/docs/browse") +def browse(path: str = "", db: Session = Depends(get_db), _: User = Depends(require_manager)): + """Проводник по документации для привязки папок к машинам: только папки и сколько в папке файлов.""" + try: + rel = nc.clean(path) + items = nc.listdir(nc.conf(db), rel) + except nc.DocsError as e: + _fail(e) + linked: dict[str, list[str]] = {} + for name, p in db.query(Equipment.name, Equipment.docs_path).filter(Equipment.docs_path != ""): + linked.setdefault(p, []).append(name) + return {"path": rel, "items": [{**x, "linked": linked.get(x["path"], [])} for x in items if x["is_dir"]], + "files": sum(not x["is_dir"] for x in items)} + + +@router.put("/equipment/{id}/docs") +def link(id: int, data: LinkIn, db: Session = Depends(get_db), _: User = Depends(require_manager)): + eq = get_or_404(db, Equipment, id) + try: + rel = nc.clean(data.path) + if rel: + nc.listdir(nc.conf(db), rel) # папка должна существовать + except nc.DocsError as e: + _fail(e) + if rel != eq.docs_path: + db.query(DocIndex).filter(DocIndex.equipment_id == id).delete() + eq.docs_path = rel + commit(db) + return {"id": eq.id, "docs_path": eq.docs_path} + + +def _equipment(db: Session, u: User, id: int) -> Equipment: + eq = get_or_404(db, Equipment, id) + if not scope.user_sees(db, u, eq): + raise HTTPException(404, "Не найдено") + if not eq.docs_path: + raise HTTPException(404, "К машине не привязана папка документации") + return eq + + +@router.get("/equipment/{id}/docs") +def equipment_docs(id: int, sub: str = "", db: Session = Depends(get_db), u: User = Depends(current_user)): + """Содержимое папки документации машины (sub — вложенная папка).""" + eq = _equipment(db, u, id) + base = nc.clean(eq.docs_path) + try: + sub = nc.clean(sub) + items = nc.listdir(nc.conf(db), nc.join(base, sub)) + except nc.DocsError as e: + _fail(e) + return {"equipment_id": eq.id, "folder": base.rsplit("/", 1)[-1], "sub": sub, + "items": [{**x, "path": x["path"][len(base):].strip("/")} for x in items]} + + +@router.get("/equipment/{id}/docs/file") +def equipment_file(id: int, path: str = Query(min_length=1, max_length=1000), view: bool = False, + db: Session = Depends(get_db), u: User = Depends(current_user)): + """Файл документации. view=1 — для просмотра: PDF, картинки и текст как есть, Office — PDF через ONLYOFFICE.""" + eq = _equipment(db, u, id) + c = nc.conf(db) + try: + info = nc.stat(c, nc.join(eq.docs_path, nc.clean(path, allow_empty=False))) + if info["is_dir"]: + raise nc.DocsError("Это папка, а не файл") + k = info["kind"] + if view and k == "office": + return FileResponse(nc.to_pdf(c, info), media_type="application/pdf", headers=NOSNIFF, + filename=info["name"].rsplit(".", 1)[0] + ".pdf", content_disposition_type="inline") + f = nc.fetch(c, info) + except nc.DocsError as e: + _fail(e) + inline = view and k in ("pdf", "image", "text") + media = {"pdf": "application/pdf", "text": "text/plain; charset=utf-8"}.get(k) or nc.IMAGES.get(nc.ext_of(info["name"])) + return FileResponse(f, media_type=media if inline else "application/octet-stream", filename=info["name"], headers=NOSNIFF, + content_disposition_type="inline" if inline else "attachment") + + +@router.get("/docs/raw/{token}", include_in_schema=False) +def raw(token: str): + """Исходник для ONLYOFFICE по короткой подписанной ссылке (без входа).""" + try: + f = nc.check_raw_token(token) + except nc.DocsError as e: + raise HTTPException(404, str(e)) + return FileResponse(f, media_type="application/octet-stream", headers={"X-Content-Type-Options": "nosniff"}) + + +# ---------- индекс и поиск (опция «Работа с документацией») ---------- +def _rebuild_bg(): + with SessionLocal() as db: + DI.rebuild(db) + + +@router.post("/docs/reindex", status_code=202) +async def reindex(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + if not DI.enabled(db): + raise HTTPException(422, "Сначала включите «Работа с документацией»") + if not DI.state["running"]: + asyncio.get_running_loop().run_in_executor(None, _rebuild_bg) + return {"started": True} + + +@router.get("/docs/search") +def search(q: str = Query(min_length=2, max_length=200), db: Session = Depends(get_db), u: User = Depends(current_user)): + if not DI.enabled(db): + raise HTTPException(404, "Работа с документацией выключена") + return DI.search(db, u, q) diff --git a/app/routers/extras.py b/app/routers/extras.py index bc49964..6c11c25 100644 --- a/app/routers/extras.py +++ b/app/routers/extras.py @@ -9,10 +9,10 @@ from fastapi.responses import FileResponse from sqlalchemy import select, func from sqlalchemy.orm import Session -from .. import schemas as S, config, logic, push, settings_store as ss +from .. import schemas as S, config, logic, photo_cleanup, push, settings_store as ss, sysinfo from ..db import get_db from ..models import Comment, Photo, Order, OrderItem, User, PushSubscription, DeviceToken -from ..security import current_user, require_admin, is_manager, is_portal_admin +from ..security import current_user, require_admin, require_portal_admin, is_manager, is_portal_admin from .catalog import commit from .orders import load, exec_guard, start, names, can_execute, to_out @@ -94,7 +94,7 @@ async def upload_photo(id: int, item_id: int, request: Request, db: Session = De raise HTTPException(415, "Допустимы JPEG, PNG, WebP") ct, ext = kind root = ss.photo_root(db) - rel = Path(f"{config.now():%Y/%m}") / (ss.new_name() + ext) + rel = Path("keep" if item.photo_keep else "") / f"{config.now():%Y/%m}" / (ss.new_name() + ext) # keep/ — не удаляются очисткой (root / rel).parent.mkdir(parents=True, exist_ok=True) (root / rel).write_bytes(data) db.add(Photo(order_id=o.id, item_id=item.id, path=rel.as_posix(), content_type=ct, size=len(data), @@ -142,8 +142,30 @@ def dir_stats(root: Path) -> dict: return {"files": len(files), "bytes": sum(x.stat().st_size for x in files), "disk_free": free} +def keep_stats(root: Path) -> dict: + a, b = dir_stats(root / "keep"), dir_stats(root / "media" / "tasks" / "keep") + return {"files": a["files"] + b["files"], "bytes": a["bytes"] + b["bytes"]} + + +@router.get("/photo-cleanup") +def cleanup_preview(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + """Сколько фото удалит очистка сейчас (ничего не удаляет).""" + return {**photo_cleanup.run(db, dry=True), "last_run": ss.get(db, "photo_cleanup_at") or None} + + +@router.post("/photo-cleanup") +def cleanup_now(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + return photo_cleanup.run(db) + + +@router.get("/server/monitor") +def monitor(db: Session = Depends(get_db), _: User = Depends(require_portal_admin)): + """Мониторинг сервера: загрузка процессора, память, место на дисках (данные портала и фото).""" + return {**sysinfo.snapshot({"Данные портала": Path(config.DATA_DIR), "Фото": ss.photo_root(db)}), "now": config.iso(config.now())} + + def settings_payload(db: Session) -> dict: - return {**ss.all_editable(db), "photo_stats": dir_stats(ss.photo_root(db)), + return {**ss.all_editable(db), "photo_stats": dir_stats(ss.photo_root(db)), "photo_keep_stats": keep_stats(ss.photo_root(db)), "vapid_public": push.public_key(db), "now": config.iso(config.now()), "fcm_enabled": push.fcm_enabled(db), "timezone": str(config.TZ)} diff --git a/app/routers/portal.py b/app/routers/portal.py index 459a79a..7473ad7 100644 --- a/app/routers/portal.py +++ b/app/routers/portal.py @@ -137,7 +137,7 @@ def user_card(id: int, db: Session = Depends(get_db), me: User = Depends(current """ФИО, фото, должность, график (ближайшие смены), что умеет, текущие задачи и наряды, выданный инвентарь.""" u = get_or_404(db, User, id) now = config.now() - sch = u.position.schedule if u.position else None + sch = u.schedule shifts = [] if sch: for i in range(14): diff --git a/app/routers/tasks.py b/app/routers/tasks.py index 9e816b8..ba3b685 100644 --- a/app/routers/tasks.py +++ b/app/routers/tasks.py @@ -86,7 +86,7 @@ def to_out(db: Session, t: DailyTask, u: User, names: dict, with_events: bool = assignee_name=names.get(t.assignee_id), created_by_id=t.created_by_id, created_by_name=names.get(t.created_by_id), equipment_id=t.equipment_id, equipment_name=eq.name if eq else None, unit_id=t.unit_id, unit_name=su.name if su else None, status=t.status, created_at=t.created_at, updated_at=t.updated_at, - finished_at=t.finished_at, labor_hours=t.labor_hours, last_comment=last, photos=photos, can_change=can_change(u, t), can_edit=can_edit(u, t), + finished_at=t.finished_at, labor_hours=t.labor_hours, photo_keep=bool(t.photo_keep), last_comment=last, photos=photos, can_change=can_change(u, t), can_edit=can_edit(u, t), can_transfer=can_transfer(u, t), events=[S.TaskEventOut(at=e.at, user_name=names.get(e.user_id), from_status=e.from_status, to_status=e.to_status, comment=e.comment, kind=e.kind or "status", from_user_name=names.get(e.from_user_id), @@ -241,7 +241,7 @@ async def add_photo(id: int, request: Request, db: Session = Depends(get_db), u: if not (is_boss(u) or u.id in (t.assignee_id, t.created_by_id)): raise HTTPException(403, "Нет прав добавлять фото") data, ct, ext = await media.read_image(db, request) - name = media.save(db, "tasks", data, ext) + name = media.save(db, "tasks", data, ext, keep=t.photo_keep) db.add(TaskPhoto(task_id=t.id, path=name, content_type=ct, size=len(data), user_id=u.id)) t.updated_at = config.now() commit(db) diff --git a/app/schemas.py b/app/schemas.py index 2bc97e4..b924f46 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -85,6 +85,7 @@ class UserIn(BaseModel): portal_admin: bool = False is_active: bool = True position_id: Optional[int] = None + schedule_id: Optional[int] = None # график работы сотрудника (пусто — всегда на смене) department_id: Optional[int] = None skills: str = Field(default="", max_length=4000) phone: str = Field(default="", max_length=40) @@ -100,6 +101,7 @@ class UserPatch(BaseModel): portal_admin: Optional[bool] = None is_active: Optional[bool] = None position_id: Optional[int] = None + schedule_id: Optional[int] = None department_id: Optional[int] = None skills: Optional[str] = Field(default=None, max_length=4000) phone: Optional[str] = Field(default=None, max_length=40) @@ -138,6 +140,7 @@ class UserOut(ORM): is_active: bool position_id: Optional[int] position: Optional[PositionBrief] = None + schedule_id: Optional[int] = None department_id: Optional[int] = None department: Optional["DepartmentBrief"] = None has_photo: bool = False @@ -147,6 +150,7 @@ class UserOut(ORM): demo_mode: bool = False crane_map: bool = False # включена отдельная карта кранов reliability: bool = False # доступен раздел «Анализ надёжности» (включён и у пользователя есть права) + docs: bool = False # подключена документация из Nextcloud portal_admin: bool = False # администратор портала: настройки сервера и обратная связь is_root: bool = False # администратор по умолчанию (admin): выдаёт права администратора портала @@ -222,7 +226,7 @@ class PreviewDay(BaseModel): class PositionIn(BaseModel): name: str kind: Literal["technician", "engineer"] = "technician" - schedule_id: Optional[int] = None + schedule_id: Optional[int] = None # устарело и не используется: график задаётся сотруднику class PositionOut(ORM): @@ -265,6 +269,7 @@ class EquipmentOut(EquipmentIn, ORM): meter_updated_at: Optional[datetime] = None map_x: Optional[float] = None map_y: Optional[float] = None + docs_path: str = "" # папка документации в Nextcloud (привязывается через PUT /equipment/{id}/docs) # ----- регламентное обслуживание: узлы машины ----- @@ -373,6 +378,7 @@ class ChecklistItemIn(BaseModel): max_value: Optional[float] = None required: bool = True photo: Literal["none", "optional", "required"] = "none" + photo_keep: bool = False # фото хранить более года (не удаляются автоочисткой) class ChecklistItemOut(ChecklistItemIn, ORM): @@ -460,6 +466,7 @@ class OrderItemOut(ORM): answered_at: Optional[datetime] out_of_range: bool photo: str + photo_keep: bool = False service_unit_id: Optional[int] = None is_meter: bool = False photos: list[PhotoOut] = [] @@ -532,6 +539,7 @@ class SettingsIn(BaseModel): setup_done: Optional[str] = Field(default=None, max_length=1000) crane_map: Optional[bool] = None # отдельная карта кранов reliability: Optional[bool] = None # раздел «Анализ надёжности» + photo_keep_days: Optional[int] = Field(default=None, ge=0, le=36500) # удалять фото старше N дней (0 — не удалять) class PushSubIn(BaseModel): @@ -841,6 +849,7 @@ class TaskIn(BaseModel): equipment_id: Optional[int] = None unit_id: Optional[int] = None labor_hours: Optional[float] = Field(default=None, ge=0, le=1000) + photo_keep: bool = False # фото к заданию хранить более года class TaskPatch(BaseModel): @@ -851,6 +860,7 @@ class TaskPatch(BaseModel): equipment_id: Optional[int] = None unit_id: Optional[int] = None labor_hours: Optional[float] = Field(default=None, ge=0, le=1000) + photo_keep: Optional[bool] = None class TaskStatusIn(BaseModel): @@ -892,6 +902,7 @@ class TaskOut(BaseModel): updated_at: datetime finished_at: Optional[datetime] labor_hours: Optional[float] = None + photo_keep: bool = False last_comment: str = "" photos: list[int] = [] can_change: bool = False diff --git a/app/settings_store.py b/app/settings_store.py index 3c36814..298f800 100644 --- a/app/settings_store.py +++ b/app/settings_store.py @@ -4,7 +4,7 @@ from pathlib import Path from sqlalchemy.orm import Session -from . import config +from . import autoconf, config from .models import Setting DEFAULTS = { @@ -19,6 +19,8 @@ DEFAULTS = { "setup_done": "", "crane_map": "0", # отдельная карта кранов "reliability": "1", # раздел «Анализ надёжности» для инженеров и руководителей + "photo_keep_days": "0", # автоочистка: удалять фото нарядов и заданий старше N дней (0 — не удалять; keep/ не трогается) + "photo_cleanup_at": "", # когда очистка выполнялась последний раз "crane_map_image": "", # картинка карты кранов (пусто — используется карта завода) # мастер первичной настройки: отмеченные шаги (через запятую) # AI-помощник (не входит в EDITABLE: ключ не отдаётся в общих настройках, правится через /assistant/config) "assistant_enabled": "0", @@ -27,14 +29,27 @@ DEFAULTS = { "assistant_model": "", "assistant_api_key": "", "assistant_scope": "", + # документация из Nextcloud (не входит в EDITABLE: пароль и секрет не отдаются, правится через /docs/config) + "nc_url": "", # адрес Nextcloud, лучше внутренний в локальной сети + "nc_user": "", # служебная учётная запись (только чтение) + "nc_password": "", # пароль приложения этой учётной записи + "nc_root": "", # корневая папка документации внутри учётной записи + "onlyoffice_url": "", # ONLYOFFICE Document Server — переводит документы Office в PDF для просмотра + "onlyoffice_jwt": "", # секрет JWT Document Server (если включён) + "portal_internal_url": "", # адрес портала, по которому его видит ONLYOFFICE (локальная сеть) + "docs_ai": "0", # «Работа с документацией» для AI-помощника + "docs_index_at": "", # когда последний раз строился индекс } EDITABLE = ("photo_dir", "max_photo_mb", "vapid_contact", "fcm_credentials_path", "public_incidents", - "backup_auto_hours", "backup_keep", "backup_media", "setup_done", "crane_map", "reliability") + "backup_auto_hours", "backup_keep", "backup_media", "setup_done", "crane_map", "reliability", "photo_keep_days") def get(db: Session, key: str) -> str: row = db.get(Setting, key) - return row.value if row and row.value != "" else DEFAULTS.get(key, "") + if row and row.value != "": + return row.value + auto = autoconf.default(key) # окружение (.env) или сервис во внутренней сети Docker + return auto if auto is not None else DEFAULTS.get(key, "") def set_(db: Session, key: str, value: str): diff --git a/app/static/admin.js b/app/static/admin.js index ce6a4f1..2c4dee6 100644 --- a/app/static/admin.js +++ b/app/static/admin.js @@ -93,5 +93,5 @@ async function assistantSettings(box) { window.assistantRefresh && window.assistantRefresh(); }) }, "Проверить"), h("button", { onclick: guard(async () => { await api("PUT", "/assistant/config", body()); toast("Сохранено"); key.value = ""; window.assistantRefresh && window.assistantRefresh(); assistantSettings(box); }) }, "Сохранить")), - out); + out, await docsAiSection()); } diff --git a/app/static/app.js b/app/static/app.js index 5333342..b49e85c 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -154,6 +154,9 @@ async function route() { else if (page === "admin") await adminPage(); else if (page === "server") await serverPage(); else if (page === "feedback") await feedbackPage(id); + else if (page === "docs") await docsPage(+id); + else if (page === "monitor") await monitorPage(); + else if (page === "monitor") await monitorPage(); else if (page === "backups") await backupsPage(); else if (page === "setup") await setupPage(id); else if (page === "new-order") await newOrderPage(); @@ -350,13 +353,14 @@ const CRUD = { { k: "notes", label: "Примечание", type: "textarea" }, { k: "is_active", label: "Активно", type: "bool" }], init: { is_active: true }, filter: r => !(me.crane_map && r.is_crane), // при отдельной службе кранов краны живут в своём разделе - canEdit: () => isEng(), icon: true, + canEdit: () => isEng(), icon: true, locFilter: true, actions: r => h("a", { class: "btn sec sm", style: "background:transparent;color:var(--text);border:1px solid var(--line)", href: "#/equipment/" + r.id }, "Карточка") }, locations: { title: "Места", path: "locations", cols: [["name", "Название"], ["parent_id", "Входит в", "locations"]], fields: [{ k: "name", label: "Название", type: "text", req: 1 }, { k: "parent_id", label: "Входит в", type: "ref", ref: "locations" }], init: {} }, - positions: { title: "Должности", path: "positions", cols: [["name", "Название"], ["kind", "Тип", null, v => v === "engineer" ? "Инженер" : "Техник"], ["schedule_id", "График", "schedules"]], - fields: [{ k: "name", label: "Название", type: "text", req: 1 }, { k: "kind", label: "Тип", type: "enum", options: [["technician", "Техник (выполняет наряды)"], ["engineer", "Инженер (планирует, проверяет)"]] }, - { k: "schedule_id", label: "График работы", hint: "Пусто — всегда на смене", type: "ref", ref: "schedules" }], init: { kind: "technician" } }, + positions: { title: "Должности", path: "positions", cols: [["name", "Название"], ["kind", "Тип", null, v => v === "engineer" ? "Инженер" : "Техник"]], + fields: [{ k: "name", label: "Название", type: "text", req: 1 }, { k: "kind", label: "Тип", type: "enum", options: [["technician", "Техник (выполняет наряды)"], ["engineer", "Инженер (планирует, проверяет)"]] }], + note: "Должность выполняет функции по графику того, кто её занимает: график задаётся каждому сотруднику в «Пользователях». Наряд или задание на должность видят и получают уведомление только те, кто сейчас на смене.", + init: { kind: "technician" } }, schedules: { title: "Графики работы", path: "schedules", cols: [["name", "Название"], ["kind", "Тип", null, (v, r) => v === "cycle" ? `${r.work_days}/${r.off_days}` : r.weekdays.map(i => DAYS[i]).join(" ")], ["start_time", "Смена", null, (v, r) => `${v.slice(0, 5)}–${r.end_time.slice(0, 5)}`]], fields: [{ k: "name", label: "Название", type: "text", req: 1 }, { k: "kind", label: "Тип", type: "enum", options: [["weekly", "По дням недели"], ["cycle", "Цикл (N через M)"]], reload: 1 }, @@ -370,10 +374,12 @@ const CRUD = { fields: [{ k: "name", label: "Название отдела", type: "text", req: 1 }, { k: "parent_id", label: "Входит в отдел", hint: "Для вложенных служб и участков; пусто — верхний уровень", type: "ref", ref: "departments" }, { k: "head_user_id", label: "Руководитель", type: "ref", ref: "users" }, { k: "notes", label: "Примечание", type: "textarea" }], init: {}, actions: r => h("a", { class: "btn sec sm", href: "#/people?dep=" + r.id }, "Сотрудники") }, - users: { title: "Пользователи", path: "users", cols: [["username", "Логин"], ["full_name", "ФИО"], ["position_id", "Должность", "positions"], ["department_id", "Отдел", "departments"], ["is_admin", "Роль", null, (v, r) => [v ? "Админ" : r.is_moderator ? "Модератор" : "", r.is_storekeeper ? "Склад" : ""].filter(Boolean).join(", ") || "—"], ["is_active", "Активен"], ["on_shift", "На смене"]], + users: { title: "Пользователи", path: "users", cols: [["username", "Логин"], ["full_name", "ФИО"], ["position_id", "Должность", "positions"], ["schedule_id", "График", "schedules"], ["department_id", "Отдел", "departments"], ["is_admin", "Роль", null, (v, r) => [v ? "Админ" : r.is_moderator ? "Модератор" : "", r.is_storekeeper ? "Склад" : ""].filter(Boolean).join(", ") || "—"], ["is_active", "Активен"], ["on_shift", "На смене"]], fields: [{ k: "username", label: "Логин", type: "text", req: 1 }, { k: "full_name", label: "ФИО", type: "text" }, { k: "password", label: "Пароль", hint: "При правке оставьте пустым, чтобы не менять", type: "password" }, - { k: "position_id", label: "Должность", type: "ref", ref: "positions" }, { k: "department_id", label: "Отдел", type: "ref", ref: "departments" }, { k: "is_moderator", label: "Модератор", hint: "Управляет пользователями, должностями, чек-листами; наряды сам не выполняет", type: "bool", show: () => me.is_admin }, + { k: "position_id", label: "Должность", type: "ref", ref: "positions" }, + { k: "schedule_id", label: "График работы", hint: "По нему сотрудник выполняет свою должность: на смене он видит наряды должности и получает уведомления, сменщики дома — нет. Пусто — всегда на смене", type: "ref", ref: "schedules" }, + { k: "department_id", label: "Отдел", type: "ref", ref: "departments" }, { k: "is_moderator", label: "Модератор", hint: "Управляет пользователями, должностями, чек-листами; наряды сам не выполняет", type: "bool", show: () => me.is_admin }, { k: "is_admin", label: "Администратор", hint: "Полный доступ к администрированию", type: "bool", show: () => me.is_admin }, { k: "portal_admin", label: "Администратор портала", hint: "Настройки сервера и ответы обратной связи. Выдаёт только администратор по умолчанию (admin); у самого admin эти права всегда", type: "bool", show: () => me.is_root }, { k: "is_storekeeper", label: "Администратор склада", hint: "Каталог инструмента, склады, выдача сотрудникам, заявки на инструмент", type: "bool" }, @@ -403,12 +409,40 @@ CRUD.cranes = { ...CRUD.equipment, title: "Краны", init: { is_active: true, cols: CRUD.equipment.cols.filter(c => c[0] !== "is_crane"), fields: CRUD.equipment.fields.map(f => f.k === "is_crane" ? { ...f, hint: "Снимите, чтобы перенести машину в обычное оборудование" } : f) }; +const crudLoc = {}; // выбранное место в фильтре списка (по разделу) +// место и все вложенные в него места +function locSubtree(locs, root) { + const out = new Set([root]); + for (let grew = true; grew;) { grew = false; for (const l of locs) if (l.parent_id && out.has(l.parent_id) && !out.has(l.id)) { out.add(l.id); grew = true; } } + return out; +} +// места деревом: «Цех №1», «— Компрессорная» … +const NBSP3 = String.fromCharCode(160).repeat(3); +function locOptions(locs) { + const kids = p => locs.filter(l => (l.parent_id || null) === p).sort((a, b) => a.name.localeCompare(b.name, "ru")); + const out = [], walk = (p, d) => { for (const l of kids(p)) { if (out.some(x => x[0] === l.id)) continue; out.push([l.id, NBSP3.repeat(d) + (d ? "— " : "") + l.name]); walk(l.id, d + 1); } }; + walk(null, 0); + for (const l of locs) if (!out.some(x => x[0] === l.id)) out.push([l.id, l.name]); + return out; +} + async function crudPage(page) { const C = CRUD[page], box = h("div"), formBox = h("div"), can = C.canEdit ? C.canEdit() : true; + const bar = h("div"); $app.replaceChildren(h("div", { class: "row" }, h("h1", { style: "flex:1" }, C.title), - can ? h("button", { onclick: () => edit(null) }, "+ Добавить") : null), formBox, box); + can ? h("button", { onclick: () => edit(null) }, "+ Добавить") : null), ...(C.note ? [h("p", { class: "small muted" }, C.note)] : []), bar, formBox, box); + if (C.locFilter) { + const locs = await ref("locations", true); + if (locs.length) bar.replaceChildren(h("div", { class: "row", style: "margin-bottom:10px" }, h("label", { style: "margin:0" }, "Место:"), + h("select", { style: "max-width:360px", onchange: e => { crudLoc[page] = e.target.value ? +e.target.value : null; load(); } }, + h("option", { value: "" }, "Все места"), ...locOptions(locs).map(([id, n]) => h("option", { value: id, selected: crudLoc[page] === id }, n))))); + } async function load() { - const rows = (await api("GET", "/" + C.path)).filter(C.filter || (() => true)); + let rows = (await api("GET", "/" + C.path)).filter(C.filter || (() => true)); + if (C.locFilter && crudLoc[page]) { + const sub = locSubtree(await ref("locations"), crudLoc[page]); + rows = rows.filter(r => sub.has(r.location_id)); + } const lookups = {}; for (const c of C.cols) if (c[2]) lookups[c[2]] = await ref(c[2], true); const cell = (r, [k, , rf, fn]) => { @@ -490,7 +524,9 @@ async function checklistEditor(id) { h("input", { type: t, step: "any", value: it[k] ?? "", onchange: e => it[k] = e.target.value === "" ? (k === "unit" ? "" : null) : (t === "number" ? +e.target.value : e.target.value) })))) : null, h("div", { class: "row", style: "margin-top:8px" }, h("label", { style: "display:flex;gap:5px;align-items:center;margin:0;color:var(--text)" }, h("input", { type: "checkbox", checked: it.required, onchange: e => it.required = e.target.checked }), "Обязательный"), - h("select", { style: "width:auto", onchange: e => it.photo = e.target.value }, ...[["none", "📷 без фото"], ["optional", "📷 фото по желанию"], ["required", "📷 фото обязательно"]].map(([k, t]) => h("option", { value: k, selected: (it.photo || "none") === k }, t))), + h("select", { style: "width:auto", onchange: e => { it.photo = e.target.value; draw(); } }, ...[["none", "📷 без фото"], ["optional", "📷 фото по желанию"], ["required", "📷 фото обязательно"]].map(([k, t]) => h("option", { value: k, selected: (it.photo || "none") === k }, t))), + (it.photo || "none") !== "none" ? h("label", { class: "chk", title: "Фото этого пункта кладутся в отдельный каталог и не удаляются автоочисткой сервера" }, + h("input", { type: "checkbox", checked: !!it.photo_keep, onchange: e => it.photo_keep = e.target.checked }), "хранить более года") : null, h("span", { style: "flex:1" }), h("button", { class: "sec sm", disabled: n === 0, onclick: () => { [items[n - 1], items[n]] = [items[n], items[n - 1]]; draw(); } }, "↑"), h("button", { class: "sec sm", disabled: n === items.length - 1, onclick: () => { [items[n + 1], items[n]] = [items[n], items[n + 1]]; draw(); } }, "↓"), @@ -503,7 +539,7 @@ async function checklistEditor(id) { h("h2", {}, "Задачи"), box, h("div", { class: "row" }, h("button", { class: "sec", onclick: add }, "+ Добавить задачу"), h("button", { onclick: guard(async () => { const body = { name: name.value, description: desc.value, kind: kv.kind, only_due: kv.only_due, items: items.map(i => ({ - text: i.text, answer_type: i.answer_type, required: i.required, photo: i.photo || "none", + text: i.text, answer_type: i.answer_type, required: i.required, photo: i.photo || "none", photo_keep: (i.photo || "none") !== "none" && !!i.photo_keep, options: i.answer_type === "choice" ? i.optText.split("\n").map(s => s.trim()).filter(Boolean) : null, unit: i.answer_type === "number" ? (i.unit || "") : "", min_value: i.answer_type === "number" ? i.min_value ?? null : null, max_value: i.answer_type === "number" ? i.max_value ?? null : null })) }; @@ -572,6 +608,7 @@ async function adminPage() { const [users, pos, sch] = await Promise.all([api("GET", "/users"), api("GET", "/positions"), api("GET", "/schedules")]); const tile = (href, title, text) => h("a", { href, class: "tile" }, h("b", {}, title), h("span", { class: "muted small" }, text)); const eqCount = me.is_admin && !me.demo_mode ? (await api("GET", "/equipment").catch(() => [])).length : 1; + const setupFinished = me.is_admin && ((await api("GET", "/settings").catch(() => ({}))).setup_done || "").split(",").includes("finished"); $app.replaceChildren(h("h1", {}, me.is_admin ? "Панель администратора" : "Панель модератора"), ...(eqCount === 0 || (me.is_admin && users.length <= 1) ? [h("div", { class: "card banner-setup" }, h("b", { style: "font-size:17px" }, "🧭 Похоже, система ещё не настроена."), h("span", { class: "muted" }, "Мастер по шагам проведёт через графики, должности, отделы, места, оборудование, сотрудников, карту и склады."), @@ -586,17 +623,19 @@ async function adminPage() { h("div", { class: "tiles" }, tile("#/checklists", "Чек-листы", "шаблоны проверок"), tile("#/equipment", "Оборудование", "справочник"), tile("#/locations", "Места", "расположение"), tile("#/templates", "Расписание нарядов", "автозапуск")), - ...(me.is_admin ? [h("h2", {}, "Сервер (только администратор)"), h("div", { class: "tiles" }, tile("#/setup", "🧭 Мастер первичной настройки", "пошагово: графики, люди, оборудование, карта, склады"), ...(me.portal_admin ? [tile("#/server", "Настройки сервера", "каталог фото, лимиты, уведомления, внешние обращения, AI-помощник"), tile("#/feedback", "Обратная связь", "идеи и замечания сотрудников по порталу")] : []), tile("#/backups", "Резервные копии", "база и конфигурация, автокопии, восстановление"), tile("#/demo", "Демонстрационный режим", me.demo_mode ? "ВКЛЮЧЁН — управление" : "тестовые данные для презентации"))] + ...(me.is_admin ? [h("h2", {}, "Сервер (только администратор)"), h("div", { class: "tiles" }, tile(setupFinished ? "#/setup/done" : "#/setup", "🧭 Мастер первичной настройки", setupFinished ? "пройден — проверка готовности и что поправить" : "пошагово: графики, люди, оборудование, карта, склады"), ...(me.portal_admin ? [tile("#/server", "Настройки сервера", "каталог фото, лимиты, уведомления, внешние обращения, AI-помощник"), tile("#/feedback", "Обратная связь", "идеи и замечания сотрудников по порталу"), tile("#/monitor", "Мониторинг сервера", "процессор, память, место на дисках"), tile("#/monitor", "Мониторинг сервера", "процессор, память, место на дисках")] : []), tile("#/backups", "Резервные копии", "база и конфигурация, автокопии, восстановление"), tile("#/demo", "Демонстрационный режим", me.demo_mode ? "ВКЛЮЧЁН — управление" : "тестовые данные для презентации"))] : [h("p", { class: "small muted" }, "Параметры сервера доступны только администратору. Модератор сам наряды не выполняет.")])); } async function serverPage() { if (!me.portal_admin) throw new Error("Настройки сервера доступны только администраторам портала"); - const keysBox = h("div", { style: "margin-top:14px" }), aiBox = h("div", { class: "card" }); + const cleanupBox = h("div", {}, "…"); + const keysBox = h("div", { style: "margin-top:14px" }), aiBox = h("div", { class: "card" }), docsBox = h("div", { class: "card" }); const s = await api("GET", "/settings"); const f = formFields([ { k: "photo_dir", label: "Каталог для фото", hint: "Абсолютный путь на сервере. В Docker — примонтированный том", type: "text" }, { k: "max_photo_mb", label: "Максимальный размер фото, МБ", type: "number" }, + { k: "photo_keep_days", label: "Автоочистка: удалять фото нарядов и заданий старше, дней", hint: "Например, 365 — раз в сутки удаляются фото старше года. 0 — не удалять. Фото пунктов и заданий с отметкой «хранить более года» лежат в отдельном каталоге keep/ и не удаляются", type: "number" }, { k: "vapid_contact", label: "Контакт для push-уведомлений", hint: "mailto:… или https://…", type: "text" }, { k: "fcm_credentials_path", label: "Android push (FCM): ключ Firebase", hint: "Путь к JSON-ключу сервисного аккаунта на сервере. Пусто — выключено", type: "text" }, { k: "public_incidents", label: "Публичная форма «Сообщить о неисправности» (/report)", hint: "Сторонние люди без входа сообщают об аварии: место, машина, описание, контакты", type: "bool" }, @@ -606,12 +645,14 @@ async function serverPage() { const mb = n => (n / 1048576).toFixed(1) + " МБ"; $app.replaceChildren(h("a", { href: "#/admin" }, "← Администрирование"), h("h1", {}, "Настройки сервера"), h("div", { class: "card" }, f.el, h("div", { style: "margin-top:12px" }, h("button", { onclick: guard(async () => { - await api("PUT", "/settings", { photo_dir: f.vals.photo_dir, max_photo_mb: f.vals.max_photo_mb, vapid_contact: f.vals.vapid_contact, fcm_credentials_path: f.vals.fcm_credentials_path ?? "", public_incidents: !!f.vals.public_incidents, crane_map: !!f.vals.crane_map, reliability: !!f.vals.reliability }); + await api("PUT", "/settings", { photo_dir: f.vals.photo_dir, max_photo_mb: f.vals.max_photo_mb, photo_keep_days: +(f.vals.photo_keep_days || 0), vapid_contact: f.vals.vapid_contact, fcm_credentials_path: f.vals.fcm_credentials_path ?? "", public_incidents: !!f.vals.public_incidents, crane_map: !!f.vals.crane_map, reliability: !!f.vals.reliability }); me = await api("GET", "/me"); toast("Сохранено"); serverPage(); }) }, "Сохранить")), h("p", { class: "small muted" }, "Смена каталога не переносит уже загруженные фото — скопируйте их вручную в новый каталог.")), h("div", { class: "card" }, h("h2", { style: "margin-top:0" }, "Состояние"), h("div", { class: "grid" }, - h("div", {}, h("label", {}, "Фото на сервере"), `${s.photo_stats.files} шт., ${mb(s.photo_stats.bytes)}`), + h("div", {}, h("label", {}, "Фото на сервере"), `${s.photo_stats.files} шт., ${mb(s.photo_stats.bytes)}`, + h("div", { class: "small muted" }, `из них хранить долго (keep/): ${s.photo_keep_stats.files} шт., ${mb(s.photo_keep_stats.bytes)}`)), + h("div", {}, h("label", {}, "Автоочистка фото"), cleanupBox), h("div", {}, h("label", {}, "Свободно на диске"), mb(s.photo_stats.disk_free)), h("div", {}, h("label", {}, "Время сервера"), `${fmt(s.now)} (${s.timezone})`), h("div", {}, h("label", {}, "Адрес клиента, каким его видит сервер"), s.client_ip || "—", @@ -620,11 +661,25 @@ async function serverPage() { h("div", {}, h("label", {}, "Браузерные уведомления"), location.protocol === "https:" || location.hostname === "localhost" ? "доступны" : "нужен HTTPS"))), h("div", { class: "card" }, h("h2", { style: "margin-top:0" }, "Сообщения об отказах и неисправностях извне"), h("p", { class: "small" }, "Публичная форма: ", h("a", { href: "/report", target: "_blank" }, location.origin + "/report"), - " — ссылку можно разместить на QR-коде у оборудования или на сайте."), keysBox), aiBox); + " — ссылку можно разместить на QR-коде у оборудования или на сайте."), keysBox), docsBox, aiBox); apiKeys(keysBox); + docsSettings(docsBox); + photoCleanupInfo(cleanupBox); assistantSettings(aiBox); } +async function photoCleanupInfo(box) { + const c = await api("GET", "/photo-cleanup"); + const mb = n => (n / 1048576).toFixed(1) + " МБ"; + if (!c.enabled) return box.replaceChildren("выключена"); + box.replaceChildren(`старше ${c.days} дн.`, h("div", { class: "small muted" }, c.photos ? `сейчас к удалению: ${c.photos} фото, ${mb(c.bytes)}` : "сейчас удалять нечего", + c.last_run ? ` · последний запуск ${fmt(c.last_run)}` : ""), + c.photos ? h("button", { class: "sec sm", style: "margin-top:4px", onclick: guard(async () => { + if (!confirm(`Удалить ${c.photos} фото старше ${c.days} дней? Это необратимо.`)) return; + const r = await api("POST", "/photo-cleanup"); toast(`Удалено фото: ${r.photos}`); serverPage(); + }) }, "Очистить сейчас") : null); +} + async function apiKeys(box) { const keys = await api("GET", "/api-keys"); const name = h("input", { placeholder: "Название системы, например «Диспетчерская» или «SCADA цех 1»" }); diff --git a/app/static/docs.js b/app/static/docs.js new file mode 100644 index 0000000..2546368 --- /dev/null +++ b/app/static/docs.js @@ -0,0 +1,155 @@ +"use strict"; +// ---------- Документация машин из Nextcloud ---------- +// Nextcloud сотрудникам не виден: папки и файлы отдаёт портал. PDF, картинки и текст открываются прямо здесь, +// документы Office — после перевода в PDF на сервере ONLYOFFICE. Привязывают папки администратор и модератор. +const DOC_ICON = { dir: "📁", pdf: "📕", image: "🖼", text: "📄", office: "📝", file: "📎" }; +const docSize = n => n >= 1048576 ? (n / 1048576).toFixed(1) + " МБ" : Math.max(1, Math.round(n / 1024)) + " КБ"; +const docUrl = (id, path, view) => `${API}/equipment/${id}/docs/file?` + new URLSearchParams({ path, ...(view ? { view: 1 } : {}) }); +const hashQuery = () => new URLSearchParams((location.hash.split("?")[1]) || ""); + +// блок в карточке машины +function docsBlock(eq, reload) { + if (!me.docs || (!eq.docs_path && !isMgr())) return null; + return h("div", { class: "card" }, + h("div", { class: "row" }, h("h2", { style: "flex:1;margin:0" }, "📁 Документация"), + eq.docs_path ? h("a", { class: "btn sm", href: "#/docs/" + eq.id }, "Открыть") : null, + isMgr() ? h("button", { class: "sec sm", onclick: () => docsPicker(eq, reload) }, eq.docs_path ? "Сменить папку" : "Привязать папку") : null), + h("div", { class: "small muted", style: "margin-top:4px" }, eq.docs_path ? "Папка: " + eq.docs_path + : "Папка с документацией ещё не привязана. Выберите её в проводнике — сотрудники будут открывать документы прямо в портале.")); +} + +// проводник для привязки папки +async function docsPicker(eq, done) { + if (document.querySelector(".dcmodal")) return; + let path = eq.docs_path ? eq.docs_path.split("/").slice(0, -1).join("/") : ""; + const body = h("div"), close = () => wrap.remove(); + const wrap = h("div", { class: "dcmodal", onclick: e => { if (e.target === wrap) close(); } }, h("div", { class: "card" }, body)); + const save = guard(async p => { + await api("PUT", `/equipment/${eq.id}/docs`, { path: p }); + delete cache.equipment; close(); toast(p ? "Папка привязана" : "Папка отвязана"); done(); + }); + async function draw() { + body.replaceChildren(h("p", { class: "muted" }, "Загрузка…")); + let d; + try { d = await api("GET", "/docs/browse?" + new URLSearchParams({ path })); } + catch (e) { body.replaceChildren(h("h2", { style: "margin-top:0" }, "Документация"), h("p", { class: "error" }, e.message), h("button", { class: "sec", onclick: close }, "Закрыть")); return; } + const parts = d.path ? d.path.split("/") : []; + const crumbs = h("div", { class: "crumbs" }, h("a", { href: "javascript:void 0", onclick: () => { path = ""; draw(); } }, "Документация"), + ...parts.flatMap((p, i) => [" / ", h("a", { href: "javascript:void 0", onclick: () => { path = parts.slice(0, i + 1).join("/"); draw(); } }, p)])); + body.replaceChildren(h("h2", { style: "margin-top:0" }, "Папка документации: " + eq.name), crumbs, + h("div", { class: "dclist" }, ...(d.items.length ? d.items.map(x => h("div", { class: "dcrow" }, + h("a", { href: "javascript:void 0", style: "flex:1", onclick: () => { path = x.path; draw(); } }, "📁 " + x.name), + x.linked.length ? h("span", { class: "small muted" }, "привязана: " + x.linked.join(", ")) : null, + h("button", { class: "sm", onclick: () => save(x.path) }, "Выбрать"))) : [h("p", { class: "muted small" }, "Вложенных папок нет")])), + h("div", { class: "small muted", style: "margin-top:6px" }, `В этой папке файлов: ${d.files}`), + h("div", { class: "row", style: "margin-top:12px" }, + d.path ? h("button", { onclick: () => save(d.path) }, "Привязать эту папку") : null, + eq.docs_path ? h("button", { class: "sec", onclick: () => confirm("Отвязать папку от машины?") && save("") }, "Отвязать") : null, + h("span", { style: "flex:1" }), h("button", { class: "sec", onclick: close }, "Отмена"))); + } + document.body.append(wrap); + draw(); +} + +// страница документации машины: #/docs/ID?d=папка или ?p=файл +async function docsPage(id) { + const q = hashQuery(), file = q.get("p"), sub = q.get("d") || (file ? file.split("/").slice(0, -1).join("/") : ""); + const eq = await api("GET", "/equipment/" + id); + const d = await api("GET", `/equipment/${id}/docs?` + new URLSearchParams({ sub })); + const viewer = h("div"); + const go = (k, v) => `#/docs/${id}` + (v ? "?" + new URLSearchParams({ [k]: v }) : ""); + const parts = sub ? sub.split("/") : []; + const row = x => h("a", { class: "dcrow" + (x.path === file ? " on" : ""), href: go(x.is_dir ? "d" : "p", x.path) }, + h("span", { style: "flex:1" }, (DOC_ICON[x.kind] || "📎") + " " + x.name), + x.is_dir ? null : h("span", { class: "small muted" }, docSize(x.size) + (x.mtime ? " · " + fmtDate(x.mtime) : ""))); + fill($app, h("a", { href: "#/equipment/" + id }, "← " + eq.name), h("h1", {}, "Документация"), + h("div", { class: "crumbs" }, h("a", { href: go() }, d.folder), ...parts.flatMap((p, i) => [" / ", h("a", { href: go("d", parts.slice(0, i + 1).join("/")) }, p)])), + h("div", { class: "dcwrap" }, h("div", { class: "card dclist" }, ...(d.items.length ? d.items.map(row) : [h("p", { class: "muted" }, "Папка пуста")])), viewer)); + if (file) await docsView(id, d.items.find(x => x.path === file) || { path: file, name: file.split("/").pop(), kind: "file" }, viewer); +} + +async function docsView(id, x, box) { + const dl = h("button", { class: "sec sm", onclick: guard(() => docsDownload(id, x)) }, "⬇ Скачать"); + const head = (...extra) => h("div", { class: "row", style: "margin-bottom:8px" }, h("b", { style: "flex:1" }, x.name), ...extra, dl); + if (x.kind === "file") return box.replaceChildren(h("div", { class: "card" }, head(), h("p", { class: "muted" }, "Этот формат в портале не открывается — скачайте файл."))); + box.replaceChildren(h("div", { class: "card" }, head(), h("p", { class: "muted" }, x.kind === "office" ? "Готовлю документ к просмотру…" : "Загрузка…"))); + const r = await authFetch(docUrl(id, x.path, true)); + if (!r.ok) { let m = r.statusText; try { m = (await r.json()).detail; } catch (_) {} return box.replaceChildren(h("div", { class: "card" }, head(), h("p", { class: "error" }, m))); } + const blob = await r.blob(), url = URL.createObjectURL(blob); + const open = h("button", { class: "sec sm", onclick: () => window.open(url, "_blank", "noopener") }, "↗ В новой вкладке"); + const view = x.kind === "image" ? h("img", { src: url, alt: x.name, class: "dcimg" }) + : x.kind === "text" ? h("pre", { class: "dctext" }, await blob.text()) + : h("iframe", { src: url, class: "dcframe", title: x.name }); + box.replaceChildren(h("div", { class: "card" }, head(open), view)); +} + +async function docsDownload(id, x) { + const r = await authFetch(docUrl(id, x.path, false)); + if (!r.ok) { let m = r.statusText; try { m = (await r.json()).detail; } catch (_) {} throw new Error(m); } + const a = document.createElement("a"); a.href = URL.createObjectURL(await r.blob()); a.download = x.name; + document.body.append(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(a.href), 5000); +} + +// настройки сервера: подключение к Nextcloud и ONLYOFFICE +async function docsSettings(box) { + const c = await api("GET", "/docs/config"); + const inp = (k, ph, type) => h("input", { value: type === "password" ? "" : c[k] || "", placeholder: ph, ...(type ? { type, autocomplete: "new-password" } : {}) }); + const f = { + nc_url: inp("nc_url", "http://192.168.1.30 или https://cloud.example.ru"), nc_user: inp("nc_user", "служебная учётная запись, например portal-docs"), + nc_password: inp("nc_password", c.nc_password_set ? "пароль задан — оставьте пустым, чтобы не менять" : "пароль приложения", "password"), + nc_root: inp("nc_root", "папка с документацией, например Документация/Оборудование"), + onlyoffice_url: inp("onlyoffice_url", "http://192.168.1.31 — Document Server в локальной сети"), + onlyoffice_jwt: inp("onlyoffice_jwt", c.onlyoffice_jwt_set ? "секрет задан — оставьте пустым, чтобы не менять" : "секрет JWT Document Server (если включён)", "password"), + portal_internal_url: inp("portal_internal_url", "http://192.168.1.20:8000 — адрес портала, видимый для ONLYOFFICE"), + }; + const out = h("div"); + const body = () => { const b = {}; for (const [k, el] of Object.entries(f)) if (!(el.type === "password" && !el.value)) b[k] = el.value.trim(); return b; }; + box.replaceChildren(h("h2", { style: "margin-top:0" }, "📁 Документация из Nextcloud"), + h("p", { class: "small muted" }, "Портал читает папки Nextcloud от служебной учётной записи и сам отдаёт документы сотрудникам — Nextcloud им не нужен и не виден. ", + "Заведите в Nextcloud отдельного пользователя, дайте ему доступ ", h("b", {}, "только на чтение"), " к папке с документацией и создайте для него пароль приложения ", + "(Настройки → Безопасность → «Создать пароль приложения»). Адрес лучше указать внутренний, в локальной сети."), + h("div", { class: "grid" }, field("Адрес Nextcloud", f.nc_url), field("Учётная запись", f.nc_user), field("Пароль приложения", f.nc_password), field("Корневая папка", f.nc_root)), + h("h3", {}, "Просмотр документов Office (ONLYOFFICE)"), + h("p", { class: "small muted" }, "Word, Excel и PowerPoint сервер ONLYOFFICE переводит в PDF, и документ открывается прямо в портале. Без ONLYOFFICE такие файлы можно только скачать. ", + "Внутренний адрес портала нужен, чтобы ONLYOFFICE забрал исходник по одноразовой ссылке."), + h("div", { class: "grid" }, field("Адрес ONLYOFFICE", f.onlyoffice_url), field("Секрет JWT", f.onlyoffice_jwt), field("Внутренний адрес портала", f.portal_internal_url)), + h("div", { class: "row", style: "margin-top:10px" }, h("span", { style: "flex:1" }), + h("button", { class: "sec", onclick: guard(async () => { + await api("PUT", "/docs/config", body()); toast("Проверяю…"); + const r = await api("POST", "/docs/test"); + out.replaceChildren(h("div", { class: "sheet", style: "margin-top:8px" }, h("div", {}, h("b", {}, "Nextcloud: "), r.nextcloud), + h("div", {}, h("b", {}, "ONLYOFFICE: "), r.onlyoffice || "не указан — документы Office только скачиваются"))); + }) }, "Проверить"), + h("button", { onclick: guard(async () => { await api("PUT", "/docs/config", body()); me = await api("GET", "/me"); toast("Сохранено"); docsSettings(box); }) }, "Сохранить")), + out, + h("p", { class: "small muted" }, `Привязано машин: ${c.index.linked}. Папку к машине привязывают администратор или модератор в карточке машины — блок «Документация».`)); +} + +// опция AI-помощника «Работа с документацией» (в карточке помощника) +async function docsAiSection() { + const c = await api("GET", "/docs/config"); + const on = h("input", { type: "checkbox", checked: c.docs_ai }); + const ix = c.index, box = h("div", { class: "docsai" }); + const status = ix.running ? `строится: прочитано ${ix.done} файлов…` : c.docs_index_at ? `в индексе ${ix.files} файлов, обновлён ${fmt(c.docs_index_at)}` : "ещё не строился"; + box.replaceChildren(h("h3", {}, "📚 Работа с документацией"), + h("label", { class: "chk" }, on, "Помощник ищет ответы в документации машин и подсказывает, какой файл открыть"), + h("div", { class: "sheet risk" }, h("b", {}, "Риски — прочитайте перед включением"), + h("ul", {}, + h("li", {}, h("b", {}, "Передача данных провайдеру ИИ. "), "В запрос к модели уходят имена файлов и фрагменты текста документов (паспорта, схемы, регламенты). ", + "С облачным провайдером это значит, что содержимое документации покидает предприятие. Для документов с ограниченным доступом используйте локальную модель (Ollama, LM Studio в своей сети)."), + h("li", {}, h("b", {}, "Большой расход токенов. "), "Каждый вопрос о документации добавляет к запросу результаты поиска — запросы становятся в несколько раз длиннее и дороже. ", + "У платного провайдера следите за расходом; у локальной модели ответы будут медленнее."), + h("li", {}, h("b", {}, "Неточность. "), "Помощник видит только начало каждого документа и подсказывает, где искать, — окончательный ответ проверяйте в самом документе."), + h("li", {}, h("b", {}, "Нагрузка. "), "Раз в сутки портал перечитывает изменившиеся файлы из Nextcloud, чтобы обновить индекс. Сканы без текстового слоя не индексируются — находятся только по имени."))), + h("div", { class: "small muted", style: "margin-top:6px" }, "Индекс: " + status, ix.error ? h("div", { class: "error" }, ix.error) : null), + h("div", { class: "row", style: "margin-top:8px" }, + h("button", { onclick: guard(async () => { + if (on.checked && !c.docs_ai && !confirm("Включить «Работа с документацией»? Фрагменты документов будут уходить провайдеру ИИ.")) return; + await api("PUT", "/docs/config", { docs_ai: on.checked }); + if (on.checked && !c.docs_index_at) await api("POST", "/docs/reindex"); + toast("Сохранено"); box.replaceWith(await docsAiSection()); + }) }, "Сохранить"), + c.docs_ai ? h("button", { class: "sec", onclick: guard(async () => { await api("POST", "/docs/reindex"); toast("Индекс обновляется в фоне"); setTimeout(async () => box.replaceWith(await docsAiSection()), 1500); }) }, "Обновить индекс") : null)); + if (!me.docs) box.append(h("p", { class: "small muted" }, "Сначала подключите Nextcloud в разделе «Документация из Nextcloud» выше.")); + return box; +} diff --git a/app/static/index.html b/app/static/index.html index 1ff4705..1356425 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -23,6 +23,9 @@ + + + diff --git a/app/static/monitor.js b/app/static/monitor.js new file mode 100644 index 0000000..6fc3a14 --- /dev/null +++ b/app/static/monitor.js @@ -0,0 +1,44 @@ +"use strict"; +// ---------- Мониторинг сервера: ЦП, память, диски (круговые диаграммы, обновление каждые 5 секунд) ---------- +const SVGNS = "http://www.w3.org/2000/svg"; +function svgEl(tag, attrs) { + const e = document.createElementNS(SVGNS, tag); + for (const [k, v] of Object.entries(attrs || {})) e.setAttribute(k, v); + return e; +} +const gb = n => n >= 1073741824 ? (n / 1073741824).toFixed(1) + " ГБ" : (n / 1048576).toFixed(0) + " МБ"; +const levelColor = p => p >= 90 ? "var(--bad)" : p >= 75 ? "var(--warn)" : "var(--ok)"; + +// кольцевая диаграмма: доля p (0–100), подпись в центре +function donut(p, center) { + const r = 52, c = 2 * Math.PI * r, v = Math.max(0, Math.min(100, p ?? 0)); + const svg = svgEl("svg", { viewBox: "0 0 140 140", class: "donut", role: "img", "aria-label": center }); + svg.append(svgEl("circle", { cx: 70, cy: 70, r, fill: "none", stroke: "var(--line)", "stroke-width": 16 }), + svgEl("circle", { cx: 70, cy: 70, r, fill: "none", stroke: levelColor(v), "stroke-width": 16, "stroke-linecap": "round", + "stroke-dasharray": `${c * v / 100} ${c}`, transform: "rotate(-90 70 70)" })); + const t = svgEl("text", { x: 70, y: 77, "text-anchor": "middle", class: "donut-v" }); + t.textContent = p == null ? "—" : Math.round(v) + "%"; + svg.append(t); + return svg; +} + +const gauge = (title, p, lines) => h("div", { class: "card gauge" }, h("h2", {}, title), donut(p, title), ...lines.filter(Boolean).map(l => h("div", { class: "small muted" }, l))); + +async function monitorPage() { + if (!me.portal_admin) throw new Error("Мониторинг доступен администраторам портала"); + const box = h("div", { class: "gauges" }), stamp = h("div", { class: "small muted", style: "margin-bottom:8px" }); + fill($app, h("a", { href: "#/admin" }, "← Администрирование"), h("h1", {}, "Мониторинг сервера"), + h("p", { class: "small muted" }, "Загрузка процессора, память и место на дисках сервера портала. Обновляется каждые 5 секунд, пока страница открыта."), stamp, box); + const load = async () => { + const m = await api("GET", "/server/monitor"); + const mem = m.memory, memP = mem ? 100 * mem.used / mem.total : null; + box.replaceChildren( + gauge("Процессор", m.cpu, [`ядер: ${m.cpu_count || "—"}`, m.load ? `средняя нагрузка: ${m.load.map(x => x.toFixed(2)).join(" / ")}` : ""]), + gauge("Память (ОЗУ)", memP, mem ? [`занято ${gb(mem.used)} из ${gb(mem.total)}`, `свободно ${gb(mem.total - mem.used)}`, mem.scope === "container" ? "лимит контейнера Docker" : ""] : ["нет данных"]), + ...m.disks.map(d => gauge("Диск: " + d.labels.join(", "), 100 * d.used / d.total, + [`свободно ${gb(d.free)} из ${gb(d.total)}`, h("span", { title: d.path }, d.path.length > 40 ? "…" + d.path.slice(-40) : d.path)]))); + stamp.textContent = "Обновлено " + new Date().toLocaleTimeString("ru-RU"); + }; + await load(); + timer = setInterval(guard(load), 5000); +} diff --git a/app/static/portal.js b/app/static/portal.js index df13b24..a806754 100644 --- a/app/static/portal.js +++ b/app/static/portal.js @@ -583,8 +583,10 @@ async function taskForm(t) { field("Трудоёмкость (норма), ч", h("input", { type: "number", min: "0", step: "0.25", value: s.labor_hours ?? "", placeholder: "необязательно", onchange: e => s.labor_hours = e.target.value === "" ? null : +e.target.value }), "Для отчёта по сотрудникам; пусто — учитывается фактическое время в работе")), h("div", { style: "margin-top:8px" }, field("Описание", h("textarea", { onchange: e => s.description = e.target.value }, s.description))), + h("label", { class: "chk", style: "margin-top:8px", title: "Фото к заданию кладутся в отдельный каталог и не удаляются автоочисткой сервера" }, + h("input", { type: "checkbox", checked: !!s.photo_keep, onchange: e => s.photo_keep = e.target.checked }), "📷 Фото к заданию хранить более года"), h("div", { class: "row", style: "margin-top:12px" }, h("button", { onclick: guard(async () => { - const body = { title: s.title, description: s.description, day: s.day || null, assignee_id: s.assignee_id, equipment_id: s.equipment_id, unit_id: s.unit_id, labor_hours: s.labor_hours ?? null }; + const body = { title: s.title, description: s.description, day: s.day || null, assignee_id: s.assignee_id, equipment_id: s.equipment_id, unit_id: s.unit_id, labor_hours: s.labor_hours ?? null, photo_keep: !!s.photo_keep }; const r = t ? await api("PATCH", "/tasks/" + t.id, body) : await api("POST", "/tasks", body); toast("Сохранено"); location.hash = "#/task/" + r.id; }) }, t ? "Сохранить" : "Создать задание")))); diff --git a/app/static/service.js b/app/static/service.js index 025498b..ee3447a 100644 --- a/app/static/service.js +++ b/app/static/service.js @@ -114,6 +114,7 @@ async function equipmentPage(id) { h("h1", {}, eq.name, eq.inventory_no ? h("span", { class: "muted", style: "font-size:16px" }, " № " + eq.inventory_no) : null), h("div", { class: "small muted", style: "margin-bottom:10px" }, loc ? "Место: " + loc.name : "", eq.notes ? " · " + eq.notes : ""), portal.head, + docsBlock(eq, reload), meterCard, h("div", { class: "row", style: "margin:8px 0" }, h("h2", { style: "flex:1;margin:0" }, "Обслуживаемые узлы"), canEdit ? h("button", { onclick: () => unitForm(null) }, "+ Добавить обслуживаемый узел") : null), diff --git a/app/static/setup.js b/app/static/setup.js index 0097b6a..55dabf5 100644 --- a/app/static/setup.js +++ b/app/static/setup.js @@ -27,6 +27,46 @@ const SCHEDULE_PRESETS = [ ["Круглосуточно (дежурная смена)", { kind: "weekly", weekdays: [0, 1, 2, 3, 4, 5, 6], start_time: "00:00", end_time: "00:00" }], ]; +// Итог мастера: проверка готовности по введённым данным — что настроено, что мешает работе, что сделать дальше. +async function setupFinish(d) { + if (!d.done.has("finished")) { d.done.add("finished"); await api("PUT", "/settings", { setup_done: [...d.done].join(",") }); } + const staff = d.users.filter(u => !u.is_admin && u.is_active); + const techPos = d.pos.filter(p => p.kind === "technician"); + const checks = []; + const add = (ok, text, href, fix) => checks.push({ ok, text, href, fix }); + add(staff.length > 0, staff.length ? `Сотрудников: ${staff.length}` : "Нет ни одного сотрудника", "#/setup/users", "Завести сотрудников"); + const noPos = staff.filter(u => !u.position_id); + add(!noPos.length, noPos.length ? `Без должности (не видят наряды должностей): ${noPos.map(u => u.full_name || u.username).join(", ")}` : "У всех сотрудников есть должность", "#/users", "Назначить должности"); + const empty = d.pos.filter(p => !staff.some(u => u.position_id === p.id)); + add(!empty.length, empty.length ? `Должности без сотрудников (наряды на них никто не получит): ${empty.map(p => p.name).join(", ")}` : "Каждую должность кто-то занимает", "#/users", "Назначить сотрудников"); + const noShift = techPos.filter(p => staff.some(u => u.position_id === p.id) && !staff.some(u => u.position_id === p.id && u.on_shift)); + add(!noShift.length, noShift.length ? `Сейчас никого на смене: ${noShift.map(p => p.name).join(", ")} — проверьте графики сотрудников` : "По графикам сейчас на смене есть кто-то на каждой должности техников", "#/users", "Проверить графики"); + const noSch = staff.filter(u => u.position_id && !u.schedule_id && techPos.some(p => p.id === u.position_id)); + if (noSch.length) add(null, `Без графика (всегда на смене, получают все уведомления должности): ${noSch.map(u => u.full_name || u.username).join(", ")}`, "#/users", "Задать графики"); + add(d.eqs.length > 0, d.eqs.length ? `Оборудования: ${d.eqs.length}` : "Оборудование не заведено", "#/setup/equipment", "Завести оборудование"); + const noLoc = d.eqs.filter(e => !e.location_id); + if (d.eqs.length) add(!noLoc.length, noLoc.length ? `Машин без места: ${noLoc.length} (не находятся в публичной форме и фильтре по местам)` : "У всех машин указано место", "#/equipment", "Указать места"); + add(!!d.map.has_image, d.map.has_image ? `Карта завода: на ней ${(d.map.equipment || []).filter(e => e.x != null).length} машин` : "Карта завода не загружена", "#/setup/map", "Загрузить карту"); + add(d.cls.length > 0, d.cls.length ? `Чек-листов: ${d.cls.length}` : "Нет чек-листов — нечего проверять в нарядах", "#/checklists/new", "Создать чек-лист"); + add(d.tpls.length > 0, d.tpls.length ? `Расписаний нарядов: ${d.tpls.length}` : "Нет расписания — наряды не будут создаваться сами", "#/templates", "Настроить расписание"); + add(!!(d.bk.auto_hours), d.bk.auto_hours ? `Автокопии каждые ${d.bk.auto_hours} ч` : "Автоматические резервные копии выключены", "#/backups", "Включить"); + if (d.settings.photo_keep_days !== undefined) add(null, +d.settings.photo_keep_days ? `Старые фото удаляются через ${d.settings.photo_keep_days} дн.` : "Автоочистка старых фото выключена — место на диске со временем закончится", "#/server", "Настроить"); + const bad = checks.filter(c => c.ok === false).length; + const icon = c => c.ok === true ? "✅" : c.ok === false ? "⚠️" : "ℹ️"; + fill($app, h("a", { href: "#/setup" }, "← Мастер"), h("h1", {}, bad ? "🧭 Настройка почти завершена" : "🎉 Портал готов к работе"), + h("p", { class: "muted" }, bad ? `Мастер пройден. Осталось поправить: ${bad}. Пока это не сделано, часть нарядов и уведомлений может никому не прийти.` + : "Мастер пройден, всё основное настроено. Раздайте сотрудникам логины и создайте первый наряд."), + h("div", { class: "card" }, h("h2", { style: "margin-top:0" }, "Проверка готовности"), + ...checks.map(c => h("div", { class: "row", style: "padding:6px 0;border-bottom:1px solid var(--line)" }, h("span", {}, icon(c)), h("span", { style: "flex:1" }, c.text), + c.ok !== true && c.href ? h("a", { class: "btn sec sm", href: c.href }, c.fix) : null))), + h("div", { class: "card" }, h("h2", { style: "margin-top:0" }, "Что дальше"), + h("ol", { class: "small" }, h("li", {}, "Раздайте логины и пароли (таблица на шаге «Сотрудники»), установите приложение: ссылка внизу бокового меню."), + h("li", {}, "Создайте первый наряд вручную и пройдите его с телефона — так проще всего показать работу сотрудникам."), + h("li", {}, "Проверьте уведомления: «🔔» внизу меню у каждого сотрудника.")), + h("div", { class: "row" }, h("a", { class: "btn", href: "#/new-order" }, "Создать наряд"), h("a", { class: "btn sec", href: "#/work" }, "Открыть «Задачи»"), + h("button", { class: "sec", onclick: guard(() => setupPage("done")) }, "↻ Проверить ещё раз")))); +} + async function setupPage(stepKey) { if (!me.is_admin) throw new Error("Мастер доступен администратору"); const G = p => api("GET", p).catch(() => []); @@ -92,21 +132,19 @@ async function setupPage(stepKey) { h("li", {}, `Каталог фото: ${settings.photo_dir}, лимит снимка ${settings.max_photo_mb} МБ.`), h("li", {}, `Время сервера сейчас: ${fmt(settings.now)} — должно совпадать с часами на стене.`)), h("a", { class: "btn sec", href: "#/server" }, "Открыть настройки сервера")) }, - { k: "schedules", t: "Графики работы", n: () => sch.length, why: "По графику система понимает, кто сейчас на смене и кому показывать наряды должности.", + { k: "schedules", t: "Графики работы", n: () => sch.length, why: "График задаётся каждому сотруднику: по нему система понимает, кто сейчас на смене, кому показывать наряды должности и слать уведомления.", body: () => h("div", {}, list(sch, x => x.name), h("p", { class: "small muted" }, "Добавьте типовые графики одним нажатием, а сложные циклы соберите в конструкторе."), h("div", { class: "row" }, ...SCHEDULE_PRESETS.map(([name, body]) => h("button", { class: "sec", disabled: !!byName(sch, name), onclick: guard(async () => { await api("POST", "/schedules", { name, ...body }); toast("Добавлен график «" + name + "»"); reload(); }) }, (byName(sch, name) ? "✔ " : "+ ") + name)), h("a", { class: "btn sec", href: "#/schedules" }, "Конструктор графиков"))) }, - { k: "positions", t: "Должности", n: () => pos.length, why: "Тип должности: техник выполняет наряды, инженер планирует и проверяет. У должности — график.", + { k: "positions", t: "Должности", n: () => pos.length, why: "Тип должности: техник выполняет наряды, инженер планирует и проверяет. График — не у должности, а у сотрудника (шаг «Сотрудники»).", body: () => h("div", {}, list(pos, x => x.name), bulk({ - hint: "Формат: Название; техник или инженер; название графика (необязательно).", - example: "Слесарь-ремонтник; техник; Сутки через трое\nЭлектромонтёр; техник; Сутки через трое\nИнженер-механик; инженер; Пятидневка 08:00–17:00", - run: async ([name, kind, s]) => { + hint: "Формат: Название; техник или инженер.", + example: "Слесарь-ремонтник; техник\nДежурный механик; техник\nЭлектромонтёр; техник\nИнженер-механик; инженер", + run: async ([name, kind]) => { if (!name) throw new Error("нет названия"); - const sc = s ? byName(sch, s) : null; - if (s && !sc) throw new Error(`график «${s}» не найден`); - const p = await api("POST", "/positions", { name, kind: norm(kind).startsWith("инж") ? "engineer" : "technician", schedule_id: sc ? sc.id : null }); + const p = await api("POST", "/positions", { name, kind: norm(kind).startsWith("инж") ? "engineer" : "technician" }); pos.push(p); return p; } })) }, { k: "departments", t: "Отделы", n: () => deps.length, why: "Службы и участки. Отделы можно вкладывать друг в друга.", @@ -145,20 +183,22 @@ async function setupPage(stepKey) { return e; } }), h("p", { class: "small muted" }, "Иконку и обслуживаемые узлы (масло, фильтры, ремни) задайте в карточке машины: «Оборудование» → «Карточка»."), h("a", { class: "btn sec", href: "#/equipment" }, "Открыть оборудование")) }, - { k: "users", t: "Сотрудники", n: () => users.filter(u => !u.is_admin).length, why: "Учётные записи: должность определяет график и права, отдел — принадлежность.", + { k: "users", t: "Сотрудники", n: () => users.filter(u => !u.is_admin).length, why: "Учётные записи: должность определяет права, график — когда сотрудник на смене, отдел — принадлежность.", body: () => { const res = h("div"); return h("div", {}, list(users.filter(u => !u.is_admin), x => x.full_name || x.username), bulk({ - hint: "Формат: ФИО; логин (пусто — придумается сам); должность; отдел; роль (склад / модератор, необязательно). Пароли создаются автоматически — распечатайте таблицу и раздайте.", - example: "Иванов Иван Петрович; ; Слесарь-ремонтник; Механическая служба\nПетров Сергей Алексеевич; ; Электромонтёр; Электрослужба\nКозлов Дмитрий Олегович; ; Слесарь-ремонтник; Механическая служба; склад", - run: async ([fio, login, p, d, role]) => { + hint: "Формат: ФИО; логин (пусто — придумается сам); должность; отдел; график; роль (склад / модератор). График и роль — необязательно; без графика сотрудник всегда на смене. Пароли создаются автоматически — распечатайте таблицу и раздайте.", + example: "Иванов Иван Петрович; ; Дежурный механик; Механическая служба; Сутки через трое\nСидоров Олег Ильич; ; Дежурный механик; Механическая служба; Сутки через трое\nПетров Сергей Алексеевич; ; Электромонтёр; Электрослужба; Пятидневка 08:00–17:00\nКозлов Дмитрий Олегович; ; Слесарь-ремонтник; Механическая служба; ; склад", + run: async ([fio, login, p, d, s, role]) => { if (!fio || fio.length < 3) throw new Error("нет ФИО"); const taken = new Set(users.map(u => u.username)); const position = p ? byName(pos, p) : null, dep = d ? byName(deps, d) : null; if (p && !position) throw new Error(`должность «${p}» не найдена`); if (d && !dep) throw new Error(`отдел «${d}» не найден`); + const sc = s ? byName(sch, s) : null; + if (s && !sc) throw new Error(`график «${s}» не найден`); const password = makePassword(), username = login || makeLogin(fio, taken); - const u = await api("POST", "/users", { username, full_name: fio, password, position_id: position ? position.id : null, department_id: dep ? dep.id : null, + const u = await api("POST", "/users", { username, full_name: fio, password, position_id: position ? position.id : null, schedule_id: sc ? sc.id : null, department_id: dep ? dep.id : null, is_storekeeper: norm(role).startsWith("скл"), is_moderator: norm(role).startsWith("мод"), is_active: true }); users.push(u); return { fio, username, password, position: position ? position.name : "", dep: dep ? dep.name : "" }; @@ -209,6 +249,7 @@ async function setupPage(stepKey) { h("a", { class: "btn sec", href: "#/backups" }, "Резервные копии"))) }, ]; const state = s => done.has(s.k) || s.n() ? "done" : "todo"; + if (stepKey === "done") return setupFinish({ STEPS, state, done, sch, pos, users, eqs, locs, map, cls, tpls, bk, settings }); const cur = STEPS.find(s => s.k === stepKey) || STEPS.find(s => state(s) === "todo") || STEPS[0]; const idx = STEPS.indexOf(cur); const ready = STEPS.filter(s => state(s) === "done").length; @@ -222,6 +263,6 @@ async function setupPage(stepKey) { h("p", { class: "muted" }, cur.why), cur.body(), h("div", { class: "row", style: "margin-top:18px;border-top:1px solid var(--line);padding-top:12px" }, idx > 0 ? h("a", { class: "btn sec", href: "#/setup/" + STEPS[idx - 1].k }, "← Назад") : null, h("span", { style: "flex:1" }), - h("label", { class: "chk" }, h("input", { type: "checkbox", checked: done.has(cur.k), onchange: guard(async e => { await markDone(cur.k, e.target.checked); reload(cur.k); }) }), "шаг выполнен"), - idx < STEPS.length - 1 ? h("a", { class: "btn", href: "#/setup/" + STEPS[idx + 1].k }, "Далее →") : h("a", { class: "btn", href: "#/admin" }, "Завершить"))))); + h("label", { class: "chk" }, h("input", { type: "checkbox", checked: done.has(cur.k), onchange: guard(async e => { await markDone(cur.k, e.target.checked); location.hash = "#/setup/" + (e.target.checked ? (STEPS[idx + 1] || { k: "done" }).k : cur.k); if (!e.target.checked) reload(cur.k); }) }), "шаг выполнен"), + idx < STEPS.length - 1 ? h("a", { class: "btn", href: "#/setup/" + STEPS[idx + 1].k }, "Далее →") : h("a", { class: "btn", href: "#/setup/done" }, "Завершить и проверить"))))); } diff --git a/app/static/style.css b/app/static/style.css index 87e5d7f..6fb2d5c 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -276,3 +276,29 @@ button.fault{background:var(--fault);border-color:var(--fault);color:#fff} .fbmodal{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:200;padding:16px} .fbmodal .card{width:min(560px,100%);margin:0}.fbmodal textarea{width:100%} .fbcard.fb-new{border-left:4px solid var(--accent)}.fbcard.fb-taken{border-left:4px solid var(--warn)}.fbcard.fb-done{opacity:.8} + +/* документация из Nextcloud */ +.dcmodal{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:200;padding:16px} +.dcmodal .card{width:min(640px,100%);max-height:86vh;overflow:auto;margin:0} +.crumbs{font-size:14px;margin:4px 0 10px;word-break:break-word}.crumbs a{text-decoration:none} +.dclist{display:flex;flex-direction:column;gap:2px;margin:0} +.dcrow{display:flex;gap:8px;align-items:center;padding:7px 8px;border-radius:8px;color:var(--text);text-decoration:none;word-break:break-word} +.dcrow:hover,.dcrow.on{background:var(--bg)} +.dcwrap{display:grid;grid-template-columns:minmax(220px,320px) 1fr;gap:12px;align-items:start}.dcwrap>.card{margin:0} +@media (max-width:900px){.dcwrap{grid-template-columns:1fr}} +.dcframe{width:100%;height:78vh;border:1px solid var(--line);border-radius:8px;background:#fff} +.dcimg{max-width:100%;border-radius:8px}.dctext{white-space:pre-wrap;max-height:78vh;overflow:auto;font-size:13px;margin:0} +.docsai{margin-top:16px;border-top:1px solid var(--line);padding-top:6px} +.sheet.risk{border-left:4px solid var(--warn);margin-top:8px}.sheet.risk ul{margin:6px 0 0;padding-left:18px}.sheet.risk li{margin:3px 0} + +/* мониторинг сервера */ +.gauges{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.gauges .card{margin:0} +.gauge{text-align:center}.gauge h2{margin:0 0 6px;font-size:16px} +.donut{width:150px;height:150px;display:block;margin:4px auto 8px}.donut circle{transition:stroke-dasharray .6s} +.donut-v{font-size:24px;font-weight:700;fill:var(--text)} + +/* мониторинг сервера */ +.gauges{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.gauges .card{margin:0} +.gauge{text-align:center}.gauge h2{margin:0 0 6px;font-size:16px} +.donut{width:150px;height:150px;display:block;margin:4px auto 8px}.donut circle{transition:stroke-dasharray .6s} +.donut-v{font-size:24px;font-weight:700;fill:var(--text)} diff --git a/app/sysinfo.py b/app/sysinfo.py new file mode 100644 index 0000000..468c50a --- /dev/null +++ b/app/sysinfo.py @@ -0,0 +1,109 @@ +"""Мониторинг сервера: загрузка процессора, память, место на дисках. Без внешних библиотек. + +Linux (в том числе Docker): /proc/stat, /proc/meminfo; если контейнеру задан лимит памяти (cgroup v2 memory.max) — +показываем память контейнера. Windows (локальный запуск): GetSystemTimes и GlobalMemoryStatusEx через ctypes.""" +import os +import shutil +import sys +import time +from pathlib import Path + +SAMPLE_SEC = 0.25 + + +def _linux_cpu_times() -> tuple[int, int]: + with open("/proc/stat") as f: + vals = [int(x) for x in f.readline().split()[1:]] + idle = vals[3] + (vals[4] if len(vals) > 4 else 0) # idle + iowait + return idle, sum(vals) + + +def _win_cpu_times() -> tuple[int, int]: + import ctypes + from ctypes import wintypes + idle, kernel, user = wintypes.FILETIME(), wintypes.FILETIME(), wintypes.FILETIME() + ctypes.windll.kernel32.GetSystemTimes(ctypes.byref(idle), ctypes.byref(kernel), ctypes.byref(user)) + v = lambda t: (t.dwHighDateTime << 32) | t.dwLowDateTime + return v(idle), v(kernel) + v(user) # время ядра уже включает простой + + +def cpu_percent() -> float | None: + get = _win_cpu_times if sys.platform == "win32" else _linux_cpu_times + try: + i1, t1 = get() + time.sleep(SAMPLE_SEC) + i2, t2 = get() + except (OSError, ValueError, AttributeError): + return None + dt = t2 - t1 + return round(100.0 * (1 - (i2 - i1) / dt), 1) if dt > 0 else 0.0 + + +def _cgroup_memory() -> tuple[int, int] | None: + try: + limit = Path("/sys/fs/cgroup/memory.max").read_text().strip() + if limit == "max": + return None + return int(Path("/sys/fs/cgroup/memory.current").read_text()), int(limit) + except (OSError, ValueError): + return None + + +def memory() -> dict | None: + """{used, total, scope: host|container} в байтах.""" + if sys.platform == "win32": + try: + import ctypes + + class MS(ctypes.Structure): + _fields_ = [("dwLength", ctypes.c_ulong), ("dwMemoryLoad", ctypes.c_ulong), ("ullTotalPhys", ctypes.c_ulonglong), + ("ullAvailPhys", ctypes.c_ulonglong), ("ullTotalPageFile", ctypes.c_ulonglong), ("ullAvailPageFile", ctypes.c_ulonglong), + ("ullTotalVirtual", ctypes.c_ulonglong), ("ullAvailVirtual", ctypes.c_ulonglong), ("ullAvailExtendedVirtual", ctypes.c_ulonglong)] + m = MS() + m.dwLength = ctypes.sizeof(MS) + ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(m)) + return {"used": m.ullTotalPhys - m.ullAvailPhys, "total": m.ullTotalPhys, "scope": "host"} + except (OSError, AttributeError): + return None + cg = _cgroup_memory() + if cg: + return {"used": cg[0], "total": cg[1], "scope": "container"} + try: + info = {} + with open("/proc/meminfo") as f: + for line in f: + k, v = line.split(":", 1) + info[k] = int(v.split()[0]) * 1024 + total = info["MemTotal"] + return {"used": total - info.get("MemAvailable", info.get("MemFree", 0)), "total": total, "scope": "host"} + except (OSError, KeyError, ValueError): + return None + + +def disks(paths: dict[str, Path]) -> list[dict]: + """Место на дисках, где лежат база, фото и т. п. Один и тот же диск показывается один раз.""" + out, seen = [], {} + for label, p in paths.items(): + p = Path(p) + while not p.exists() and p.parent != p: + p = p.parent + try: + u = shutil.disk_usage(p) + except OSError: + continue + key = (u.total, os.stat(p).st_dev) + if key in seen: + seen[key]["labels"].append(label) + continue + seen[key] = {"labels": [label], "path": str(p), "used": u.used, "total": u.total, "free": u.free} + out.append(seen[key]) + return out + + +def snapshot(paths: dict[str, Path]) -> dict: + try: + load = os.getloadavg() + except (OSError, AttributeError): + load = None + return {"cpu": cpu_percent(), "cpu_count": os.cpu_count(), "load": load, "memory": memory(), "disks": disks(paths), + "platform": sys.platform} diff --git a/docker-compose.yml b/docker-compose.yml index 1648e89..7e6009b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,13 @@ -# Развёртывание в Coolify (build pack «Docker Compose») или вручную: docker compose up -d --build -# Переменные задаются в интерфейсе Coolify (или в .env): DB_PASSWORD, SECRET_KEY, ADMIN_PASSWORD, при желании APP_TZ, APP_PORT; FORWARDED_ALLOW_IPS — IP вашего NPM. +# Развёртывание: docker compose up -d --build (или Coolify, build pack «Docker Compose») +# Переменные — в .env рядом с этим файлом (образец — .env.example): DB_PASSWORD, SECRET_KEY, ADMIN_PASSWORD и др. +# +# Дополнительные сервисы включаются профилями — по одному или вместе: +# docs — Nextcloud для документации оборудования (+ своя база), +# office — ONLYOFFICE Document Server: просмотр Word/Excel/PowerPoint в портале, +# ai — Ollama: локальная модель для AI-помощника (данные не уходят наружу). +# Пример: в .env COMPOSE_PROFILES=docs,office,ai и затем docker compose up -d +# Портал сам находит запущенные сервисы во внутренней сети Docker и подставляет их адреса в настройки +# (Nextcloud, ONLYOFFICE, адрес модели). Значения из .env и из интерфейса важнее автоматических. services: db: image: postgres:16-alpine @@ -30,9 +38,24 @@ services: APP_ENV: production # слабый SECRET_KEY/ADMIN_PASSWORD -> отказ запуска FORWARDED_ALLOW_IPS: ${FORWARDED_ALLOW_IPS:-127.0.0.1} # адрес NPM (доверенный прокси) PHOTO_DIR: /data/photos # каталог фото можно сменить в админке (он должен лежать на томе) + # --- документация (Nextcloud): пустой адрес — найдётся сам, если запущен профиль docs --- + NC_URL: ${NC_URL:-} + NC_USER: ${NC_USER:-${NEXTCLOUD_ADMIN_USER:-ncadmin}} + NC_PASSWORD: ${NC_PASSWORD:-${NEXTCLOUD_ADMIN_PASSWORD:-}} + NC_ROOT: ${NC_ROOT:-} + # --- ONLYOFFICE: секрет общий с сервисом onlyoffice ниже; пустой адрес — найдётся сам (профиль office) --- + ONLYOFFICE_URL: ${ONLYOFFICE_URL:-} + ONLYOFFICE_JWT: ${ONLYOFFICE_JWT:-change-me-onlyoffice-jwt} + PORTAL_INTERNAL_URL: ${PORTAL_INTERNAL_URL:-} + # --- AI-помощник: пустой адрес — при профиле ai подставится Ollama и модель OLLAMA_MODEL --- + ASSISTANT_PROVIDER: ${ASSISTANT_PROVIDER:-} + ASSISTANT_BASE_URL: ${ASSISTANT_BASE_URL:-} + ASSISTANT_MODEL: ${ASSISTANT_MODEL:-} + OLLAMA_MODEL: ${OLLAMA_MODEL:-qwen2.5:7b} # модель, если найден сервис ollama + ASSISTANT_API_KEY: ${ASSISTANT_API_KEY:-} volumes: - photos:/data/photos # фотографии нарядов - - appdata:/srv/data # демо-режим и файл APK (appdata/apk/*.apk раздаётся по /app.apk) + - appdata:/srv/data # демо-режим, кэш документов и файл APK (appdata/apk/*.apk раздаётся по /app.apk) ports: - "${APP_PORT:-8080}:8000" # на этот порт хоста смотрит прокси (NPM) healthcheck: @@ -42,7 +65,97 @@ services: retries: 5 start_period: 25s + # ---------- профиль docs: Nextcloud ---------- + nextcloud-db: + image: postgres:16-alpine + profiles: ["docs"] + restart: unless-stopped + environment: + POSTGRES_DB: nextcloud + POSTGRES_USER: nextcloud + POSTGRES_PASSWORD: ${NEXTCLOUD_DB_PASSWORD:-${DB_PASSWORD}} + volumes: + - nc_pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U nextcloud -d nextcloud"] + interval: 5s + timeout: 5s + retries: 20 + + nextcloud: + image: nextcloud:30-apache + profiles: ["docs"] + restart: unless-stopped + depends_on: + nextcloud-db: + condition: service_healthy + environment: + POSTGRES_HOST: nextcloud-db + POSTGRES_DB: nextcloud + POSTGRES_USER: nextcloud + POSTGRES_PASSWORD: ${NEXTCLOUD_DB_PASSWORD:-${DB_PASSWORD}} + NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER:-ncadmin} + NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD:-} + # «nextcloud» — имя, по которому к нему обращается портал внутри сети Docker + NEXTCLOUD_TRUSTED_DOMAINS: nextcloud localhost ${NEXTCLOUD_DOMAIN:-} + OVERWRITEPROTOCOL: ${NEXTCLOUD_PROTOCOL:-} # https — если Nextcloud открывают через прокси с HTTPS + volumes: + - nextcloud:/var/www/html + ports: + - "${NEXTCLOUD_PORT:-8081}:80" # веб-интерфейс Nextcloud: сюда загружают документацию + healthcheck: + test: ["CMD", "php", "-r", "exit(strpos((string)@file_get_contents('http://localhost/status.php'), '\"installed\":true') === false ? 1 : 0);"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 120s + + # ---------- профиль office: ONLYOFFICE Document Server (только внутренняя сеть) ---------- + onlyoffice: + image: onlyoffice/documentserver:8.2 + profiles: ["office"] + restart: unless-stopped + environment: + JWT_ENABLED: "true" + JWT_SECRET: ${ONLYOFFICE_JWT:-change-me-onlyoffice-jwt} + ALLOW_PRIVATE_IP_ADDRESS: "true" # иначе не скачает документ с портала по внутреннему адресу Docker + volumes: + - onlyoffice_data:/var/www/onlyoffice/Data + healthcheck: + test: ["CMD-SHELL", "curl -fs http://localhost/healthcheck | grep -q true"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 90s + + # ---------- профиль ai: локальная модель Ollama ---------- + ollama: + image: ollama/ollama:0.5.7 + profiles: ["ai"] + restart: unless-stopped + volumes: + - ollama:/root/.ollama + # Видеокарта NVIDIA (необязательно, нужен nvidia-container-toolkit) — раскомментируйте: + # deploy: + # resources: + # reservations: + # devices: [{driver: nvidia, count: all, capabilities: [gpu]}] + + ollama-pull: # один раз скачивает модель в том ollama и завершается + image: ollama/ollama:0.5.7 + profiles: ["ai"] + depends_on: [ollama] + restart: "no" + environment: + OLLAMA_HOST: http://ollama:11434 + MODEL: ${OLLAMA_MODEL:-qwen2.5:7b} + entrypoint: ["/bin/sh", "-c", "sleep 5 && ollama pull \"$$MODEL\""] + volumes: pgdata: photos: appdata: + nc_pgdata: + nextcloud: + onlyoffice_data: + ollama: diff --git a/docs/API.md b/docs/API.md index 08c536e..8bd1ff2 100644 --- a/docs/API.md +++ b/docs/API.md @@ -81,6 +81,10 @@ Пример «3 дня, отсыпной, 3 ночи, отсыпной, 3 выходных» — 5 шагов, цикл 11 дней. `POST /schedules/preview` `{schedule, from_date, days}` — календарь без сохранения (для конструктора). +График назначается **сотруднику**: `schedule_id` в `POST/PATCH /users` и в `UserOut` (пусто — всегда на смене). `on_shift` +считается по графику сотрудника. Наряд на должность видят и получают уведомление только сотрудники этой должности, которые +сейчас на смене. `schedule_id` у должности устарел и не используется (при обновлении перенесён сотрудникам). + ## Отчёты (инженер/модератор/админ) `GET /reports` — список отчётов. `GET /reports/completed-orders?from=…&to=…` — **период обязателен** (дата и время). Фильтры: `equipment_id`, `position_id` (должность исполнителя **на момент выполнения**), `user_id`, `include_review`. @@ -225,6 +229,37 @@ deviations, tasks_cancelled, tasks_paused, hours, calendar: {"2026-10-12": {n, h - Только администраторам портала доступны `PUT /settings` (кроме отметок мастера `setup_done`), полный `GET /settings`, `/api-keys`, `/assistant/config` и `/assistant/test`. Остальные администраторы получают в `GET /settings` сокращённый ответ (`limited: true`). +## Документация оборудования (Nextcloud) +Портал читает Nextcloud по WebDAV от служебной учётной записи (пароль приложения, только чтение) и сам отдаёт файлы; +Office переводится в PDF через ONLYOFFICE Document Server (ConvertService). +- `GET/PUT /api/v1/docs/config` — **администратор портала**: `nc_url, nc_user, nc_password, nc_root, onlyoffice_url, onlyoffice_jwt, + portal_internal_url, docs_ai`. Пароль и секрет JWT в ответе не возвращаются (`nc_password_set`, `onlyoffice_jwt_set`) и не попадают + в резервные копии. `POST /api/v1/docs/test` — проверка Nextcloud и ONLYOFFICE. +- `GET /api/v1/docs/browse?path=` — администратор или модератор: вложенные папки (с `linked` — к каким машинам привязаны) и число файлов. +- `PUT /api/v1/equipment/{id}/docs` `{path}` — администратор или модератор: привязать папку (пустой `path` — отвязать). В `EquipmentOut` — `docs_path`. +- `GET /api/v1/equipment/{id}/docs?sub=` — кто видит машину: `{folder, sub, items: [{name, path, is_dir, kind: dir|pdf|image|text|office|file, size, mtime}]}`. +- `GET /api/v1/equipment/{id}/docs/file?path=...&view=1` — файл; `view=1` — для просмотра: PDF, картинки, текст — inline, + Office — PDF через ONLYOFFICE (кэшируется). Без `view` — скачивание оригинала. Пути `..` отклоняются (422). +- `GET /api/v1/docs/raw/{token}` — без входа, только для ONLYOFFICE: подписанная ссылка на исходник, живёт 10 минут. +- В `GET /me` — `docs: true`, если Nextcloud подключён. + +Опция AI-помощника **«Работа с документацией»** (`docs_ai`): раз в сутки портал строит индекс (имя и начало текста файлов, +PDF — через pypdf, docx/xlsx/pptx/odt — из XML; файлы больше 60 МБ — только по имени), `POST /api/v1/docs/reindex` — вручную +(администратор портала). `GET /api/v1/docs/search?q=` — поиск по индексу среди машин, которые видит пользователь (только при +включённой опции). Помощник получает инструменты `search_docs` и `equipment_docs`. Фрагменты документов уходят провайдеру ИИ — +для закрытой документации нужна локальная модель. + +## Фото: «хранить более года» и автоочистка +- `photo_keep` у пункта чек-листа (`ChecklistItemIn`), копируется в пункт наряда (`OrderItemOut.photo_keep`); у задания — + `photo_keep` в `POST/PATCH /tasks` и `TaskOut`. Такие фото сохраняются в каталог `keep/` и очисткой не удаляются. +- Настройка `photo_keep_days` (`PUT /settings`, администратор портала): раз в сутки удалять фото нарядов и заданий старше N дней, + 0 — выключено (по умолчанию). `GET /photo-cleanup` — сколько удалится сейчас (ничего не удаляет), `POST /photo-cleanup` — очистить сразу. + В `GET /settings` — `photo_keep_stats` (сколько фото в `keep/`). + +## Мониторинг сервера +`GET /api/v1/server/monitor` (администратор портала) → `{cpu: %, cpu_count, load: [1, 5, 15 мин] | null, +memory: {used, total, scope: host|container}, disks: [{labels, path, used, total, free}], now}`. + ## AI-помощник Включает администратор: «Администрирование → Настройки сервера → AI-помощник» (`GET/PUT /api/v1/assistant/config`: `enabled, provider (openai | anthropic | gigachat), base_url, model, api_key, scope`; ключ в ответах не возвращается и не попадает diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 005032e..8e3ab61 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -98,6 +98,36 @@ docker compose cp checklists-1.7.0.apk app:/srv/data/apk/ Версия берётся из имени файла (`checklists-<версия>.apk`); приложения сами предложат обновиться. В приложении на телефоне указывается адрес вашего сервера (`https://portal.example.ru`). +## 7а. Дополнительные сервисы: документация, просмотр Office, локальный ИИ +`docker-compose.yml` умеет поднять рядом с порталом ещё три сервиса. Они включаются **профилями** — строкой в `.env`: + +```bash +COMPOSE_PROFILES=docs,office,ai +``` + +| Профиль | Что запускается | Зачем | Ресурсы сверх портала | +|---|---|---|---| +| `docs` | Nextcloud + своя PostgreSQL | хранилище документации оборудования: паспорта, схемы, руководства | 1 ГБ памяти, место под документы | +| `office` | ONLYOFFICE Document Server | Word, Excel, PowerPoint открываются прямо в портале (переводятся в PDF) | 2–4 ГБ памяти | +| `ai` | Ollama + скачивание модели | локальная модель для AI-помощника: вопросы и документы не уходят в интернет | 6–8 ГБ памяти для 7B-модели, лучше видеокарта | + +Профили независимы: можно взять только `ai` или подключить свой Nextcloud и ONLYOFFICE, указав их адреса в `.env` +(`NC_URL`, `ONLYOFFICE_URL`, `PORTAL_INTERNAL_URL`). + +**Настройки заполняются сами.** Портал ищет сервисы во внутренней сети Docker по именам `nextcloud`, `onlyoffice`, `ollama` +и подставляет адреса в «Настройки сервера»: Nextcloud — `http://nextcloud`, ONLYOFFICE — `http://onlyoffice` (документы +он забирает у портала по `http://app:8000`), модель — `http://ollama:11434/v1` с моделью из `OLLAMA_MODEL`. Учётная запись +Nextcloud и секрет ONLYOFFICE берутся из `.env`. Всё, что администратор поменяет в интерфейсе, важнее автоматических значений. + +Что сделать после запуска: +1. **Nextcloud** откройте на порту `NEXTCLOUD_PORT` (по умолчанию 8081), войдите администратором из `.env`, создайте папку + документации (например, «Документация») с подпапками по машинам и укажите её в `NC_ROOT` или в настройках портала. + Для безопасности заведите отдельного пользователя с доступом только на чтение к этой папке и пароль приложения для + него — и впишите их в `NC_USER` / `NC_PASSWORD`. +2. **ONLYOFFICE** наружу не публикуется: портал общается с ним внутри сети Docker. Задайте свой `ONLYOFFICE_JWT`. +3. **ИИ**: дождитесь, пока `ollama-pull` скачает модель (`docker compose logs -f ollama-pull`), затем в «Настройках сервера → + AI-помощник» нажмите «Проверить» и включите помощника. Опция «Работа с документацией» включается там же. + ## 8. Обновление ```bash cd /opt/portal diff --git a/docs/user-guide.pdf b/docs/user-guide.pdf index 1bc92cc3d1b6316cad91beaf2e480fe6a84ced73..d11bca6ad418247f6ab9644253108107e5787949 100644 GIT binary patch delta 180353 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

GIRG$ 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 Qp*JDX>E3;)ql3=>0X{$YmH+?% diff --git a/requirements.txt b/requirements.txt index e2c1aea..25f067e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ google-auth==2.* requests==2.* fpdf2==2.* tzdata +pypdf==5.* diff --git a/tests/test_flow.py b/tests/test_flow.py index 218a589..61ec8d9 100644 --- a/tests/test_flow.py +++ b/tests/test_flow.py @@ -47,14 +47,12 @@ def test_full_flow(): "weekdays": [0, 1, 2, 3, 4, 5, 6], "start_time": "00:00:00", "end_time": "23:59:00"}).json() night = c.post("/api/schedules", headers=adm, json={"name": "Выходной", "kind": "weekly", "weekdays": []}).json() - tech = c.post("/api/positions", headers=adm, json={"name": "Слесарь", "kind": "technician", - "schedule_id": sch["id"]}).json() - tech2 = c.post("/api/positions", headers=adm, json={"name": "Слесарь (выходной)", "kind": "technician", - "schedule_id": night["id"]}).json() + # одну должность занимают сменщики: график у каждого свой + tech = c.post("/api/positions", headers=adm, json={"name": "Слесарь", "kind": "technician"}).json() eng = c.post("/api/positions", headers=adm, json={"name": "Инженер", "kind": "engineer"}).json() - for name, pos in (("ivan", tech), ("petr", tech2), ("eng", eng)): + for name, pos, sid in (("ivan", tech, sch["id"]), ("petr", tech, night["id"]), ("eng", eng, None)): r = c.post("/api/users", headers=adm, json={"username": name, "password": "Pass1234567", - "position_id": pos["id"]}) + "position_id": pos["id"], "schedule_id": sid}) assert r.status_code == 201, r.text ivan, petr, engh = login("ivan", "Pass1234567"), login("petr", "Pass1234567"), login("eng", "Pass1234567") @@ -80,7 +78,13 @@ def test_full_flow(): assert tpl["next_run"] order_id = c.post(f"/api/order-templates/{tpl['id']}/run-now", headers=engh).json()["order_id"] - # видимость: Иван (на смене) видит, Пётр (выходной) — нет + # видимость: Иван (на смене) видит, его сменщик Пётр (выходной) — нет; уведомление — только Ивану + from app import push + from app.db import SessionLocal + with SessionLocal() as db: + ids_ = {u["username"]: u["id"] for u in c.get("/api/users", headers=adm).json()} + assert push.users_for_assignment(db, None, tech["id"]) == [ids_["ivan"]] + assert c.get("/api/me", headers=petr).json()["on_shift"] is False assert [o["id"] for o in c.get("/api/orders", headers=ivan).json()] == [order_id] assert c.get("/api/orders", headers=petr).json() == [] assert c.get(f"/api/orders/{order_id}", headers=petr).status_code == 404 diff --git a/tests/test_scope_autoconf.py b/tests/test_scope_autoconf.py new file mode 100644 index 0000000..4c37520 --- /dev/null +++ b/tests/test_scope_autoconf.py @@ -0,0 +1,56 @@ +"""Автонастройка: значения из окружения и сервисов во внутренней сети Docker; сохранённое в интерфейсе важнее.""" +import os +import tempfile + +os.environ.setdefault("DATABASE_URL", os.environ.get("TEST_DATABASE_URL") or "sqlite:///" + os.path.join(tempfile.mkdtemp(), "ac.db").replace("\\", "/")) +os.environ.setdefault("SCHEDULER_INTERVAL", "3600") +os.environ.setdefault("SECRET_KEY", "test-secret-key-for-pytest-0123456789abcdef") +os.environ.setdefault("DATA_DIR", tempfile.mkdtemp()) + +from fastapi.testclient import TestClient # noqa: E402 + +from app import assistant, autoconf, settings_store as ss # noqa: E402 +from app.db import SessionLocal # noqa: E402 +from app.main import app # noqa: E402 + +V1 = "/api/v1" + + +def test_autoconf_from_docker_network_and_env(monkeypatch): + running = {"nextcloud", "onlyoffice", "ollama"} + monkeypatch.setattr(autoconf, "resolves", lambda host: host in running) + monkeypatch.setenv("NC_USER", "ncadmin") + monkeypatch.setenv("NC_PASSWORD", "nc-secret-pass") + monkeypatch.setenv("OLLAMA_MODEL", "qwen2.5:3b") + monkeypatch.delenv("NC_URL", raising=False) + with TestClient(app) as c: + adm = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "admin", "password": "admin"}).json()["access_token"]} + cfg = c.get(f"{V1}/docs/config", headers=adm).json() + assert cfg["nc_url"] == "http://nextcloud" and cfg["nc_user"] == "ncadmin" and cfg["nc_password_set"] + assert cfg["onlyoffice_url"] == "http://onlyoffice" and cfg["portal_internal_url"] == "http://app:8000" + assert "nc-secret" not in str(cfg) + assert c.get(f"{V1}/me", headers=adm).json()["docs"] is True + a = c.get(f"{V1}/assistant/config", headers=adm).json() + assert a["base_url"] == "http://ollama:11434/v1" and a["model"] == "qwen2.5:3b" and not a["enabled"] + # локальная модель в сети Docker включается без ключа + assert c.put(f"{V1}/assistant/config", headers=adm, json={"enabled": True}).status_code == 200 + with SessionLocal() as db: + assert assistant.ready(db) + c.put(f"{V1}/assistant/config", headers=adm, json={"enabled": False}) + # значение из интерфейса важнее автоматического + c.put(f"{V1}/docs/config", headers=adm, json={"nc_url": "https://cloud.example.ru"}) + assert c.get(f"{V1}/docs/config", headers=adm).json()["nc_url"] == "https://cloud.example.ru" + with SessionLocal() as db: + ss.set_(db, "nc_url", "") + db.commit() + # сервисов нет — пусто, Nextcloud не подключён + running.clear() + monkeypatch.delenv("NC_USER") + with SessionLocal() as db: + assert ss.get(db, "nc_url") == "" and ss.get(db, "onlyoffice_url") == "" and ss.get(db, "portal_internal_url") == "" + + +def test_is_local(): + assert autoconf.is_local("http://ollama:11434/v1") and autoconf.is_local("http://localhost:11434/v1") + assert autoconf.is_local("http://192.168.1.50:11434/v1") and autoconf.is_local("http://10.0.0.5/v1") + assert not autoconf.is_local("https://api.openai.com/v1") and not autoconf.is_local("http://8.8.8.8/v1") and not autoconf.is_local("") diff --git a/tests/test_scope_docs.py b/tests/test_scope_docs.py new file mode 100644 index 0000000..4f7dd0b --- /dev/null +++ b/tests/test_scope_docs.py @@ -0,0 +1,191 @@ +"""Документация из Nextcloud: настройки (секреты не утекают), проводник и привязка, права на просмотр, просмотр Office +через ONLYOFFICE, индекс и поиск для опции AI «Работа с документацией». Nextcloud и ONLYOFFICE имитируются.""" +import io +import json +import os +import tempfile +import time +import zipfile +from urllib.parse import quote, unquote + +os.environ.setdefault("DATABASE_URL", os.environ.get("TEST_DATABASE_URL") or "sqlite:///" + os.path.join(tempfile.mkdtemp(), "dc.db").replace("\\", "/")) +os.environ.setdefault("SCHEDULER_INTERVAL", "3600") +os.environ.setdefault("SECRET_KEY", "test-secret-key-for-pytest-0123456789abcdef") +os.environ.setdefault("DATA_DIR", tempfile.mkdtemp()) + +import httpx # noqa: E402 +import jwt # noqa: E402 +from fastapi.testclient import TestClient # noqa: E402 + +from app import assistant as A, docs_index as DI, nextcloud as nc # noqa: E402 +from app.main import app # noqa: E402 + +V1 = "/api/v1" +PW = "Pass1234567" +DAV = "/remote.php/dav/files/svc" + + +def docx(text: str) -> bytes: + buf = io.BytesIO() + with zipfile.ZipFile(buf, "w") as z: + z.writestr("word/document.xml", f'{text}') + return buf.getvalue() + + +TREE = { # путь в учётной записи → содержимое (None — папка) + "Документы": None, + "Документы/DC-Компрессор": None, + "Документы/DC-Компрессор/Паспорт.pdf": b"%PDF-1.4 fake", + "Документы/DC-Компрессор/notes.txt": "Давление масла в норме 4 бар".encode(), + "Документы/DC-Компрессор/Схемы": None, + "Документы/DC-Компрессор/Схемы/Электросхема.docx": docx("Схема подключения двигателя М1, пускатель КМ2"), + "Документы/DC-Компрессор/.скрытый": b"x", + "Документы/DC-Пресс": None, +} + + +def multistatus(paths: list[str]) -> bytes: + rows = [] + for p in paths: + body = TREE[p] + rt = "" if body is None else "" + rows.append(f"{quote(DAV + '/' + p)}{'/' if body is None else ''}" + f"{rt}{len(body or b'')}" + f"Mon, 12 Oct 2026 10:00:00 GMT\"e{len(body or b'')}\"" + f"HTTP/1.1 200 OK") + return ('' + "".join(rows) + "").encode() + + +def nextcloud(request: httpx.Request) -> httpx.Response: + if request.headers.get("authorization") != httpx.BasicAuth("svc", "app-pass-1234")._auth_header: + return httpx.Response(401) + path = unquote(request.url.path) + assert path.startswith(DAV) + rel = path[len(DAV):].strip("/") + if rel not in TREE: + return httpx.Response(404) + if request.method == "PROPFIND": + kids = [p for p in TREE if p.startswith(rel + "/") and "/" not in p[len(rel) + 1:]] if request.headers["depth"] == "1" else [] + return httpx.Response(207, content=multistatus([rel] + kids)) + return httpx.Response(200, content=TREE[rel]) + + +converted = [] + + +def onlyoffice(request: httpx.Request) -> httpx.Response: + if request.url.path == "/healthcheck": + return httpx.Response(200, text="true") + if request.url.path == "/ConvertService.ashx": + body = json.loads(request.content) + assert jwt.decode(body["token"], "oo-secret", algorithms=["HS256"])["url"] == body["url"] + assert body["url"].startswith("http://portal.lan:8000/api/v1/docs/raw/") and body["outputtype"] == "pdf" + src = nc.check_raw_token(body["url"].rsplit("/", 1)[1]).read_bytes() # то, что скачал бы ONLYOFFICE + converted.append((body, src)) + return httpx.Response(200, json={"endConvert": True, "fileUrl": "http://oo.lan/cache/out.pdf", "percent": 100}) + if request.url.path == "/cache/out.pdf": + return httpx.Response(200, content=b"%PDF-1.7 converted") + return httpx.Response(404) + + +def test_docs(monkeypatch): + monkeypatch.setattr(nc, "_client", lambda c: httpx.Client(transport=httpx.MockTransport(nextcloud), auth=(c.user, c.password))) + monkeypatch.setattr(nc, "_oo_client", lambda timeout: httpx.Client(transport=httpx.MockTransport(onlyoffice))) + with TestClient(app) as c: + login = lambda u, p=PW: {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": u, "password": p}).json()["access_token"]} + adm = login("admin", "admin") + d1 = c.post(f"{V1}/departments", headers=adm, json={"name": "DC-Компрессорная"}).json() + d2 = c.post(f"{V1}/departments", headers=adm, json={"name": "DC-Прессовый"}).json() + comp = c.post(f"{V1}/equipment", headers=adm, json={"name": "DC-Компрессор", "department_id": d1["id"]}).json() + c.post(f"{V1}/users", headers=adm, json={"username": "dc_mod", "full_name": "Модератор", "password": PW, "is_moderator": True}) + c.post(f"{V1}/users", headers=adm, json={"username": "dc_t1", "full_name": "Техник компрессорной", "password": PW, "department_id": d1["id"]}) + c.post(f"{V1}/users", headers=adm, json={"username": "dc_t2", "full_name": "Техник прессового", "password": PW, "department_id": d2["id"]}) + mod, t1, t2 = login("dc_mod"), login("dc_t1"), login("dc_t2") + + # настройки — только администратор портала, секреты не возвращаются + assert c.put(f"{V1}/docs/config", headers=mod, json={"nc_url": "http://nc.lan"}).status_code == 403 + assert c.get(f"{V1}/docs/browse", headers=mod).status_code == 422 # ещё не настроено + assert c.put(f"{V1}/docs/config", headers=adm, json={"nc_url": "ftp://x"}).status_code == 422 + assert c.put(f"{V1}/docs/config", headers=adm, json={"nc_root": "a/../b"}).status_code == 422 + r = c.put(f"{V1}/docs/config", headers=adm, json={"nc_url": "http://nc.lan/", "nc_user": "svc", "nc_password": "app-pass-1234", + "nc_root": "/Документы/", "onlyoffice_url": "http://oo.lan", "onlyoffice_jwt": "oo-secret", + "portal_internal_url": "http://portal.lan:8000"}) + assert r.status_code == 200 and r.json()["nc_password_set"] and r.json()["onlyoffice_jwt_set"] and r.json()["nc_root"] == "Документы" + assert "app-pass" not in r.text and "oo-secret" not in r.text + assert "app-pass" not in c.get(f"{V1}/settings", headers=adm).text + t = c.post(f"{V1}/docs/test", headers=adm).json() + assert "2 папок" in t["nextcloud"] and t["onlyoffice"] == "работает" + + # проводник и привязка: администратор или модератор, техник — нет + assert c.get(f"{V1}/docs/browse", headers=t1).status_code == 403 + b = c.get(f"{V1}/docs/browse", headers=mod).json() + assert [x["name"] for x in b["items"]] == ["DC-Компрессор", "DC-Пресс"] + assert [x["name"] for x in c.get(f"{V1}/docs/browse", headers=mod, params={"path": "DC-Компрессор"}).json()["items"]] == ["Схемы"] + assert c.get(f"{V1}/docs/browse", headers=mod, params={"path": "../etc"}).status_code == 422 + assert c.put(f"{V1}/equipment/{comp['id']}/docs", headers=t1, json={"path": "DC-Компрессор"}).status_code == 403 + assert c.put(f"{V1}/equipment/{comp['id']}/docs", headers=mod, json={"path": "Нет такой"}).status_code == 404 + assert c.put(f"{V1}/equipment/{comp['id']}/docs", headers=mod, json={"path": "DC-Компрессор"}).json()["docs_path"] == "DC-Компрессор" + assert c.get(f"{V1}/docs/browse", headers=mod).json()["items"][0]["linked"] == ["DC-Компрессор"] + + # просмотр: кто видит машину; скрытые файлы не показываются; выйти из папки машины нельзя + assert c.get(f"{V1}/equipment/{comp['id']}/docs", headers=t2).status_code == 404 + ls = c.get(f"{V1}/equipment/{comp['id']}/docs", headers=t1).json() + assert [(x["name"], x["kind"]) for x in ls["items"]] == [("Схемы", "dir"), ("notes.txt", "text"), ("Паспорт.pdf", "pdf")] + assert ls["items"][2]["path"] == "Паспорт.pdf" + sub = c.get(f"{V1}/equipment/{comp['id']}/docs", headers=t1, params={"sub": "Схемы"}).json() + assert sub["items"][0]["path"] == "Схемы/Электросхема.docx" and sub["items"][0]["kind"] == "office" + assert c.get(f"{V1}/equipment/{comp['id']}/docs/file", headers=t1, params={"path": "../DC-Пресс"}).status_code == 422 + f = c.get(f"{V1}/equipment/{comp['id']}/docs/file", headers=t1, params={"path": "Паспорт.pdf", "view": 1}) + assert f.status_code == 200 and f.headers["content-type"] == "application/pdf" and f.content == b"%PDF-1.4 fake" + assert f.headers["content-disposition"].startswith("inline") and f.headers["x-content-type-options"] == "nosniff" + f = c.get(f"{V1}/equipment/{comp['id']}/docs/file", headers=t1, params={"path": "Схемы/Электросхема.docx"}) + assert f.headers["content-disposition"].startswith("attachment") and f.content == TREE["Документы/DC-Компрессор/Схемы/Электросхема.docx"] + # Office → PDF через ONLYOFFICE: исходник отдаётся по подписанной ссылке, результат кэшируется + f = c.get(f"{V1}/equipment/{comp['id']}/docs/file", headers=t1, params={"path": "Схемы/Электросхема.docx", "view": 1}) + assert f.status_code == 200, f.text + assert f.content == b"%PDF-1.7 converted" and converted[0][1] == TREE["Документы/DC-Компрессор/Схемы/Электросхема.docx"] + c.get(f"{V1}/equipment/{comp['id']}/docs/file", headers=t1, params={"path": "Схемы/Электросхема.docx", "view": 1}) + assert len(converted) == 1 + raw = converted[0][0]["url"].split("/api/v1")[1] + assert c.get(V1 + raw).content == converted[0][1] + assert c.get(V1 + raw[:-3] + "abc").status_code == 404 + + # опция AI выключена — поиска нет; включается только администратором портала + assert c.get(f"{V1}/docs/search", headers=t1, params={"q": "схема"}).status_code == 404 + assert c.post(f"{V1}/docs/reindex", headers=adm).status_code == 422 + assert c.put(f"{V1}/docs/config", headers=adm, json={"docs_ai": True}).json()["docs_ai"] is True + assert c.post(f"{V1}/docs/reindex", headers=mod).status_code == 403 + assert c.post(f"{V1}/docs/reindex", headers=adm).status_code == 202 + for _ in range(100): + time.sleep(0.05) + if not DI.state["running"] and c.get(f"{V1}/docs/config", headers=adm).json()["index"]["files"]: + break + idx = c.get(f"{V1}/docs/config", headers=adm).json()["index"] + assert idx["files"] == 3 and idx["linked"] == 1, idx + hits = c.get(f"{V1}/docs/search", headers=t1, params={"q": "схема двигателя"}).json() + assert hits[0]["path"] == "Схемы/Электросхема.docx" and "двигателя м1" in hits[0]["snippet"] + assert c.get(f"{V1}/docs/search", headers=t1, params={"q": "давление масла"}).json()[0]["name"] == "notes.txt" + assert c.get(f"{V1}/docs/search", headers=t2, params={"q": "схема двигателя"}).json() == [] + + # помощник: инструменты документации есть только при включённой опции + c.put(f"{V1}/assistant/config", headers=adm, json={"enabled": True, "provider": "openai", "model": "m", "api_key": "sk-test-0000", + "base_url": "https://llm.example/v1"}) + calls = [] + + async def fake(cfg, system, messages): + calls.append((system, messages[-1]["content"])) + if messages[-1]["content"].startswith("РЕЗУЛЬТАТ"): + return "Смотрите [Электросхема.docx](#/docs/1?p=Схемы/Электросхема.docx)" + return 'CALL search_docs {"q": "схема двигателя"}' + monkeypatch.setattr(A, "complete", fake) + r = c.post(f"{V1}/assistant/chat", headers=t1, json={"messages": [{"role": "user", "content": "Где схема двигателя компрессора?"}]}).json() + assert r["tools"] == ["search_docs"] and "search_docs" in calls[0][0] and "Электросхема.docx" in calls[1][1] + c.put(f"{V1}/docs/config", headers=adm, json={"docs_ai": False}) + c.post(f"{V1}/assistant/chat", headers=t1, json={"messages": [{"role": "user", "content": "Где схема?"}]}) + assert "search_docs" not in calls[-2][0] and "нет инструмента" in calls[-1][1] + c.put(f"{V1}/assistant/config", headers=adm, json={"enabled": False, "api_key": "", "model": "", "base_url": ""}) # как было + + # отвязка чистит индекс + assert c.put(f"{V1}/equipment/{comp['id']}/docs", headers=adm, json={"path": ""}).json()["docs_path"] == "" + assert c.get(f"{V1}/docs/config", headers=adm).json()["index"]["files"] == 0 + assert c.get(f"{V1}/equipment/{comp['id']}/docs", headers=t1).status_code == 404 diff --git a/tests/test_scope_tweaks.py b/tests/test_scope_tweaks.py new file mode 100644 index 0000000..2149c25 --- /dev/null +++ b/tests/test_scope_tweaks.py @@ -0,0 +1,115 @@ +"""График у сотрудника (сменщики на одной должности), автоочистка старых фото с каталогом keep/, мониторинг сервера.""" +import os +import tempfile +from datetime import timedelta + +os.environ.setdefault("DATABASE_URL", os.environ.get("TEST_DATABASE_URL") or "sqlite:///" + os.path.join(tempfile.mkdtemp(), "tw.db").replace("\\", "/")) +os.environ.setdefault("SCHEDULER_INTERVAL", "3600") +os.environ.setdefault("SECRET_KEY", "test-secret-key-for-pytest-0123456789abcdef") +os.environ.setdefault("DATA_DIR", tempfile.mkdtemp()) + +from fastapi.testclient import TestClient # noqa: E402 + +from app import config, logic, photo_cleanup, settings_store as ss # noqa: E402 +from app.db import SessionLocal # noqa: E402 +from app.main import app # noqa: E402 +from app.models import Photo, Position, TaskPhoto, User # noqa: E402 + +V1 = "/api/v1" +PW = "Pass1234567" +PNG = b"\x89PNG\r\n\x1a\n" + b"0" * 64 + + +def test_schedule_moves_from_position_to_user(): + with TestClient(app) as c: + adm = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "admin", "password": "admin"}).json()["access_token"]} + off = c.post(f"{V1}/schedules", headers=adm, json={"name": "TW-выходной", "kind": "weekly", "weekdays": []}).json() + assert c.post(f"{V1}/users", headers=adm, json={"username": "tw_bad", "password": PW, "schedule_id": 999999}).status_code == 422 + # старые данные: график задан должности — однократный перенос ставит его сотрудникам без своего графика + with SessionLocal() as db: + p = Position(name="TW-Дежурный", kind="technician", schedule_id=off["id"]) + db.add(p) + db.flush() + db.add_all([User(username="tw_old1", password_hash="x", position_id=p.id), User(username="tw_old2", password_hash="x", position_id=p.id)]) + ss.set_(db, "schedule_per_user", "0") + db.commit() + assert logic.schedules_to_users(db) >= 2 + assert logic.schedules_to_users(db) == 0 # повторно не выполняется + u1 = db.query(User).filter_by(username="tw_old1").one() + assert u1.schedule_id == off["id"] and not logic.user_on_shift(u1) + u1.schedule_id = None # свой график убрали — всегда на смене, график должности больше не влияет + db.commit() + db.refresh(u1) + assert logic.user_on_shift(u1) + r = c.get(f"{V1}/users", headers=adm).json() + assert next(u for u in r if u["username"] == "tw_old2")["schedule_id"] == off["id"] + + +def test_photo_cleanup_keeps_marked_photos(): + with TestClient(app) as c: + adm = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "admin", "password": "admin"}).json()["access_token"]} + cl = c.post(f"{V1}/checklists", headers=adm, json={"name": "TW-фото", "items": [ + {"text": "Общий вид", "answer_type": "status", "photo": "optional"}, + {"text": "Табличка (хранить)", "answer_type": "status", "photo": "optional", "photo_keep": True}]}).json() + assert [i["photo_keep"] for i in cl["items"]] == [False, True] + pos = c.post(f"{V1}/positions", headers=adm, json={"name": "TW-слесарь", "kind": "technician"}).json() + tech = c.post(f"{V1}/users", headers=adm, json={"username": "tw_tech", "password": PW, "position_id": pos["id"]}).json() + th = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "tw_tech", "password": PW}).json()["access_token"]} + o = c.post(f"{V1}/orders", headers=adm, json={"title": "TW-наряд", "checklist_id": cl["id"], "assignee_user_id": tech["id"]}).json() + a, b = o["items"] + assert b["photo_keep"] is True + for it in (a, b): + r = c.post(f"{V1}/orders/{o['id']}/items/{it['id']}/photos", headers={**th, "Content-Type": "image/png"}, content=PNG) + assert r.status_code in (200, 201), r.text + t = c.post(f"{V1}/tasks", headers=adm, json={"title": "TW-задание", "photo_keep": True}).json() + assert t["photo_keep"] is True + c.post(f"{V1}/tasks/{t['id']}/photos", headers={**adm, "Content-Type": "image/png"}, content=PNG) + t2 = c.post(f"{V1}/tasks", headers=adm, json={"title": "TW-задание 2"}).json() + c.post(f"{V1}/tasks/{t2['id']}/photos", headers={**adm, "Content-Type": "image/png"}, content=PNG) + + with SessionLocal() as db: + root = ss.photo_root(db) + ph = {p.item_id: p for p in db.query(Photo).filter(Photo.order_id == o["id"])} + assert ph[b["id"]].path.startswith("keep/") and not ph[a["id"]].path.startswith("keep/") + tp = {p.task_id: p for p in db.query(TaskPhoto).filter(TaskPhoto.task_id.in_([t["id"], t2["id"]]))} + assert tp[t["id"]].path.startswith("keep/") and not tp[t2["id"]].path.startswith("keep/") + files = [root / ph[a["id"]].path, root / ph[b["id"]].path, root / "media" / "tasks" / tp[t["id"]].path, root / "media" / "tasks" / tp[t2["id"]].path] + assert all(f.is_file() for f in files) + # всё «состарим» на два года + old = config.now() - timedelta(days=730) + for p in list(ph.values()) + list(tp.values()): + p.created_at = old + db.commit() + + # выключено по умолчанию — ничего не удаляется + assert c.get(f"{V1}/photo-cleanup", headers=adm).json()["enabled"] is False + assert c.put(f"{V1}/settings", headers=adm, json={"photo_keep_days": 365}).status_code == 200 + prev = c.get(f"{V1}/photo-cleanup", headers=adm).json() + assert prev["enabled"] and prev["photos"] >= 2 + assert all(f.is_file() for f in files) # предпросмотр ничего не удаляет + r = c.post(f"{V1}/photo-cleanup", headers=adm).json() + assert r["photos"] >= 2 + assert not files[0].exists() and files[1].is_file() and files[2].is_file() and not files[3].exists() + with SessionLocal() as db: + assert not photo_cleanup.due(db) + # фото «хранить» по-прежнему открывается + o2 = c.get(f"{V1}/orders/{o['id']}", headers=adm).json() + assert [len(i["photos"]) for i in o2["items"]] == [0, 1] + assert c.get(f"{V1}/photos/{o2['items'][1]['photos'][0]['id']}", headers=adm).status_code == 200 + tid = c.get(f"{V1}/tasks/{t['id']}", headers=adm).json()["photos"][0] + assert c.get(f"{V1}/task-photos/{tid}", headers=adm).status_code == 200 + s = c.get(f"{V1}/settings", headers=adm).json() + assert s["photo_keep_days"] == "365" and s["photo_keep_stats"]["files"] >= 2 + c.put(f"{V1}/settings", headers=adm, json={"photo_keep_days": 0}) + + +def test_server_monitor(): + with TestClient(app) as c: + adm = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "admin", "password": "admin"}).json()["access_token"]} + c.post(f"{V1}/users", headers=adm, json={"username": "tw_adm", "password": PW, "is_admin": True}) + adm2 = {"Authorization": "Bearer " + c.post(f"{V1}/auth/login", json={"username": "tw_adm", "password": PW}).json()["access_token"]} + assert c.get(f"{V1}/server/monitor", headers=adm2).status_code == 403 # только администратор портала + m = c.get(f"{V1}/server/monitor", headers=adm).json() + assert m["cpu"] is None or 0 <= m["cpu"] <= 100 + assert m["memory"] is None or 0 < m["memory"]["used"] <= m["memory"]["total"] + assert m["disks"] and all(0 < d["used"] <= d["total"] for d in m["disks"]) diff --git a/tests/test_security.py b/tests/test_security.py index efa1702..0752413 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -77,7 +77,8 @@ def env(): # ---------------------------------------------------------------- аутентификация PUBLIC = {("POST", "/auth/login"), ("POST", "/auth/refresh"), ("POST", "/auth/logout"), ("GET", "/health"), ("GET", "/demo/status"), - ("GET", "/public/report-options"), ("POST", "/public/incidents"), ("GET", "/app/version")} # публичная форма инцидента: без входа, с лимитом + ("GET", "/public/report-options"), ("POST", "/public/incidents"), ("GET", "/app/version"), + ("GET", "/docs/raw/{token}")} # исходник для ONLYOFFICE: только по короткой подписанной ссылке (проверка — test_scope_docs) # публичная форма инцидента: без входа, с лимитом def test_every_api_route_requires_auth(env): @@ -392,7 +393,9 @@ def test_stored_photo_name_is_server_generated(env): from app.models import Photo with SessionLocal() as s: for p in s.query(Photo).all(): - assert ".." not in p.path and not p.path.startswith(("/", "\\")) and len(p.path.split("/")) == 3 + parts = p.path.split("/") + parts = parts[1:] if parts[0] == "keep" else parts # keep/ — фото «хранить более года» + assert ".." not in p.path and not p.path.startswith(("/", "\\")) and len(parts) == 3 def test_static_path_traversal_blocked(env): diff --git a/tools/guide/build_pdf.py b/tools/guide/build_pdf.py index daddb89..0dd6bd0 100644 --- a/tools/guide/build_pdf.py +++ b/tools/guide/build_pdf.py @@ -333,8 +333,9 @@ def build(): ["Наряд", "Одно задание: что проверить или обслужить, на каком оборудовании, кому, до какого срока и кто проверит."], ["Чек-лист", "Список задач внутри наряда. У каждой задачи свой тип ответа: статус, выбор из списка, число или текст; " "у некоторых нужно приложить фото."], - ["Должность и график", "Сотрудник привязан к должности, у должности есть график (смены). Наряд, назначенный на должность, " - "видят и могут взять только те, кто сейчас на смене."], + ["Должность и график", "Сотрудник занимает должность и работает по своему графику (смены). Одну должность могут занимать " + "несколько сменщиков. Наряд, назначенный на должность, видят, берут и получают уведомление только те, " + "кто сейчас на смене, — сменщики дома не беспокоятся."], ["Расписание нарядов", "Наряд настраивается один раз и затем создаётся автоматически: каждый день, по дням недели, " "раз в месяц или каждые N дней."], ["Проверка", "После сдачи наряд уходит проверяющему: он принимает его или возвращает на доработку с комментарием."], @@ -562,7 +563,8 @@ def build(): pdf.bullets([ "для **числа** задайте единицу измерения и границы нормы — отклонения будут подсвечиваться и попадут в отчёт;", "для **списка** — варианты ответа, по одному в строке;", - "для любой задачи — режим фото: без фото, по желанию, обязательно;", + "для любой задачи — режим фото: без фото, по желанию, обязательно; если фото нужно сохранить надолго (табличка, акт, " + "дефект), отметьте **«хранить более года»** — такие снимки лежат в отдельном каталоге и не удаляются автоочисткой сервера;", "стрелками меняется порядок задач, галочкой «Обязательный» отмечаются пункты, без которых наряд не сдать.", ]) pdf.wide("d_checklist", "Редактор чек-листа") @@ -708,6 +710,15 @@ def build(): "В приложении Android история открывается кнопкой «История машины».") pdf.wide("d_history", "История машины с фильтрами") pdf.phone("a_history", "История машины в приложении Android", width=70) + pdf.h2("Документация машины (Nextcloud)") + pdf.p("Если подключён Nextcloud, в карточке машины есть блок **«Документация»**: паспорта, руководства, схемы, хранящиеся " + "в Nextcloud по машинам. Кнопка **«Открыть»** показывает папку машины: слева файлы и вложенные папки, справа — документ. " + "**PDF, картинки и текстовые файлы** открываются прямо в портале, документы **Word, Excel, PowerPoint** — тоже, после перевода в PDF " + "на сервере ONLYOFFICE (первое открытие — несколько секунд). Любой файл можно **скачать**. Заходить в Nextcloud не нужно: " + "портал сам забирает файлы и показывает только документацию машин, которые видит сотрудник.") + pdf.p("**Привязывают папку администратор или модератор**: «Привязать папку» в блоке «Документация» открывает проводник по " + "документации — выберите папку машины и нажмите «Выбрать» или «Привязать эту папку». Рядом с папкой видно, к каким машинам " + "она уже привязана. «Сменить папку» и «Отвязать» — там же.") pdf.h2("Сообщения об отказах извне") pdf.p("Об аварии может сообщить и человек без учётной записи — подрядчик, охрана, смежная служба. Для этого есть **публичная " "форма** по адресу **/report** (её удобно разместить на QR-коде у оборудования). В форме выбирается **место** — после этого " @@ -832,16 +843,22 @@ def build(): "висит приглашение запустить мастер). Мастер проводит по шагам в порядке зависимостей данных:") pdf.bullets([ "**Сервер** — проверить часовой пояс, каталог фото, время сервера.", - "**Графики** — типовые одним нажатием (пятидневка, сутки через трое, 2/2 день-ночь, круглосуточно) или в конструкторе.", + "**Графики** — типовые одним нажатием (пятидневка, сутки через трое, 2/2 день-ночь, круглосуточно) или в конструкторе. " + "График потом назначается каждому сотруднику.", "**Должности → отделы → места → оборудование** — пакетным вводом: одна строка — одна запись, поля через «;». " "Для оборудования: «Название; инв. №; место; наработка; отдел; кран» — отдел закрепляет машину за службой, слово «кран» " "отправляет её в раздел «Краны».", - "**Сотрудники** — строки «ФИО; логин; должность; отдел; роль». Логины и временные пароли создаются сами и показываются " + "**Сотрудники** — строки «ФИО; логин; должность; отдел; график; роль». Логины и временные пароли создаются сами и показываются " "таблицей для печати или CSV — **только один раз**.", "**Карта завода** (и подсказка про отдельную карту кранов), **склады и начальные остатки инструмента**, **чек-листы и расписание**, **внешние обращения**, **резервные копии**.", ]) pdf.wide("d_setup", "Мастер первичной настройки") - pdf.note("Шаг считается выполненным, когда в нём есть записи, или если отметить «шаг выполнен». К мастеру можно возвращаться в любое время.", "info") + pdf.note("Шаг считается выполненным, когда в нём есть записи, или если отметить «шаг выполнен» — мастер сразу переходит к следующему. " + "К мастеру можно возвращаться в любое время.", "info") + pdf.p("Кнопка **«Завершить и проверить»** на последнем шаге открывает **проверку готовности**: мастер смотрит на введённые данные и " + "показывает, что мешает работе — сотрудники без должности, должности, которые никто не занимает, должности, где сейчас по " + "графикам никого нет на смене, машины без места, нет чек-листов или расписания, выключены автокопии или автоочистка фото. " + "Рядом с каждым пунктом — кнопка, чтобы поправить. После завершения плитка мастера ведёт сразу к этой проверке.") pdf.h2("AI-помощник (включает администратор)") pdf.p("В «Настройках сервера» администратор может включить **AI-помощника**: указать провайдера (OpenAI-совместимый — OpenAI, " @@ -855,6 +872,12 @@ def build(): "Ключ API не показывается в настройках и не попадает в резервные копии.", ]) pdf.wide("d_assistant_settings", "Настройка AI-помощника") + pdf.p("**«Работа с документацией»** — отдельная опция помощника в той же карточке. Включённая, она раз в сутки собирает индекс " + "документации машин (имена файлов и начало текста PDF и документов Office), и помощник отвечает на вопросы вида " + "«где схема двигателя компрессора?» ссылкой на нужный файл и подсказкой, что в нём искать.") + pdf.note("Риски опции: имена файлов и фрагменты документов уходят провайдеру ИИ — с облачным провайдером документация покидает " + "предприятие, для закрытых документов используйте локальную модель. Запросы становятся в разы длиннее, расход токенов " + "растёт. Помощник видит только начало документов — ответ проверяйте в самом файле.", "warn") pdf.wide("d_assistant", "Помощник: ответ по данным пользователя и подсказка «как сделать»") pdf.phone("a_assistant", "Помощник в приложении Android (круглая кнопка справа внизу или пункт меню «Помощник»)", width=70) @@ -883,9 +906,13 @@ def build(): "пользователя. Остальным администраторам мастер первичной настройки и резервные копии доступны как обычно.") pdf.h2("Должности и графики работы") - pdf.p("**Должность** имеет тип (**техник** выполняет наряды, **инженер** проверяет и планирует) и **график**. По графику " - "система определяет, кто сейчас «на смене» и кому показывать наряды, назначенные на должность. Должность без графика " - "считается всегда на смене.") + pdf.p("**Должность** имеет тип (**техник** выполняет наряды, **инженер** проверяет и планирует). **График** задаётся не должности, " + "а **сотруднику** («Пользователи» → «Изменить» → «График работы»): должность выполняет тот, кто её занимает и сейчас на смене. " + "Например, «Дежурный механик» — четыре сменщика с графиком «сутки через трое», каждый со своим первым днём цикла: наряд или " + "задание на эту должность увидит и получит уведомление только тот, кто сегодня дежурит. Сотрудник без графика считается " + "всегда на смене.") + pdf.note("При обновлении портала графики, которые раньше были заданы должностям, автоматически перенесены их сотрудникам. " + "Для сменщиков поправьте графики (или первый день цикла), чтобы на смене был один человек.", "info") pdf.p("Графики настраиваются **конструктором**: можно собрать любой цикл — дневные и ночные смены, отсыпные, сутки через трое. " "Каждый шаг цикла — это «Смена» или «Выходной», число дней подряд, время начала и конца и метка (например, «день», " "«ночь», «отсыпной»). Если конец смены раньше начала (20:00–08:00), смена ночная; одинаковое время (08:00–08:00) — сутки.") @@ -901,6 +928,8 @@ def build(): pdf.p("Разделы **«Оборудование»** и **«Места»** — справочники для нарядов и отчётов. Оборудование можно привязать к месту " "(например, «Цех №1»), указать инвентарный номер, **отдел** (кто его видит) и отметку **«Кран»**. Неиспользуемое оборудование " "отключается флажком «Активно».") + pdf.p("Над списком оборудования (и кранов) — фильтр **«Место»**: выберите цех или участок, и в списке останутся машины этого " + "места и всех вложенных в него мест (например, «Цех №1» покажет и машины «Компрессорной», входящей в цех).") pdf.h2("Обратная связь по порталу") pdf.p("Внизу каждой страницы сайта — неприметная ссылка **«Предложить улучшение портала или сообщить об ошибке»**; в мобильной версии и " @@ -914,6 +943,15 @@ def build(): pdf.p("Раздел **«Настройки сервера»** позволяет изменить каталог для хранения фотографий и максимальный размер снимка, а также " "ключ для мгновенных уведомлений Android, включить публичную форму заявок и **«Краны — отдельная служба»**. Внизу виден " "расход места: сколько фото хранится и сколько свободно на диске.") + pdf.p("**Автоочистка фото**: поле «удалять фото нарядов и заданий старше, дней» (например, 365). Раз в сутки сервер удаляет " + "фото старше этого срока; 0 — не удалять. Снимки с отметкой **«хранить более года»** (в чек-листе у пункта или в задании) " + "с самого начала лежат в отдельном каталоге **keep/** и не удаляются. В блоке «Состояние» видно, сколько фото будет удалено " + "при следующей очистке; кнопка «Очистить сейчас» запускает её сразу.") + pdf.note("Удалённые фото не восстановить, кроме как из резервной копии с фото. По умолчанию автоочистка выключена.", "warn") + pdf.h2("Мониторинг сервера (только администратор портала)") + pdf.p("«Администрирование → **Мониторинг сервера**»: круговые диаграммы загрузки **процессора**, занятой **памяти** и места на " + "**дисках** с данными портала и фото. Значения обновляются каждые 5 секунд, пока страница открыта; от 75 % диаграмма " + "становится жёлтой, от 90 % — красной. В Docker с лимитом памяти показывается память контейнера.") pdf.wide("d_server", "Настройки сервера", width=150) pdf.note("При смене каталога фото старые снимки автоматически **не переносятся** — скопируйте файлы вручную.", "warn") @@ -935,6 +973,19 @@ def build(): pdf.note("Рабочие данные в демо-режиме не изменяются и не удаляются.", "info") # ================= 7 ================= + pdf.h2("Подключение Nextcloud и ONLYOFFICE") + pdf.p("«Настройки сервера» → **«Документация из Nextcloud»** (только администратор портала).") + pdf.bullets([ + "В Nextcloud заведите служебного пользователя (например, portal-docs), дайте ему **доступ только на чтение** к папке " + "с документацией и создайте для него **пароль приложения** (Настройки → Безопасность).", + "В портале укажите адрес Nextcloud (лучше внутренний, в локальной сети), учётную запись, пароль приложения и корневую папку " + "документации. Кнопка «Проверить» покажет, сколько папок видно в корне.", + "Для документов Office укажите адрес **ONLYOFFICE Document Server** в локальной сети, его **секрет JWT** (если включён) и " + "**внутренний адрес портала** — по нему ONLYOFFICE забирает исходник по одноразовой ссылке. Без ONLYOFFICE документы " + "Office только скачиваются.", + "Сотрудникам Nextcloud не нужен: портал сам ходит в него и проверяет, кто какую машину видит. Пароль приложения и секрет " + "JWT не показываются в настройках и не попадают в резервные копии.", + ]) pdf.h1("14. Частые вопросы") faq = [ ("Я не вижу машину (или кран), которая точно есть.", @@ -944,8 +995,8 @@ def build(): "Вы в демонстрационном режиме — это тестовые данные. Нажмите **«Выйти из демо»** в полосе. Если кнопки нет или не помогает, " "попросите администратора выключить демо для вас."), ("Я не вижу наряд, который мне должны были назначить.", - "Наряд, назначенный на должность, виден только сотрудникам этой должности **на смене**. Проверьте график смены и должность в " - "настройках; если вы вне смены — наряд появится, когда смена начнётся. Нажмите обновление списка. Если наряд назначен " + "Наряд, назначенный на должность, виден только сотрудникам этой должности **на смене** по их графику. Проверьте свой график " + "и должность в карточке; если вы вне смены — наряд появится, когда смена начнётся. Нажмите обновление списка. Если наряд назначен " "другому человеку или уже взят коллегой, вы его не увидите."), ("Пишет «Нет связи» или «не отправлено» и не исчезает.", "Проверьте интернет на телефоне. Данные сохранены и не пропадут: они уйдут сами. Для немедленной попытки нажмите кнопку "