Each call into opencloud_token_store is wrapped in its own asyncio.run()
(app/app_config.py, main.py's token popover), so every call gets a fresh
event loop. The asyncpg connection pool is bound to the loop it was created
in, so the next asyncio.run() could try to reuse a connection from an
already-closed loop and fail — unrelated to actual DB availability, but the
broad except Exception in resolve_opencloud_access_token() silently
swallowed it and fell through to "token not found", masking the real cause.
- add_token/get_token/remove_token/list_usernames now call engine.dispose()
in a finally block so each asyncio.run() starts with a clean pool.
- resolve_opencloud_access_token() now surfaces the real exception via
st.error() before falling back to the .env token (kept only for local dev
without a DB).
- Added tests/unit/test_opencloud_token_store.py covering that dispose() is
called after every store operation, including when commit() raises.
- Drop SMB/SAMBA subsystem completely: app/samba_access.py, app/path_settings.py,
app/path_store.py, app/pipeline/file_manager.py and their tests, plus the
smbprotocol dependency and all related UI (login/password fields, path editor,
SMB diagnostics) in main.py.
- run_batch now takes a .zip attached via Streamlit's file_uploader (same as the
local dev tool) instead of reading from SMB or a fictitious OpenCloud "Attach"
folder; renamed from run_local_zip_batch since it's now the single canonical
batch entrypoint used by both main.py and scripts/local_zip_test_app.py.
- Fixed run number resolution: added next_opencloud_run_number() which lists the
Out/year/month/day folder via WebDAV PROPFIND, so repeated runs land in
incrementing subfolders instead of always overwriting "1".
- Added a self-service "Добавить токен" popover in main.py so users can link
their own OpenCloud App Token to their Keycloak account (opencloud_token_store)
without an admin running the CLI script; reworded the missing-token error to
point at it.
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.