{"id":50548325,"url":"https://github.com/mergi72/dms-provider-bridge","last_synced_at":"2026-06-07T23:01:36.429Z","repository":{"id":362376052,"uuid":"1255385626","full_name":"mergi72/dms-provider-bridge","owner":"mergi72","description":"Provider bridge service for DMS systems such as eDoCat, Alfresco and FSO, with WFX API support.","archived":false,"fork":false,"pushed_at":"2026-06-03T23:12:39.000Z","size":199,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T00:13:48.473Z","etag":null,"topics":["alfresco","dms","document-management","edocat","fastapi","python","rest-api","total-commander","wfx"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mergi72.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-31T19:06:26.000Z","updated_at":"2026-06-03T23:10:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mergi72/dms-provider-bridge","commit_stats":null,"previous_names":["mergi72/dms-provider-bridge"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mergi72/dms-provider-bridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mergi72%2Fdms-provider-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mergi72%2Fdms-provider-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mergi72%2Fdms-provider-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mergi72%2Fdms-provider-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mergi72","download_url":"https://codeload.github.com/mergi72/dms-provider-bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mergi72%2Fdms-provider-bridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33886153,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alfresco","dms","document-management","edocat","fastapi","python","rest-api","total-commander","wfx"],"created_at":"2026-06-04T01:00:22.040Z","updated_at":"2026-06-07T23:01:36.349Z","avatar_url":"https://github.com/mergi72.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dms-provider-bridge\n\n[![CI](https://github.com/mergi72/dms-provider-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/mergi72/dms-provider-bridge/actions/workflows/ci.yml)\n[![Status](https://img.shields.io/badge/Status-Alpha-orange)](https://github.com/mergi72/dms-provider-bridge)\n[![Bridge Version](https://img.shields.io/badge/Bridge-v0.4.3-blue)](https://github.com/mergi72/dms-provider-bridge/releases/tag/v0.4.3)\n[![Bridge Setup](https://img.shields.io/badge/Setup-v0.4.3-blueviolet)](https://github.com/mergi72/dms-provider-bridge/releases/tag/v0.4.3)\n\nCurrent development branch: `develop`  \nStable release branch: `main`\n\n`dms-provider-bridge` is a base bridge service skeleton for integrating multiple DMS providers (eDoCat, Alfresco, FSO, ...).\n\nCurrent release mapping:\n\n- Bridge repository latest changelog version: `0.4.3`\n- Latest bridge-only release: `v0.4.3`\n\n## Runtime Modes\n\nBridge can be run in two different Windows runtime models. Keep them separate:\n\n- TC user mode:\n  - Intended for Total Commander / TC-WFX interactive usage.\n  - Runs under the logged-in Windows user, for example from a user startup task.\n  - Can access that user's Windows Credential Manager entries.\n  - This is the right model for `credential_id` values created by the TC-WFX plugin.\n- Service mode:\n  - Intended for server-style local bridge usage.\n  - Runs as the `DMSProviderBridge` Windows Service via NSSM.\n  - Current `v0.4.3` installer installs this service as `LocalSystem`.\n  - `LocalSystem` cannot see credentials stored in an interactive user's Windows Credential Manager.\n\nCurrent setup release `v0.4.3` is the Service mode installer. TC user mode remains a separate runtime model for scenarios where user-scoped credentials are required.\n\n## Related Projects\n\n- `tc-wfx-plugin`\n- `dms-provider-installer`\n\n## Quick Start\n\n```bash\npython -m venv .venv312\n.venv312\\\\Scripts\\\\activate\npip install -e .\npython -m uvicorn dms_provider_bridge.app.server:app --host 127.0.0.1 --port 8765\n```\n\nVerify bridge:\n\n- Health: [http://127.0.0.1:8765/health](http://127.0.0.1:8765/health)\n- Swagger UI: [http://127.0.0.1:8765/docs](http://127.0.0.1:8765/docs)\n- OpenAPI: [http://127.0.0.1:8765/openapi.json](http://127.0.0.1:8765/openapi.json)\n\nThe Swagger UI is the fastest way to:\n\n- test provider connectivity\n- test credentials\n- browse providers\n- validate request payloads before integrating clients\n\nStandalone executable quick run:\n\n```powershell\n.\\dist\\dms-provider-bridge.exe\n```\n\n## Testing the Bridge\n\nUse these endpoints first when diagnosing install/config/auth problems:\n\n- Swagger UI: [http://127.0.0.1:8765/docs](http://127.0.0.1:8765/docs)\n- OpenAPI: [http://127.0.0.1:8765/openapi.json](http://127.0.0.1:8765/openapi.json)\n- Health: [http://127.0.0.1:8765/health](http://127.0.0.1:8765/health)\n\nMost recent bridge issues were diagnosable directly through Swagger UI without adding debug code, including credential resolution, LocalSystem visibility, root path handling, and provider config problems.\n\n## VS Code (Windows)\n\nThe workspace is preconfigured to use the `.venv312` interpreter in `.vscode/settings.json`.\n\n## Tests\n\nInstall test dependencies:\n\n```bash\npython -m pip install -e .[dev]\n```\n\nQuick run (PowerShell):\n\n```powershell\n.\\scripts\\run-tests.ps1 unit\n.\\scripts\\run-tests.ps1 integration\n.\\scripts\\run-tests.ps1 all\n```\n\nQuick run (Bash, for example Git Bash/WSL):\n\n```bash\n./scripts/run-tests.sh unit\n./scripts/run-tests.sh integration\n./scripts/run-tests.sh all\n```\n\nNote: both scripts look for the interpreter in this order: `.venv312`, `.venv`, and finally system `python`/`python3` from PATH.\n\n## Clean Release ZIP (No Cache)\n\nFor release packaging, use Git archive so only tracked files are included:\n\n```powershell\n.\\scripts\\build-release-zip.ps1\n```\n\nThis workflow automatically excludes local artifacts such as `__pycache__/`, `*.pyc`, `.venv/`, and runtime logs.\n\nIf you want to clean the local workspace before that, run:\n\n```powershell\n.\\scripts\\clean-artifacts.ps1\n```\n\n## Build bridge.exe (Windows)\n\nFor a release executable build (PyInstaller onefile), run:\n\n```powershell\n.\\scripts\\build-bridge.ps1\n```\n\nOutput:\n\n- `dist/dms-provider-bridge.exe`\n\nAlternative build (onedir layout) is still available:\n\n```powershell\n.\\scripts\\build-bridge-exe.ps1\n```\n\nQuick health check:\n\n```powershell\nInvoke-RestMethod -Uri http://127.0.0.1:8765/health\n```\n\n## Safe Testing (ENV)\n\nFor local smoke testing, avoid hardcoded passwords in shell history. Put them into environment variables and build payloads from them.\n\nPowerShell:\n\n```powershell\n$env:BRIDGE_USER = \"user@domain\"\n$env:BRIDGE_PASSWORD = \"secret\"\n\n$body = @{\n  path = \"edocat:/deals\"\n  auth = @{\n    mode = \"credentials\"\n    username = $env:BRIDGE_USER\n    password = $env:BRIDGE_PASSWORD\n  }\n} | ConvertTo-Json -Depth 10\n\nInvoke-RestMethod -Method Post -Uri http://127.0.0.1:8765/bridge/wfx/list -ContentType \"application/json\" -Body $body\n```\n\nBash:\n\n```bash\nexport BRIDGE_USER='user@domain'\nexport BRIDGE_PASSWORD='secret'\n\ncurl -sS http://127.0.0.1:8765/bridge/wfx/list \\\n  -H 'Content-Type: application/json' \\\n  -d \"{\\\"path\\\":\\\"edocat:/deals\\\",\\\"auth\\\":{\\\"mode\\\":\\\"credentials\\\",\\\"username\\\":\\\"$BRIDGE_USER\\\",\\\"password\\\":\\\"$BRIDGE_PASSWORD\\\"}}\"\n```\n\n## WFX Bridge API (for C# plugin)\n\n## Config Layers\n\nConfiguration is loaded from two fixed Windows scopes:\n\n- Machine config: `%ProgramData%\\DMS Provider\\config`\n- User config: `%APPDATA%\\DMS Provider\\config`\n\nThe machine config is authoritative. For each config file, the bridge loads the machine JSON first, then merges the matching user `*.local.json` only if the machine JSON exists.\n\n- Bridge/system config: `bridge.json`\n- Provider config (`edocat`, `alfresco`, `fso`, ...): `\u003cprovider\u003e.json`\n- User overrides: `bridge.local.json`, `\u003cprovider\u003e.local.json`\n\nUser `*.local.json` values override existing machine keys and may add missing keys. If the machine JSON file is missing, the matching user `*.local.json` is ignored.\n\nFor local development, set `DMS_PROVIDER_MACHINE_CONFIG_DIR` and optionally `DMS_PROVIDER_USER_CONFIG_DIR` to test config directories.\n\nRemote path format:\n\n- `edocat:/folder/file.txt`\n- `alfresco:/folder/file.txt`\n\nEndpoints:\n\n- `GET /bridge/wfx/providers` (provider discovery for root listing in the WFX plugin)\n\n- `POST /bridge/wfx/list`\n- `POST /bridge/wfx/stat`\n- `POST /bridge/wfx/mkdir`\n- `POST /bridge/wfx/delete`\n- `POST /bridge/wfx/move`\n- `POST /bridge/wfx/copy`\n- `POST /bridge/wfx/download`\n- `POST /bridge/wfx/download-raw`\n- `POST /bridge/wfx/upload`\n- `POST /bridge/wfx/upload-raw`\n- `POST /bridge/wfx/upload-stream` (alias for `upload-raw`)\n- `POST /bridge/wfx/resolve-share-url`\n- `POST /bridge/wfx/browse-share-url`\n- `POST /bridge/wfx/browse-share-url-validate`\n\nAuthentication (`auth`) is required for every call:\n\nThe bridge uses one incoming authentication model for all providers. Provider specific upstream HTTP authentication is resolved inside the provider implementation.\n\n- `credentials`:\n  `{ \"auth\": { \"mode\": \"credentials\", \"credential_id\": \"edocat-prod\" } }`\n  or\n  `{ \"auth\": { \"mode\": \"credentials\", \"username\": \"user\", \"password\": \"secret\" } }`\n\n  `credential_id` is read from Windows Credential Manager. For regular credentials, use a generic credential with `UserName` and a secret in the credential blob; if the blob contains JSON, the bridge can also read `username`, `password`, `token`, and optional `base_url`.\n- `winuser`:\n  `{ \"auth\": { \"mode\": \"winuser\", \"win_user\": \"DOMAIN\\\\user\" } }`\n\nExample request:\n\n`{ \"path\": \"edocat:/\", \"auth\": { \"mode\": \"winuser\", \"win_user\": \"DOMAIN\\\\user\" } }`\n\nResponse uses a unified shape:\n\n- `ok` (`true/false`)\n- `error_code` (`0` = OK)\n- `message` (error text)\n- `data` (operation payload)\n- `metadata.provider` (active provider)\n- `metadata.upstream_auth_scheme` (for example `basic`, `ticket`)\n- `metadata.upstream_endpoint` (actual upstream endpoint for the provider)\n\nExecution mode notes:\n\n- If the bridge obtains usable upstream credentials or ticket, operations run in `live` mode.\n- If credentials or ticket are not available, the bridge returns a safe `preview` result (including exact endpoint) so the WFX layer can see what would be called.\n\nTransfer operations:\n\n- `download`: `{ \"path\": \"alfresco:/contracts/sample.txt\", \"auth\": { ... } }`\n- `upload`: `{ \"destination\": \"alfresco:/contracts\", \"file_name\": \"upload.txt\", \"content_base64\": \"...\", \"overwrite\": true, \"auth\": { ... } }`\n  - Intended only for small test payloads (`upload.inline.maxBytes`, default `4194304` = 4 MB).\n  - For larger files use `upload-raw` / `upload-stream`.\n- `upload-raw` / `upload-stream`: multipart form-data with fields `destination`, `file_name`, `overwrite`, `auth_json`, and binary field `file`\n\nRaw upload limits:\n\n- Config key: `upload.raw.maxBytes` in machine `bridge.json` (can be overridden by user `bridge.local.json`)\n- Default: `536870912` (512 MB)\n- Stream chunk size key: `upload.raw.chunkBytes` (clamped to 1-4 MB, default `1048576` = 1 MB)\n- Bridge rejects larger payloads before provider upload and logs `bytes`, `max_bytes`, and `duration_ms`\n\nAlfresco upload transport:\n\n- Raw upload writes incoming multipart file to a temporary file in chunks (1-4 MB policy).\n- Bridge then forwards this file to Alfresco as `multipart/form-data` using streamed file chunks.\n- Large uploads avoid `content_base64` in the transfer path.\n\neDoCat Share URL to bridge path conversion:\n\n- `resolve-share-url`: `{ \"share_url\": \"https://.../documentlibrary#/Team%20Documents/Upload?page=1\", \"provider\": \"alfresco\" }`\n- Response returns `data.path` in `alfresco:/...` format, usable for `list/stat/copy/...`.\n\nOne-shot browse via Share URL:\n\n- `browse-share-url`: `{ \"share_url\": \"https://.../documentlibrary#/Team%20Documents/Upload?page=1\", \"provider\": \"alfresco\", \"operation\": \"list|stat|download|copy|move|mkdir|delete|upload\", \"execute\": true, \"auth\": { ... }, \"provider_path_override\": \"/optional/manual/path\", \"destination_share_url\": \"https://...\", \"destination_path_override\": \"/target/path\", \"file_name\": \"upload.txt\", \"content_base64\": \"...\", \"overwrite\": true }`\n- `browse-share-url` is the canonical endpoint; for dry-run validation, use the same endpoint with `execute=false`.\n- Response includes `data.resolved` (URL resolution result), `data.path_source` (`share_url` or `provider_path_override`), and `data.result` (selected operation result).\n- For `copy|move`, `destination_path_override` or `destination_share_url` is additionally required; response contains `data.destination`.\n- For `upload`, `file_name` is required; target can be provided via `destination_path_override` or `destination_share_url`, otherwise the path resolved from `share_url` is used.\n- OpenAPI operationId: `bridgeResolveShareUrl`, `bridgeBrowseShareUrl` (canonical), deprecated alias: `bridgeBrowseShareUrlValidateDeprecated`.\n- If `execute=false`, the endpoint performs no action and returns dry-run validation with the same logic as `browse-share-url-validate`.\n\nLightweight validation without operation execution:\n\n- `browse-share-url-validate`: same inputs as `browse-share-url`, but without `auth` and without `content_base64`.\n- Endpoint returns computed `source` and `destination` paths and payload validation errors without calling provider operations.\n- Internally this is an alias to `browse-share-url` with `execute=false` (single shared implementation path).\n- In OpenAPI this endpoint is marked as deprecated; preferred endpoint is `browse-share-url` with `execute=false`.\n\n`download` payload notes:\n\n- `/bridge/wfx/download` always returns JSON contract (`ok`, `error_code`, `data`).\n- `/bridge/wfx/download-raw` returns raw file bytes (`Content-Disposition` + `Content-Type`).\n- In `live` mode, JSON `download` includes `data.content_base64`, `data.mime_type`, and `data.size`.\n- In `preview` mode, those fields are `null` and target endpoint is provided in `data.message`.\n\nFSO security notes:\n\n- FSO provider supports local path restrictions via `allowedRoots` in `config/fso.json`.\n- Operations outside these roots are blocked (`ProviderOperationError`).\n- The packaged default is an empty list because FSO is disabled until you choose explicit local roots.\n- For local environments, set your own absolute paths in `fso.local.json` (Windows example):\n\n```json\n{\n  \"key\": \"fso\",\n  \"fso\": {\n    \"allowedRoots\": [\n      \"C:/Users/\u003cuser\u003e/Documents\"\n    ]\n  }\n}\n```\n\nAlfresco performance notes:\n\n- The client contains in-memory cache for doc library resolution, child lookup, and path resolution.\n- Repeated calls for the same Alfresco path are significantly faster than the first cold lookup.\n\n## Runbook\n\nRestart local server:\n\n```powershell\n$conn = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 8765 -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Listen' }\nif ($conn) { Stop-Process -Id $conn.OwningProcess -Force }\n.\\.venv312\\Scripts\\python.exe -m uvicorn dms_provider_bridge.app.server:app --app-dir src --host 127.0.0.1 --port 8765\n```\n\nHealth check:\n\n```powershell\nInvoke-RestMethod -Method Get -Uri http://127.0.0.1:8765/health | ConvertTo-Json -Depth 10\n```\n\nDiagnostics when the same file keeps appearing (typically \"welcome.pdf\"):\n\n- Verify you are calling `POST /bridge/wfx/list` (not legacy `GET /listing`).\n- Verify provider format is `provider=edocat`, not `provider=edocat:`.\n- Verify payload sends correct provider-prefixed `path` (`edocat:/...` or `alfresco:/...`) and `auth`.\n- For Alfresco, first call may be slower; repeated call should be faster (warm cache).\n\n## Structure\n\nProject is split into:\n\n- `app/` API layer\n- `services/` business logic\n- `providers/` provider implementations\n- `clients/` API clients\n- `models/` data models\n- `core/` configuration, logging, and core utilities\n\n## License\n\nThe project is licensed under the MIT License. Full text is available in `LICENSE`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmergi72%2Fdms-provider-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmergi72%2Fdms-provider-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmergi72%2Fdms-provider-bridge/lists"}