SFM/app/app_config.py
Raykov-MS f47953273c Switch OpenCloud uploads to per-user personal drives
Files no longer go into a shared test space with a guessed folder name.
Instead, resolve each user's personal drive id via GET /graph/v1.0/me/drives
(Basic Auth, same App Token as uploads) on every run, and upload into
Out/год/месяц/день/номер_запуска inside that drive, creating the path via
MKCOL since a fresh personal drive won't have it yet.

Removes the now-obsolete OPENCLOUD_SPACE_ID / resolve_opencloud_user_folder
and the shared-space smoke script.
2026-09-17 13:37:51 +03:00

76 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import os
from pathlib import Path
from tempfile import gettempdir
import streamlit as st
SFM_SMB_BASE_PATH = "//sgo-fc01-r06/sfm/ОТЧЕТ_7081У"
SFM_DEFAULT_INPUT_DIR = r"//sgo-fc01-r06/sfm/ОТЧЕТ_7081У"
ACCESS_SUPPORT_CONTACT = "администратору СФМ"
EDIT_PATH_OPEN_KEY = "edit_path_open"
EDIT_PATH_VALUE_KEY = "edit_path_value"
# Runtime-only user paths storage. In bank contour we use tmpfs.
PATH_STORE_FILE = Path(gettempdir()) / "user_paths.json"
def resolve_user_id() -> str:
profile = st.session_state.get("user_profile", {})
if not isinstance(profile, dict):
return "anonymous|anonymous"
email = str(
profile.get("email") or profile.get("preferred_username") or "anonymous"
)
full_name = str(profile.get("name") or profile.get("full_name") or "anonymous")
return f"{email}|{full_name}"
def resolve_opencloud_username() -> str | None:
"""preferred_username из Keycloak — им же заказчик регистрировал App Token
в веб-интерфейсе OpenCloud (сверено вручную, совпадает без домена/регистра).
Для локального теста (mock-провайдер) — fallback на .env.
"""
profile = st.session_state.get("user_profile", {})
if isinstance(profile, dict):
value = profile.get("preferred_username")
if value:
return str(value)
return os.getenv("SFM_OPENCLOUD_USERNAME") or None
def resolve_opencloud_access_token() -> str | None:
"""Достаёт App Token заказчика для авторизации в OpenCloud (Basic Auth).
ИСТОРИЯ (см. app/opencloud_access.py): изначально пытались использовать
Bearer ID-токен Keycloak-клиента datalab — Graph API OpenCloud отвечал
401 (несовпадение audience). Подтверждено на проде 16.09: рабочий путь —
HTTP Basic Auth с username (resolve_opencloud_username) и долгоживущим
App Token, который заказчик сам создаёт в веб-интерфейсе OpenCloud.
Токены хранятся зашифрованными в БД (app/opencloud_token_store.py),
заказчики присылают их нам сами при онбординге.
Приложение синхронное (Streamlit) — обёртка asyncio.run вокруг async
store оправдана только частотой вызова (раз на запуск обработки), при
росте нагрузки стоит пересмотреть.
"""
username = resolve_opencloud_username()
if not username:
return None
import asyncio
from app.opencloud_token_store import OpenCloudTokenStoreError, get_token
try:
return asyncio.run(get_token(username))
except OpenCloudTokenStoreError as exc:
st.error(f"Ошибка доступа к токену OpenCloud: {exc}")
return None
except Exception: # noqa: BLE001
# Локальный тест без поднятой БД/переменных окружения — fallback.
return os.getenv("SFM_OPENCLOUD_ACCESS_TOKEN") or None